[Feature][ZXW-265]merge P56U03 version

Only Configure: No
Affected branch: master
Affected module: unknow
Is it affected on both ZXIC and MTK: only ZXIC
Self-test: Yes
Doc Update: No

Change-Id: I873f6df64e2605a77b8b8bfec35b21e7f33c5444
diff --git a/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/include/mach/iomap.h b/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/include/mach/iomap.h
old mode 100644
new mode 100755
index 890d139..c721782
--- a/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/include/mach/iomap.h
+++ b/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/include/mach/iomap.h
@@ -247,6 +247,7 @@
 

 #define SECURE_PUK_BASE						(POWERON_TYPE_BASE + 0xc)/*secure pub key 256byte*/

 #define WDT_IRAM_BASE						(SECURE_PUK_BASE + 0x100)/*wdt iram flag 36byte*/

+#define MMC_CFG_BASE						(WDT_IRAM_BASE+0x34)/*mmc flag 4Byte*/

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

  *                        iram: tsc

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

diff --git a/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/include/mach/spinlock.h b/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/include/mach/spinlock.h
old mode 100644
new mode 100755
index c50656d..21fb2d9
--- a/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/include/mach/spinlock.h
+++ b/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/include/mach/spinlock.h
@@ -82,7 +82,7 @@
 #else

 	SFLOCK_ID9,

 #endif

-	SFLOCK_ID10,

+	EFUSE_SFLOCK,

 	SFLOCK_ID11,

 	SFLOCK_ID12,

 	SFLOCK_ID13,

diff --git a/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/pwr_ctrl.c b/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/pwr_ctrl.c
index 3244e59..f8bf4c1 100755
--- a/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/pwr_ctrl.c
+++ b/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/pwr_ctrl.c
@@ -253,7 +253,7 @@
 EXPORT_SYMBOL(ssv6x5x_wifi_enable);
 #endif
 
-#if defined (CONFIG_AIC8800)
+#if (defined CONFIG_AIC8800 || defined CONFIG_AIC8800D80L)
 
 void aic8800_wifi_enable(int bval)
 {	
@@ -325,7 +325,7 @@
 }
 EXPORT_SYMBOL(aic8800_wifi_enable);
 #endif
-#if defined (CONFIG_AIC8800)
+#if (defined CONFIG_AIC8800 || defined CONFIG_AIC8800D80L)
 void aic8800_wifi_disable(int bval)
 {	
 	int ret = 0;
@@ -365,7 +365,7 @@
 }
 EXPORT_SYMBOL(aic8800_wifi_disable);
 #endif
-#if defined (CONFIG_AIC8800)
+#if (defined CONFIG_AIC8800 || defined CONFIG_AIC8800D80L)
 void aic8800_wifi_re_enable(int bval)
 {	
 	int ret = 0;
diff --git a/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/reset.c b/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/reset.c
index 777dfbf..e8c891c 100755
--- a/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/reset.c
+++ b/ap/os/linux/linux-3.4.x/arch/arm/mach-zx297520v3/reset.c
@@ -30,7 +30,7 @@
 #include <mach/spinlock.h>
 #include <linux/mfd/zx234290.h>
 #include <linux/delay.h>
-
+#include <linux/reboot.h>
 
 #define ZX_RESET_DEV    "/dev/zx_reset"
 
@@ -84,6 +84,10 @@
 extern void wdt_restart(void);
 #endif
 
+extern void zx_denali_nand_lock(void);
+extern void zx_denali_nand_unlock(void);
+
+
 void zx29_restart(char mode, const char *cmd)
 {
 /* change to "#ifdef CONFIG_MFD_ZX234290/CONFIG_MFD_ZX234290_I2C" later! */
@@ -202,7 +206,14 @@
 	switch(cmd)
 	{
 		case ZX_RESET_FAST_REBOOT:
-			zx29_restart	(NULL, "at set fast reboot!\n\n");
+			//zx29_restart	(NULL, "at set fast reboot!\n\n");
+#ifndef CONFIG_MTD_SPI_NOR
+			zx_denali_nand_lock();	
+#endif
+			kernel_restart("at set fast reboot!\n\n");
+#ifndef CONFIG_MTD_SPI_NOR
+			zx_denali_nand_unlock();
+#endif
 			break;
 			
 		default:
diff --git a/ap/os/linux/linux-3.4.x/arch/arm/mm/fault.c b/ap/os/linux/linux-3.4.x/arch/arm/mm/fault.c
index 92b9163..d53f1cd 100755
--- a/ap/os/linux/linux-3.4.x/arch/arm/mm/fault.c
+++ b/ap/os/linux/linux-3.4.x/arch/arm/mm/fault.c
@@ -21,12 +21,12 @@
 #ifdef CONFIG_MMU
 #ifdef CONFIG_KPROBES
 static inline int notify_page_fault(struct pt_regs*regs,unsigned int fsr){int 
-ret=(0x180+4391-0x12a7);if(!user_mode(regs)){preempt_disable();if(kprobe_running
-()&&kprobe_fault_handler(regs,fsr))ret=(0x1da7+1721-0x245f);preempt_enable();}
+ret=(0x687+378-0x801);if(!user_mode(regs)){preempt_disable();if(kprobe_running()
+&&kprobe_fault_handler(regs,fsr))ret=(0x222c+983-0x2602);preempt_enable();}
 return ret;}
 #else
 static inline int notify_page_fault(struct pt_regs*regs,unsigned int fsr){return
-(0xf+3053-0xbfc);}
+(0x126a+4944-0x25ba);}
 #endif
 void show_pte(struct mm_struct*mm,unsigned long addr){pgd_t*pgd;if(!mm)mm=&
 init_mm;printk(KERN_ALERT"\x70\x67\x64\x20\x3d\x20\x25\x70" "\n",mm->pgd);pgd=
@@ -34,10 +34,10 @@
 "\x5b\x25\x30\x38\x6c\x78\x5d\x20\x2a\x70\x67\x64\x3d\x25\x30\x38\x6c\x6c\x78",
 addr,(long long)pgd_val(*pgd));do{pud_t*pud;pmd_t*pmd;pte_t*pte;if(pgd_none(*pgd
 ))break;if(pgd_bad(*pgd)){printk("\x28\x62\x61\x64\x29");break;}pud=pud_offset(
-pgd,addr);if(PTRS_PER_PUD!=(0xbba+5234-0x202b))printk(
+pgd,addr);if(PTRS_PER_PUD!=(0x51+2919-0xbb7))printk(
 "\x2c\x20\x2a\x70\x75\x64\x3d\x25\x30\x38\x6c\x6c\x78",(long long)pud_val(*pud))
 ;if(pud_none(*pud))break;if(pud_bad(*pud)){printk("\x28\x62\x61\x64\x29");break;
-}pmd=pmd_offset(pud,addr);if(PTRS_PER_PMD!=(0xb67+1542-0x116c))printk(
+}pmd=pmd_offset(pud,addr);if(PTRS_PER_PMD!=(0x101+584-0x348))printk(
 "\x2c\x20\x2a\x70\x6d\x64\x3d\x25\x30\x38\x6c\x6c\x78",(long long)pmd_val(*pmd))
 ;if(pmd_none(*pmd))break;if(pmd_bad(*pmd)){printk("\x28\x62\x61\x64\x29");break;
 }if(PageHighMem(pfn_to_page(pmd_val(*pmd)>>PAGE_SHIFT)))break;pte=pte_offset_map
@@ -47,18 +47,18 @@
 printk("\x2c\x20\x2a\x70\x70\x74\x65\x3d\x25\x30\x38\x6c\x6c\x78",(long long)
 pte_val(pte[PTE_HWTABLE_PTRS]));
 #endif
-pte_unmap(pte);}while((0x10f8+759-0x13ef));printk("\n");}
+pte_unmap(pte);}while((0x505+7273-0x216e));printk("\n");}
 #else					
 void show_pte(struct mm_struct*mm,unsigned long addr){}
 #endif					
 static void __do_kernel_fault(struct mm_struct*mm,unsigned long addr,unsigned 
 int fsr,struct pt_regs*regs){if(fixup_exception(regs))return;bust_spinlocks(
-(0x148f+3042-0x2070));printk(KERN_ALERT
+(0xcd4+2857-0x17fc));printk(KERN_ALERT
 "\x55\x6e\x61\x62\x6c\x65\x20\x74\x6f\x20\x68\x61\x6e\x64\x6c\x65\x20\x6b\x65\x72\x6e\x65\x6c\x20\x25\x73\x20\x61\x74\x20\x76\x69\x72\x74\x75\x61\x6c\x20\x61\x64\x64\x72\x65\x73\x73\x20\x25\x30\x38\x6c\x78" "\n"
 ,(addr<PAGE_SIZE)?
 "\x4e\x55\x4c\x4c\x20\x70\x6f\x69\x6e\x74\x65\x72\x20\x64\x65\x72\x65\x66\x65\x72\x65\x6e\x63\x65"
 :"\x70\x61\x67\x69\x6e\x67\x20\x72\x65\x71\x75\x65\x73\x74",addr);show_pte(mm,
-addr);die("\x4f\x6f\x70\x73",regs,fsr);bust_spinlocks((0x36f+1475-0x932));
+addr);die("\x4f\x6f\x70\x73",regs,fsr);bust_spinlocks((0x1d04+1134-0x2172));
 do_exit(SIGKILL);}static void __do_user_fault(struct task_struct*tsk,unsigned 
 long addr,unsigned int fsr,unsigned int sig,int code,struct pt_regs*regs){struct
  siginfo si;
@@ -69,7 +69,7 @@
 ,tsk->comm,sig,addr,fsr);show_pte(tsk->mm,addr);show_regs(regs);}
 #endif
 tsk->thread.address=addr;tsk->thread.error_code=fsr;tsk->thread.trap_no=
-(0x1e27+305-0x1f4a);si.si_signo=sig;si.si_errno=(0x38f+5541-0x1934);si.si_code=
+(0x2206+895-0x2577);si.si_signo=sig;si.si_errno=(0x63a+1563-0xc55);si.si_code=
 code;si.si_addr=(void __user*)addr;force_sig_info(sig,&si,tsk);}void do_bad_area
 (unsigned long addr,unsigned int fsr,struct pt_regs*regs){struct task_struct*tsk
 =current;struct mm_struct*mm=tsk->active_mm;if(user_mode(regs))__do_user_fault(
@@ -91,8 +91,8 @@
 static int __kprobes do_page_fault(unsigned long addr,unsigned int fsr,struct 
 pt_regs*regs){struct task_struct*tsk;struct mm_struct*mm;int fault,sig,code;int 
 write=fsr&FSR_WRITE;unsigned int flags=FAULT_FLAG_ALLOW_RETRY|
-FAULT_FLAG_KILLABLE|(write?FAULT_FLAG_WRITE:(0xb19+1830-0x123f));if(
-notify_page_fault(regs,fsr))return(0x468+7141-0x204d);tsk=current;mm=tsk->mm;if(
+FAULT_FLAG_KILLABLE|(write?FAULT_FLAG_WRITE:(0x1c6+6173-0x19e3));if(
+notify_page_fault(regs,fsr))return(0x232+472-0x40a);tsk=current;mm=tsk->mm;if(
 interrupts_enabled(regs))local_irq_enable();if(!mm||pagefault_disabled())goto 
 no_context;if(!down_read_trylock(&mm->mmap_sem)){if(!user_mode(regs)&&!
 search_exception_tables(regs->ARM_pc))goto no_context;retry:down_read(&mm->
@@ -101,22 +101,23 @@
 if(!user_mode(regs)&&!search_exception_tables(regs->ARM_pc))goto no_context;
 #endif
 }fault=__do_page_fault(mm,addr,fsr,flags,tsk);if((fault&VM_FAULT_RETRY)&&
-fatal_signal_pending(current))return(0x1dcc+716-0x2098);perf_sw_event(
-PERF_COUNT_SW_PAGE_FAULTS,(0xd5b+2358-0x1690),regs,addr);if(!(fault&
+fatal_signal_pending(current))return(0x17ea+1129-0x1c53);perf_sw_event(
+PERF_COUNT_SW_PAGE_FAULTS,(0x51d+1459-0xacf),regs,addr);if(!(fault&
 VM_FAULT_ERROR)&&flags&FAULT_FLAG_ALLOW_RETRY){if(fault&VM_FAULT_MAJOR){tsk->
-maj_flt++;perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ,(0x632+69-0x676),regs,addr
-);}else{tsk->min_flt++;perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN,
-(0x1072+1414-0x15f7),regs,addr);}if(fault&VM_FAULT_RETRY){flags&=~
+maj_flt++;perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ,(0x7d8+2658-0x1239),regs,
+addr);}else{tsk->min_flt++;perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN,
+(0x1829+3484-0x25c4),regs,addr);}if(fault&VM_FAULT_RETRY){flags&=~
 FAULT_FLAG_ALLOW_RETRY;goto retry;}}up_read(&mm->mmap_sem);if(likely(!(fault&(
-VM_FAULT_ERROR|VM_FAULT_BADMAP|VM_FAULT_BADACCESS))))return(0xed7+981-0x12ac);if
-(fault&VM_FAULT_OOM){pagefault_out_of_memory();return(0x8f3+6782-0x2371);}if(!
+VM_FAULT_ERROR|VM_FAULT_BADMAP|VM_FAULT_BADACCESS))))return(0xc43+862-0xfa1);if(
+fault&VM_FAULT_OOM){pagefault_out_of_memory();return(0x12fa+654-0x1588);}if(!
 user_mode(regs))goto no_context;if(fault&VM_FAULT_SIGBUS){sig=SIGBUS;code=
 BUS_ADRERR;}else{sig=SIGSEGV;code=fault==VM_FAULT_BADACCESS?SEGV_ACCERR:
-SEGV_MAPERR;}__do_user_fault(tsk,addr,fsr,sig,code,regs);return(0x8f9+500-0xaed)
-;no_context:__do_kernel_fault(mm,addr,fsr,regs);return(0x1ad7+822-0x1e0d);}
+SEGV_MAPERR;}__do_user_fault(tsk,addr,fsr,sig,code,regs);return
+(0x9fd+1895-0x1164);no_context:__do_kernel_fault(mm,addr,fsr,regs);return
+(0xec2+1242-0x139c);}
 #else					
 static int do_page_fault(unsigned long addr,unsigned int fsr,struct pt_regs*regs
-){return(0x245d+469-0x2632);}
+){return(0xd3d+2134-0x1593);}
 #endif					
 #ifdef CONFIG_MMU
 static int __kprobes do_translation_fault(unsigned long addr,unsigned int fsr,
@@ -129,21 +130,21 @@
 bad_area;if(!pud_present(*pud))set_pud(pud,*pud_k);pmd=pmd_offset(pud,addr);
 pmd_k=pmd_offset(pud_k,addr);
 #ifdef CONFIG_ARM_LPAE
-index=(0xdc+3416-0xe34);
+index=(0x2439+121-0x24b2);
 #else
-index=(addr>>SECTION_SHIFT)&(0x92+2410-0x9fb);
+index=(addr>>SECTION_SHIFT)&(0xaf5+5540-0x2098);
 #endif
 if(pmd_none(pmd_k[index]))goto bad_area;copy_pmd(pmd,pmd_k);return
-(0x126a+1523-0x185d);bad_area:do_bad_area(addr,fsr,regs);return
-(0x104f+4245-0x20e4);}
+(0x1c67+296-0x1d8f);bad_area:do_bad_area(addr,fsr,regs);return
+(0x14c6+2172-0x1d42);}
 #else					
 static int do_translation_fault(unsigned long addr,unsigned int fsr,struct 
-pt_regs*regs){return(0x13b1+1997-0x1b7e);}
+pt_regs*regs){return(0x1372+4488-0x24fa);}
 #endif					
 static int do_sect_fault(unsigned long addr,unsigned int fsr,struct pt_regs*regs
 ){if(interrupts_enabled(regs))local_irq_enable();do_bad_area(addr,fsr,regs);
-return(0x5e1+3395-0x1324);}static int do_bad(unsigned long addr,unsigned int fsr
-,struct pt_regs*regs){return(0xc4f+2381-0x159b);}struct fsr_info{int(*fn)(
+return(0xe1d+1384-0x1385);}static int do_bad(unsigned long addr,unsigned int fsr
+,struct pt_regs*regs){return(0xe81+5596-0x245c);}struct fsr_info{int(*fn)(
 unsigned long addr,unsigned int fsr,struct pt_regs*regs);int sig;int code;const 
 char*name;};
 #ifdef CONFIG_ARM_LPAE
@@ -152,21 +153,21 @@
 #include "fsr-2level.c"
 #endif
 void __init hook_fault_code(int nr,int(*fn)(unsigned long,unsigned int,struct 
-pt_regs*),int sig,int code,const char*name){if(nr<(0x1a21+696-0x1cd9)||nr>=
+pt_regs*),int sig,int code,const char*name){if(nr<(0x1536+793-0x184f)||nr>=
 ARRAY_SIZE(fsr_info))BUG();fsr_info[nr].fn=fn;fsr_info[nr].sig=sig;fsr_info[nr].
 code=code;fsr_info[nr].name=name;}
 #ifdef CONFIG_MODEM_CODE_IS_MAPPING
 static DECLARE_RWSEM(shrinker_rwsem);atomic_t _code_page_count=ATOMIC_INIT(
-(0xfd1+1670-0x1657));struct addr_info{struct list_head node;unsigned long vaddr;
+(0x1b22+33-0x1b43));struct addr_info{struct list_head node;unsigned long vaddr;
 unsigned long kaddr;unsigned long page_index;};enum modem_access_technology{GSM=
-(0x1128+3741-0x1fc5),UTRAN=(0x57f+1951-0xd1d),LTE=(0xc85+1783-0x137a),COM=
-(0x5b1+6459-0x1ee9),NR_MODEM_ACCESS=(0x18a8+817-0x1bd5)};struct list_head 
+(0x13c5+527-0x15d4),UTRAN=(0xadd+3141-0x1721),LTE=(0x13a9+3732-0x223b),COM=
+(0x1023+5573-0x25e5),NR_MODEM_ACCESS=(0x328+7878-0x21ea)};struct list_head 
 modem_page_list[NR_MODEM_ACCESS]={LIST_HEAD_INIT(modem_page_list[
-(0x992+4592-0x1b82)]),LIST_HEAD_INIT(modem_page_list[(0xb25+1503-0x1103)]),
-LIST_HEAD_INIT(modem_page_list[(0x8b9+4411-0x19f2)]),LIST_HEAD_INIT(
-modem_page_list[(0x6a3+2369-0xfe1)]),};unsigned int page_used[
-(0x208+9204-0x25d4)];struct completion page_completion[(0xb08+1958-0x1286)*
-(0x16c7+4070-0x268d)];static void unmap_pte_range(pmd_t*pmd,unsigned long addr,
+(0xc79+4806-0x1f3f)]),LIST_HEAD_INIT(modem_page_list[(0x525+3120-0x1154)]),
+LIST_HEAD_INIT(modem_page_list[(0x59b+1712-0xc49)]),LIST_HEAD_INIT(
+modem_page_list[(0x202a+1270-0x251d)]),};unsigned int page_used[
+(0x5b3+5472-0x1aeb)];struct completion page_completion[(0x1af4+2293-0x23c1)*
+(0x1811+2992-0x23a1)];static void unmap_pte_range(pmd_t*pmd,unsigned long addr,
 unsigned long end){pte_t*pte;pte=pte_offset_kernel(pmd,addr);do{pte_t ptent=
 ptep_get_and_clear(&init_mm,addr,pte);WARN_ON(!pte_none(ptent)&&!pte_present(
 ptent));}while(pte++,addr+=PAGE_SIZE,addr!=end);}static void unmap_pmd_range(
@@ -180,14 +181,14 @@
 addr,unsigned long end){pgd_t*pgd;unsigned long next;BUG_ON(addr>=end);pgd=
 pgd_offset_k(addr);do{next=pgd_addr_end(addr,end);if(pgd_none_or_clear_bad(pgd))
 continue;unmap_pud_range(pgd,addr,next);}while(pgd++,addr=next,addr!=end);}void 
-shrink_modem_mem(unsigned int access_type){int i=(0x449+7294-0x20c7);unsigned 
+shrink_modem_mem(unsigned int access_type){int i=(0x1b82+101-0x1be7);unsigned 
 long vaddr;struct addr_info*addr,*tmp_addr;struct list_head tmp_page_list;for(i=
-(0x573+4473-0x16ec);i<NR_MODEM_ACCESS;i++){if(i==access_type)continue;down_write
+(0xfaf+3688-0x1e17);i<NR_MODEM_ACCESS;i++){if(i==access_type)continue;down_write
 (&shrinker_rwsem);list_replace_init(&modem_page_list[i],&tmp_page_list);up_write
 (&shrinker_rwsem);list_for_each_entry_safe(addr,tmp_addr,&tmp_page_list,node){
 list_del_init(&addr->node);page_completion[addr->page_index].done=
-(0xa77+6608-0x2447);page_used[addr->page_index/BITS_PER_LONG]&=~(
-(0xc24+2978-0x17c5)<<(addr->page_index%BITS_PER_LONG));vaddr=addr->vaddr&
+(0x28+6573-0x19d5);page_used[addr->page_index/BITS_PER_LONG]&=~(
+(0xf1+9113-0x2489)<<(addr->page_index%BITS_PER_LONG));vaddr=addr->vaddr&
 PAGE_MASK;if(vaddr<cpps_global_var.cpko_text_start||vaddr>cpps_global_var.
 modem_text_end){panic(
 "\x61\x64\x64\x72\x5f\x69\x6e\x66\x6f\x3a\x20\x25\x30\x38\x78\x20\x69\x73\x20\x20\x64\x65\x73\x74\x72\x6f\x79"
@@ -195,14 +196,14 @@
 PAGE_SIZE);flush_tlb_kernel_range(vaddr,vaddr+PAGE_SIZE);
 #ifdef CONFIG_DEBUG_RODATA
 unsigned int flags;local_irq_save(flags);set_memory_rw(addr->kaddr,
-(0xf45+5537-0x24e5));local_irq_restore(flags);
+(0x9af+2379-0x12f9));local_irq_restore(flags);
 #endif
 free_page(addr->kaddr);kfree(addr);atomic_dec(&_code_page_count);};}}
 EXPORT_SYMBOL(shrink_modem_mem);phys_addr_t virt_is_mapping(unsigned long addr){
 pgd_t*pgd;pmd_t*pmd;pte_t*ptep,pte;unsigned long pfn;pgd=pgd_offset_k(addr);if(!
 pgd_none(*pgd)){pmd=pmd_offset(pgd,addr);if(!pmd_none(*pmd)){ptep=pte_offset_map
 (pmd,addr);pte=*ptep;if(pte_present(pte)){pfn=pte_pfn(pte);return __pfn_to_phys(
-pfn);}}}return(0x17d+5982-0x18db);}static int sync_pgd(unsigned long addr,
+pfn);}}}return(0x107f+915-0x1412);}static int sync_pgd(unsigned long addr,
 unsigned int fsr,struct pt_regs*regs){unsigned int index;pgd_t*pgd,*pgd_k;pud_t*
 pud,*pud_k;pmd_t*pmd,*pmd_k;index=pgd_index(addr);pgd=cpu_get_pgd()+index;pgd_k=
 init_mm.pgd+index;if(pgd_none(*pgd_k))goto bad_area;if(!pgd_present(*pgd))
@@ -210,37 +211,38 @@
 pud_none(*pud_k))goto bad_area;if(!pud_present(*pud))set_pud(pud,*pud_k);pmd=
 pmd_offset(pud,addr);pmd_k=pmd_offset(pud_k,addr);
 #ifdef CONFIG_ARM_LPAE
-index=(0x88b+2904-0x13e3);
+index=(0xac+6622-0x1a8a);
 #else
-index=(addr>>SECTION_SHIFT)&(0x1bc7+2608-0x25f6);
+index=(addr>>SECTION_SHIFT)&(0x1565+1140-0x19d8);
 #endif
 if(pmd_none(pmd_k[index]))goto bad_area;copy_pmd(pmd,pmd_k);return
-(0xe4+1098-0x52e);bad_area:do_bad_area(addr,fsr,regs);return(0x192+8777-0x23db);
-}unsigned long*read_code_file(unsigned long page_index){unsigned long*code_buf;
-ssize_t result;code_buf=get_zeroed_page(GFP_ATOMIC);if(!code_buf)panic(
+(0x1b42+879-0x1eb1);bad_area:do_bad_area(addr,fsr,regs);return
+(0x1275+2184-0x1afd);}unsigned long*read_code_file(unsigned long page_index){
+unsigned long*code_buf;ssize_t result;code_buf=get_zeroed_page(GFP_ATOMIC);if(!
+code_buf)panic(
 "\x6d\x65\x6d\x65\x6f\x72\x79\x20\x6e\x6f\x74\x20\x65\x6e\x6f\x75\x67\x68\x21\x21"
 );atomic_inc(&_code_page_count);if(IS_ERR(cpps_global_var.fp_code)||
 cpps_global_var.fp_code==NULL){panic(
 "\x6f\x70\x65\x6e\x20\x66\x69\x6c\x65\x20\x65\x72\x72\x6f\x72" "\n");}
 mm_segment_t old_fs;old_fs=get_fs();set_fs(KERNEL_DS);loff_t pos;pos=page_index*
 PAGE_SIZE+cpps_global_var.modem_offset;result=vfs_read(cpps_global_var.fp_code,(
-char*)code_buf,PAGE_SIZE,&pos);if(result<(0x1008+1240-0x14e0)){panic(
+char*)code_buf,PAGE_SIZE,&pos);if(result<(0x12e6+512-0x14e6)){panic(
 "\x72\x65\x61\x64\x20\x63\x6f\x64\x65\x20\x66\x69\x6c\x65\x20\x65\x72\x72\x6f\x72" "\n"
 );}
 #ifdef CONFIG_DEBUG_RODATA
 unsigned int flags;local_irq_save(flags);set_memory_ro((unsigned long)code_buf,
-(0xba2+1301-0x10b6));local_irq_restore(flags);
+(0x1e08+939-0x21b2));local_irq_restore(flags);
 #endif
 set_fs(old_fs);return code_buf;}void read_code_mapping(unsigned long addr,
 unsigned int fsr,struct pt_regs*regs){unsigned long offset;unsigned long vaddr;
 const struct mem_type*mtype;unsigned long*vir_codebuf;unsigned long page_index;
-unsigned long page_shift;if(virt_is_mapping(addr&PAGE_MASK)!=(0x296+8148-0x226a)
+unsigned long page_shift;if(virt_is_mapping(addr&PAGE_MASK)!=(0xf39+1490-0x150b)
 ){sync_pgd(addr&PAGE_MASK,fsr,regs);return;}vaddr=addr&PAGE_MASK;offset=vaddr&(~
 cpps_global_var.cpko_text_start);page_index=offset>>PAGE_SHIFT;page_shift=
 page_index%BITS_PER_LONG;if((page_used[page_index/BITS_PER_LONG]>>page_shift)&
-(0x14d7+1130-0x1940)){wait_for_completion(&page_completion[page_index]);sync_pgd
-(vaddr,fsr,regs);return;}else page_used[page_index/BITS_PER_LONG]|=(
-(0xf86+1290-0x148f)<<page_shift);local_irq_enable();vir_codebuf=read_code_file(
+(0x38b+9019-0x26c5)){wait_for_completion(&page_completion[page_index]);sync_pgd(
+vaddr,fsr,regs);return;}else page_used[page_index/BITS_PER_LONG]|=(
+(0x1636+1449-0x1bde)<<page_shift);local_irq_enable();vir_codebuf=read_code_file(
 page_index);struct addr_info*addr_info;addr_info=kzalloc(sizeof(struct addr_info
 ),GFP_KERNEL);addr_info->kaddr=vir_codebuf;addr_info->vaddr=addr;addr_info->
 page_index=page_index;down_write(&shrinker_rwsem);if(vaddr<cpps_global_var.
@@ -258,46 +260,46 @@
 struct pt_regs*regs){const struct fsr_info*inf=fsr_info+fsr_fs(fsr);struct 
 siginfo info;
 #ifdef CONFIG_MODEM_CODE_IS_MAPPING
-if(addr!=(0x17fc+2588-0x2218)&&addr>=cpps_global_var.cpko_text_start&&addr<=
+if(addr!=(0x7a3+3616-0x15c3)&&addr>=cpps_global_var.cpko_text_start&&addr<=
 cpps_global_var.modem_text_end){read_code_mapping(addr,fsr&~FSR_LNX_PF,regs);
 return;}
 #endif
 if(!inf->fn(addr,fsr&~FSR_LNX_PF,regs))return;printk(KERN_ALERT
 "\x55\x6e\x68\x61\x6e\x64\x6c\x65\x64\x20\x66\x61\x75\x6c\x74\x3a\x20\x25\x73\x20\x28\x30\x78\x25\x30\x33\x78\x29\x20\x61\x74\x20\x30\x78\x25\x30\x38\x6c\x78" "\n"
-,inf->name,fsr,addr);info.si_signo=inf->sig;info.si_errno=(0x6a4+5067-0x1a6f);
+,inf->name,fsr,addr);info.si_signo=inf->sig;info.si_errno=(0x103f+3157-0x1c94);
 info.si_code=inf->code;info.si_addr=(void __user*)addr;arm_notify_die("",regs,&
-info,fsr,(0x888+4338-0x197a));}void __init hook_ifault_code(int nr,int(*fn)(
+info,fsr,(0x1a6d+205-0x1b3a));}void __init hook_ifault_code(int nr,int(*fn)(
 unsigned long,unsigned int,struct pt_regs*),int sig,int code,const char*name){if
-(nr<(0xf64+5639-0x256b)||nr>=ARRAY_SIZE(ifsr_info))BUG();ifsr_info[nr].fn=fn;
+(nr<(0xe7c+2781-0x1959)||nr>=ARRAY_SIZE(ifsr_info))BUG();ifsr_info[nr].fn=fn;
 ifsr_info[nr].sig=sig;ifsr_info[nr].code=code;ifsr_info[nr].name=name;}
 asmlinkage void __exception do_PrefetchAbort(unsigned long addr,unsigned int 
 ifsr,struct pt_regs*regs){const struct fsr_info*inf=ifsr_info+fsr_fs(ifsr);
 struct siginfo info;
 #ifdef CONFIG_MODEM_CODE_IS_MAPPING
-if(addr!=(0x99b+1934-0x1129)&&addr>=cpps_global_var.cpko_text_start&&addr<=
+if(addr!=(0x27c+5211-0x16d7)&&addr>=cpps_global_var.cpko_text_start&&addr<=
 cpps_global_var.modem_text_end){read_code_mapping(addr,ifsr|FSR_LNX_PF,regs);
 return;}
 #endif
 if(!inf->fn(addr,ifsr|FSR_LNX_PF,regs))return;printk(KERN_ALERT
 "\x55\x6e\x68\x61\x6e\x64\x6c\x65\x64\x20\x70\x72\x65\x66\x65\x74\x63\x68\x20\x61\x62\x6f\x72\x74\x3a\x20\x25\x73\x20\x28\x30\x78\x25\x30\x33\x78\x29\x20\x61\x74\x20\x30\x78\x25\x30\x38\x6c\x78" "\n"
-,inf->name,ifsr,addr);info.si_signo=inf->sig;info.si_errno=(0x1ae7+638-0x1d65);
+,inf->name,ifsr,addr);info.si_signo=inf->sig;info.si_errno=(0x85c+5488-0x1dcc);
 info.si_code=inf->code;info.si_addr=(void __user*)addr;arm_notify_die("",regs,&
-info,ifsr,(0xd39+653-0xfc6));}
+info,ifsr,(0xf4a+444-0x1106));}
 #ifndef CONFIG_ARM_LPAE
 static int __init exceptions_init(void){if(cpu_architecture()>=CPU_ARCH_ARMv6){
-hook_fault_code((0x117a+644-0x13fa),do_translation_fault,SIGSEGV,SEGV_MAPERR,
+hook_fault_code((0xc1d+2036-0x140d),do_translation_fault,SIGSEGV,SEGV_MAPERR,
 "\x49\x2d\x63\x61\x63\x68\x65\x20\x6d\x61\x69\x6e\x74\x65\x6e\x61\x6e\x63\x65\x20\x66\x61\x75\x6c\x74"
-);}if(cpu_architecture()>=CPU_ARCH_ARMv7){hook_fault_code((0x1adc+134-0x1b5f),
+);}if(cpu_architecture()>=CPU_ARCH_ARMv7){hook_fault_code((0x832+4193-0x1890),
 do_bad,SIGSEGV,SEGV_MAPERR,
 "\x73\x65\x63\x74\x69\x6f\x6e\x20\x61\x63\x63\x65\x73\x73\x20\x66\x6c\x61\x67\x20\x66\x61\x75\x6c\x74"
-);hook_fault_code((0x17bc+1887-0x1f15),do_bad,SIGSEGV,SEGV_MAPERR,
+);hook_fault_code((0x1f01+635-0x2176),do_bad,SIGSEGV,SEGV_MAPERR,
 "\x73\x65\x63\x74\x69\x6f\x6e\x20\x61\x63\x63\x65\x73\x73\x20\x66\x6c\x61\x67\x20\x66\x61\x75\x6c\x74"
 );}
 #ifdef CONFIG_MODEM_CODE_IS_MAPPING
-int index=(0x1395+4146-0x23c7);for(index=(0x15fb+3774-0x24b9);index<
-(0x1642+3952-0x258a)*(0x70d+6231-0x1f44);index++)init_completion(&
-page_completion[index]);
+int index=(0x1bc6+1652-0x223a);for(index=(0x1a30+457-0x1bf9);index<
+(0x1e15+144-0x1e7d)*(0x1bc+9368-0x2634);index++)init_completion(&page_completion
+[index]);
 #endif
-return(0x91+1966-0x83f);}arch_initcall(exceptions_init);
+return(0x2a6+1281-0x7a7);}arch_initcall(exceptions_init);
 #endif
 
diff --git a/ap/os/linux/linux-3.4.x/drivers/cpko/cpko_main.c b/ap/os/linux/linux-3.4.x/drivers/cpko/cpko_main.c
index 341d668..81a678e 100755
--- a/ap/os/linux/linux-3.4.x/drivers/cpko/cpko_main.c
+++ b/ap/os/linux/linux-3.4.x/drivers/cpko/cpko_main.c
@@ -83,31 +83,31 @@
 __utran_modem_text_start;unsigned int __lte_modem_text_start;unsigned int 
 __comm_modem_text_start;unsigned int modem_text_end;unsigned int cpko_data_start
 ;unsigned int cpko_bss_start;unsigned int cpko_text_offset;}cpko_section_layout;
-cpko_section_layout cpko_ps_section;int raise(int signo){return(0x6ed+448-0x8ad)
-;}extern unsigned int SysEntry(void);static int ko_Main_Thread(void*data){struct
- sched_param param={.sched_priority=MAX_USER_RT_PRIO/(0x81a+3335-0x151f)-
-(0x931+4823-0x1c05)};int ret=(0x138+9138-0x24ea);sched_setscheduler(current,
-SCHED_FIFO,&param);ret=SysEntry();if(ret!=(0x4c9+1313-0x9ea))panic(
-"Main_Thread\n");param.sched_priority=MAX_USER_RT_PRIO-(0x7a1+7631-0x2542);
-sched_setscheduler(kthreadd_task,SCHED_FIFO,&param);return(0x1239+1857-0x197a);}
-int zte_modem_ko_start(void){kthread_run(ko_Main_Thread,NULL,
-"\x5a\x54\x45\x4d\x61\x69\x6e\x54\x68\x72\x65\x61\x64");return
-(0x1050+3114-0x1c7a);}static void cpko_sectioninfo_set(void){int ret;struct file
-*fp;mm_segment_t old_fs;loff_t cpko_pos=(0x160b+1733-0x1cd0);struct 
+cpko_section_layout cpko_ps_section;int raise(int signo){return
+(0x15f6+2674-0x2068);}extern unsigned int SysEntry(void);static int 
+ko_Main_Thread(void*data){struct sched_param param={.sched_priority=
+MAX_USER_RT_PRIO/(0x1d1+4208-0x123f)-(0x54f+8304-0x25bc)};int ret=
+(0xe8d+4168-0x1ed5);sched_setscheduler(current,SCHED_FIFO,&param);ret=SysEntry()
+;if(ret!=(0x6+2090-0x830))panic("Main_Thread\n");param.sched_priority=
+MAX_USER_RT_PRIO-(0x1d79+297-0x1e74);sched_setscheduler(kthreadd_task,SCHED_FIFO
+,&param);return(0x15a9+3926-0x24ff);}int zte_modem_ko_start(void){kthread_run(
+ko_Main_Thread,NULL,"\x5a\x54\x45\x4d\x61\x69\x6e\x54\x68\x72\x65\x61\x64");
+return(0x1162+2562-0x1b64);}static void cpko_sectioninfo_set(void){int ret;
+struct file*fp;mm_segment_t old_fs;loff_t cpko_pos=(0xa42+997-0xe27);struct 
 cpps_globalModem globalVar;fp=filp_open(
 "\x2f\x6c\x69\x62\x2f\x63\x70\x6b\x6f\x2f\x63\x70\x6b\x6f\x5f\x73\x65\x63\x69\x6e\x66\x6f\x2e\x62\x69\x6e"
-,(0xd6c+3198-0x19ea),(0x655+5701-0x1c9a));if(IS_ERR(fp)||fp==NULL)panic(
+,(0xc80+6420-0x2594),(0x117c+481-0x135d));if(IS_ERR(fp)||fp==NULL)panic(
 "\x6f\x70\x65\x6e\x20\x66\x69\x6c\x65\x20\x65\x72\x72\x6f\x72" "\n");old_fs=
 get_fs();set_fs(KERNEL_DS);ret=vfs_read(fp,(char*)&cpko_ps_section,sizeof(
-cpko_section_layout),&cpko_pos);if(ret<=(0x235f+23-0x2376))panic(
+cpko_section_layout),&cpko_pos);if(ret<=(0x12a8+4252-0x2344))panic(
 "\x72\x65\x61\x64\x20\x66\x69\x6c\x65\x20\x65\x72\x72\x6f\x72" "\n");filp_close(
 fp,NULL);
 #ifdef CONFIG_MODEM_CODE_IS_MAPPING
 fp=filp_open(
 "\x2f\x6c\x69\x62\x2f\x63\x70\x6b\x6f\x2f\x63\x70\x6b\x6f\x2e\x6b\x6f",
-(0x940+3724-0x17cc),(0xb2a+3215-0x17b9));if(IS_ERR(fp)||fp==NULL)panic(
+(0x564+8331-0x25ef),(0x7b0+4976-0x1b20));if(IS_ERR(fp)||fp==NULL)panic(
 "\x6f\x70\x65\x6e\x20\x66\x69\x6c\x65\x20\x65\x72\x72\x6f\x72" "\n");fp->f_ra.
-ra_pages=(0x15a8+769-0x18a9);
+ra_pages=(0x52d+4990-0x18ab);
 #endif
 if(cpko_ps_section.cpko_text_start){globalVar.cpko_text_start=(unsigned long)
 cpko_ps_section.cpko_text_start;globalVar.cpko_rodata_start=(unsigned long)
@@ -127,7 +127,7 @@
 vfree_modem_section(globalVar.cpko_text_start,globalVar.modem_text_end);
 #endif
 }else panic("\x66\x69\x6c\x65\x20\x65\x72\x72\x6f\x72" "\n");}static int 
-cpko_start(void){struct cpps_callbacks callback={(0x66f+4526-0x181d)};callback.
+cpko_start(void){struct cpps_callbacks callback={(0x2f9+3601-0x110a)};callback.
 zOss_ResetNVFactory=zOss_ResetNVFactory;callback.zOss_NvramFlush=zOss_NvramFlush
 ;callback.zOss_NvItemWrite=zOss_NvItemWrite;callback.zOss_NvItemWriteFactory=
 zOss_NvItemWriteFactory;callback.zOss_NvItemRead=zOss_NvItemRead;callback.
@@ -200,5 +200,5 @@
 psm_GetModemSleepFlagStatus=psm_GetModemSleepFlagStatus;
 #endif
 cpps_callbacks_register(&callback);cpko_sectioninfo_set();zte_modem_ko_start();
-return(0x1a6d+2639-0x24bc);}static int cpko_stop(void){return(0x62+561-0x293);}
-module_init(cpko_start);module_exit(cpko_stop);
+return(0x588+5231-0x19f7);}static int cpko_stop(void){return(0x6e8+4175-0x1737);
+}module_init(cpko_start);module_exit(cpko_stop);
diff --git a/ap/os/linux/linux-3.4.x/drivers/mmc/host/zx29_mmc-pltfm.c b/ap/os/linux/linux-3.4.x/drivers/mmc/host/zx29_mmc-pltfm.c
old mode 100644
new mode 100755
index 5d3ef3c..e3c1033
--- a/ap/os/linux/linux-3.4.x/drivers/mmc/host/zx29_mmc-pltfm.c
+++ b/ap/os/linux/linux-3.4.x/drivers/mmc/host/zx29_mmc-pltfm.c
@@ -381,7 +381,17 @@
 		drv_data = match->data;
 	}
 #endif
+
+#ifdef _USE_VEHICLE_DC
+	if(ioread32(MMC_CFG_BASE)&(1<<pdev->id)){
+		return dw_mci_pltfm_register(pdev, drv_data);
+	}else
+	{
+		return 0;
+	}
+#else
 	return dw_mci_pltfm_register(pdev, drv_data);
+#endif
 }
 
 int dw_mci_pltfm_remove(struct platform_device *pdev)
diff --git a/ap/os/linux/linux-3.4.x/drivers/mmc/host/zx29_mmc.c b/ap/os/linux/linux-3.4.x/drivers/mmc/host/zx29_mmc.c
index 4724b88..7df7603 100755
--- a/ap/os/linux/linux-3.4.x/drivers/mmc/host/zx29_mmc.c
+++ b/ap/os/linux/linux-3.4.x/drivers/mmc/host/zx29_mmc.c
@@ -3953,7 +3953,7 @@
 	//struct mmc_host *mmc = slot->mmc;
 
 	BUG_ON(id > 3);
-	BUG_ON(dw_mmc_host[id] == NULL);
+	//BUG_ON(dw_mmc_host[id] == NULL);
 	if (dw_mmc_host[id] == NULL)
 		return;
 	dw_host = dw_mmc_host[id];
diff --git a/ap/os/linux/linux-3.4.x/drivers/mtd/nand/spi_nand.c b/ap/os/linux/linux-3.4.x/drivers/mtd/nand/spi_nand.c
index 0376155..8eadc4b 100755
--- a/ap/os/linux/linux-3.4.x/drivers/mtd/nand/spi_nand.c
+++ b/ap/os/linux/linux-3.4.x/drivers/mtd/nand/spi_nand.c
@@ -840,6 +840,18 @@
 	return 0;
 }
 
+void zx_denali_nand_lock(void)
+{    	
+	mutex_lock(&otpMutex);	
+	soft_spin_lock(NAND_SFLOCK);
+}
+
+void zx_denali_nand_unlock(void)
+{   	
+	soft_spin_unlock(NAND_SFLOCK); 	
+	mutex_unlock(&otpMutex);
+}
+
 void denali_nand_lock(struct mtd_info *mtd)
 {    
     struct spi_nand_info *spi_nand = mtd_to_spi_nand(mtd);
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/ppp/ppp_async.c b/ap/os/linux/linux-3.4.x/drivers/net/ppp/ppp_async.c
old mode 100644
new mode 100755
index 415598b..87d98b0
--- a/ap/os/linux/linux-3.4.x/drivers/net/ppp/ppp_async.c
+++ b/ap/os/linux/linux-3.4.x/drivers/net/ppp/ppp_async.c
@@ -361,6 +361,11 @@
 	struct asyncppp *ap = ap_get(tty);
 	unsigned long flags;
 
+	if(current->rt_priority == 0){
+		struct sched_param param = { .sched_priority = 1 };
+		param.sched_priority = 1;
+		sched_setscheduler(current, SCHED_FIFO, &param);
+	}
 	if (!ap)
 	{
 
@@ -546,6 +551,14 @@
 		*buf++ = c;				\
 } while (0)
 
+#define PUT_BYTE_SIMPLE(xaccm, buf, c)	do {		\
+		if (xaccm[c >> 5] & (1 << (c & 0x1f))) {\
+			*buf++ = PPP_ESCAPE;			\
+			*buf++ = c ^ PPP_TRANS; 		\
+		} else						\
+			*buf++ = c; 			\
+	} while (0)
+
 static int
 ppp_async_encode(struct asyncppp *ap)
 {
@@ -569,7 +582,7 @@
 	 * had been negotiated.
 	 */
 	islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;
-
+	if(unlikely(islcp)){
 	if (i == 0) {
 		if (islcp)
 			async_lcp_peek(ap, data, count, 0);
@@ -608,6 +621,46 @@
 		fcs = PPP_FCS(fcs, c);
 		PUT_BYTE(ap, buf, c, islcp);
 	}
+	}else{
+		u32 *xaccm = ap->xaccm;
+		unsigned int flags = ap->flags;
+		if (i == 0) {
+			if (flag_time == 0 ||time_after_eq(jiffies, ap->last_xmit + flag_time))
+				*buf++ = PPP_FLAG;
+			ap->last_xmit = jiffies;
+			fcs = PPP_INITFCS;
+			if ((flags & SC_COMP_AC) == 0 ) {
+				PUT_BYTE_SIMPLE(xaccm, buf, 0xff);
+				fcs = PPP_FCS(fcs, 0xff);
+				PUT_BYTE_SIMPLE(xaccm, buf, 0x03);
+				fcs = PPP_FCS(fcs, 0x03);
+			}
+		}
+		buflim = ap->obuf + OBUFSIZE - 6;
+		while (i < count && buf < buflim) {
+			c = data[i++];
+			if (i == 1 && c == 0 && (flags & SC_COMP_PROT))
+				continue;
+			fcs = PPP_FCS(fcs, c);
+			PUT_BYTE_SIMPLE(xaccm, buf, c);
+		}
+		if (i < count) {
+			ap->olim = buf;
+			ap->tpkt_pos = i;
+			ap->tfcs = fcs;
+			return 0;
+		}
+		fcs = ~fcs;
+		c = fcs & 0xff;
+		PUT_BYTE_SIMPLE(xaccm, buf, c);
+		c = (fcs >> 8) & 0xff;
+		PUT_BYTE_SIMPLE(xaccm, buf, c);
+		*buf++ = PPP_FLAG;
+		ap->olim = buf;
+		kfree_skb(ap->tpkt);
+		ap->tpkt = NULL;
+		return 1;
+	}
 
 	if (i < count) {
 		/*
@@ -811,11 +864,13 @@
 	len = skb->len;
 	if (len < 3)
 		goto err;	/* too short */
+#if 0
 	fcs = PPP_INITFCS;
 	for (; len > 0; --len)
 		fcs = PPP_FCS(fcs, *p++);
 	if (fcs != PPP_GOODFCS)
 		goto err;	/* bad FCS */
+#endif
 	skb_trim(skb, skb->len - 2);
 
 	/* check for address/control and protocol compression */
@@ -862,7 +917,7 @@
 ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
 		char *flags, int count)
 {
-	struct sk_buff *skb;
+	struct sk_buff *skb = NULL;
 	int c, i, j, n, s, f;
 	unsigned char *sp;
 
@@ -946,6 +1001,19 @@
 				start_tty(ap->tty);
 			else if (c == STOP_CHAR(ap->tty))
 				stop_tty(ap->tty);
+		} else {
+			if(skb && (!strncmp(skb->data, "+++", 3) || !strncmp(skb->data, "ATH", 3))){
+				struct task_struct *p;
+				
+				read_lock(&tasklist_lock);
+				for_each_process(p){
+					if (strcmp(p->comm, "pppd"))
+						continue;
+					do_send_sig_info(SIGUSR1, SEND_SIG_FORCED, p, true);
+				}
+				read_unlock(&tasklist_lock);
+				printk("pppd recv +++/ATH=%s\n", skb->data);
+			}
 		}
 		/* otherwise it's a char in the recv ACCM */
 		++n;
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/Kconfig b/ap/os/linux/linux-3.4.x/drivers/net/wireless/Kconfig
old mode 100644
new mode 100755
index 3e608fb..405747c
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/Kconfig
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/Kconfig
@@ -298,4 +298,5 @@
 source "drivers/net/wireless/xradio/Kconfig"
 source "drivers/net/wireless/ssv6x5x/Kconfig"
 source "drivers/net/wireless/aic8800/Kconfig"
+source "drivers/net/wireless/aic8800d80l/Kconfig"
 endif # WLAN
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/Makefile b/ap/os/linux/linux-3.4.x/drivers/net/wireless/Makefile
old mode 100644
new mode 100755
index 17e88cd..7a6941d
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/Makefile
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/Makefile
@@ -66,8 +66,11 @@
 obj-$(CONFIG_ESP8089)	+= esp8089/
 obj-$(CONFIG_XR_WLAN)	+= xradio/
 obj-$(CONFIG_SSV6X5X)	+= ssv6x5x/
+ifeq ($(CONFIG_AIC8800),y)
 obj-$(CONFIG_AIC8800)	+= aic8800/
-
+else
+obj-$(CONFIG_AIC8800D80L)	+= aic8800d80l/
+endif
 
 
 obj-$(CONFIG_BRCMFMAC)	+= brcm80211/
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/Makefile b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/Makefile
index 5555d41..65c5521 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/Makefile
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/Makefile
@@ -93,7 +93,7 @@
 endif # INGENTIC_T20
 endif # ALLWINNER
 endif # NANOPI_M4
-CONFIG_VENDOR_SPECIFIED_FW_PATH ?= "/etc/firmware/aic8800D80"
+CONFIG_VENDOR_SPECIFIED_FW_PATH ?= "/etc/firmware"
 ifneq ($(CONFIG_VENDOR_SPECIFIED_FW_PATH),)
 subdir-ccflags-y += -DCONFIG_VENDOR_SPECIFIED_FW_PATH=\"$(CONFIG_VENDOR_SPECIFIED_FW_PATH)\"
 endif
@@ -118,7 +118,7 @@
 CONFIG_USB_SUPPORT =n
 CONFIG_RX_REORDER ?=y
 CONFIG_ARP_OFFLOAD =y
-CONFIG_USE_5G =y
+CONFIG_USE_5G =n
 CONFIG_RADAR_OR_IR_DETECT =n
 CONFIG_DOWNLOAD_FW =n
 CONFIG_LOAD_USERCONFIG ?=y
@@ -129,14 +129,22 @@
 CONFIG_RFTEST_UART_BT=y
 CONFIG_USB_BT =y
 CONFIG_MAC_RANDOM_IF_NO_MAC_IN_EFUSE ?= y
-CONFIG_SDIO_PWRCTRL ?= n
-CONFIG_WPA3_FOR_OLD_KERNEL ?= n
+CONFIG_SDIO_PWRCTRL ?= y
+CONFIG_WPA3_FOR_OLD_KERNEL ?= y
 CONFIG_USB_MSG_OUT_EP =y
 CONFIG_USB_MSG_IN_EP =y
 CONFIG_USB_TX_AGGR=n
+ifeq ($(CONFIG_AIC8800_SDIO_TX_AGGR), y)
 CONFIG_SDIO_AGGR=y
+else
+CONFIG_SDIO_AGGR=n
+endif
 CONFIG_SET_VENDOR_EXTENSION_IE = n
+ifeq ($(CONFIG_AIC8800_SDIO_RX_AGGR), y)
 CONFIG_LESS_SKB = y
+else
+CONFIG_LESS_SKB = n
+endif
 CONFIG_CREATE_TRACE_POINTS = n
 CONFIG_TX_NETIF_FLOWCTRL = y
 CONFIG_AGGRESSIVE_TX = n
@@ -145,7 +153,8 @@
 CONFIG_DPD = y
 CONFIG_FORCE_DPD_CALIB = y
 CONFIG_TEMP_PW = y
-CONFIG_FILTER_TCP_ACK =y
+CONFIG_FILTER_TCP_ACK =n
+CONFIG_SET_AP_PS = y
 
 # Support of MU-MIMO transmission (need FW support)
 ifeq ($(CONFIG_RWNX_BFMER), y)
@@ -192,7 +201,7 @@
                regdb.o				\
                wifi_dev_aic88.o		\
                aic88-generic-wlan.o    \
-               aicwf_mem_prealloc.o
+	       aicwf_mem_prealloc.o
 
 aic8818_fdrv-$(CONFIG_RWNX_RADAR)       += rwnx_radar.o
 aic8818_fdrv-$(CONFIG_DEBUG_FS_AIC)         += rwnx_debugfs.o
@@ -205,7 +214,6 @@
 aic8818_fdrv-$(CONFIG_SDIO_SUPPORT)     += aicwf_sdio.o
 aic8818_fdrv-$(CONFIG_FILTER_TCP_ACK)   += aicwf_tcp_ack.o
 
-
 aic8818_fdrv-$(CONFIG_USB_SUPPORT)      += usb_host.o
 aic8818_fdrv-$(CONFIG_USB_SUPPORT)      += aicwf_txrxif.o
 aic8818_fdrv-$(CONFIG_USB_SUPPORT)      += aicwf_usb.o
@@ -265,6 +273,7 @@
 ccflags-$(CONFIG_FORCE_DPD_CALIB) += -DCONFIG_FORCE_DPD_CALIB -DCONFIG_DPD
 ccflags-$(CONFIG_TEMP_PW) += -DCONFIG_TEMP_PW
 ccflags-$(CONFIG_FILTER_TCP_ACK) += -DCONFIG_FILTER_TCP_ACK
+ccflags-$(CONFIG_SET_AP_PS) += -DCONFIG_SET_AP_PS
 
 ifeq ($(CONFIG_LESS_SKB), y)
 ccflags-y += -DLESS_SKB
@@ -315,7 +324,7 @@
 ccflags-$(CONFIG_USB_BT)  += -DCONFIG_USB_BT
 ccflags-$(CONFIG_MAC_RANDOM_IF_NO_MAC_IN_EFUSE) += -DCONFIG_MAC_RANDOM_IF_NO_MAC_IN_EFUSE
 
-ldflags-y += --strip-debug
+#ldflags-y += --strip-debug
 
 all: modules
 modules:
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/aicwf_sdio.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/aicwf_sdio.c
index afe9c75..5fc63bd 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/aicwf_sdio.c
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/aicwf_sdio.c
@@ -40,41 +40,12 @@
 int tx_fc_high_water = AICWF_SDIO_TX_HIGH_WATER;
 module_param_named(tx_fc_high_water, tx_fc_high_water, int, 0644);
 #endif
-
-/* SDIO Device ID */
 #define SDIO_VENDOR_ID_AIC8801              0x5449
 #define SDIO_VENDOR_ID_AIC8800DC            0xc8a1
 #define SDIO_VENDOR_ID_AIC8800D80           0xc8a1
-
 #define SDIO_DEVICE_ID_AIC8801				0x0145
 #define SDIO_DEVICE_ID_AIC8800DC			0xc08d
 #define SDIO_DEVICE_ID_AIC8800D80           0x0082
-
-uint8_t crc8_ponl_107(uint8_t *p_buffer, uint16_t cal_size)
-{
-    uint8_t i;
-    uint8_t crc = 0;
-    if (cal_size==0) {
-        return crc;
-    }
-    while (cal_size--) {
-        for (i = 0x80; i > 0; i /= 2) {
-            if (crc & 0x80)  {
-                crc *= 2;
-                crc ^= 0x07; //polynomial X8 + X2 + X + 1,(0x107)
-            } else {
-                crc *= 2;
-            }
-            if ((*p_buffer) & i) {
-                crc ^= 0x07;
-            }
-        }
-        p_buffer++;
-    }
-
-    return crc;
-}
-
 int aicwf_sdio_readb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 *val)
 {
     int ret;
@@ -133,18 +104,12 @@
     u8 fc_reg = 0;
     u32 count = 0;
     while (true) {
-        ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.flow_ctrl_reg, &fc_reg);
+        ret = aicwf_sdio_readb(sdiodev, SDIOWIFI_FLOW_CTRL_REG, &fc_reg);
         if (ret) {
             return -1;
         }
-
-        if (sdiodev->chipid == PRODUCT_ID_AIC8801 || sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
-            sdiodev->chipid == PRODUCT_ID_AIC8800DW) {
-            fc_reg = fc_reg & SDIOWIFI_FLOWCTRL_MASK_REG;
-        }
-
-        if (fc_reg != 0) {
-            ret = fc_reg;
+        if ((fc_reg & SDIOWIFI_FLOWCTRL_MASK_REG) != 0) {
+            ret = fc_reg & SDIOWIFI_FLOWCTRL_MASK_REG;
             if(ret > tx_aggr_counter){
 				ret = tx_aggr_counter;
 			}
@@ -172,18 +137,13 @@
     u32 count = 0;
 
     while (true) {
-        ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.flow_ctrl_reg, &fc_reg);
+        ret = aicwf_sdio_readb(sdiodev, SDIOWIFI_FLOW_CTRL_REG, &fc_reg);
         if (ret) {
             return -1;
         }
 
-        if (sdiodev->chipid == PRODUCT_ID_AIC8801 || sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
-            sdiodev->chipid == PRODUCT_ID_AIC8800DW) {
-            fc_reg = fc_reg & SDIOWIFI_FLOWCTRL_MASK_REG;
-        }
-
-        if ( fc_reg > 2) {
-            ret = fc_reg;
+        if ((fc_reg & SDIOWIFI_FLOWCTRL_MASK_REG) > 2) {
+            ret = fc_reg & SDIOWIFI_FLOWCTRL_MASK_REG;
             if(ret > tx_aggr_counter){
 				ret = tx_aggr_counter;
 			}
@@ -196,6 +156,7 @@
             count++;
             if (count < 30)
                 udelay(200);
+                //usleep_range(150, 200);
             else if(count < 40)
                 msleep(2);
             else
@@ -211,11 +172,11 @@
     int ret = 0;
 if (func_flag){
     sdio_claim_host(sdiodev->func_msg);
-    ret = sdio_writesb(sdiodev->func_msg, sdiodev->sdio_reg.wr_fifo_addr, buf, count);
+    ret = sdio_writesb(sdiodev->func_msg, 7, buf, count);
     sdio_release_host(sdiodev->func_msg);
 } else {
     sdio_claim_host(sdiodev->func);
-    ret = sdio_writesb(sdiodev->func, sdiodev->sdio_reg.wr_fifo_addr, buf, count);
+    ret = sdio_writesb(sdiodev->func, 7, buf, count);
     sdio_release_host(sdiodev->func);
 }
     return ret;
@@ -227,7 +188,7 @@
     int ret = 0;
 
     sdio_claim_host(sdiodev->func);
-    ret = sdio_writesb(sdiodev->func, sdiodev->sdio_reg.wr_fifo_addr, buf, count);
+    ret = sdio_writesb(sdiodev->func, 7, buf, count);
     sdio_release_host(sdiodev->func);
 
     return ret;
@@ -246,16 +207,16 @@
     if (func_flag){
         if(!msg) {
             sdio_claim_host(sdiodev->func);
-            ret = sdio_readsb(sdiodev->func, rxbuff->data, sdiodev->sdio_reg.rd_fifo_addr, size);
+            ret = sdio_readsb(sdiodev->func, rxbuff->data, 8, size);
             sdio_release_host(sdiodev->func);
         } else {
             sdio_claim_host(sdiodev->func_msg);
-            ret = sdio_readsb(sdiodev->func_msg, rxbuff->data, sdiodev->sdio_reg.rd_fifo_addr, size);
+            ret = sdio_readsb(sdiodev->func_msg, rxbuff->data, 8, size);
             sdio_release_host(sdiodev->func_msg);
         }
     } else{
             sdio_claim_host(sdiodev->func);
-            ret = sdio_readsb(sdiodev->func, rxbuff->data, sdiodev->sdio_reg.rd_fifo_addr, size);
+            ret = sdio_readsb(sdiodev->func, rxbuff->data, 8, size);
             sdio_release_host(sdiodev->func);
     }
 
@@ -278,16 +239,16 @@
 if (func_flag){
     if(!msg) {
         sdio_claim_host(sdiodev->func);
-        ret = sdio_readsb(sdiodev->func, skbbuf->data, sdiodev->sdio_reg.rd_fifo_addr, size);
+        ret = sdio_readsb(sdiodev->func, skbbuf->data, 8, size);
         sdio_release_host(sdiodev->func);
     } else {
         sdio_claim_host(sdiodev->func_msg);
-        ret = sdio_readsb(sdiodev->func_msg, skbbuf->data, sdiodev->sdio_reg.rd_fifo_addr, size);
+        ret = sdio_readsb(sdiodev->func_msg, skbbuf->data, 8, size);
         sdio_release_host(sdiodev->func_msg);
     }
 } else{
         sdio_claim_host(sdiodev->func);
-        ret = sdio_readsb(sdiodev->func, skbbuf->data, sdiodev->sdio_reg.rd_fifo_addr, size);
+        ret = sdio_readsb(sdiodev->func, skbbuf->data, 8, size);
         sdio_release_host(sdiodev->func);
 }
     if (ret < 0) {
@@ -301,7 +262,6 @@
 #endif
 
 static int aicwf_sdio_chipmatch(struct aic_sdio_dev *sdio_dev, uint16_t vid, uint16_t did){
-
 	if(vid == SDIO_VENDOR_ID_AIC8801 && did == SDIO_DEVICE_ID_AIC8801){
 		sdio_dev->chipid = PRODUCT_ID_AIC8801;
 		printk("%s USE AIC8801\r\n", __func__);
@@ -312,14 +272,12 @@
 		return 0;
 	}else if(vid == SDIO_VENDOR_ID_AIC8800D80 && did == SDIO_DEVICE_ID_AIC8800D80){
 		sdio_dev->chipid = PRODUCT_ID_AIC8800D80;
-		func_flag = false;
 		printk("%s USE AIC8800D80\r\n", __func__);
 		return 0;
 	}else{
 		return -1;
 	}
 }
-
 struct aic_sdio_dev *g_sdiodev;
 static int aicwf_sdio_probe(struct sdio_func *func,
     const struct sdio_device_id *id)
@@ -356,26 +314,15 @@
     if (err < 0) {
         sdio_err("sdio chipmatch fail\n");
     }
-
     sdiodev->func = func;
-    if(sdiodev->chipid == PRODUCT_ID_AIC8800DC || sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-		sdiodev->func_msg = func->card->sdio_func[1];
-	}
+    sdiodev->func_msg = func->card->sdio_func[1];
     sdiodev->bus_if = bus_if;
     bus_if->bus_priv.sdio = sdiodev;
-    if(sdiodev->chipid == PRODUCT_ID_AIC8800DC || sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-		dev_set_drvdata(&sdiodev->func_msg->dev, bus_if);
-		printk("the device is PRODUCT_ID_AIC8800DC \n");
-	}
-
+    dev_set_drvdata(&sdiodev->func_msg->dev, bus_if);
+    
     dev_set_drvdata(&func->dev, bus_if);
     sdiodev->dev = &func->dev;
-
-	if (sdiodev->chipid != PRODUCT_ID_AIC8800D80) {
-		err = aicwf_sdio_func_init(sdiodev);
-    } else {
-        err = aicwf_sdiov3_func_init(sdiodev);
-    }
+    err = aicwf_sdio_func_init(sdiodev);
     if (err < 0) {
         sdio_err("sdio func init fail\n");
         goto fail;
@@ -388,11 +335,14 @@
 	}
 
     host->caps |= MMC_CAP_NONREMOVABLE;
-    aicwf_rwnx_sdio_platform_init(sdiodev);
+    if(aicwf_rwnx_sdio_platform_init(sdiodev) != 0){
+		err = -2;
+		goto fail;
+	}
     aicwf_hostif_ready();
 
     g_sdiodev = sdiodev;
-
+	sdio_dbg("aicwf_sdio_probe over.\n");
     return 0;
 fail:
 	aicwf_sdio_func_deinit(sdiodev);
@@ -504,7 +454,6 @@
 
 static const struct sdio_device_id aicwf_sdmmc_ids[] = {
     {SDIO_DEVICE(SDIO_VENDOR_ID_AIC, SDIO_DEVICE_ID_AIC)},
-    {SDIO_DEVICE(SDIO_VENDOR_ID_AIC8800D80, SDIO_DEVICE_ID_AIC8800D80)},
     { },
 };
 
@@ -608,8 +557,10 @@
 
 void aicwf_sdio_exit(void)
 {
-    if(g_rwnx_plat && g_rwnx_plat->enabled)
+    if(g_rwnx_plat && g_rwnx_plat->enabled && g_rwnx_plat->sdiodev){
+		if(g_rwnx_plat->sdiodev->rwnx_hw)
         rwnx_platform_deinit(g_rwnx_plat->sdiodev->rwnx_hw);
+	}
 
 	sdio_unregister_driver(&aicwf_sdio_driver);
 	func_flag = true;
@@ -636,17 +587,8 @@
 int aicwf_sdio_wakeup(struct aic_sdio_dev *sdiodev)
 {
     int ret = 0;
-    int read_retry;
-    int write_retry = 20;
-	int wakeup_reg_val = 0;
-
-    if (sdiodev->chipid == PRODUCT_ID_AIC8801 ||
-        sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
-        sdiodev->chipid == PRODUCT_ID_AIC8800DW) {
-        wakeup_reg_val = 1;
-    } else if (sdiodev->chipid == PRODUCT_ID_AIC8800D80) {
-        wakeup_reg_val = 0x11;
-    }
+        int read_retry;
+        int write_retry = 20;
 
     if (sdiodev->state == SDIO_SLEEP_ST) {
         down(&sdiodev->pwrctl_wakeup_sema);
@@ -657,7 +599,7 @@
             }
             sdio_dbg("w\n");
             while(write_retry) {
-                ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, wakeup_reg_val);
+                ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_WAKEUP_REG, 1);
                 if (ret) {
                     txrx_err("sdio wakeup fail\n");
                     ret = -1;
@@ -665,7 +607,7 @@
                     read_retry=10;
                     while (read_retry) {
                         u8 val;
-                        ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.sleep_reg, &val);
+                        ret = aicwf_sdio_readb(sdiodev, SDIOWIFI_SLEEP_REG, &val);
                         if (ret==0 && val&0x10) {
                             break;
                         }
@@ -696,7 +638,7 @@
     struct rwnx_hw *rwnx_hw = sdiodev->rwnx_hw;
 
     if (bus_if->state == BUS_DOWN_ST) {
-        ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x10);
+        ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_SLEEP_REG, 0x10);
         if (ret) {
             sdio_err("Write sleep fail!\n");
     }
@@ -706,11 +648,11 @@
 
     sdio_info("sleep: %d, %d\n", sdiodev->state, scanning);
     if (sdiodev->state == SDIO_ACTIVE_ST  && !scanning && !rwnx_hw->is_p2p_alive \
-                && !rwnx_hw->is_p2p_connected && dhcped) {
+                && !rwnx_hw->is_p2p_connected) {
         down(&sdiodev->pwrctl_wakeup_sema);
         if (rwnx_hw->vif_started) {
             sdio_dbg("s\n");
-            ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x10);
+            ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_SLEEP_REG, 0x10);
             if (ret)
                	sdio_err("Write sleep fail!\n");
         }
@@ -785,7 +727,7 @@
     if (sdiodev->bus_if->state == BUS_DOWN_ST) {
         *byte_len = 0;
     } else {
-        ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.bytemode_len_reg, byte_len);
+        ret = aicwf_sdio_readb(sdiodev, SDIOWIFI_BYTEMODE_LEN_REG, byte_len);
         sdiodev->rx_priv->data_len = (*byte_len)*4;
     }
 
@@ -874,7 +816,7 @@
 
     ret = aicwf_sdio_recv_pkt(sdiodev, rxbuff, size, msg);
     if (ret) {
-        printk("%s %d, sdio recv pkt fail", __func__, ret);
+        printk("%s %d, sdio recv pkt fail", __func__, __LINE__);
 #if 0
         rxbuff_free(rxbuff);
 #else
@@ -906,7 +848,7 @@
 
     ret = aicwf_sdio_recv_pkt(sdiodev, skb, size, msg);
     if (ret) {
-        printk("%s %d, recv pkt error\n", __func__, ret);
+        printk("%s %d, recv pkt error\n", __func__, __LINE__);
         dev_kfree_skb(skb);
         skb = NULL;
     }
@@ -943,49 +885,27 @@
     } else
         len = payload_len;
 
-	if(sdiodev->chipid == PRODUCT_ID_AIC8800D80){
-		buffer_cnt = aicwf_sdio_flow_ctrl_msg(sdiodev);
-		while ((buffer_cnt <= 0 || (buffer_cnt > 0 && len > (buffer_cnt * BUFFER_SIZE))) && retry < 10) {
-			retry++;
-			buffer_cnt = aicwf_sdio_flow_ctrl_msg(sdiodev);
-			printk("buffer_cnt = %d\n", buffer_cnt);
-		}
-	}else if(sdiodev->chipid == PRODUCT_ID_AIC8800DC ||sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-		if (!func_flag){
-		    buffer_cnt = aicwf_sdio_flow_ctrl_msg(sdiodev);
-		    while ((buffer_cnt <= 0 || (buffer_cnt > 0 && len > (buffer_cnt * BUFFER_SIZE))) && retry < 10) {
-		        retry++;
-		        buffer_cnt = aicwf_sdio_flow_ctrl_msg(sdiodev);
-		    }
-		}
-	}
+if (!func_flag){
+    buffer_cnt = aicwf_sdio_flow_ctrl_msg(sdiodev);
+    while ((buffer_cnt <= 0 || (buffer_cnt > 0 && len > (buffer_cnt * BUFFER_SIZE))) && retry < 10) {
+        retry++;
+        buffer_cnt = aicwf_sdio_flow_ctrl_msg(sdiodev);
+    }
+}
     printk("sdio txmsg: %d\n", buffer_cnt);
     down(&sdiodev->tx_priv->cmd_txsema);
-	if(sdiodev->chipid == PRODUCT_ID_AIC8800DC ||sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-		if(((!func_flag) && (buffer_cnt > 0 && len <= (buffer_cnt * BUFFER_SIZE))) || func_flag) {
-		    //err = aicwf_sdio_send_pkt(sdiodev, payload, len);
-		    err = aicwf_sdio_send_msg(sdiodev, payload, len);
-		    if (err) {
-		        sdio_err("aicwf_sdio_send_pkt fail%d\n", err);
-		    }
-		}else {
-		    sdio_err("tx msg fc retry fail\n");
-		    up(&sdiodev->tx_priv->cmd_txsema);
-		    return -1;
-		}
-	}else if (sdiodev->chipid == PRODUCT_ID_AIC8800D80){
-		if (buffer_cnt > 0 && len < (buffer_cnt * BUFFER_SIZE)) {
-			err = aicwf_sdio_send_pkt(sdiodev, payload, len);
-			if (err) {
-				sdio_err("aicwf_sdio_send_pkt fail%d\n", err);
-			}
-		} else {
-			sdio_err("tx msg fc retry fail:%d, %d\n", buffer_cnt, len);
-			up(&sdiodev->tx_priv->cmd_txsema);
-			return -1;
-		}
-	}
-	
+    if(((!func_flag) && (buffer_cnt > 0 && len <= (buffer_cnt * BUFFER_SIZE))) || func_flag) {
+        //err = aicwf_sdio_send_pkt(sdiodev, payload, len);
+        err = aicwf_sdio_send_msg(sdiodev, payload, len);
+        if (err) {
+            sdio_err("aicwf_sdio_send_pkt fail%d\n", err);
+        }
+    } else {
+        sdio_err("tx msg fc retry fail\n");
+        up(&sdiodev->tx_priv->cmd_txsema);
+        return -1;
+    }
+
     sdiodev->tx_priv->cmd_txstate = false;
     if (!err)
         sdiodev->tx_priv->cmd_tx_succ= true;
@@ -1096,9 +1016,9 @@
         return;
     }
 
-    if (!aicwf_is_framequeue_empty(&sdiodev->tx_priv->txq)){
+     if (!aicwf_is_framequeue_empty(&sdiodev->tx_priv->txq)){
         sdiodev->tx_priv->fw_avail_bufcnt = aicwf_sdio_flow_ctrl(sdiodev);
-    }
+     }
 
     #ifdef CONFIG_SDIO_AGGR
 	while (!aicwf_is_framequeue_empty(&sdiodev->tx_priv->txq)) {
@@ -1119,34 +1039,34 @@
         	}
 	}
     #else
-    while (!aicwf_is_framequeue_empty(&sdiodev->tx_priv->txq)) {
+     while (!aicwf_is_framequeue_empty(&sdiodev->tx_priv->txq)) {
         if(sdiodev->tx_priv->cmd_txstate || (sdiodev->bus_if->state == BUS_DOWN_ST)){
             break;
 	    }
         if(sdiodev->tx_priv->fw_avail_bufcnt <= DATA_FLOW_CTRL_THRESH) {
 			sdiodev->tx_priv->fw_avail_bufcnt = aicwf_sdio_flow_ctrl(sdiodev);
         } else {
-//spin_lock_bh(&sdiodev->tx_priv->txqlock);//old
-    	    struct sk_buff *skb=NULL;
+        //spin_lock_bh(&sdiodev->tx_priv->txqlock);//old
+	    struct sk_buff *skb=NULL;
             spin_lock_bh(&sdiodev->tx_priv->txqlock);
             skb = aicwf_txframe_dequeue(&sdiodev->tx_priv->txq);
-            if (skb == NULL) {
-                sdio_err("txq no pkt\n");
+        if (skb == NULL) {
+            sdio_err("txq no pkt\n");
                 spin_unlock_bh(&sdiodev->tx_priv->txqlock);
-                break;
-            }
-            atomic_dec(&sdiodev->tx_priv->tx_pktcnt);
-            spin_unlock_bh(&sdiodev->tx_priv->txqlock);
-
-            aicwf_sdio_send_single_pkt(skb, sdiodev);
-            sdiodev->tx_priv->fw_avail_bufcnt--;
+            break;
         }
+        atomic_dec(&sdiodev->tx_priv->tx_pktcnt);
+            spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+        
+        aicwf_sdio_send_single_pkt(skb, sdiodev);
+        sdiodev->tx_priv->fw_avail_bufcnt--;
+	    }
     }
 
     #ifdef CONFIG_TX_NETIF_FLOWCTRL
         spin_lock_irqsave(&sdiodev->tx_flow_lock, flags);
         if (atomic_read(&sdiodev->tx_priv->tx_pktcnt) < tx_fc_low_water) {
-                        if (sdiodev->flowctrl) {
+            if (sdiodev->flowctrl) {
                 sdiodev->flowctrl = 0;
                 aicwf_sdio_tx_netif_flowctrl(sdiodev->rwnx_hw, false);
             }
@@ -1169,19 +1089,19 @@
 #endif
 
     prio = (pkt->priority & 0xF);
-    if(prio >(AICWF_TXQ_CNT - 1)){
-        //printk("prio eceed: %d\n", prio);
-        prio = 0;
-    }
+	if(prio >(AICWF_TXQ_CNT - 1)){
+		//printk("prio eceed: %d\n", prio);
+		prio = 0;
+	}
     spin_lock_bh(&sdiodev->tx_priv->txqlock);
     if (!aicwf_txframe_enq(sdiodev->dev, &sdiodev->tx_priv->txq, pkt, prio)) {
         struct rwnx_txhdr *txhdr = (struct rwnx_txhdr *)pkt->data;
-        int headroom = txhdr->sw_hdr.headroom;
-        //kmem_cache_free(txhdr->sw_hdr->rwnx_vif->rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
-        skb_pull(pkt, headroom);
-        consume_skb(pkt);
+		int headroom = txhdr->sw_hdr.headroom;
+		//kmem_cache_free(txhdr->sw_hdr->rwnx_vif->rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+		skb_pull(pkt, headroom);
+		consume_skb(pkt);
         spin_unlock_bh(&sdiodev->tx_priv->txqlock);
-        ret = -ENOSR;
+		ret = -ENOSR;
         goto flowctrl;
     } else {
         ret = 0;
@@ -1199,7 +1119,7 @@
 //#ifdef CONFIG_SDIO_AGGR
     if (atomic_read(&sdiodev->tx_priv->tx_pktcnt) == 1)
 //#endif
-        complete(&bus_if->bustx_trgg);
+    complete(&bus_if->bustx_trgg);
 #else
     tasklet_schedule(&sdiodev->tx_priv->tx_tasklet);
 #endif
@@ -1207,7 +1127,7 @@
 #ifdef CONFIG_TX_NETIF_FLOWCTRL
     spin_lock_irqsave(&sdiodev->tx_flow_lock, flags);
     if (atomic_read(&sdiodev->tx_priv->tx_pktcnt) >= tx_fc_high_water) {
-                if (!sdiodev->flowctrl) {
+        if (!sdiodev->flowctrl) {
             sdiodev->flowctrl = 1;
             aicwf_sdio_tx_netif_flowctrl(sdiodev->rwnx_hw, true);
         }
@@ -1294,10 +1214,10 @@
 		}
 		atomic_dec(&sdiodev->tx_priv->tx_pktcnt);
 		spin_unlock_bh(&sdiodev->tx_priv->txqlock);
-        #ifdef CONFIG_TX_NETIF_FLOWCTRL
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
         spin_lock_irqsave(&sdiodev->tx_flow_lock, flags);
         if (atomic_read(&sdiodev->tx_priv->tx_pktcnt) < tx_fc_low_water) {
-                        if (sdiodev->flowctrl) {
+            if (sdiodev->flowctrl) {
                 sdiodev->flowctrl = 0;
                 aicwf_sdio_tx_netif_flowctrl(sdiodev->rwnx_hw, false);
             }
@@ -1338,13 +1258,7 @@
     sdio_header[0] =((pkt->len - sizeof(struct rwnx_txhdr) + sizeof(struct txdesc_api)) & 0xff);
     sdio_header[1] =(((pkt->len - sizeof(struct rwnx_txhdr) + sizeof(struct txdesc_api)) >> 8)&0x0f);
     sdio_header[2] = 0x01; //data
-	if (tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8801 || 
-        tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
-        tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8800DW)
-        sdio_header[3] = 0; //reserved
-    else if (tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8800D80)
-	    sdio_header[3] = crc8_ponl_107(&sdio_header[0], 3); // crc8
-
+    sdio_header[3] = 0; //reserved
 
     memcpy(tx_priv->tail, (u8 *)&sdio_header, sizeof(sdio_header));
     tx_priv->tail += sizeof(sdio_header);
@@ -1362,12 +1276,8 @@
         tx_priv->tail += allign_len;
     }
 
-	if (tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8801 || 
-		tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
-		tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8800DW) {
-	    start_ptr[0] = ((tx_priv->tail - start_ptr - 4) & 0xff);
-	    start_ptr[1] = (((tx_priv->tail - start_ptr - 4)>>8) & 0x0f);
-	}
+    start_ptr[0] = ((tx_priv->tail - start_ptr - 4) & 0xff);
+    start_ptr[1] = (((tx_priv->tail - start_ptr - 4)>>8) & 0x0f);
     tx_priv->aggr_buf->dev = pkt->dev;
 
     if(!txhdr->sw_hdr.need_cfm) {
@@ -1418,62 +1328,45 @@
     struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
     int ret = 0;
 
-	if(sdiodev->chipid == PRODUCT_ID_AIC8800DC || sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-
-	    sdio_claim_host(sdiodev->func);
+    sdio_claim_host(sdiodev->func);
 #if 0
-	    sdio_claim_irq(sdiodev->func, aicwf_sdio_hal_irqhandler);
-	    sdio_claim_irq(sdiodev->func_msg, aicwf_sdio_hal_irqhandler_func2);
+    sdio_claim_irq(sdiodev->func, aicwf_sdio_hal_irqhandler);
+    sdio_claim_irq(sdiodev->func_msg, aicwf_sdio_hal_irqhandler_func2);
 #else
-	    //since we have func2 we don't register irq handler
-	    sdio_claim_irq(sdiodev->func, NULL);
-	    sdio_claim_irq(sdiodev->func_msg, NULL);
+    //since we have func2 we don't register irq handler
+    sdio_claim_irq(sdiodev->func, NULL);
+    sdio_claim_irq(sdiodev->func_msg, NULL);
 
-	    sdiodev->func->irq_handler = (sdio_irq_handler_t *)aicwf_sdio_hal_irqhandler;
-	    sdiodev->func_msg->irq_handler = (sdio_irq_handler_t *)aicwf_sdio_hal_irqhandler_func2;
+    sdiodev->func->irq_handler = (sdio_irq_handler_t *)aicwf_sdio_hal_irqhandler;
+    sdiodev->func_msg->irq_handler = (sdio_irq_handler_t *)aicwf_sdio_hal_irqhandler_func2;
 #endif 
-	    sdio_release_host(sdiodev->func);
+    sdio_release_host(sdiodev->func);
 
-	    //enable sdio interrupt
-	    ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x07);
+    //enable sdio interrupt
+    ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_INTR_CONFIG_REG, 0x07);
 
-	    if (ret != 0)
-	        sdio_err("intr register failed:%d\n", ret);
+    if (ret != 0)
+        sdio_err("intr register failed:%d\n", ret);
 
-	    //enable sdio interrupt
-	    ret = aicwf_sdio_writeb_func2(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x07);
+    //enable sdio interrupt
+    ret = aicwf_sdio_writeb_func2(sdiodev, SDIOWIFI_INTR_CONFIG_REG, 0x07);
 
-	    if (ret != 0)
-	        sdio_err("func2 intr register failed:%d\n", ret);
+    if (ret != 0)
+        sdio_err("func2 intr register failed:%d\n", ret);
 
-	}else if(sdiodev->chipid == PRODUCT_ID_AIC8800D80){
-		sdio_claim_host(sdiodev->func);
-		sdio_claim_irq(sdiodev->func, aicwf_sdio_hal_irqhandler);
-
-        sdio_f0_writeb(sdiodev->func, 0x07, 0x04, &ret);
-        if (ret) {
-            sdio_err("set func0 int en fail %d\n", ret);
-        }
-        sdio_release_host(sdiodev->func);
-		//enable sdio interrupt
-		ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x07);
-		if (ret != 0)
-			sdio_err("intr register failed:%d\n", ret);
-	}
     bus_if->state = BUS_UP_ST;
 
     return ret;
 }
 
-
-static inline void aic_thread_wait_stop(void)
+void aic_thread_wait_stop(void)
 {
-	set_current_state(TASK_INTERRUPTIBLE);
-	while (!kthread_should_stop()) {
-		schedule();
-		set_current_state(TASK_INTERRUPTIBLE);
-	}
-	__set_current_state(TASK_RUNNING);
+    set_current_state(TASK_INTERRUPTIBLE);
+    while (!kthread_should_stop()) {
+            schedule();
+            set_current_state(TASK_INTERRUPTIBLE);
+    }
+    __set_current_state(TASK_RUNNING);
 }
 #ifdef CONFIG_TXRX_THREAD_PRIO
 int bustx_thread_prio = 36;
@@ -1502,8 +1395,10 @@
 //            break;
 //        }
         if (!wait_for_completion_interruptible(&bus->bustx_trgg)) {
-	    if(sdiodev->bus_if->state == BUS_DOWN_ST)
+		    if(sdiodev->bus_if->state == BUS_DOWN_ST){
+				printk("rwnx tx thread break.\n");
                 break;
+			}
 
             while((int)(atomic_read(&sdiodev->tx_priv->tx_pktcnt) > 0) || (sdiodev->tx_priv->cmd_txstate == true)) {
                 aicwf_sdio_tx_process(sdiodev);
@@ -1512,8 +1407,8 @@
             }
         }
     }
-    aic_thread_wait_stop();
-
+	aic_thread_wait_stop();
+	printk("rwnx tx thread exit.\n");
     return 0;
 }
 #else
@@ -1559,12 +1454,15 @@
 //            break;
 //        }
         if (!wait_for_completion_interruptible(&bus_if->busrx_trgg)) {
-            if(bus_if->state == BUS_DOWN_ST)
+		    if(bus_if->state == BUS_DOWN_ST){
+				printk("rwnx rx thread break.\n");
                 break;
+			}
             aicwf_process_rxframes(rx_priv);
         }
     }
-    aic_thread_wait_stop();
+	aic_thread_wait_stop();
+	printk("rwnx rx thread exit.\n");
     return 0;
 }
 #else
@@ -1700,96 +1598,32 @@
     } 
 #endif
 
-    if (sdiodev->chipid == PRODUCT_ID_AIC8801 || sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
-        sdiodev->chipid == PRODUCT_ID_AIC8800DW) {
-		ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.block_cnt_reg, &intstatus);
-		while(ret || (intstatus & SDIO_OTHER_INTERRUPT)) {
-		    sdio_err("ret=%d, intstatus=%x\r\n",ret, intstatus);
-		    ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.block_cnt_reg, &intstatus);
-		}
-		sdiodev->rx_priv->data_len = intstatus * SDIOWIFI_FUNC_BLOCKSIZE;
-
-		if (intstatus > 0) {
-		    if(intstatus < 64) {
-		        pkt = aicwf_sdio_readframes(sdiodev, 0);
-		    } else {
-		        aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len);//byte_len must<= 128
-		        sdio_info("byte mode len=%d\r\n", byte_len);
-		        pkt = aicwf_sdio_readframes(sdiodev, 0);
-		    }
-		} else {
-#ifndef CONFIG_PLATFORM_ALLWINNER
-		    sdio_err("Interrupt but no data\n");
-#endif
-		}
-
-		if (pkt)
-		    aicwf_sdio_enq_rxpkt(sdiodev, pkt);
-
-		if (atomic_read(&sdiodev->rx_priv->rx_cnt) == 1)
-		    complete(&bus_if->busrx_trgg);
-
-	}else if (sdiodev->chipid == PRODUCT_ID_AIC8800D80) {
-        do {
-            ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.misc_int_status_reg, &intstatus);
-            if (!ret) {
-                break;
-            }
-            sdio_err("ret=%d, intstatus=%x\r\n",ret, intstatus);
-        } while (1);
-        if (intstatus & SDIO_OTHER_INTERRUPT) {
-            u8 int_pending;
-            ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.sleep_reg, &int_pending);
-            if (ret < 0) {
-                sdio_err("reg:%d read failed!\n", sdiodev->sdio_reg.sleep_reg);
-            }
-            int_pending &= ~0x01; // dev to host soft irq
-            ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.sleep_reg, int_pending);
-            if (ret < 0) {
-                sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.sleep_reg);
-            }
-        }
-
-        if (intstatus > 0) {
-            uint8_t intmaskf2 = intstatus | (0x1UL << 3);
-            if (intmaskf2 > 120U) { // func2
-                if (intmaskf2 == 127U) { // byte mode
-                    //aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len, 1);//byte_len must<= 128
-                    aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len);//byte_len must<= 128
-                    sdio_info("byte mode len=%d\r\n", byte_len);
-                    //pkt = aicwf_sdio_readframes(sdiodev, 1);
-                    pkt = aicwf_sdio_readframes(sdiodev,0);
-                } else { // block mode
-                    sdiodev->rx_priv->data_len = (intstatus & 0x7U) * SDIOWIFI_FUNC_BLOCKSIZE;
-                    //pkt = aicwf_sdio_readframes(sdiodev, 1);
-                    pkt = aicwf_sdio_readframes(sdiodev,0);
-                }
-            } else { // func1
-                if (intstatus == 120U) { // byte mode
-                    //aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len, 0);//byte_len must<= 128
-                    aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len);//byte_len must<= 128
-                    sdio_info("byte mode len=%d\r\n", byte_len);
-                    //pkt = aicwf_sdio_readframes(sdiodev, 0);
-                    pkt = aicwf_sdio_readframes(sdiodev,0);
-                } else { // block mode
-                    sdiodev->rx_priv->data_len = (intstatus & 0x7FU) * SDIOWIFI_FUNC_BLOCKSIZE;
-                    //pkt = aicwf_sdio_readframes(sdiodev, 0);
-                    pkt = aicwf_sdio_readframes(sdiodev,0);
-                }
-    		}
-        } else {
-    #ifndef CONFIG_PLATFORM_ALLWINNER
-            //sdio_err("Interrupt but no data\n");
-    #endif
-        }
-
-        if (pkt)
-            aicwf_sdio_enq_rxpkt(sdiodev, pkt);
-
-        if(atomic_read(&sdiodev->rx_priv->rx_cnt) == 1){
-            complete(&bus_if->busrx_trgg);
-        }
+    ret = aicwf_sdio_readb(sdiodev, SDIOWIFI_BLOCK_CNT_REG, &intstatus);
+    while(ret || (intstatus & SDIO_OTHER_INTERRUPT)) {
+        sdio_err("ret=%d, intstatus=%x\r\n",ret, intstatus);
+        ret = aicwf_sdio_readb(sdiodev, SDIOWIFI_BLOCK_CNT_REG, &intstatus);
     }
+    sdiodev->rx_priv->data_len = intstatus * SDIOWIFI_FUNC_BLOCKSIZE;
+
+    if (intstatus > 0) {
+        if(intstatus < 64) {
+            pkt = aicwf_sdio_readframes(sdiodev, 0);
+        } else {
+            aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len);//byte_len must<= 128
+            sdio_info("byte mode len=%d\r\n", byte_len);
+            pkt = aicwf_sdio_readframes(sdiodev, 0);
+        }
+    } else {
+	#ifndef CONFIG_PLATFORM_ALLWINNER
+        //sdio_err("Int no data\n");
+	#endif
+    }
+
+    if (pkt)
+        aicwf_sdio_enq_rxpkt(sdiodev, pkt);
+
+    if (atomic_read(&sdiodev->rx_priv->rx_cnt) == 1)
+    complete(&bus_if->busrx_trgg);
 }
 
 
@@ -1820,11 +1654,11 @@
     } 
 #endif
 
-    ret = aicwf_sdio_readb_func2(sdiodev, sdiodev->sdio_reg.block_cnt_reg, &intstatus);
+    ret = aicwf_sdio_readb_func2(sdiodev, SDIOWIFI_BLOCK_CNT_REG, &intstatus);
 
     while(ret || (intstatus & SDIO_OTHER_INTERRUPT)) {
         sdio_err("ret=%d, intstatus=%x\r\n",ret, intstatus);
-        ret = aicwf_sdio_readb_func2(sdiodev, sdiodev->sdio_reg.block_cnt_reg, &intstatus);
+        ret = aicwf_sdio_readb_func2(sdiodev, SDIOWIFI_BLOCK_CNT_REG, &intstatus);
     }
     sdiodev->rx_priv->data_len = intstatus * SDIOWIFI_FUNC_BLOCKSIZE;
     if (intstatus > 0) {
@@ -1882,9 +1716,9 @@
     sdio_dbg("%s\n", __func__);
     sdio_claim_host(sdiodev->func_msg);
     //disable sdio interrupt
-    ret = aicwf_sdio_writeb_func2(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x0);
+    ret = aicwf_sdio_writeb_func2(sdiodev, SDIOWIFI_INTR_CONFIG_REG, 0x0);
     if (ret < 0) {
-        sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.intr_config_reg);
+        sdio_err("reg:%d write failed!\n", SDIOWIFI_INTR_CONFIG_REG);
     }
     sdio_release_irq(sdiodev->func_msg);
     sdio_release_host(sdiodev->func_msg);
@@ -1902,9 +1736,9 @@
 
     sdio_claim_host(sdiodev->func);
     //disable sdio interrupt
-    ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x0);
+    ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_INTR_CONFIG_REG, 0x0);
     if (ret < 0) {
-        sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.intr_config_reg);
+        sdio_err("reg:%d write failed!\n", SDIOWIFI_INTR_CONFIG_REG);
     }
     sdio_release_irq(sdiodev->func);
     sdio_release_host(sdiodev->func);
@@ -1920,6 +1754,7 @@
     if (sdiodev->cmd_mgr.cmd_thread) {
         complete_all(&sdiodev->cmd_mgr.cmdproc_trgg);
         kthread_stop(sdiodev->cmd_mgr.cmd_thread);
+		printk("rwnx cmd kthread stop.\n");
         sdiodev->cmd_mgr.cmd_thread = NULL;
     }
 #endif
@@ -1931,40 +1766,11 @@
     sdio_dbg("exit %s\n", __func__);
 }
 
-#define FEATURE_SDIO_PHASE          2          // 0: default, 2: 180
+#define FEATURE_SDIO_PHASE          0        // 0: default, 2: 180°
 int sdio_phase = FEATURE_SDIO_PHASE;
 module_param(sdio_phase, int, 0644);
 MODULE_PARM_DESC(sdio_phase, "sdio_phase:0: default, 2: 180");
 
-void aicwf_sdio_reg_init(struct aic_sdio_dev *sdiodev)
-{
-    sdio_dbg("%s\n", __func__);
-
-    if(sdiodev->chipid == PRODUCT_ID_AIC8801 || sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
-       sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-        sdiodev->sdio_reg.bytemode_len_reg =       SDIOWIFI_BYTEMODE_LEN_REG;
-        sdiodev->sdio_reg.intr_config_reg =        SDIOWIFI_INTR_CONFIG_REG;
-        sdiodev->sdio_reg.sleep_reg =              SDIOWIFI_SLEEP_REG;
-        sdiodev->sdio_reg.wakeup_reg =             SDIOWIFI_WAKEUP_REG;
-        sdiodev->sdio_reg.flow_ctrl_reg =          SDIOWIFI_FLOW_CTRL_REG;
-        sdiodev->sdio_reg.register_block =         SDIOWIFI_REGISTER_BLOCK;
-        sdiodev->sdio_reg.bytemode_enable_reg =    SDIOWIFI_BYTEMODE_ENABLE_REG;
-        sdiodev->sdio_reg.block_cnt_reg =          SDIOWIFI_BLOCK_CNT_REG;
-        sdiodev->sdio_reg.rd_fifo_addr =           SDIOWIFI_RD_FIFO_ADDR;
-        sdiodev->sdio_reg.wr_fifo_addr =           SDIOWIFI_WR_FIFO_ADDR;
-	} else if (sdiodev->chipid == PRODUCT_ID_AIC8800D80){
-        sdiodev->sdio_reg.bytemode_len_reg =       SDIOWIFI_BYTEMODE_LEN_REG_V3;
-        sdiodev->sdio_reg.intr_config_reg =        SDIOWIFI_INTR_ENABLE_REG_V3;
-        sdiodev->sdio_reg.sleep_reg =              SDIOWIFI_INTR_PENDING_REG_V3;
-        sdiodev->sdio_reg.wakeup_reg =             SDIOWIFI_INTR_TO_DEVICE_REG_V3;
-        sdiodev->sdio_reg.flow_ctrl_reg =          SDIOWIFI_FLOW_CTRL_Q1_REG_V3;
-        sdiodev->sdio_reg.bytemode_enable_reg =    SDIOWIFI_BYTEMODE_ENABLE_REG_V3;
-        sdiodev->sdio_reg.misc_int_status_reg =    SDIOWIFI_MISC_INT_STATUS_REG_V3;
-        sdiodev->sdio_reg.rd_fifo_addr =           SDIOWIFI_RD_FIFO_ADDR_V3;
-        sdiodev->sdio_reg.wr_fifo_addr =           SDIOWIFI_WR_FIFO_ADDR_V3;
-    }
-}
-
 int aicwf_sdio_func_init(struct aic_sdio_dev *sdiodev)
 {
     struct mmc_host *host;
@@ -1996,7 +1802,7 @@
 
 
     #if 1 // limit clock for fpga
-    host->ios.clock = 78000000;
+    host->ios.clock = 100000000;
     #else
     host->ios.clock = 60000000;
     #endif
@@ -2047,116 +1853,6 @@
     return ret;
 }
 
-int aicwf_sdiov3_func_init(struct aic_sdio_dev *sdiodev)
-{
-	struct mmc_host *host;
-	u8 byte_mode_disable = 0x1;//1: no byte mode
-	int ret = 0;
-	uint32_t sdio_clock = 10000000;
-    u8 val;
-	u8 val1 = 0;
-	//struct aicbsp_feature_t feature;
-
-	//aicbsp_get_feature(&feature, NULL);
-    aicwf_sdio_reg_init(sdiodev);
-
-	host = sdiodev->func->card->host;
-
-	sdio_claim_host(sdiodev->func);
-	sdiodev->func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
-
-	ret = sdio_set_block_size(sdiodev->func, SDIOWIFI_FUNC_BLOCKSIZE);
-	if (ret < 0) {
-		sdio_err("set blocksize fail %d\n", ret);
-		sdio_release_host(sdiodev->func);
-		return ret;
-	}
-	ret = sdio_enable_func(sdiodev->func);
-	if (ret < 0) {
-        sdio_err("enable func fail %d.\n", ret);
-		sdio_release_host(sdiodev->func);
-		return ret;
-	}
-
-    sdio_f0_writeb(sdiodev->func, 0x7F, 0xF2, &ret);
-    if (ret) {
-        sdio_err("set fn0 0xF2 fail %d\n", ret);
-        sdio_release_host(sdiodev->func);
-        return ret;
-    }
-#if 1
-    if (host->ios.timing == MMC_TIMING_UHS_DDR50) {
-        val = 0x21;//0x20;//0x1D;//0x5;
-    } else {
-        val = 0x01;//0x00;//0x19;//0x1;
-    }
-    val |= SDIOCLK_FREE_RUNNING_BIT;
-    sdio_f0_writeb(sdiodev->func, val, 0xF0, &ret);
-    if (ret) {
-        sdio_err("set iopad ctrl fail %d\n", ret);
-        sdio_release_host(sdiodev->func);
-        return ret;
-    }
-    sdio_f0_writeb(sdiodev->func, 0x0, 0xF8, &ret);
-    if (ret) {
-        sdio_err("set iopad delay2 fail %d\n", ret);
-        sdio_release_host(sdiodev->func);
-        return ret;
-    }
-    sdio_f0_writeb(sdiodev->func, 0x00, 0xF1, &ret);
-    if (ret) {
-        sdio_err("set iopad delay1 fail %d\n", ret);
-        sdio_release_host(sdiodev->func);
-        return ret;
-    }
-	sdio_f0_writeb(sdiodev->func, 0x20, 0xF3, &ret);
-    if (ret) {
-        sdio_err("set iopad delay3 fail %d\n", ret);
-        sdio_release_host(sdiodev->func);
-        return ret;
-    }
-    msleep(1);
-#if 0//SDIO CLOCK SETTING
-	if ((sdio_clock > 0) && (host->ios.timing != MMC_TIMING_UHS_DDR50)) {
-		host->ios.clock = sdio_clock;
-		host->ops->set_ios(host, &host->ios);
-		sdio_dbg("Set SDIO Clock %d MHz\n", host->ios.clock/1000000);
-	}
-#endif
-#endif
-	sdio_dbg("Set SDIO Clock %d MHz\n", host->ios.clock/1000000);
-	sdio_release_host(sdiodev->func);
-
-	//1: no byte mode
-	ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.bytemode_enable_reg, byte_mode_disable);
-	if (ret < 0) {
-		sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.bytemode_enable_reg);
-		return ret;
-	}
-
-	ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, 0x11);
-	if (ret < 0) {
-		sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.wakeup_reg);
-		return ret;
-	}
-	
-#if 1
-	mdelay(5);
-	ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.sleep_reg, &val1);
-	if (ret < 0) {
-		sdio_err("reg:%d read failed!\n", sdiodev->sdio_reg.sleep_reg);
-		return ret;
-	}
-
-	if(!(val1 & 0x10)){
-		sdio_err("wakeup fail\n");
-	}else{
-		sdio_err("sdio ready\n");
-	}
-#endif
-
-	return ret;
-}
 
 void aicwf_sdio_func_deinit(struct aic_sdio_dev *sdiodev)
 {
@@ -2242,7 +1938,7 @@
 		init_timer(&sdiodev->tp_timer);
 		sdiodev->tp_timer.data = (ulong) sdiodev;
 		sdiodev->tp_timer.function = aicwf_temp_timer;
-//sdiodev->tp_timer.expires  = jiffies + msecs_to_jiffies(TEMP_GET_INTERVAL);
+		//sdiodev->tp_timer.expires  = jiffies + msecs_to_jiffies(TEMP_GET_INTERVAL);
 #else
 		timer_setup(&sdiodev->tp_timer, aicwf_temp_timer, 0);
 #endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/aicwf_sdio.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/aicwf_sdio.h
index 6891d1f..01cca9b 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/aicwf_sdio.h
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/aicwf_sdio.h
@@ -29,26 +29,6 @@
 #define SDIOWIFI_BYTEMODE_ENABLE_REG    0x11
 #define SDIOWIFI_BLOCK_CNT_REG          0x12
 #define SDIOWIFI_FLOWCTRL_MASK_REG      0x7F
-#define SDIOWIFI_WR_FIFO_ADDR			    0x07
-#define SDIOWIFI_RD_FIFO_ADDR			    0x08
-
-#define SDIO_VENDOR_ID_AIC8800D80           0xc8a1
-#define SDIO_DEVICE_ID_AIC8800D80           0x0082
-#define SDIOWIFI_INTR_ENABLE_REG_V3         0x00
-#define SDIOWIFI_INTR_PENDING_REG_V3        0x01
-#define SDIOWIFI_INTR_TO_DEVICE_REG_V3      0x02
-#define SDIOWIFI_FLOW_CTRL_Q1_REG_V3        0x03
-#define SDIOWIFI_MISC_INT_STATUS_REG_V3     0x04
-#define SDIOWIFI_BYTEMODE_LEN_REG_V3        0x05
-#define SDIOWIFI_BYTEMODE_LEN_MSB_REG_V3    0x06
-#define SDIOWIFI_BYTEMODE_ENABLE_REG_V3     0x07
-#define SDIOWIFI_MISC_CTRL_REG_V3           0x08
-#define SDIOWIFI_FLOW_CTRL_Q2_REG_V3        0x09
-#define SDIOWIFI_CLK_TEST_RESULT_REG_V3     0x0A
-#define SDIOWIFI_RD_FIFO_ADDR_V3            0x0F
-#define SDIOWIFI_WR_FIFO_ADDR_V3            0x10
-
-#define SDIOCLK_FREE_RUNNING_BIT        (1 << 6)
 
 #define SDIOWIFI_PWR_CTRL_INTERVAL      30
 #define FLOW_CTRL_RETRY_COUNT           50
@@ -60,8 +40,8 @@
 #define SDIO_ACTIVE_ST                   1
 
 #define TEMP_GET_INTERVAL                (60 * 1000)   //time interval
-#define TEMP_THD_1                       80            //temperature 1 (��)
-#define TEMP_THD_2                       100           //temperature 2 (��)
+#define TEMP_THD_1                       80            //temperature 1 (¡æ)
+#define TEMP_THD_2                       100           //temperature 2 (¡æ)
 #define TEMP_STEP_1                      4             //step (dB)
 #define TEMP_STEP_2                      8             //step (dB)
 #define DATA_FLOW_CTRL_THRESH 2
@@ -82,7 +62,6 @@
 	PRODUCT_ID_AIC8800DW,
 	PRODUCT_ID_AIC8800D80
 };
-
 struct rwnx_hw;
 
 #ifdef LESS_SKB
@@ -96,21 +75,6 @@
 };
 #endif
 
-struct aic_sdio_reg {
-    u8 bytemode_len_reg;
-    u8 intr_config_reg;
-    u8 sleep_reg;
-    u8 wakeup_reg;
-    u8 flow_ctrl_reg;
-    u8 flowctrl_mask_reg;
-    u8 register_block;
-    u8 bytemode_enable_reg;
-    u8 block_cnt_reg;
-    u8 misc_int_status_reg;
-    u8 rd_fifo_addr;
-    u8 wr_fifo_addr;
-};
-
 struct aic_sdio_dev {
     struct rwnx_hw *rwnx_hw;
     struct sdio_func *func;
@@ -141,7 +105,6 @@
     struct semaphore pwrctl_wakeup_sema;
     #endif
 	u16 chipid;
-	struct aic_sdio_reg sdio_reg;
 };
 int aicwf_sdio_writeb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val);
 void aicwf_sdio_hal_irqhandler(struct sdio_func *func);
@@ -155,7 +118,6 @@
 int aicwf_sdio_pwr_stctl(struct  aic_sdio_dev *sdiodev, uint target);
 #endif
 int aicwf_sdio_func_init(struct aic_sdio_dev *sdiodev);
-int aicwf_sdiov3_func_init(struct aic_sdio_dev *sdiodev);
 void aicwf_sdio_func_deinit(struct aic_sdio_dev *sdiodev);
 #ifdef CONFIG_TX_NETIF_FLOWCTRL
 void aicwf_sdio_tx_netif_flowctrl(struct rwnx_hw *rwnx_hw, bool state);
@@ -190,11 +152,11 @@
 void aicwf_sdio_aggrbuf_reset(struct aicwf_tx_priv* tx_priv);
 extern void aicwf_hostif_ready(void);
 extern void aicwf_hostif_fail(void);
+void aic_thread_wait_stop(void);
 #ifdef CONFIG_PLATFORM_NANOPI
 extern void extern_wifi_set_enable(int is_on);
 extern void sdio_reinit(void);
 #endif /*CONFIG_PLATFORM_NANOPI*/
-uint8_t crc8_ponl_107(uint8_t *p_buffer, uint16_t cal_size);
 
 #endif /* AICWF_SDIO_SUPPORT */
 
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/lmac_mac.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/lmac_mac.h
old mode 100644
new mode 100755
index 11013ee..d35bc5c
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/lmac_mac.h
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/lmac_mac.h
@@ -480,7 +480,7 @@
 	u32 cap; /* use IEEE80211_VHT_CAP_ */
 	struct ieee80211_vht_mcs_info vht_mcs;
 };
-#define RATE_INFO_FLAGS_VHT_MCS  1<<1;
+#define RATE_INFO_FLAGS_VHT_MCS                        1<<1;
 #endif
 
 #ifdef CONFIG_HE_FOR_OLD_KERNEL
@@ -528,7 +528,7 @@
 
 #define IEEE80211_HE_PPE_THRES_MAX_LEN		25
 
-#define RATE_INFO_FLAGS_HE_MCS   1<<4;
+#define RATE_INFO_FLAGS_HE_MCS                  1<<4;
 #define WLAN_EID_EXTENSION  255
 /* Element ID Extensions for Element ID 255 */
 enum ieee80211_eid_ext {
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/lmac_msg.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/lmac_msg.h
index b671c14..6dc0784 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/lmac_msg.h
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/lmac_msg.h
@@ -388,9 +388,6 @@
 	MM_GET_FW_VERSION_REQ,
     MM_GET_FW_VERSION_CFM,
 
-    MM_SET_TXPWR_LVL_ADJ_REQ,
-    MM_SET_TXPWR_LVL_ADJ_CFM,
-
 	/// MAX number of messages
 	MM_MAX,
 };
@@ -1301,38 +1298,14 @@
     s8_l pwrlvl_11ax_2g4[12];
 } txpwr_lvl_conf_v2_t;
 
-typedef struct
-{
-    u8_l enable;
-    s8_l pwrlvl_11b_11ag_2g4[12];
-    s8_l pwrlvl_11n_11ac_2g4[10];
-    s8_l pwrlvl_11ax_2g4[12];
-    s8_l pwrlvl_11a_5g[12];
-    s8_l pwrlvl_11n_11ac_5g[10];
-    s8_l pwrlvl_11ax_5g[12];
-} txpwr_lvl_conf_v3_t;
-
-typedef struct
-{
-    u8_l enable;
-    s8_l pwrlvl_adj_tbl_2g4[3];
-    s8_l pwrlvl_adj_tbl_5g[6];
-} txpwr_lvl_adj_conf_t;
-
 struct mm_set_txpwr_lvl_req
 {
   union {
     txpwr_lvl_conf_t txpwr_lvl;
     txpwr_lvl_conf_v2_t txpwr_lvl_v2;
-	txpwr_lvl_conf_v3_t txpwr_lvl_v3;
   };
 };
 
-struct mm_set_txpwr_lvl_adj_req
-{
-    txpwr_lvl_adj_conf_t txpwr_lvl_adj;
-};
-
 typedef struct
 {
     u8_l loss_enable;
@@ -1350,43 +1323,9 @@
     s8_l chan_142_165;
 } txpwr_ofst_conf_t;
 
-/*
- * pwrofst2x_tbl_2g4[3][3]:
- * +---------------+----------+----------+----------+
- * | RateTyp\ChGrp |  CH_1_4  |  CH_5_9  | CH_10_13 |
- * +---------------+----------+----------+----------+
- * | DSSS          |  [0][0]  |  [0][1]  |  [0][2]  |
- * +---------------+----------+----------+----------+
- * | OFDM_HIGHRATE |  [1][0]  |  [1][1]  |  [1][2]  |
- * +---------------+----------+----------+----------+
- * | OFDM_LOWRATE  |  [2][0]  |  [2][1]  |  [2][2]  |
- * +---------------+----------+----------+----------+
- * pwrofst2x_tbl_5g[3][6]:
- * +---------------+--------------+--------------+----------------+----------------+----------------+----------------+
- * | RateTyp\ChGrp | CH_42(36~50) | CH_58(51~64) | CH_106(98~114) | CH_122(115~130)| CH_138(131~146)| CH_155(147~166)|
- * +---------------+--------------+--------------+----------------+----------------+----------------+----------------+
- * | OFDM_LOWRATE  |    [0][0]    |    [0][1]    |     [0][2]     |     [0][3]     |     [0][4]     |     [0][5]     |
- * +---------------+--------------+--------------+----------------+----------------+----------------+----------------+
- * | OFDM_HIGHRATE |    [1][0]    |    [1][1]    |     [1][2]     |     [1][3]     |     [1][4]     |     [1][5]     |
- * +---------------+--------------+--------------+----------------+----------------+----------------+----------------+
- * | OFDM_MIDRATE  |    [2][0]    |    [2][1]    |     [2][2]     |     [2][3]     |     [2][4]     |     [2][5]     |
- * +---------------+--------------+--------------+----------------+----------------+----------------+----------------+
- */
-
-typedef struct
-{
-    int8_t enable;
-    int8_t pwrofst2x_tbl_2g4[3][3];
-    int8_t pwrofst2x_tbl_5g[3][6];
-} txpwr_ofst2x_conf_t;
-
-
 struct mm_set_txpwr_ofst_req
 {
-    union {
-	  txpwr_ofst_conf_t txpwr_ofst;
-	  txpwr_ofst2x_conf_t txpwr_ofst2x;
-	};
+    txpwr_ofst_conf_t txpwr_ofst;
 };
 
 struct mm_set_stack_start_req {
@@ -1924,7 +1863,7 @@
 };
 struct mm_get_chip_temp_cfm
 {
-        s8_l degree;
+    s8_l degree;
 };
 struct mm_set_vendor_hwconfig_cfm
 {
@@ -1933,6 +1872,11 @@
         struct mm_get_chip_temp_cfm chip_temp_cfm;
     };
 };
+struct mm_set_ap_ps_lvl_req
+{
+    u32_l hwconfig_id;
+    u8_l level;
+};
 struct mm_get_fw_version_cfm
 {
     u8_l fw_version_len;
@@ -2956,5 +2900,26 @@
     u8_l status;
 };
 
+enum vendor_hwconfig_tag{
+	ACS_TXOP_REQ = 0,
+	CHANNEL_ACCESS_REQ,
+	MAC_TIMESCALE_REQ,
+	CCA_THRESHOLD_REQ,
+	BWMODE_REQ,
+	CHIP_TEMP_GET_REQ,
+	AP_PS_LEVEL_SET_REQ,
+};
 
+typedef struct
+{
+    u8_l ap_ps_clk;
+} ap_ps_conf_t;
+enum AIC_WIFI_AP_PS_CLK_MODE
+{
+	AP_PS_CLK_1 = 1,    // 3:7 -> active 3, doze 7
+	AP_PS_CLK_2,        // 5:5 -> active 5, doze 5
+	AP_PS_CLK_3,        // 7:3 -> active 7, doze 3
+	AP_PS_CLK_4,        // 8:2 -> active 8, doze 2
+	AP_PS_CLK_5,        // 9:1 -> active 9, doze 1
+};
 #endif // LMAC_MSG_H_
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_defs.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_defs.h
index 828a473..8701c46 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_defs.h
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_defs.h
@@ -637,11 +637,10 @@
     u8 cur_chanctx;
 
     u8 monitor_vif; /* FW id of the monitor interface, RWNX_INVALID_VIF if no monitor vif at fw level */
+
 #ifdef CONFIG_FILTER_TCP_ACK
-	/* tcp ack management */
 	struct tcp_ack_manage ack_m;
 #endif
-
     /* RoC Management */
     struct rwnx_roc_elem *roc_elem;             /* Information provided by cfg80211 in its remain on channel request */
     u32 roc_cookie_cnt;                         /* Counter used to identify RoC request sent by cfg80211 */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_main.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_main.c
index 4049a3c..b0a1015 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_main.c
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_main.c
@@ -47,18 +47,15 @@
 #ifdef AICWF_USB_SUPPORT
 #include "aicwf_usb.h"
 #endif
-
+#include  "pub_debug_info.h"
 //static u8 wifi_mac_addr[6] = {0};
 
 u8 chip_sub_id;
 u8 chip_mcu_id;
-
 extern const struct aicbsp_firmware *aicbsp_firmware_list;
 extern const struct aicbsp_firmware fw_8800dc_u01[];
 extern const struct aicbsp_firmware fw_8800dc_u02[];
 extern const struct aicbsp_firmware fw_8800dc_h_u02[];
-extern const struct aicbsp_firmware fw_8800d80_u01[];
-extern const struct aicbsp_firmware fw_8800d80_u02[];
 extern struct aicbsp_info_t aicbsp_info;
 
 
@@ -366,9 +363,9 @@
 #endif
 
 static struct ieee80211_iface_limit rwnx_limits[] = {
-    { .max = 1,
+    { .max = 2,
       .types = BIT(NL80211_IFTYPE_STATION)},
-    { .max = 1,
+    { .max = 2,
       .types = BIT(NL80211_IFTYPE_AP)},
     { .max = 1,
       .types = BIT(NL80211_IFTYPE_P2P_CLIENT)},
@@ -620,7 +617,7 @@
     int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
     int len = bcn->len - var_offset;
     u8 * var_pos = buf + var_offset;
-    #if 0
+#if 0
     #define IS_BASIC_RATE(r) (r & 0x80) && ((r & ~0x80) <= (54 * 2))
 
     rate_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
@@ -641,7 +638,7 @@
     } 
 //    else
 // 	printk("ie not found\n");
-    #endif
+#endif
 #endif
     return buf;
 }
@@ -891,6 +888,10 @@
     struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
     struct mm_add_if_cfm add_if_cfm;
     int error = 0;
+#ifdef CONFIG_SET_AP_PS
+    ap_ps_conf_t ap_ps_lvl_tmp;
+    ap_ps_conf_t *ap_ps_lvl;
+#endif
 
     RWNX_DBG(RWNX_FN_ENTRY_STR);
 
@@ -952,6 +953,10 @@
         #ifdef CONFIG_COEX
         rwnx_send_coex_req(rwnx_hw, 1, 0);
         #endif
+        #ifdef CONFIG_SET_AP_PS
+        ap_ps_lvl = & ap_ps_lvl_tmp;
+        rwnx_set_ap_ps_lvl_req(rwnx_hw, get_userconfig_set_ap_ps_lvl(ap_ps_lvl));
+        #endif
     }
 
     if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MONITOR){
@@ -1064,7 +1069,7 @@
             netif_tx_stop_all_queues(dev);
             netif_carrier_off(dev);
         } else if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP_VLAN) {
-			netif_carrier_off(dev);
+            netif_carrier_off(dev);
         } else {
             netdev_warn(dev, "AP not stopped when disabling interface");
         }
@@ -1172,6 +1177,7 @@
     u8_l mode;
     u8_l rate;
     u16_l length;
+    u16_l tx_intv_us;
 }cmd_rf_settx_t;
 
 typedef struct
@@ -1366,8 +1372,13 @@
             settx_param.mode = command_strtoul(argv[3], NULL, 10);
             settx_param.rate = command_strtoul(argv[4], NULL, 10);
             settx_param.length = command_strtoul(argv[5], NULL, 10);
-            printk("txparam:%d,%d,%d,%d,%d\n", settx_param.chan, settx_param.bw,
-                settx_param.mode, settx_param.rate, settx_param.length);
+            if (argc > 6) {
+                settx_param.tx_intv_us = command_strtoul(argv[6], NULL, 10);
+            } else {
+                settx_param.tx_intv_us = 10000; // set default val 10ms
+            }
+            printk("txparam:%d,%d,%d,%d,%d,%d\n", settx_param.chan, settx_param.bw,
+                settx_param.mode, settx_param.rate, settx_param.length, settx_param.tx_intv_us);
             rwnx_send_rftest_req(p_rwnx_hw, SET_TX, sizeof(cmd_rf_settx_t), (u8_l *)&settx_param, NULL);
         } else if (strcasecmp(argv[0], "SET_TXSTOP") == 0) {
             printk("settx_stop\n");
@@ -1591,16 +1602,10 @@
             } else {
                 printk("wrong func: %x\n", func);
             }
-			if(g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
-				memcpy(command, &cfm.rftest_result[0], 6 * 12);
-				bytes_written = 6 * 12;
-			} else {
-				memcpy(command, &cfm.rftest_result[0], 3 * 12);
-				bytes_written = 3 * 12;
-			}
+            memcpy(command, &cfm.rftest_result[0], 3 * 12);
+            bytes_written = 3 * 12;
         } else if (strcasecmp(argv[0], "RDWR_PWROFST") == 0) {
             u8_l func = 0;
-			int res_len = 0;
             printk("read/write txpwr offset\n");
             if (argc > 1) {
                 func = (u8_l)command_strtoul(argv[1], NULL, 16);
@@ -1608,14 +1613,7 @@
             if (func == 0) { // read cur
                 rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWROFST, 0, NULL, &cfm);
             } else if (func <= 2) { // write 2.4g/5g pwr ofst
-				if ((argc > 4) && (g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800D80)) {
-                    u8_l type = (u8_l)command_strtoul(argv[2], NULL, 16);
-                    u8_l chgrp = (u8_l)command_strtoul(argv[3], NULL, 16);
-                    s8_l pwrofst = (u8_l)command_strtoul(argv[4], NULL, 10);
-                    u8_l buf[4] = {func, type, chgrp, (u8_l)pwrofst};
-                    printk("set pwrofst_%s:[%x][%x]=%d\r\n", (func == 1) ? "2.4g" : "5g", type, chgrp, pwrofst);
-                    rwnx_send_rftest_req(g_rwnx_plat->sdiodev->rwnx_hw, RDWR_PWROFST, sizeof(buf), buf, &cfm);
-                }else if ((argc > 3) && (g_rwnx_plat->sdiodev->chipid != PRODUCT_ID_AIC8800D80)) {
+                if (argc > 3) {
                     u8_l chgrp = (u8_l)command_strtoul(argv[2], NULL, 16);
                     s8_l pwrofst = (u8_l)command_strtoul(argv[3], NULL, 10);
                     u8_l buf[3] = {func, chgrp, (u8_l)pwrofst};
@@ -1623,24 +1621,12 @@
                     rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWROFST, sizeof(buf), buf, &cfm);
                 } else {
                     printk("wrong args\n");
-					bytes_written = -EINVAL;
-					break;
                 }
             } else {
                 printk("wrong func: %x\n", func);
-				bytes_written = -EINVAL;
-				break;
             }
-			if ((g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800DC) ||
-                (g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800DW)) {
-	            res_len = 7;
-            }else if (g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800D80) { // 3 * 2 (2.4g) + 3 * 6 (5g)
-                res_len = 3 * 3 + 3 * 6;
-            } else {
-                res_len = 3 + 4;
-            }
-			memcpy(command, &cfm.rftest_result[0], res_len);
-			bytes_written = res_len;
+            memcpy(command, &cfm.rftest_result[0], 7);
+            bytes_written = 7;
         } else if (strcasecmp(argv[0], "RDWR_DRVIBIT") == 0) {
             u8_l func = 0;
             printk("read/write pa drv_ibit\n");
@@ -1665,7 +1651,6 @@
             bytes_written = 16;
         } else if (strcasecmp(argv[0], "RDWR_EFUSE_PWROFST") == 0) {
             u8_l func = 0;
-			int res_len = 0;
             printk("read/write txpwr offset into efuse\n");
             if (argc > 1) {
                 func = (u8_l)command_strtoul(argv[1], NULL, 16);
@@ -1673,14 +1658,7 @@
             if (func == 0) { // read cur
                 rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_PWROFST, 0, NULL, &cfm);
             } else if (func <= 2) { // write 2.4g/5g pwr ofst
-				if ((argc > 4) && (g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800D80)) {
-                    u8_l type = (u8_l)command_strtoul(argv[2], NULL, 16);
-                    u8_l chgrp = (u8_l)command_strtoul(argv[3], NULL, 16);
-                    s8_l pwrofst = (u8_l)command_strtoul(argv[4], NULL, 10);
-                    u8_l buf[4] = {func, type, chgrp, (u8_l)pwrofst};
-                    printk("set efuse pwrofst_%s:[%x][%x]=%d\r\n", (func == 1) ? "2.4g" : "5g", type, chgrp, pwrofst);
-                    rwnx_send_rftest_req(g_rwnx_plat->sdiodev->rwnx_hw, RDWR_EFUSE_PWROFST, sizeof(buf), buf, &cfm);
-                } else if ((argc > 3) && (g_rwnx_plat->sdiodev->chipid != PRODUCT_ID_AIC8800D80)) {
+                if (argc > 3) {
                     u8_l chgrp = (u8_l)command_strtoul(argv[2], NULL, 16);
                     s8_l pwrofst = (u8_l)command_strtoul(argv[3], NULL, 10);
                     u8_l buf[3] = {func, chgrp, (u8_l)pwrofst};
@@ -1688,24 +1666,12 @@
                     rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_PWROFST, sizeof(buf), buf, &cfm);
                 } else {
                     printk("wrong args\n");
-					bytes_written = -EINVAL;
-					break;
                 }
             } else {
                 printk("wrong func: %x\n", func);
-				bytes_written = -EINVAL;
-				break;
             }
-            if ((g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800DC) ||
-                (g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800DW)) { // 6 = 3 (2.4g) * 2
-                res_len = 3 * 2;
-            } else if (g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800D80) { // 3 * 2 (2.4g) + 3 * 6 (5g)
-                res_len = 3 * 3 + 3 * 6;
-            } else { // 7 = 3(2.4g) + 4(5g)
-                res_len = 3 + 4;
-            }
-            memcpy(command, &cfm.rftest_result[0], res_len);
-            bytes_written = res_len;
+            memcpy(command, &cfm.rftest_result[0], 6);
+            bytes_written = 6;
         } else if (strcasecmp(argv[0], "RDWR_EFUSE_DRVIBIT") == 0) {
             u8_l func = 0;
             printk("read/write pa drv_ibit into efuse\n");
@@ -1758,8 +1724,20 @@
                 bytes_written = -EINVAL;
                 break;
             }
+#ifdef CONFIG_SET_AP_PS
+        } else if (strcasecmp(argv[0], "SET_AP_PS_LVL") == 0) {
+            if (argc > 1) {
+                u8_l func = (s8_l)command_strtoul(argv[1], NULL, 10);
+                printk("set ap ps lvl: %d\n", func);
+                set_txpwr_loss_ofst(func);
+                rwnx_set_ap_ps_lvl_req(p_rwnx_hw, func);
+            } else {
+                printk("wrong args\n");
+                bytes_written = -EINVAL;
+                break;
+            }
+#endif
         }
-
         #ifdef CONFIG_RFTEST_USB_BT
         else if (strcasecmp(argv[0], "BT_RESET") == 0) {
             if (argc == 5) {
@@ -3401,10 +3379,10 @@
             list_add_tail(&sta->list, &rwnx_vif->ap.sta_list);
             sta->valid = true;
             rwnx_ps_bh_enable(rwnx_hw, sta, sta->ps.active || me_sta_add_cfm.pm_state);
-
+            
 	    
-            spin_unlock_bh(&rwnx_hw->cb_lock);
-	    		            error = 0;
+			spin_unlock_bh(&rwnx_hw->cb_lock);
+            error = 0;
 
 #if 0
             if(rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP || rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
@@ -3477,94 +3455,101 @@
 #endif
 
 	do {
-		spin_lock_bh(&rwnx_hw->cb_lock);
+	spin_lock_bh(&rwnx_hw->cb_lock);
 		if(list_empty(&rwnx_vif->ap.sta_list)) {
 			spin_unlock_bh(&rwnx_hw->cb_lock);
 			break;
 		}
-	    list_for_each_entry_safe(cur, tmp, &rwnx_vif->ap.sta_list, list) {
-	        if ((!mac) || (!memcmp(cur->mac_addr, mac, ETH_ALEN))) {
-				found = 1;
-				break;
-	    	}
-	    }
+    list_for_each_entry_safe(cur, tmp, &rwnx_vif->ap.sta_list, list) {
+        if ((!mac) || (!memcmp(cur->mac_addr, mac, ETH_ALEN))) {
+			found = 1;
+			break;
+    	}
+    }
 
-        if(found) {
-/* Ensure that we won't process PS change ind */
+	if(found) {
+            /* Ensure that we won't process PS change ind */
             cur->ps.active = false;
             cur->valid = false;
             list_del(&cur->list);
         }
-		spin_unlock_bh(&rwnx_hw->cb_lock);
+            spin_unlock_bh(&rwnx_hw->cb_lock);
 
 		if(found) {
 			netdev_info(dev, "Del sta %d (%pM)", cur->sta_idx, cur->mac_addr);
-			if (cur->vif_idx != cur->vlan_idx) {
-				struct rwnx_vif *vlan_vif;
-				vlan_vif = rwnx_hw->vif_table[cur->vlan_idx];
-				if (vlan_vif->up) {
-					if ((RWNX_VIF_TYPE(vlan_vif) == NL80211_IFTYPE_AP_VLAN) &&
-						(vlan_vif->use_4addr)) {
-						vlan_vif->ap_vlan.sta_4a = NULL;
-					} else {
-						WARN(1, "Deleting sta belonging to VLAN other than AP_VLAN 4A");
-					}
-				}
-			}
+#if (defined CONFIG_HE_FOR_OLD_KERNEL) || (defined CONFIG_VHT_FOR_OLD_KERNEL)
+			if ((rwnx_vif->ap.aic_index < NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX) && (rwnx_vif->ap.aic_index > 0))
+				rwnx_vif->ap.aic_index--;
+			else
+				rwnx_vif->ap.aic_index = 0;
+#endif
+            if (cur->vif_idx != cur->vlan_idx) {
+                struct rwnx_vif *vlan_vif;
+                vlan_vif = rwnx_hw->vif_table[cur->vlan_idx];
+                if (vlan_vif->up) {
+                    if ((RWNX_VIF_TYPE(vlan_vif) == NL80211_IFTYPE_AP_VLAN) &&
+                        (vlan_vif->use_4addr)) {
+                        vlan_vif->ap_vlan.sta_4a = NULL;
+                    } else {
+                        WARN(1, "Deleting sta belonging to VLAN other than AP_VLAN 4A");
+                    }
+                }
+            }
 
 #ifdef AICWF_RX_REORDER
 #ifdef AICWF_SDIO_SUPPORT
-			rx_priv = rwnx_hw->sdiodev->rx_priv;
+            rx_priv = rwnx_hw->sdiodev->rx_priv;
 #else
-			rx_priv = rwnx_hw->usbdev->rx_priv;
+            rx_priv = rwnx_hw->usbdev->rx_priv;
 #endif
-			if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) {
-				BUG();//should be other function
-			}
-			else if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)){
-				macaddr = cur->mac_addr;
-				printk("deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0],macaddr[1],macaddr[2], \
-									   macaddr[3],macaddr[4],macaddr[5]);
-				spin_lock_bh(&rx_priv->stas_reord_lock);
-				list_for_each_entry_safe(reord_info, reord_tmp,
-					&rx_priv->stas_reord_list, list) {
-					printk("reord_mac:%x,%x,%x,%x,%x,%x\r\n", reord_info->mac_addr[0],reord_info->mac_addr[1],reord_info->mac_addr[2], \
-										   reord_info->mac_addr[3],reord_info->mac_addr[4],reord_info->mac_addr[5]);
-					if (!memcmp(reord_info->mac_addr, macaddr, 6)) {
-						reord_deinit_sta(rx_priv, reord_info);
-						break;
-					}
-				}
-				spin_unlock_bh(&rx_priv->stas_reord_lock);
-			}
+            if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) {
+                BUG();//should be other function
+            }
+            else if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)){
+                macaddr = cur->mac_addr;
+                printk("deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0],macaddr[1],macaddr[2], \
+                                       macaddr[3],macaddr[4],macaddr[5]);
+                spin_lock_bh(&rx_priv->stas_reord_lock);
+                list_for_each_entry_safe(reord_info, reord_tmp,
+                    &rx_priv->stas_reord_list, list) {
+                    printk("reord_mac:%x,%x,%x,%x,%x,%x\r\n", reord_info->mac_addr[0],reord_info->mac_addr[1],reord_info->mac_addr[2], \
+                                           reord_info->mac_addr[3],reord_info->mac_addr[4],reord_info->mac_addr[5]);
+                    if (!memcmp(reord_info->mac_addr, macaddr, 6)) {
+                        reord_deinit_sta(rx_priv, reord_info);
+                        break;
+                    }
+                }
+                spin_unlock_bh(&rx_priv->stas_reord_lock);
+            }
 #endif
 
-			rwnx_txq_sta_deinit(rwnx_hw, cur);
-			error = rwnx_send_me_sta_del(rwnx_hw, cur->sta_idx, false);
-			if ((error != 0) && (error != -EPIPE))
-				return error;
+            rwnx_txq_sta_deinit(rwnx_hw, cur);
+            error = rwnx_send_me_sta_del(rwnx_hw, cur->sta_idx, false);
+            if ((error != 0) && (error != -EPIPE))
+                return error;
 
 #ifdef CONFIG_RWNX_BFMER
-			// Disable Beamformer if supported
-			rwnx_bfmer_report_del(rwnx_hw, cur);
-			rwnx_mu_group_sta_del(rwnx_hw, cur);
+            // Disable Beamformer if supported
+            rwnx_bfmer_report_del(rwnx_hw, cur);
+            rwnx_mu_group_sta_del(rwnx_hw, cur);
 #endif /* CONFIG_RWNX_BFMER */
+
 			
 #if CONFIG_DEBUG_FS_AIC
-			rwnx_dbgfs_unregister_rc_stat(rwnx_hw, cur);
+            rwnx_dbgfs_unregister_rc_stat(rwnx_hw, cur);
 #endif
-		}
+	}
 
 		if(mac)
-			break;
+            break;
 	}	while (1);
-
     rwnx_update_mesh_power_mode(rwnx_vif);
 
 	if(!found && mac != NULL)
-		return -ENOENT;
+        return -ENOENT;
 	else
-    	return 0;
+
+    return 0;
 }
 
 void apm_staloss_work_process(struct work_struct *work)
@@ -3599,75 +3584,79 @@
     }
 
     found = false;
-    spin_lock_bh(&rwnx_hw->cb_lock);
-    list_for_each_entry_safe(cur, tmp, &rwnx_vif->ap.sta_list, list) {
-        if ((mac) && (!memcmp(cur->mac_addr, mac, ETH_ALEN))) {
-            found = true;
-            break;
-        }
-    }
+	spin_lock_bh(&rwnx_hw->cb_lock);
+	list_for_each_entry_safe(cur, tmp, &rwnx_vif->ap.sta_list, list) {
+		if ((mac) && (!memcmp(cur->mac_addr, mac, ETH_ALEN))) {
+			found = true;
+			break;
+		}
+	}
 
-    if(found) {
-        cur->ps.active = false;
-        cur->valid = false;
+	if(found) {
+			/* Ensure that we won't process PS change ind */
+			cur->ps.active = false;
+			cur->valid = false;
         list_del(&cur->list);
     }
-    spin_unlock_bh(&rwnx_hw->cb_lock);
+			spin_unlock_bh(&rwnx_hw->cb_lock);
 
 	if(found) {
 		netdev_info(rwnx_vif->ndev, "Del sta %d (%pM)", cur->sta_idx, cur->mac_addr);
-		if (cur->vif_idx != cur->vlan_idx) {
-			struct rwnx_vif *vlan_vif;
-			vlan_vif = rwnx_hw->vif_table[cur->vlan_idx];
-			if (vlan_vif->up) {
-				if ((RWNX_VIF_TYPE(vlan_vif) == NL80211_IFTYPE_AP_VLAN) &&
-					(vlan_vif->use_4addr)) {
-					vlan_vif->ap_vlan.sta_4a = NULL;
-				} else {
-					WARN(1, "Deleting sta belonging to VLAN other than AP_VLAN 4A");
+			if (cur->vif_idx != cur->vlan_idx) {
+				struct rwnx_vif *vlan_vif;
+				vlan_vif = rwnx_hw->vif_table[cur->vlan_idx];
+				if (vlan_vif->up) {
+					if ((RWNX_VIF_TYPE(vlan_vif) == NL80211_IFTYPE_AP_VLAN) &&
+						(vlan_vif->use_4addr)) {
+						vlan_vif->ap_vlan.sta_4a = NULL;
+					} else {
+						WARN(1, "Deleting sta belonging to VLAN other than AP_VLAN 4A");
+					}
 				}
 			}
-		}
+		//    if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP || rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
+		//		cfg80211_del_sta(rwnx_vif->ndev, cur->mac_addr, GFP_KERNEL);
+		//	}
 
 #ifdef AICWF_RX_REORDER
 #ifdef AICWF_SDIO_SUPPORT
-		rx_priv = rwnx_hw->sdiodev->rx_priv;
+			rx_priv = rwnx_hw->sdiodev->rx_priv;
 #else
-		rx_priv = rwnx_hw->usbdev->rx_priv;
+			rx_priv = rwnx_hw->usbdev->rx_priv;
 #endif
-		if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) {
-			BUG();//should be other function
-		} else if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) {
-			macaddr = cur->mac_addr;
-			printk("deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0], macaddr[1], macaddr[2], \
-								   macaddr[3], macaddr[4], macaddr[5]);
-			spin_lock_bh(&rx_priv->stas_reord_lock);
-			list_for_each_entry_safe(reord_info, reord_tmp,
-				&rx_priv->stas_reord_list, list) {
-				printk("reord_mac:%x,%x,%x,%x,%x,%x\r\n", reord_info->mac_addr[0], reord_info->mac_addr[1], reord_info->mac_addr[2], \
-									   reord_info->mac_addr[3], reord_info->mac_addr[4], reord_info->mac_addr[5]);
-				if (!memcmp(reord_info->mac_addr, macaddr, 6)) {
-					reord_deinit_sta(rx_priv, reord_info);
-					break;
+			if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) {
+				BUG();//should be other function
+			} else if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) {
+				macaddr = cur->mac_addr;
+				printk("deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0], macaddr[1], macaddr[2], \
+									   macaddr[3], macaddr[4], macaddr[5]);
+				spin_lock_bh(&rx_priv->stas_reord_lock);
+				list_for_each_entry_safe(reord_info, reord_tmp,
+					&rx_priv->stas_reord_list, list) {
+					printk("reord_mac:%x,%x,%x,%x,%x,%x\r\n", reord_info->mac_addr[0], reord_info->mac_addr[1], reord_info->mac_addr[2], \
+										   reord_info->mac_addr[3], reord_info->mac_addr[4], reord_info->mac_addr[5]);
+					if (!memcmp(reord_info->mac_addr, macaddr, 6)) {
+						reord_deinit_sta(rx_priv, reord_info);
+						break;
+					}
 				}
+				spin_unlock_bh(&rx_priv->stas_reord_lock);
 			}
-			spin_unlock_bh(&rx_priv->stas_reord_lock);
-		}
 #endif
 
-		rwnx_txq_sta_deinit(rwnx_hw, cur);
-		error = rwnx_send_me_sta_del(rwnx_hw, cur->sta_idx, false);
-		if ((error != 0) && (error != -EPIPE))
-			return;
+			rwnx_txq_sta_deinit(rwnx_hw, cur);
+			error = rwnx_send_me_sta_del(rwnx_hw, cur->sta_idx, false);
+			if ((error != 0) && (error != -EPIPE))
+				return;
 
 #ifdef CONFIG_RWNX_BFMER
-		// Disable Beamformer if supported
-		rwnx_bfmer_report_del(rwnx_hw, cur);
-		rwnx_mu_group_sta_del(rwnx_hw, cur);
+			// Disable Beamformer if supported
+			rwnx_bfmer_report_del(rwnx_hw, cur);
+			rwnx_mu_group_sta_del(rwnx_hw, cur);
 #endif /* CONFIG_RWNX_BFMER */
 
 #ifdef CONFIG_DEBUG_FS_AIC
-		rwnx_dbgfs_unregister_rc_stat(rwnx_hw, cur);
+			rwnx_dbgfs_unregister_rc_stat(rwnx_hw, cur);
 #endif
 	}else {
 		printk("sta not found: %pM\n", mac);
@@ -7143,7 +7132,7 @@
 };
 
 uint32_t txgain_map[96] =  {
-#ifdef CONFIG_FPGA_VERIFICATION
+    #ifdef CONFIG_FPGA_VERIFICATION
     0x20c0c971,
     0x20c0c980,
     0x20c0c992,
@@ -7240,7 +7229,7 @@
     0x20c0cba8,
     0x20c0cbbb,
     0x20c0cbd2,
-#else
+    #else
     //11b
     0x00ffd780,
     0x00ffd872,
@@ -7274,13 +7263,12 @@
     0x00ffd672,
     0x00ffd680,
     0x00ffd772,
-    //high
     0x00ffc87d,
     0x00ffc88b,
     0x00ffc979,
     0x00ffc989,
-    0x00ffca7d,
-    0x00ffca88,
+    0x00ffcc4b,
+    0x00ffcc54,
     0x00ffcc5e,
     0x00ffcc69,
     0x00ffcc78,
@@ -7307,13 +7295,12 @@
     0x00ffc689,
     0x00ffc780,
     0x00ffc790,
-    //low
     0x00ffc87d,
     0x00ffc88b,
     0x00ffc979,
     0x00ffc989,
-    0x00ffca7d,
-    0x00ffca88,
+    0x00ffcc4b,
+    0x00ffcc54,
     0x00ffcc5e,
     0x00ffcc69,
     0x00ffcc78,
@@ -7340,12 +7327,10 @@
     0x00ffc689,
     0x00ffc780,
     0x00ffc790,
-#endif
+    #endif
 };
-
 const uint32_t txgain_map_h[96] =
 {
-    //11b
     0xffd888, //11
     0xffd979, //12
     0xffd988, //13
@@ -7378,12 +7363,11 @@
     0xffd688, //8
     0xffd779, //9
     0xffd879, //10
-    //high
     0xffc879, //8
     0xffc96b, //9
     0xffc979, //10
-    0xffca6b, //11
-    0xffca79, //12
+    0xffcc45, //11
+    0xffcc4d, //12
     0xffcc56, //13
     0xffcc60, //14
     0xffcc6b, //15
@@ -7411,12 +7395,11 @@
     0xffc76b, //5
     0xffc779, //6
     0xffc86b, //7
-    //low
     0xffc879, //8
     0xffc96b, //9
     0xffc979, //10
-    0xffca6b, //11
-    0xffca79, //12
+    0xffcc45, //11
+    0xffcc4d, //12
     0xffcc56, //13
     0xffcc60, //14
     0xffcc6b, //15
@@ -7448,37 +7431,37 @@
 
 u32 wifi_txgain_table_24g_8800dcdw[32] =
 {
-    0xA4B22189, //index 0
+    0xA4B22189,
     0x00007825,
-    0xA4B2214B, //index 1
+    0xA4B2214B,
     0x00007825,
-    0xA4B2214F, //index 2
+    0xA4B2214F,
     0x00007825,
-    0xA4B221D5, //index 3
+    0xA4B221D5,
     0x00007825,
-    0xA4B221DC, //index 4
+    0xA4B221DC,
     0x00007825,
-    0xA4B221E5, //index 5
+    0xA4B221E5,
     0x00007825,
-    0xAC9221E5, //index 6
+    0xAC9221E5,
     0x00006825,
-    0xAC9221EF, //index 7
+    0xAC9221EF,
     0x00006825,
-    0xBC9221EE, //index 8
+    0xBC9221EE,
     0x00006825,
-    0xBC9221FF, //index 9
+    0xBC9221FF,
     0x00006825,
-    0xBC9221FF, //index 10
+    0xBC9221FF,
     0x00004025,
-    0xB792203F, //index 11
+    0xB792203F,
     0x00004026,
-    0xDC92203F, //index 12
+    0xDC92203F,
     0x00004025,
-    0xE692203F, //index 13
+    0xE692203F,
     0x00004025,
-    0xFF92203F, //index 14
+    0xFF92203F,
     0x00004035,
-    0xFFFE203F, //index 15
+    0xFFFE203F,
     0x00004832
 };
 
@@ -7517,7 +7500,6 @@
     0x3FF2203F, //index 15
     0x00004001,
 };
-
 u32 wifi_txgain_table_24g_8800dcdw_h[32] =
 {
     0xA55629C9, //index 0
@@ -7553,7 +7535,6 @@
     0xFF5628FF, //index 15
     0x00001025,
 };
-
 u32 wifi_txgain_table_24g_1_8800dcdw_h[32] =
 {
     0x941A2048, //index 0
@@ -7589,37 +7570,36 @@
     0xD73A207F, //index 15
     0x00001825,
 };
-
 u32 wifi_rxgain_table_24g_20m_8800dcdw[64] = {
-    0x82f282d1,//index 0
+    0x82f282d1,
     0x9591a324,
     0x80808419,
     0x000000f0,
-    0x42f282d1,//index 1
+    0x42f282d1,
     0x95923524,
     0x80808419,
     0x000000f0,
-    0x22f282d1,//index 2
+    0x22f282d1,
     0x9592c724,
     0x80808419,
     0x000000f0,
-    0x02f282d1,//index 3
+    0x02f282d1,
     0x9591a324,
     0x80808419,
     0x000000f0,
-    0x06f282d1,//index 4
+    0x06f282d1,
     0x9591a324,
     0x80808419,
     0x000000f0,
-    0x0ef29ad1,//index 5
+    0x0ef29ad1,
     0x9591a324,
     0x80808419,
     0x000000f0,
-    0x0ef29ad3,//index 6
+    0x0ef29ad3,
     0x95923524,
     0x80808419,
     0x000000f0,
-    0x0ef29ad7,//index 7
+    0x0ef29ad7,
     0x9595a324,
     0x80808419,
     0x000000f0,
@@ -7647,46 +7627,48 @@
     0x959f5924,
     0x80808419,
     0x000000f0,
-    0x06f282e6,//index 14
+    0x06f282e6,
     0x959f5924,
     0x80808419,
     0x000000f0,
-    0x0ef29ae6,//index 15
+    0x0ef29ae6,
     0x959f5924,           //loft [35:34]=3
     0x80808419,
     0x000000f0
 };
 
+
+
 u32 wifi_rxgain_table_24g_40m_8800dcdw[64] = {
-    0x83428151,//index 0
+    0x83428151,
     0x9631a328,
     0x80808419,
     0x000000f0,
-    0x43428151,//index 1
+    0x43428151,
     0x96323528,
     0x80808419,
     0x000000f0,
-    0x23428151,//index 2
+    0x23428151,
     0x9632c728,
     0x80808419,
     0x000000f0,
-    0x03428151,//index 3
+    0x03428151,
     0x9631a328,
     0x80808419,
     0x000000f0,
-    0x07429951,//index 4
+    0x07429951,
     0x9631a328,
     0x80808419,
     0x000000f0,
-    0x0f42d151,//index 5
+    0x0f42d151,
     0x9631a328,
     0x80808419,
     0x000000f0,
-    0x0f42d153,//index 6
+    0x0f42d153,
     0x96323528,
     0x80808419,
     0x000000f0,
-    0x0f42d157,//index 7
+    0x0f42d157,
     0x9635a328,
     0x80808419,
     0x000000f0,
@@ -7714,16 +7696,17 @@
     0x963f5928,
     0x80808419,
     0x000000f0,
-    0x07429966,//index 14
+    0x07429966,
     0x963f5928,
     0x80808419,
     0x000000f0,
-    0x0f42d166,//index 15
+    0x0f42d166,
     0x963f5928,
     0x80808419,
     0x000000f0
 };
 
+
 u32 patch_tbl_wifisetting[][2] =
 {
     #if !defined(CONFIG_FPGA_VERIFICATION)
@@ -7771,14 +7754,11 @@
 {
     {0x00110bf0, 0x00181001},
 };
-
-//adap test
 u32 adaptivity_patch_tbl[][2] = {
     {0x000C, 0x0000320A}, //linkloss_thd
     {0x009C, 0x00000000}, //ac_param_conf
     {0x0128, 0xF6140001}, //tx_adaptivity_en
 };
-//adap test
 
 static void patch_config(struct rwnx_hw *rwnx_hw)
 {
@@ -7828,16 +7808,16 @@
         printk("wifisetting_cfg_addr=%x, ldpc_cfg_addr=%x, agc_cfg_addr=%x, txgain_cfg_addr=%x\n", wifisetting_cfg_addr, ldpc_cfg_addr, agc_cfg_addr, txgain_cfg_addr);
 
         if ((chip_sub_id == 0) && (chip_mcu_id == 0)) {
-            for (cnt = 0; cnt < patch_tbl_wifisetting_num; cnt++) {
-                if ((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, wifisetting_cfg_addr + patch_tbl_wifisetting[cnt][0], patch_tbl_wifisetting[cnt][1]))) {
-                    printk("wifisetting %x write fail\n", patch_tbl_wifisetting[cnt][0]);
+        for (cnt = 0; cnt < patch_tbl_wifisetting_num; cnt++) {
+            if ((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, wifisetting_cfg_addr + patch_tbl_wifisetting[cnt][0], patch_tbl_wifisetting[cnt][1]))) {
+                printk("wifisetting %x write fail\n", patch_tbl_wifisetting[cnt][0]);
                 }
             }
         } else {
             for (cnt = 0; cnt < patch_tbl_wifisetting_u02_num; cnt++) {
                 if ((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, wifisetting_cfg_addr + patch_tbl_wifisetting_u02[cnt][0], patch_tbl_wifisetting_u02[cnt][1]))) {
                     printk("wifisetting %x write fail\n", patch_tbl_wifisetting_u02[cnt][0]);
-                }
+        }
             }
         }
         if (ldpc_cfg_size > 512) {// > 0.5KB data
@@ -7857,7 +7837,6 @@
             }
         }
 
-//adap test
         if(adap_test){
             adap_patch_num = sizeof(adaptivity_patch_tbl)/sizeof(u32)/2;
         	for(cnt = 0; cnt < adap_patch_num; cnt++)
@@ -7867,8 +7846,6 @@
         		}
         	}
         }
-//adap test
-
         if (agc_cfg_size > 512) {// > 0.5KB data
             for (i = 0; i < (agc_cfg_size - 512); i += 512) {//each time write 0.5KB
                 ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, agc_cfg_addr + i, 512, agc_cfg_ram + i / 4);
@@ -7906,17 +7883,17 @@
 		ret = aicwf_patch_table_load(rwnx_hw, RWNX_MAC_PATCH_TABLE_8800DC_H_U02);
 	} else {
 		printk("unsupported id: %d\n", chip_sub_id);
-	}
+                }
 	if (ret) {
 	    printk("patch_tbl upload fail: err:%d\r\n", ret);
-	}
-	#endif
+        }
+        #endif
     } else {
 	if ((chip_sub_id == 0) && (chip_mcu_id == 0)) {
-            u32 patch_tbl_rf_func_num = sizeof(patch_tbl_rf_func)/sizeof(u32)/2;
-            for (cnt = 0; cnt < patch_tbl_rf_func_num; cnt++) {
-                if ((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, patch_tbl_rf_func[cnt][0], patch_tbl_rf_func[cnt][1]))) {
-                    printk("patch_tbl_rf_func %x write fail\n", patch_tbl_rf_func[cnt][0]);
+        u32 patch_tbl_rf_func_num = sizeof(patch_tbl_rf_func)/sizeof(u32)/2;
+        for (cnt = 0; cnt < patch_tbl_rf_func_num; cnt++) {
+            if ((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, patch_tbl_rf_func[cnt][0], patch_tbl_rf_func[cnt][1]))) {
+                printk("patch_tbl_rf_func %x write fail\n", patch_tbl_rf_func[cnt][0]);
                 }
             }
         }
@@ -7981,7 +7958,7 @@
 u32 syscfg_tbl_masked_8800dc[][3] = {
     //#ifdef CONFIG_PMIC_SETTING
     #if defined(CONFIG_VRF_DCDC_MODE)
-    {0x7000216C, (0x3 << 2), (0x1 << 2)}, // pmic_pmu_init
+    {0x7000216C, (0x3 << 2), (0x1 << 2)}, // vrf select dcdc mode
     {0x700021BC, (0x3 << 2), (0x1 << 2)},
     {0x70002118, ((0x7 << 4) | (0x1 << 7)), ((0x2 << 4) | (0x1 << 7))},
     {0x70002104, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))},
@@ -8015,7 +7992,6 @@
                  ((0x0 << 0) | (0x1 << 20) | (0x0 << 22))},
     {0x70001028, (0xf << 2), (0x1 << 2)},
     #endif
-    //#endif /* CONFIG_PMIC_SETTING */
     {0x00000000, 0x00000000, 0x00000000}, // last one
 };
 u32 syscfg_tbl_masked_8800dc_h[][3] = {
@@ -8031,7 +8007,6 @@
     {0x70002190, (0x3F << 0), (24 << 0)},
     {0x700021CC, ((0x7 << 4) | (0x1 << 7)), ((0x0 << 4) | (0x0 << 7))},
     {0x700010A0, (0x1 << 11), (0x1 << 11)},
-    //{0x70001034, ((0x1 << 20) | (0x7 << 26)), ((0x0 << 20) | (0x2 << 26))},
     {0x70001038, (0x1 << 8), (0x1 << 8)},
     {0x70001094, (0x3 << 2), (0x0 << 2)},
     {0x700021D0, ((0x1 << 5) | (0x1 << 6)), ((0x1 << 5) | (0x1 << 6))},
@@ -8041,7 +8016,7 @@
     #else
     {0x70001000, ((0x1 << 0) | (0x1 << 20) | (0x1 << 22)),
                  ((0x0 << 0) | (0x1 << 20) | (0x0 << 22))},
-    #endif
+    #endif /* CONFIG_PMIC_SETTING */
     {0x70001028, (0xf << 2), (0x1 << 2)},
     {0x00000000, 0x00000000, 0x00000000}, // last one
 };
@@ -8072,7 +8047,7 @@
 	}
     //printk("%x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata);
     if (((rd_mem_addr_cfm.memdata >> 25) & 0x01UL) == 0x00UL) {
-		chip_mcu_id = 1;
+    chip_mcu_id = 1;
     }
     ret = rwnx_send_dbg_mem_read_req(rwnx_hw, 0x00000020, &rd_mem_addr_cfm);
     if (ret) {
@@ -8082,9 +8057,9 @@
 	
     chip_sub_id = (u8)(rd_mem_addr_cfm.memdata);
     //printk("%x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata);
-	printk("chip_id=%x, chip_sub_id=%x\n", chip_id, chip_sub_id);
+    printk("chip_id=%x, chip_sub_id=%x\n", chip_id, chip_sub_id);
     if (IS_CHIP_ID_H()){
-		printk("IS_CHIP_ID_H\n");
+	printk("IS_CHIP_ID_H\n");
 		aicbsp_firmware_list = fw_8800dc_h_u02;
     }else{
 		if(aicbsp_info.chip_rev == CHIP_REV_U01){
@@ -8120,12 +8095,12 @@
 
     if (chip_mcu_id == 0) {
         if (chip_sub_id == 0) {
-            syscfg_num = sizeof(syscfg_tbl_8800dc_sdio_u01) / sizeof(u32) / 2;
-            for (cnt = 0; cnt < syscfg_num; cnt++) {
-                ret = rwnx_send_dbg_mem_write_req(rwnx_hw, syscfg_tbl_8800dc_sdio_u01[cnt][0], syscfg_tbl_8800dc_sdio_u01[cnt][1]);
-                if (ret) {
-                     printk("%x write fail: %d\n", syscfg_tbl_8800dc_sdio_u01[cnt][0], ret);
-                    return;
+        syscfg_num = sizeof(syscfg_tbl_8800dc_sdio_u01) / sizeof(u32) / 2;
+        for (cnt = 0; cnt < syscfg_num; cnt++) {
+            ret = rwnx_send_dbg_mem_write_req(rwnx_hw, syscfg_tbl_8800dc_sdio_u01[cnt][0], syscfg_tbl_8800dc_sdio_u01[cnt][1]);
+            if (ret) {
+                 printk("%x write fail: %d\n", syscfg_tbl_8800dc_sdio_u01[cnt][0], ret);
+                return;
                 }
             }
         } else if (chip_sub_id == 1) {
@@ -8163,53 +8138,7 @@
             return;
         }
     }
-
 }
-
-u32 aicbsp_syscfg_tbl_8800d80[][2] = {
-};
-
-int aicbsp_system_config_8800d80(struct rwnx_hw *rwnx_hw)
-{
-	int syscfg_num = sizeof(aicbsp_syscfg_tbl_8800d80) / sizeof(u32) / 2;
-	int ret, cnt;
-	for (cnt = 0; cnt < syscfg_num; cnt++) {
-		ret = rwnx_send_dbg_mem_write_req(rwnx_hw, aicbsp_syscfg_tbl_8800d80[cnt][0], aicbsp_syscfg_tbl_8800d80[cnt][1]);
-		if (ret) {
-			printk("%x write fail: %d\n", aicbsp_syscfg_tbl_8800d80[cnt][0], ret);
-			return ret;
-		}
-	}
-	return 0;
-}
-
-static void system_config_d80(struct rwnx_hw *rwnx_hw){
-	u32 mem_addr;
-	struct dbg_mem_read_cfm rd_mem_addr_cfm;
-	u32 btenable = 0;
-	u8 is_chip_id_h = 0;
-	int ret = 0;
-
-	mem_addr = 0x40500000;
-
-	if (rwnx_send_dbg_mem_read_req(rwnx_hw, mem_addr, &rd_mem_addr_cfm))
-		return -1;
-
-	aicbsp_info.chip_rev = (u8)(rd_mem_addr_cfm.memdata >> 16);
-	btenable = 1;
-	aicbsp_info.cpmode = (testmode > 0 ? 1 : 0);
-	
-	printk("%s chip_rev %u cpmode %u \n",__func__,aicbsp_info.chip_rev,aicbsp_info.cpmode);
-	if (aicbsp_info.chip_rev == CHIP_REV_U01)
-        aicbsp_firmware_list = fw_8800d80_u01;
-    if (aicbsp_info.chip_rev == CHIP_REV_U02 || aicbsp_info.chip_rev == CHIP_REV_U03)
-        aicbsp_firmware_list = fw_8800d80_u02;
-    if (aicbsp_system_config_8800d80(rwnx_hw))
-        return -1;
-
-	
-}
-
 extern int aicwf_dpd_result_apply_8800dc(struct rwnx_hw *rwnx_hw, rf_misc_ram_lite_t *dpd_res);
 extern int aicwf_dpd_result_load_8800dc(struct rwnx_hw *rwnx_hw, rf_misc_ram_lite_t *dpd_res);
 
@@ -8251,18 +8180,18 @@
             if (is_file_exist(FW_DPDRESULT_NAME_8800DC) == 1) {
                 printk("%s load dpd bin\n", __func__);
                 ret = aicwf_dpd_result_load_8800dc(rwnx_hw, &dpd_res);
-                if (ret) {
+    if (ret) {
                     printk("load dpd bin fail: %d\n", ret);
                     return ret;
-                }
+    }
             }
-#endif
+    #endif
             if (dpd_res.bit_mask[1]) {
                 ret = aicwf_dpd_result_apply_8800dc(rwnx_hw, &dpd_res);
                 if (ret) {
                     printk("apply dpd bin fail: %d\n", ret);
                     return ret;
-                }
+}
             }
 #else
             {
@@ -8282,30 +8211,6 @@
 	}
 	return 0;
 }
-
-int rf_config_d80(struct rwnx_hw *rwnx_hw){
-	int ret = 0;
-	struct mm_set_rf_calib_cfm cfm;
-
-	if ((ret = rwnx_send_txpwr_lvl_v3_req(rwnx_hw))) {
-		return -1;
-	}
-
-	if ((ret = rwnx_send_txpwr_lvl_adj_req(rwnx_hw))) {
-		return -1;
-	}
-
-	if ((ret = rwnx_send_txpwr_ofst2x_req(rwnx_hw))) {
-		return -1;
-	}
-
-    if ((ret = rwnx_send_rf_calib_req(rwnx_hw, &cfm))) {
-		return -1;
-	}
-
-	return 0 ;
-}
-
 static int start_from_bootrom(struct rwnx_hw *rwnx_hw)
 {
     int ret = 0;
@@ -8339,24 +8244,6 @@
 	return 0;
 }
 
-static int start_from_bootrom_d80(struct rwnx_hw *rwnx_hw)
-{
-	int ret = 0;
-
-	/* memory access */
-	const u32 fw_addr = RAM_FMAC_FW_ADDR;
-	//struct dbg_start_app_cfm start_app_cfm;
-
-	/* fw start */
-	ret = rwnx_send_dbg_start_app_req(rwnx_hw, fw_addr, HOST_START_APP_AUTO);
-	if (ret) {
-		return -1;
-	}
-	//aicbsp_info.hwinfo_r = start_app_cfm.bootstatus & 0xFF;
-
-	return 0;
-}
-
 /**
  *
  */
@@ -8382,6 +8269,7 @@
 
     if (!wiphy) {
         dev_err(rwnx_platform_get_dev(rwnx_plat), "Failed to create new wiphy\n");
+		sc_debug_info_record(MODULE_ID_AP_WIFI,"rwnx cfg80211 wiphy_new failed \n");
         ret = -ENOMEM;
         goto err_out;
     }
@@ -8418,23 +8306,21 @@
 #endif
 
 //#if 1
-	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-		if ((ret = rwnx_parse_configfile(rwnx_hw, AIC_MACCONFIG_NAME, &init_conf[0], "MAC_A1="))) {
-	        printk("read macconfig fail, use random macaddr\n");
-	        get_random_bytes(&dflt_mac[4], 2);
-	        memcpy(init_conf[0].mac_addr, dflt_mac, ETH_ALEN);
-	    }
-	    if ((ret = rwnx_parse_configfile(rwnx_hw, AIC_MACCONFIG_NAME, &init_conf[1], "MAC_A2="))) {
-	        printk("read macconfig fail, use random macaddr\n");
-	        get_random_bytes(&dflt_mac[4], 2);
-	        memcpy(init_conf[1].mac_addr, dflt_mac, ETH_ALEN);
-	    }
-	    if ((ret = rwnx_parse_configfile(rwnx_hw, AIC_MACCONFIG_NAME, &init_conf[2], "MAC_A3="))) {
-	        printk("read macconfig fail, use random macaddr\n");
-	        get_random_bytes(&dflt_mac[4], 2);
-	        memcpy(init_conf[2].mac_addr, dflt_mac, ETH_ALEN);
-	    }
-	}
+	if ((ret = rwnx_parse_configfile(rwnx_hw, AIC_MACCONFIG_NAME, &init_conf[0], "MAC_A1="))) {
+        printk("read macconfig fail, use random macaddr\n");
+        get_random_bytes(&dflt_mac[4], 2);
+        memcpy(init_conf[0].mac_addr, dflt_mac, ETH_ALEN);
+    }
+    if ((ret = rwnx_parse_configfile(rwnx_hw, AIC_MACCONFIG_NAME, &init_conf[1], "MAC_A2="))) {
+        printk("read macconfig fail, use random macaddr\n");
+        get_random_bytes(&dflt_mac[4], 2);
+        memcpy(init_conf[1].mac_addr, dflt_mac, ETH_ALEN);
+    }
+    if ((ret = rwnx_parse_configfile(rwnx_hw, AIC_MACCONFIG_NAME, &init_conf[2], "MAC_A3="))) {
+        printk("read macconfig fail, use random macaddr\n");
+        get_random_bytes(&dflt_mac[4], 2);
+        memcpy(init_conf[2].mac_addr, dflt_mac, ETH_ALEN);
+}
 //#else
 	// if(wifi_mac_addr[0] != 0) 
 	// 	memcpy(init_conf[0].mac_addr, wifi_mac_addr, ETH_ALEN);
@@ -8483,11 +8369,7 @@
     usb_config(rwnx_hw);
 #endif
 
-	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-    	system_config(rwnx_hw);
-	}else if(rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
-		system_config_d80(rwnx_hw);
-	}
+    system_config(rwnx_hw);
 
     if ((ret = rwnx_platform_on(rwnx_hw, NULL)))
         goto err_platon;
@@ -8495,37 +8377,24 @@
 	aicwf_misc_ram_init_8800dc(rwnx_hw);
 #endif
 
-	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-    	patch_config(rwnx_hw);
-	}
+    patch_config(rwnx_hw);
 
 #if defined(CONFIG_START_FROM_BOOTROM)
-	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-    	if ((ret = start_from_bootrom(rwnx_hw)))
-			goto err_lmac_reqs;
-	}else if(rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
-		if ((ret = start_from_bootrom_d80(rwnx_hw)))
-			goto err_lmac_reqs;
-	}
+    if ((ret = start_from_bootrom(rwnx_hw)))
+        goto err_lmac_reqs;
 #endif
-
-	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
-				rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-		if (testmode == 0){
-		    func_flag = false;
-		    aicwf_sdio_release_func2(rwnx_hw->sdiodev);
-		}
-		ret = rwnx_send_set_stack_start_req(rwnx_hw, 1, 0, 0, 0, &set_start_cfm);
-		set_start_cfm.is_5g_support = false;
-	} else {
-		ret = rwnx_send_set_stack_start_req(rwnx_hw, 1, 0, CO_BIT(5), 0, &set_start_cfm);
-	}
-
+if (testmode == 0){
+    func_flag = false;
+    aicwf_sdio_release_func2(rwnx_hw->sdiodev);
+}
+#ifdef USE_5G
+    ret = rwnx_send_set_stack_start_req(rwnx_hw, 1, 0, 0x20, 0, &set_start_cfm);
+#else
+    ret = rwnx_send_set_stack_start_req(rwnx_hw, 1, 0, 0x0, 0, &set_start_cfm);
+#endif
 	if (ret)
 		goto err_lmac_reqs;
 
-	printk("is 5g support = %d, vendor_info = 0x%02X\n", set_start_cfm.is_5g_support, set_start_cfm.vendor_info);
-	rwnx_hw->band_5g_support = set_start_cfm.is_5g_support;
 	ret = rwnx_send_get_fw_version_req(rwnx_hw, &fw_version);
 	memcpy(wiphy->fw_version, fw_version.fw_version, fw_version.fw_version_len>32? 32 : fw_version.fw_version_len);
 	printk("Firmware Version: %s\r\n", fw_version.fw_version);
@@ -8533,10 +8402,9 @@
     wiphy->mgmt_stypes = rwnx_default_mgmt_stypes;
 
     wiphy->bands[NL80211_BAND_2GHZ] = &rwnx_band_2GHz;
-	#ifdef USE_5G
-	if (rwnx_hw->band_5g_support)
-    	wiphy->bands[NL80211_BAND_5GHZ] = &rwnx_band_5GHz;
-	#endif
+#ifdef USE_5G
+    wiphy->bands[NL80211_BAND_5GHZ] = &rwnx_band_5GHz;
+#endif
     wiphy->interface_modes =
     BIT(NL80211_IFTYPE_STATION)     |
     BIT(NL80211_IFTYPE_AP)          |
@@ -8605,13 +8473,14 @@
 #endif
     tasklet_init(&rwnx_hw->task, rwnx_task, (unsigned long)rwnx_hw);
 
-	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-		if (ret = rf_config(rwnx_hw))
-			goto err_lmac_reqs;
-	}else if(rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
-		if (ret = rf_config_d80(rwnx_hw))
-			goto err_lmac_reqs;
-	}
+    if (ret = rf_config(rwnx_hw))
+
+
+
+				goto err_lmac_reqs;
+
+
+
 
     if ((ret = rwnx_send_get_macaddr_req(rwnx_hw, (struct mm_get_mac_addr_cfm *)mac_addr_efuse)))
         goto err_lmac_reqs;
@@ -8679,10 +8548,10 @@
     /* Add an initial station interface */
     vif = rwnx_interface_add(rwnx_hw, "wlan%d", NET_NAME_UNKNOWN,
                             NL80211_IFTYPE_STATION, NULL, init_conf[0]);
-    vif = rwnx_interface_add(rwnx_hw, "wlan%d", NET_NAME_UNKNOWN + 1,
+    vif = rwnx_interface_add(rwnx_hw, "wlan%d-vxd", NET_NAME_UNKNOWN,
                             NL80211_IFTYPE_STATION, NULL, init_conf[1]);
-    vif = rwnx_interface_add(rwnx_hw, "wlan%d", NET_NAME_UNKNOWN + 2,
-                            NL80211_IFTYPE_STATION, NULL, init_conf[2]);
+    //vif = rwnx_interface_add(rwnx_hw, "wlan%d-va1", NET_NAME_UNKNOWN,
+    //                        NL80211_IFTYPE_STATION, NULL, init_conf[2]);
 
     rtnl_unlock();
 
@@ -8754,7 +8623,7 @@
     rwnx_wdev_unregister(rwnx_hw);
     if(rwnx_hw->wiphy && rwnx_hw->wiphy->registered){
         printk("%s wiphy_unregister \r\n", __func__);
-        wiphy_unregister(rwnx_hw->wiphy);
+    wiphy_unregister(rwnx_hw->wiphy);
     }
     rwnx_radar_detection_deinit(&rwnx_hw->radar);
     rwnx_platform_off(rwnx_hw, NULL);
@@ -8809,6 +8678,7 @@
 
 	if ((wait_for_completion_timeout(&hostif_register_done, msecs_to_jiffies(REGISTRATION_TIMEOUT)) == 0) || rwnx_driver_err) {
 		printk("register_driver timeout or error\n");
+		sc_debug_info_record(MODULE_ID_AP_WIFI,"rwnx register_driver timeout or error \n");
 #ifdef AICWF_SDIO_SUPPORT
         aicwf_sdio_exit();
 #endif /* AICWF_SDIO_SUPPORT */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_msg_tx.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_msg_tx.c
index b73ee35..8b3b072 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_msg_tx.c
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_msg_tx.c
@@ -410,7 +410,7 @@
     return rwnx_send_msg(rwnx_hw, start_req_param, 1, MM_START_CFM, NULL);
 }
 
-rwnx_send_get_temp_req(struct rwnx_hw *rwnx_hw, struct mm_set_vendor_hwconfig_cfm *cfm)
+int rwnx_send_get_temp_req(struct rwnx_hw *rwnx_hw, struct mm_set_vendor_hwconfig_cfm *cfm)
 {
 	int error;
 	struct mm_get_chip_temp_req *req;
@@ -418,8 +418,8 @@
 	req = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, sizeof(struct mm_get_chip_temp_req));
 	if (!req)
 		return -ENOMEM;
-	req->hwconfig_id = 5;
-		error = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_HWCONFIG_CFM, cfm);
+	req->hwconfig_id = CHIP_TEMP_GET_REQ;
+	error = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_HWCONFIG_CFM, cfm);
 	if (!error) {
 		printk("get_chip_temp degree=%d\n", cfm->chip_temp_cfm.degree);
 	} else {
@@ -428,6 +428,18 @@
 	}
 	return error;
 }
+int rwnx_set_ap_ps_lvl_req(struct rwnx_hw *rwnx_hw, u8 level)
+{
+	struct mm_set_ap_ps_lvl_req *req;
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+	req = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, sizeof(struct mm_set_ap_ps_lvl_req));
+	if (!req)
+		return -ENOMEM;
+	req->hwconfig_id = AP_PS_LEVEL_SET_REQ;
+	req->level = level;
+	printk("%s:AP_PS_CLK_%d\n", __func__, level);
+	return rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL);
+}
 int rwnx_send_version_req(struct rwnx_hw *rwnx_hw, struct mm_version_cfm *cfm)
 {
     void *void_param;
@@ -983,17 +995,8 @@
         return -ENOMEM;
     }
 
-	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8801){
-    	rf_calib_req->cal_cfg_24g = 0xbf;
-    	rf_calib_req->cal_cfg_5g = 0x3f;
-    } else if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW) {
-        rf_calib_req->cal_cfg_24g = 0x0f8f;
-        rf_calib_req->cal_cfg_5g = 0;
-    } else if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80) {
-    	rf_calib_req->cal_cfg_24g = 0x0f8f;
-    	rf_calib_req->cal_cfg_5g = 0x0f0f;
-    }
-
+    rf_calib_req->cal_cfg_24g = 0x0f8f;
+    rf_calib_req->cal_cfg_5g = 0x3f;
     rf_calib_req->param_alpha = 0x0c34c008;
     rf_calib_req->bt_calib_en = 0;
     rf_calib_req->bt_calib_param = 0x264203;
@@ -1152,42 +1155,42 @@
         for (i = 0; i <= 11; i++)
             txpwr_lvl_v2->pwrlvl_11ax_2g4[i] += txpwr_loss->loss_value;
 	}
-printk("%s:enable:%d\r\n",               __func__, txpwr_lvl_v2->enable);
-		printk("%s:lvl_11b_11ag_1m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[0]);
+        printk("%s:enable:%d\r\n",               __func__, txpwr_lvl_v2->enable);
+        printk("%s:lvl_11b_11ag_1m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[0]);
 #if 0
-		printk("%s:lvl_11b_11ag_2m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[1]);
-		printk("%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[2]);
-		printk("%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]);
-		printk("%s:lvl_11b_11ag_6m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[4]);
-		printk("%s:lvl_11b_11ag_9m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[5]);
-		printk("%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[6]);
-		printk("%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[7]);
-		printk("%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[8]);
-		printk("%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[9]);
-		printk("%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[10]);
-		printk("%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[11]);
-		printk("%s:lvl_11n_11ac_mcs0_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[0]);
-		printk("%s:lvl_11n_11ac_mcs1_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[1]);
-		printk("%s:lvl_11n_11ac_mcs2_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[2]);
-		printk("%s:lvl_11n_11ac_mcs3_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[3]);
-		printk("%s:lvl_11n_11ac_mcs4_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[4]);
-		printk("%s:lvl_11n_11ac_mcs5_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[5]);
-		printk("%s:lvl_11n_11ac_mcs6_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[6]);
-		printk("%s:lvl_11n_11ac_mcs7_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[7]);
-		printk("%s:lvl_11n_11ac_mcs8_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[8]);
-		printk("%s:lvl_11n_11ac_mcs9_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[9]);
-		printk("%s:lvl_11ax_mcs0_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[0]);
-		printk("%s:lvl_11ax_mcs1_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[1]);
-		printk("%s:lvl_11ax_mcs2_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[2]);
-		printk("%s:lvl_11ax_mcs3_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[3]);
-		printk("%s:lvl_11ax_mcs4_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[4]);
-		printk("%s:lvl_11ax_mcs5_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[5]);
-		printk("%s:lvl_11ax_mcs6_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[6]);
-		printk("%s:lvl_11ax_mcs7_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[7]);
-		printk("%s:lvl_11ax_mcs8_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[8]);
-		printk("%s:lvl_11ax_mcs9_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[9]);
-		printk("%s:lvl_11ax_mcs10_2g4:%d\r\n",   __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[10]);
-		printk("%s:lvl_11ax_mcs11_2g4:%d\r\n",   __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[11]);
+        printk("%s:lvl_11b_11ag_2m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[1]);
+        printk("%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[2]);
+        printk("%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]);
+        printk("%s:lvl_11b_11ag_6m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[4]);
+        printk("%s:lvl_11b_11ag_9m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[5]);
+        printk("%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[6]);
+        printk("%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[7]);
+        printk("%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[8]);
+        printk("%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[9]);
+        printk("%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[10]);
+        printk("%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[11]);
+        printk("%s:lvl_11n_11ac_mcs0_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[0]);
+        printk("%s:lvl_11n_11ac_mcs1_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[1]);
+        printk("%s:lvl_11n_11ac_mcs2_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[2]);
+        printk("%s:lvl_11n_11ac_mcs3_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[3]);
+        printk("%s:lvl_11n_11ac_mcs4_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[4]);
+        printk("%s:lvl_11n_11ac_mcs5_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[5]);
+        printk("%s:lvl_11n_11ac_mcs6_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[6]);
+        printk("%s:lvl_11n_11ac_mcs7_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[7]);
+        printk("%s:lvl_11n_11ac_mcs8_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[8]);
+        printk("%s:lvl_11n_11ac_mcs9_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[9]);
+        printk("%s:lvl_11ax_mcs0_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[0]);
+        printk("%s:lvl_11ax_mcs1_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[1]);
+        printk("%s:lvl_11ax_mcs2_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[2]);
+        printk("%s:lvl_11ax_mcs3_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[3]);
+        printk("%s:lvl_11ax_mcs4_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[4]);
+        printk("%s:lvl_11ax_mcs5_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[5]);
+        printk("%s:lvl_11ax_mcs6_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[6]);
+        printk("%s:lvl_11ax_mcs7_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[7]);
+        printk("%s:lvl_11ax_mcs8_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[8]);
+        printk("%s:lvl_11ax_mcs9_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[9]);
+        printk("%s:lvl_11ax_mcs10_2g4:%d\r\n",   __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[10]);
+        printk("%s:lvl_11ax_mcs11_2g4:%d\r\n",   __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[11]);
 #endif
 
 
@@ -1213,185 +1216,6 @@
     }
 }
 
-int rwnx_send_txpwr_lvl_v3_req(struct rwnx_hw *rwnx_hw)
-{
-    struct mm_set_txpwr_lvl_req *txpwr_lvl_req;
-    txpwr_lvl_conf_v3_t txpwr_lvl_v3_tmp;
-    txpwr_lvl_conf_v3_t *txpwr_lvl_v3;
-	txpwr_loss_conf_t txpwr_loss_tmp;
-    txpwr_loss_conf_t *txpwr_loss;
-    int error;
-	int i;
-
-    RWNX_DBG(RWNX_FN_ENTRY_STR);
-
-    /* Build the MM_SET_TXPWR_LVL_REQ message */
-    txpwr_lvl_req = rwnx_msg_zalloc(MM_SET_TXPWR_LVL_REQ, TASK_MM, DRV_TASK_ID,
-                                  sizeof(struct mm_set_txpwr_lvl_req));
-
-    if (!txpwr_lvl_req) {
-        return -ENOMEM;
-    }
-
-    txpwr_lvl_v3 = &txpwr_lvl_v3_tmp;
-    txpwr_loss = &txpwr_loss_tmp;
-    txpwr_loss->loss_enable = 0;
-
-	#ifdef CONFIG_LOAD_USERCONFIG
-    get_userconfig_txpwr_lvl_v3_in_fdrv(txpwr_lvl_v3);
-    get_userconfig_txpwr_loss(txpwr_loss);
-	#endif
-
-    if (txpwr_loss->loss_enable == 1) {
-        printk("%s:loss_value:%d\r\n", __func__, txpwr_loss->loss_value);
-
-        for (i = 0; i <= 11; i++)
-            txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[i] += txpwr_loss->loss_value;
-        for (i = 0; i <= 9; i++)
-            txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[i] += txpwr_loss->loss_value;
-        for (i = 0; i <= 11; i++)
-            txpwr_lvl_v3->pwrlvl_11ax_2g4[i] += txpwr_loss->loss_value;
-
-		for (i = 0; i <= 11; i++)
-            txpwr_lvl_v3->pwrlvl_11a_5g[i] += txpwr_loss->loss_value;
-        for (i = 0; i <= 9; i++)
-            txpwr_lvl_v3->pwrlvl_11n_11ac_5g[i] += txpwr_loss->loss_value;
-        for (i = 0; i <= 11; i++)
-            txpwr_lvl_v3->pwrlvl_11ax_5g[i] += txpwr_loss->loss_value;
-    }
-
-    if (txpwr_lvl_v3->enable == 0) {
-        rwnx_msg_free(rwnx_hw, txpwr_lvl_req);
-        return 0;
-    } else {
-        printk("%s:enable:%d\r\n",               __func__, txpwr_lvl_v3->enable);
-        printk("%s:lvl_11b_11ag_1m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[0]);
-        printk("%s:lvl_11b_11ag_2m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[1]);
-        printk("%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[2]);
-        printk("%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[3]);
-        printk("%s:lvl_11b_11ag_6m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[4]);
-        printk("%s:lvl_11b_11ag_9m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[5]);
-        printk("%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[6]);
-        printk("%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[7]);
-        printk("%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[8]);
-        printk("%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[9]);
-        printk("%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[10]);
-        printk("%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[11]);
-        printk("%s:lvl_11n_11ac_mcs0_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[0]);
-        printk("%s:lvl_11n_11ac_mcs1_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[1]);
-        printk("%s:lvl_11n_11ac_mcs2_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[2]);
-        printk("%s:lvl_11n_11ac_mcs3_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[3]);
-        printk("%s:lvl_11n_11ac_mcs4_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[4]);
-        printk("%s:lvl_11n_11ac_mcs5_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[5]);
-        printk("%s:lvl_11n_11ac_mcs6_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[6]);
-        printk("%s:lvl_11n_11ac_mcs7_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[7]);
-        printk("%s:lvl_11n_11ac_mcs8_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[8]);
-        printk("%s:lvl_11n_11ac_mcs9_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[9]);
-        printk("%s:lvl_11ax_mcs0_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[0]);
-        printk("%s:lvl_11ax_mcs1_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[1]);
-        printk("%s:lvl_11ax_mcs2_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[2]);
-        printk("%s:lvl_11ax_mcs3_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[3]);
-        printk("%s:lvl_11ax_mcs4_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[4]);
-        printk("%s:lvl_11ax_mcs5_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[5]);
-        printk("%s:lvl_11ax_mcs6_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[6]);
-        printk("%s:lvl_11ax_mcs7_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[7]);
-        printk("%s:lvl_11ax_mcs8_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[8]);
-        printk("%s:lvl_11ax_mcs9_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[9]);
-        printk("%s:lvl_11ax_mcs10_2g4:%d\r\n",   __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[10]);
-        printk("%s:lvl_11ax_mcs11_2g4:%d\r\n",   __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[11]);
-
-        printk("%s:lvl_11a_1m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[0]);
-        printk("%s:lvl_11a_2m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[1]);
-        printk("%s:lvl_11a_5m5_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[2]);
-        printk("%s:lvl_11a_11m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[3]);
-        printk("%s:lvl_11a_6m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[4]);
-        printk("%s:lvl_11a_9m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[5]);
-        printk("%s:lvl_11a_12m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[6]);
-        printk("%s:lvl_11a_18m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[7]);
-        printk("%s:lvl_11a_24m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[8]);
-        printk("%s:lvl_11a_36m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[9]);
-        printk("%s:lvl_11a_48m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[10]);
-        printk("%s:lvl_11a_54m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[11]);
-        printk("%s:lvl_11n_11ac_mcs0_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[0]);
-        printk("%s:lvl_11n_11ac_mcs1_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[1]);
-        printk("%s:lvl_11n_11ac_mcs2_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[2]);
-        printk("%s:lvl_11n_11ac_mcs3_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[3]);
-        printk("%s:lvl_11n_11ac_mcs4_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[4]);
-        printk("%s:lvl_11n_11ac_mcs5_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[5]);
-        printk("%s:lvl_11n_11ac_mcs6_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[6]);
-        printk("%s:lvl_11n_11ac_mcs7_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[7]);
-        printk("%s:lvl_11n_11ac_mcs8_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[8]);
-        printk("%s:lvl_11n_11ac_mcs9_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[9]);
-        printk("%s:lvl_11ax_mcs0_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[0]);
-        printk("%s:lvl_11ax_mcs1_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[1]);
-        printk("%s:lvl_11ax_mcs2_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[2]);
-        printk("%s:lvl_11ax_mcs3_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[3]);
-        printk("%s:lvl_11ax_mcs4_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[4]);
-        printk("%s:lvl_11ax_mcs5_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[5]);
-        printk("%s:lvl_11ax_mcs6_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[6]);
-        printk("%s:lvl_11ax_mcs7_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[7]);
-        printk("%s:lvl_11ax_mcs8_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[8]);
-        printk("%s:lvl_11ax_mcs9_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[9]);
-        printk("%s:lvl_11ax_mcs10_5g:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[10]);
-        printk("%s:lvl_11ax_mcs11_5g:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[11]);
-
-        txpwr_lvl_req->txpwr_lvl_v3 = *txpwr_lvl_v3;
-
-        /* Send the MM_SET_TXPWR_LVL_REQ message to UMAC FW */
-        error = rwnx_send_msg(rwnx_hw, txpwr_lvl_req, 1, MM_SET_TXPWR_LVL_CFM, NULL);
-
-        return (error);
-    }
-}
-
-int rwnx_send_txpwr_lvl_adj_req(struct rwnx_hw *rwnx_hw)
-{
-    struct mm_set_txpwr_lvl_adj_req *txpwr_lvl_adj_req;
-    txpwr_lvl_adj_conf_t txpwr_lvl_adj_tmp;
-    txpwr_lvl_adj_conf_t *txpwr_lvl_adj;
-    int error;
-
-    RWNX_DBG(RWNX_FN_ENTRY_STR);
-
-    /* Build the MM_SET_TXPWR_LVL_REQ message */
-    txpwr_lvl_adj_req = rwnx_msg_zalloc(MM_SET_TXPWR_LVL_ADJ_REQ, TASK_MM, DRV_TASK_ID,
-                                  sizeof(struct mm_set_txpwr_lvl_adj_req));
-
-    if (!txpwr_lvl_adj_req) {
-        return -ENOMEM;
-    }
-
-    txpwr_lvl_adj = &txpwr_lvl_adj_tmp;
-
-	#ifdef CONFIG_LOAD_USERCONFIG
-    get_userconfig_txpwr_lvl_adj_in_fdrv(txpwr_lvl_adj);
-	#endif
-
-    if (txpwr_lvl_adj->enable == 0) {
-        rwnx_msg_free(rwnx_hw, txpwr_lvl_adj_req);
-        return 0;
-    } else {
-        printk("%s:enable:%d\r\n",                   __func__, txpwr_lvl_adj->enable);
-        printk( "%s:lvl_adj_2g4_chan_1_4:%d\r\n",     __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[0]);
-        printk("%s:lvl_adj_2g4_chan_5_9:%d\r\n",     __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[1]);
-        printk( "%s:lvl_adj_2g4_chan_10_13:%d\r\n",   __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[2]);
-
-        printk("%s:lvl_adj_5g_chan_42:%d\r\n",       __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[0]);
-        printk("%s:lvl_adj_5g_chan_58:%d\r\n",       __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[1]);
-        printk("%s:lvl_adj_5g_chan_106:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[2]);
-        printk("%s:lvl_adj_5g_chan_122:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[3]);
-        printk("%s:lvl_adj_5g_chan_138:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[4]);
-        printk("%s:lvl_adj_5g_chan_155:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[5]);
-
-        txpwr_lvl_adj_req->txpwr_lvl_adj  = *txpwr_lvl_adj;
-
-        /* Send the MM_SET_TXPWR_LVL_REQ message to UMAC FW */
-        error = rwnx_send_msg(rwnx_hw, txpwr_lvl_adj_req, 1, MM_SET_TXPWR_LVL_ADJ_CFM, NULL);
-
-        return (error);
-    }
-}
-
 int rwnx_send_txpwr_ofst_req(struct rwnx_hw *rwnx_hw)
 {
     struct mm_set_txpwr_ofst_req *txpwr_ofst_req;
@@ -1442,67 +1266,6 @@
     }
 }
 
-int rwnx_send_txpwr_ofst2x_req(struct rwnx_hw *rwnx_hw)
-{
-    struct mm_set_txpwr_ofst_req *txpwr_ofst_req;
-    txpwr_ofst2x_conf_t *txpwr_ofst2x;
-    int error = 0;
-    int type, ch_grp;
-
-    RWNX_DBG(RWNX_FN_ENTRY_STR);
-
-    /* Build the MM_SET_TXPWR_OFST_REQ message */
-    txpwr_ofst_req = rwnx_msg_zalloc(MM_SET_TXPWR_OFST_REQ, TASK_MM, DRV_TASK_ID,
-                                  sizeof(struct mm_set_txpwr_ofst_req));
-
-    if (!txpwr_ofst_req) {
-        return -ENOMEM;
-    }
-
-    txpwr_ofst2x = &txpwr_ofst_req->txpwr_ofst2x;
-    txpwr_ofst2x->enable = 0;
-    for (type = 0; type < 3; type++) {
-        for (ch_grp = 0; ch_grp < 6; ch_grp++) {
-            if (ch_grp < 3) {
-                txpwr_ofst2x->pwrofst2x_tbl_2g4[type][ch_grp] = 0;
-            }
-            txpwr_ofst2x->pwrofst2x_tbl_5g[type][ch_grp] = 0;
-        }
-    }
-	#ifdef CONFIG_LOAD_USERCONFIG
-    if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
-        get_userconfig_txpwr_ofst2x_in_fdrv(txpwr_ofst2x);
-    }
-	#endif
-    if (txpwr_ofst2x->enable){
-        printk("%s:enable:%d\r\n", __func__, txpwr_ofst2x->enable);
-        printk("pwrofst2x 2.4g: [0]:11b, [1]:ofdm_highrate, [2]:ofdm_lowrate\n"
-            "  chan=" "\t1-4" "\t5-9" "\t10-13");
-        for (type = 0; type < 3; type++) {
-            printk("\n  [%d] =", type);
-            for (ch_grp = 0; ch_grp < 3; ch_grp++) {
-                printk("\t%d", txpwr_ofst2x->pwrofst2x_tbl_2g4[type][ch_grp]);
-            }
-        }
-        printk("\npwrofst2x 5g: [0]:ofdm_lowrate, [1]:ofdm_highrate, [2]:ofdm_midrate\n"
-            "  chan=" "\t36-50" "\t51-64" "\t98-114" "\t115-130" "\t131-146" "\t147-166");
-        for (type = 0; type < 3; type++) {
-            printk("\n  [%d] =", type);
-            for (ch_grp = 0; ch_grp < 6; ch_grp++) {
-                printk("\t%d", txpwr_ofst2x->pwrofst2x_tbl_5g[type][ch_grp]);
-            }
-        }
-        printk("\n");
-
-        /* Send the MM_SET_TXPWR_OFST_REQ message to UMAC FW */
-        error = rwnx_send_msg(rwnx_hw, txpwr_ofst_req, 1, MM_SET_TXPWR_OFST_CFM, NULL);
-    }else{
-        printk("%s:Do not use txpwr_ofst2x\r\n", __func__);
-        rwnx_msg_free(rwnx_hw, txpwr_ofst_req);
-    }
-
-    return (error);
-}
 
 /******************************************************************************
  *    Control messages handling functions (FULLMAC only)
@@ -1552,33 +1315,14 @@
 
     RWNX_DBG(RWNX_FN_ENTRY_STR);
 
-    if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80) {
-        rwnx_hw->mod_params->use_80 = true;
-    }
-
-#ifdef USE_5G
-	if (rwnx_hw->band_5g_support) {
-		ht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->ht_cap;
-	#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
-		vht_cap = &rwnx_hw->vht_cap_5G;
-	#endif
-	} else {
-		ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap;
-	#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
-		vht_cap = &rwnx_hw->vht_cap_2G;
-	#endif
-	}
-#else
     ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap;
 	#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
 	vht_cap = &rwnx_hw->vht_cap_2G;
 	printk("%s, cap=0x%x\n", __func__, vht_cap->cap);
 	#endif
-#endif
-
     #ifdef CONFIG_VHT_FOR_OLD_KERNEL
     rwnx_vht_capa = vht_cap;
-    #endif
+	#endif
 
     ht_mcs = (uint8_t *)&ht_cap->mcs;
 
@@ -1748,9 +1492,9 @@
 
     printk("assoc_req idx %d, he: %d, vht: %d\n ", rwnx_vif->ap.aic_index, sta->he, sta->vht);
     if (rwnx_vif->ap.aic_index < NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)
-        rwnx_vif->ap.aic_index++;
+			rwnx_vif->ap.aic_index++;
     else
-        rwnx_vif->ap.aic_index = 0;
+			rwnx_vif->ap.aic_index = 0;
     #endif
 
     /* Build the MM_STA_ADD_REQ message */
@@ -1904,7 +1648,7 @@
 {
     struct me_traffic_ind_req *req;
 
-    RWNX_DBG(RWNX_FN_ENTRY_STR);
+    //RWNX_DBG(RWNX_FN_ENTRY_STR);
 
     /* Build the ME_UTRAFFIC_IND_REQ message */
     req = rwnx_msg_zalloc(ME_TRAFFIC_IND_REQ, TASK_ME, DRV_TASK_ID,
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_msg_tx.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_msg_tx.h
index ffaca24..e063500 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_msg_tx.h
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_msg_tx.h
@@ -19,6 +19,7 @@
 int rwnx_send_start(struct rwnx_hw *rwnx_hw);
 int rwnx_send_get_temp_req(struct rwnx_hw *rwnx_hw, struct mm_set_vendor_hwconfig_cfm *cfm);
 int rwnx_send_version_req(struct rwnx_hw *rwnx_hw, struct mm_version_cfm *cfm);
+int rwnx_set_ap_ps_lvl_req(struct rwnx_hw *rwnx_hw, u8 level);
 int rwnx_send_add_if(struct rwnx_hw *rwnx_hw, const unsigned char *mac,
                      enum nl80211_iftype iftype, bool p2p, struct mm_add_if_cfm *cfm);
 int rwnx_send_remove_if(struct rwnx_hw *rwnx_hw, u8 vif_index, bool defer);
@@ -169,10 +170,7 @@
 int rwnx_send_txop_req(struct rwnx_hw *rwnx_hw, uint16_t *txop, u8_l long_nav_en, u8_l cfe_en);
 int rwnx_send_get_fw_version_req(struct rwnx_hw *rwnx_hw, struct mm_get_fw_version_cfm *cfm);
 int rwnx_send_txpwr_lvl_req(struct rwnx_hw *rwnx_hw);
-int rwnx_send_txpwr_lvl_v3_req(struct rwnx_hw *rwnx_hw);
-int rwnx_send_txpwr_lvl_adj_req(struct rwnx_hw *rwnx_hw);
 int rwnx_send_txpwr_ofst_req(struct rwnx_hw *rwnx_hw);
-int rwnx_send_txpwr_ofst2x_req(struct rwnx_hw *rwnx_hw);
 int rwnx_send_reboot(struct rwnx_hw *rwnx_hw);
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)
 int rwnx_send_set_channel_new(struct rwnx_hw *rwnx_hw, struct mac_chan_op *chan,
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_platform.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_platform.c
index ac724da..06b86c3 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_platform.c
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_platform.c
@@ -36,7 +36,6 @@
 extern u8 chip_sub_id;
 extern u8 chip_mcu_id;
 extern u8 btenable;
-
 static struct aicbt_info_t aicbt_info[]={
     {
         .btmode        = AICBT_BTMODE_DEFAULT,
@@ -71,7 +70,6 @@
         .txpwr_lvl     = AICBT_TXPWR_LVL_DEFAULT_8800d80,
     }//PRODUCT_ID_AIC8800D80
 };
-
 const struct aicbsp_firmware fw_8800dc_u01[] = {
 	[AICBSP_CPMODE_WORK] = {
 		.desc          = "normal work mode(sdio u01)",
@@ -80,7 +78,6 @@
 		.bt_table      = "fw_patch_table_8800dc.bin",
 		.wl_fw         = "fmacfw_8800dc.bin"
 	},
-
 	[AICBSP_CPMODE_TEST] = {
 		.desc          = "rf test mode(sdio u01)",
 		.bt_adid       = "fw_adid_8800dc.bin",
@@ -89,7 +86,6 @@
 		.wl_fw         = "fmacfw_rf_8800dc.bin"
 	},
 };
-
 const struct aicbsp_firmware fw_8800dc_u02[] = {
 	[AICBSP_CPMODE_WORK] = {
 		.desc          = "normal work mode(8800dc sdio u02)",
@@ -98,7 +94,6 @@
 		.bt_table      = "fw_patch_table_8800dc_u02.bin",
 		.wl_fw         = "fmacfw_patch_8800dc_u02.bin"
 	},
-
 	[AICBSP_CPMODE_TEST] = {
 		.desc          = "rf test mode(8800dc sdio u02)",
 		.bt_adid       = "fw_adid_8800dc_u02.bin",
@@ -107,7 +102,6 @@
 		.wl_fw         = "lmacfw_rf_8800dc.bin" //u01,u02 lmacfw load same bin
 	},
 };
-
 const struct aicbsp_firmware fw_8800dc_h_u02[] = {
 	[AICBSP_CPMODE_WORK] = {
 		.desc          = "normal work mode(8800dc_h sdio u02)",
@@ -116,7 +110,6 @@
 		.bt_table      = "fw_patch_table_8800dc_u02h.bin",
 		.wl_fw         = "fmacfw_patch_8800dc_h_u02.bin"
 	},
-
 	[AICBSP_CPMODE_TEST] = {
 		.desc          = "rf test mode(8800dc_h sdio u02)",
 		.bt_adid       = "fw_adid_8800dc_u02h.bin",
@@ -125,50 +118,7 @@
 		.wl_fw         = "lmacfw_rf_8800dc.bin" //u01,u02 lmacfw load same bin
 	},
 };
-
-const struct aicbsp_firmware fw_8800d80_u01[] = {
-	[AICBSP_CPMODE_WORK] = {
-		.desc          = "normal work mode(8800d80 sdio u01)",
-		.bt_adid       = "fw_adid_8800d80.bin",
-		.bt_patch      = "fw_patch_8800d80.bin",
-		.bt_table      = "fw_patch_table_8800d80.bin",
-		.wl_fw         = "fmacfw_8800d80.bin"
-	},
-
-	[AICBSP_CPMODE_TEST] = {
-		.desc          = "rf test mode(8800d80 sdio u01)",
-		.bt_adid       = "fw_adid_8800d80.bin",
-		.bt_patch      = "fw_patch_8800d80.bin",
-		.bt_table      = "fw_patch_table_8800d80.bin",
-		.wl_fw         = "lmacfw_rf_8800d80.bin"
-	},
-};
-
-const struct aicbsp_firmware fw_8800d80_u02[] = {
-	[AICBSP_CPMODE_WORK] = {
-		.desc          = "normal work mode(8800d80 sdio u02)",
-		.bt_adid       = "fw_adid_8800d80_u02.bin",
-		.bt_patch      = "fw_patch_8800d80_u02.bin",
-		.bt_table      = "fw_patch_table_8800d80_u02.bin",
-	#ifdef CONFIG_SDIO_BT
-		.wl_fw         = "fmacfwbt_8800d80_u02.bin"
-	#else
-		.wl_fw         = "fmacfw_8800d80_u02.bin"
-	#endif
-	},
-
-	[AICBSP_CPMODE_TEST] = {
-		.desc          = "rf test mode(8800d80 sdio u02)",
-		.bt_adid       = "fw_adid_8800d80_u02.bin",
-		.bt_patch      = "fw_patch_8800d80_u02.bin",
-		.bt_table      = "fw_patch_table_8800d80_u02.bin",
-		.wl_fw         = "lmacfw_rf_8800d80_u02.bin"
-	},
-};
-
-
 const struct aicbsp_firmware *aicbsp_firmware_list = fw_8800dc_u02;
-
 struct aicbsp_info_t aicbsp_info = {
 	.hwinfo_r = AICBSP_HWINFO_DEFAULT,
 	.hwinfo   = AICBSP_HWINFO_DEFAULT,
@@ -180,7 +130,6 @@
 	.irqf     = 0,
 #endif
 };
-
 //#ifndef CONFIG_ROM_PATCH_EN
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0))
 static inline struct inode *file_inode(const struct file *f)
@@ -208,12 +157,10 @@
 {
     txpwr_lvl_conf_t txpwr_lvl;
     txpwr_lvl_conf_v2_t txpwr_lvl_v2;
-	txpwr_lvl_conf_v3_t txpwr_lvl_v3;
-	txpwr_lvl_adj_conf_t txpwr_lvl_adj;
     txpwr_loss_conf_t txpwr_loss;
     txpwr_ofst_conf_t txpwr_ofst;
-	txpwr_ofst2x_conf_t txpwr_ofst2x;
     xtal_cap_conf_t xtal_cap;
+    ap_ps_conf_t ap_ps_lvl;
 } nvram_info_t;
 
 nvram_info_t nvram_info = {
@@ -232,36 +179,12 @@
     .txpwr_lvl_v2 = {
         .enable             = 1,
         .pwrlvl_11b_11ag_2g4 =
-            //1M,   2M,   5M5,  11M,  6M,   9M,   12M,  18M,  24M,  36M,  48M,  54M
             { 20,   20,   20,   20,   20,   20,   20,   20,   18,   18,   16,   16},
         .pwrlvl_11n_11ac_2g4 =
-            //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
             { 20,   20,   20,   20,   18,   18,   16,   16,   16,   16},
         .pwrlvl_11ax_2g4 =
-            //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, MCS10,MCS11
             { 20,   20,   20,   20,   18,   18,   16,   16,   16,   16,   15,   15},
     },
-    .txpwr_lvl_v3 = {
-        .enable             = 1,
-        .pwrlvl_11b_11ag_2g4 =
-            //1M,   2M,   5M5,  11M,  6M,   9M,   12M,  18M,  24M,  36M,  48M,  54M
-            { 20,   20,   20,   20,   20,   20,   20,   20,   18,   18,   16,   16},
-        .pwrlvl_11n_11ac_2g4 =
-            //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
-            { 20,   20,   20,   20,   18,   18,   16,   16,   16,   16},
-        .pwrlvl_11ax_2g4 =
-            //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, MCS10,MCS11
-            { 20,   20,   20,   20,   18,   18,   16,   16,   16,   16,   15,   15},
-         .pwrlvl_11a_5g =
-            //NA,   NA,   NA,   NA,   6M,   9M,   12M,  18M,  24M,  36M,  48M,  54M
-            { 0x80, 0x80, 0x80, 0x80, 20,   20,   20,   20,   18,   18,   16,   16},
-        .pwrlvl_11n_11ac_5g =
-            //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
-            { 20,   20,   20,   20,   18,   18,   16,   16,   16,   15},
-        .pwrlvl_11ax_5g =
-            //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, MCS10,MCS11
-            { 20,   20,   20,   20,   18,   18,   16,   16,   16,   15,   14,   14},
-    },
     .txpwr_loss = {
         .loss_enable      = 1,
         .loss_value       = 0,
@@ -276,259 +199,16 @@
         .chan_122_140 = 0,
         .chan_142_165 = 0,
     },
-    .txpwr_ofst2x = {
-        .enable       = 0,
-        .pwrofst2x_tbl_2g4 =
-        { // ch1-4, ch5-9, ch10-13
-            {   0,    0,    0   }, // 11b
-            {   0,    0,    0   }, // ofdm_highrate
-            {   0,    0,    0   }, // ofdm_lowrate
-        },
-        .pwrofst2x_tbl_5g =
-        { // ch42,  ch58, ch106,ch122,ch138,ch155
-            {   0,    0,    0,    0,    0,    0   }, // ofdm_lowrate
-            {   0,    0,    0,    0,    0,    0   }, // ofdm_highrate
-            {   0,    0,    0,    0,    0,    0   }, // ofdm_midrate
-        },
-    },
     .xtal_cap = {
         .enable        = 0,
         .xtal_cap      = 24,
         .xtal_cap_fine = 31,
     },
+    .ap_ps_lvl = {
+        .ap_ps_clk     = 5,
+    },
 };
 
-//add d80
-extern int adap_test;
-
-typedef u32 (*array2_tbl_t)[2];
-
-#define AIC_PATCH_MAGIG_NUM     0x48435450 // "PTCH"
-#define AIC_PATCH_MAGIG_NUM_2   0x50544348 // "HCTP"
-#define AIC_PATCH_BLOCK_MAX     4
-
-typedef struct {
-    uint32_t magic_num;
-    uint32_t pair_start;
-    uint32_t magic_num_2;
-    uint32_t pair_count;
-    uint32_t block_dst[AIC_PATCH_BLOCK_MAX];
-    uint32_t block_src[AIC_PATCH_BLOCK_MAX];
-    uint32_t block_size[AIC_PATCH_BLOCK_MAX]; // word count
-} aic_patch_t;
-
-#define AIC_PATCH_OFST(mem) ((size_t) &((aic_patch_t *)0)->mem)
-#define AIC_PATCH_ADDR(mem) ((u32)(aic_patch_str_base + AIC_PATCH_OFST(mem)))
-
-u32 adaptivity_patch_tbl_8800d80[][2] = {
-	{0x000C, 0x0000320A}, //linkloss_thd
-	{0x009C, 0x00000000}, //ac_param_conf
-	{0x0168, 0x00010000}, //tx_adaptivity_en
-};
-
-#define USER_CHAN_MAX_TXPWR_EN_FLAG     (0x01U << 1)
-#define USER_TX_USE_ANA_F_FLAG          (0x01U << 2)
-
-#define CFG_USER_CHAN_MAX_TXPWR_EN  0
-#define CFG_USER_TX_USE_ANA_F       0
-
-#define CFG_USER_EXT_FLAGS_EN   (CFG_USER_CHAN_MAX_TXPWR_EN || CFG_USER_TX_USE_ANA_F)
-
-u32 patch_tbl_8800d80[][2] = {
-	#ifdef USE_5G
-	{0x00b4, 0xf3010001},
-	#else
-	{0x00b4, 0xf3010000},
-	#endif
-#if defined(CONFIG_AMSDU_RX)
-        {0x170, 0x0100000a}
-#endif
-#ifdef CONFIG_IRQ_FALL
-	{0x00000170, 0x0000010a}, //irqf
-#endif
-
-    #if CFG_USER_EXT_FLAGS_EN
-    {0x0188, 0x00000001
-        #if CFG_USER_CHAN_MAX_TXPWR_EN
-        | USER_CHAN_MAX_TXPWR_EN_FLAG
-        #endif
-        #if CFG_USER_TX_USE_ANA_F
-        | USER_TX_USE_ANA_F_FLAG
-        #endif
-    }, // user_ext_flags
-    #endif
-};
-
-#ifdef CONFIG_OOB
-// for 8800d40/d80     map data1 isr to gpiob1
-u32 gpio_cfg_tbl_8800d40d80[][2] = {
-    {0x40504084, 0x00000006},
-    {0x40500040, 0x00000000},
-    {0x40100030, 0x00000001},
-    {0x40241020, 0x00000001},
-    {0x40240030, 0x00000004},
-    {0x40240020, 0x03020700},
-};
-#endif
-
-int aicwifi_sys_config_8800d80(struct rwnx_hw *rwnx_hw)
-{
-#ifdef CONFIG_OOB
-    int ret, cnt;
-	int gpiocfg_num = sizeof(gpio_cfg_tbl_8800d40d80) / sizeof(u32) / 2;
-	for (cnt = 0; cnt < gpiocfg_num; cnt++) {
-		ret = rwnx_send_dbg_mem_write_req(rwnx_hw, gpio_cfg_tbl_8800d40d80[cnt][0], gpio_cfg_tbl_8800d40d80[cnt][1]);
-		if (ret) {
-			printk("%x write fail: %d\n", gpio_cfg_tbl_8800d40d80[cnt][0], ret);
-			return ret;
-		}
-	}
-#endif
-
-	return 0;
-}
-
-#define NEW_PATCH_BUFFER_MAP    1
-
-int aicwifi_patch_config_8800d80(struct rwnx_hw *rwnx_hw)
-{
-	const u32 rd_patch_addr = RAM_FMAC_FW_ADDR + 0x0198;
-	u32 aic_patch_addr;
-	u32 config_base, aic_patch_str_base;
-	#if (NEW_PATCH_BUFFER_MAP)
-	u32 patch_buff_addr, patch_buff_base, rd_version_addr, rd_version_val;
-	#endif
-	uint32_t start_addr = 0x0016F800;
-	u32 patch_addr = start_addr;
-	u32 patch_cnt = sizeof(patch_tbl_8800d80)/sizeof(u32)/2;
-	struct dbg_mem_read_cfm rd_patch_addr_cfm;
-	int ret = 0;
-	int cnt = 0;
-	//adap test
-	int adap_patch_cnt = 0;
-
-	if (adap_test) {
-        printk("%s for adaptivity test \r\n", __func__);
-		adap_patch_cnt = sizeof(adaptivity_patch_tbl_8800d80)/sizeof(u32)/2;
-	}
-
-	aic_patch_addr = rd_patch_addr + 8;
-
-	ret = rwnx_send_dbg_mem_read_req(rwnx_hw, rd_patch_addr, &rd_patch_addr_cfm);
-	if (ret) {
-		printk("patch rd fail\n");
-		return ret;
-	}
-
-	config_base = rd_patch_addr_cfm.memdata;
-
-	ret = rwnx_send_dbg_mem_read_req(rwnx_hw, aic_patch_addr, &rd_patch_addr_cfm);
-	if (ret) {
-		printk("patch str rd fail\n");
-		return ret;
-	}
-	aic_patch_str_base = rd_patch_addr_cfm.memdata;
-
-	#if (NEW_PATCH_BUFFER_MAP)
-	rd_version_addr = RAM_FMAC_FW_ADDR + 0x01C;
-	if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, rd_version_addr, &rd_patch_addr_cfm))) {
-		printk("version val[0x%x] rd fail: %d\n", rd_version_addr, ret);
-		return ret;
-	}
-	rd_version_val = rd_patch_addr_cfm.memdata;
-	printk("rd_version_val=%08X\n", rd_version_val);
-	//sdiodev->fw_version_uint = rd_version_val;
-	if (rd_version_val > 0x06090100) {
-		patch_buff_addr = rd_patch_addr + 12;
-		ret = rwnx_send_dbg_mem_read_req(rwnx_hw, patch_buff_addr, &rd_patch_addr_cfm);
-		if (ret) {
-			printk("patch buf rd fail\n");
-			return ret;
-		}
-		patch_buff_base = rd_patch_addr_cfm.memdata;
-		patch_addr = start_addr = patch_buff_base;
-	}
-	#endif
-
-	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(magic_num), AIC_PATCH_MAGIG_NUM);
-	if (ret) {
-		printk("0x%x write fail\n", AIC_PATCH_ADDR(magic_num));
-		return ret;
-	}
-
-	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(magic_num_2), AIC_PATCH_MAGIG_NUM_2);
-	if (ret) {
-		printk("0x%x write fail\n", AIC_PATCH_ADDR(magic_num_2));
-		return ret;
-	}
-
-	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(pair_start), patch_addr);
-	if (ret) {
-		printk("0x%x write fail\n", AIC_PATCH_ADDR(pair_start));
-		return ret;
-	}
-
-	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(pair_count), patch_cnt + adap_patch_cnt);
-	if (ret) {
-		printk("0x%x write fail\n", AIC_PATCH_ADDR(pair_count));
-		return ret;
-	}
-
-	for (cnt = 0; cnt < patch_cnt; cnt++) {
-		ret = rwnx_send_dbg_mem_write_req(rwnx_hw, start_addr+8*cnt, patch_tbl_8800d80[cnt][0]+config_base);
-		if (ret) {
-			printk("%x write fail\n", start_addr+8*cnt);
-			return ret;
-		}
-		ret = rwnx_send_dbg_mem_write_req(rwnx_hw, start_addr+8*cnt+4, patch_tbl_8800d80[cnt][1]);
-		if (ret) {
-			printk("%x write fail\n", start_addr+8*cnt+4);
-			return ret;
-		}
-	}
-
-	if (adap_test){
-		int tmp_cnt = patch_cnt + adap_patch_cnt;
-		for (cnt = patch_cnt; cnt < tmp_cnt; cnt++) {
-			int tbl_idx = cnt - patch_cnt;
-			ret = rwnx_send_dbg_mem_write_req(rwnx_hw, start_addr+8*cnt, adaptivity_patch_tbl_8800d80[tbl_idx][0]+config_base);
-			if(ret) {
-				printk("%x write fail\n", start_addr+8*cnt);
-				return ret;
-			}
-			ret = rwnx_send_dbg_mem_write_req(rwnx_hw, start_addr+8*cnt+4, adaptivity_patch_tbl_8800d80[tbl_idx][1]);
-			if(ret) {
-				printk("%x write fail\n", start_addr+8*cnt+4);
-				return ret;
-			}
-		}
-	}
-
-	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(block_size[0]), 0);
-	if (ret) {
-		printk("block_size[0x%x] write fail: %d\n", AIC_PATCH_ADDR(block_size[0]), ret);
-		return ret;
-	}
-	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(block_size[1]), 0);
-	if (ret) {
-		printk("block_size[0x%x] write fail: %d\n", AIC_PATCH_ADDR(block_size[1]), ret);
-		return ret;
-	}
-	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(block_size[2]), 0);
-	if (ret) {
-		printk("block_size[0x%x] write fail: %d\n", AIC_PATCH_ADDR(block_size[2]), ret);
-		return ret;
-	}
-	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(block_size[3]), 0);
-	if (ret) {
-		printk("block_size[0x%x] write fail: %d\n", AIC_PATCH_ADDR(block_size[3]), ret);
-		return ret;
-	}
-
-	return 0;
-}
-// end d80
-
 #ifdef CONFIG_TEMP_PW
 void set_txpwr_ctrl(struct aic_sdio_dev *sdiodev, s8_l value)
 {
@@ -597,7 +277,7 @@
 #endif
 		len = snprintf(path, FW_PATH_MAX_LEN, "%s/%s", VENDOR_SPECIFIED_DPD_PATH, name);
 	} else {
-    	len = snprintf(path, FW_PATH_MAX_LEN, "%s/%s", VENDOR_SPECIFIED_FW_PATH, name);
+    len = snprintf(path, FW_PATH_MAX_LEN, "%s/%s", VENDOR_SPECIFIED_FW_PATH, name);
 	}
     if (len >= FW_PATH_MAX_LEN) {
         printk("%s: %s file's path too long\n", __func__, name);
@@ -1208,100 +888,6 @@
     printk("%s:lvl_11ax_mcs11_2g4:%d\r\n",   __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[11]);
 #endif
 }
-
-void get_userconfig_txpwr_lvl_v3_in_fdrv(txpwr_lvl_conf_v3_t *txpwr_lvl_v3)
-{
-    *txpwr_lvl_v3 = nvram_info.txpwr_lvl_v3;
-
-    printk("%s:enable:%d\r\n",               __func__, txpwr_lvl_v3->enable);
-    printk("%s:lvl_11b_11ag_1m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[0]);
-    printk("%s:lvl_11b_11ag_2m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[1]);
-    printk("%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[2]);
-    printk("%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[3]);
-    printk("%s:lvl_11b_11ag_6m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[4]);
-    printk("%s:lvl_11b_11ag_9m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[5]);
-    printk("%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[6]);
-    printk("%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[7]);
-    printk("%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[8]);
-    printk("%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[9]);
-    printk("%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[10]);
-    printk("%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[11]);
-    printk("%s:lvl_11n_11ac_mcs0_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[0]);
-    printk("%s:lvl_11n_11ac_mcs1_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[1]);
-    printk("%s:lvl_11n_11ac_mcs2_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[2]);
-    printk("%s:lvl_11n_11ac_mcs3_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[3]);
-    printk("%s:lvl_11n_11ac_mcs4_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[4]);
-    printk("%s:lvl_11n_11ac_mcs5_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[5]);
-    printk("%s:lvl_11n_11ac_mcs6_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[6]);
-    printk("%s:lvl_11n_11ac_mcs7_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[7]);
-    printk("%s:lvl_11n_11ac_mcs8_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[8]);
-    printk("%s:lvl_11n_11ac_mcs9_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[9]);
-    printk("%s:lvl_11ax_mcs0_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[0]);
-    printk("%s:lvl_11ax_mcs1_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[1]);
-    printk("%s:lvl_11ax_mcs2_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[2]);
-    printk("%s:lvl_11ax_mcs3_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[3]);
-    printk("%s:lvl_11ax_mcs4_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[4]);
-    printk("%s:lvl_11ax_mcs5_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[5]);
-    printk("%s:lvl_11ax_mcs6_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[6]);
-    printk("%s:lvl_11ax_mcs7_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[7]);
-    printk("%s:lvl_11ax_mcs8_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[8]);
-    printk("%s:lvl_11ax_mcs9_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[9]);
-    printk("%s:lvl_11ax_mcs10_2g4:%d\r\n",   __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[10]);
-    printk("%s:lvl_11ax_mcs11_2g4:%d\r\n",   __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[11]);
-
-    printk("%s:lvl_11a_1m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[0]);
-    printk("%s:lvl_11a_2m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[1]);
-    printk("%s:lvl_11a_5m5_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[2]);
-    printk("%s:lvl_11a_11m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[3]);
-    printk("%s:lvl_11a_6m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[4]);
-    printk("%s:lvl_11a_9m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[5]);
-    printk("%s:lvl_11a_12m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[6]);
-    printk("%s:lvl_11a_18m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[7]);
-    printk("%s:lvl_11a_24m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[8]);
-    printk("%s:lvl_11a_36m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[9]);
-    printk("%s:lvl_11a_48m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[10]);
-    printk("%s:lvl_11a_54m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[11]);
-    printk("%s:lvl_11n_11ac_mcs0_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[0]);
-    printk("%s:lvl_11n_11ac_mcs1_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[1]);
-    printk("%s:lvl_11n_11ac_mcs2_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[2]);
-    printk("%s:lvl_11n_11ac_mcs3_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[3]);
-    printk("%s:lvl_11n_11ac_mcs4_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[4]);
-    printk("%s:lvl_11n_11ac_mcs5_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[5]);
-    printk("%s:lvl_11n_11ac_mcs6_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[6]);
-    printk("%s:lvl_11n_11ac_mcs7_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[7]);
-    printk("%s:lvl_11n_11ac_mcs8_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[8]);
-    printk("%s:lvl_11n_11ac_mcs9_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[9]);
-    printk("%s:lvl_11ax_mcs0_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[0]);
-    printk("%s:lvl_11ax_mcs1_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[1]);
-    printk("%s:lvl_11ax_mcs2_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[2]);
-    printk("%s:lvl_11ax_mcs3_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[3]);
-    printk("%s:lvl_11ax_mcs4_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[4]);
-    printk("%s:lvl_11ax_mcs5_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[5]);
-    printk("%s:lvl_11ax_mcs6_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[6]);
-    printk("%s:lvl_11ax_mcs7_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[7]);
-    printk("%s:lvl_11ax_mcs8_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[8]);
-    printk("%s:lvl_11ax_mcs9_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[9]);
-    printk("%s:lvl_11ax_mcs10_5g:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[10]);
-    printk("%s:lvl_11ax_mcs11_5g:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[11]);
-}
-
-void get_userconfig_txpwr_lvl_adj_in_fdrv(txpwr_lvl_adj_conf_t *txpwr_lvl_adj)
-{
-    *txpwr_lvl_adj = nvram_info.txpwr_lvl_adj;
-
-    printk("%s:enable:%d\r\n",                   __func__, txpwr_lvl_adj->enable);
-    printk("%s:lvl_adj_2g4_chan_1_4:%d\r\n",     __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[0]);
-    printk("%s:lvl_adj_2g4_chan_5_9:%d\r\n",     __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[1]);
-    printk("%s:lvl_adj_2g4_chan_10_13:%d\r\n",   __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[2]);
-
-    printk("%s:lvl_adj_5g_chan_42:%d\r\n",       __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[0]);
-    printk("%s:lvl_adj_5g_chan_58:%d\r\n",       __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[1]);
-    printk("%s:lvl_adj_5g_chan_106:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[2]);
-    printk("%s:lvl_adj_5g_chan_122:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[3]);
-    printk("%s:lvl_adj_5g_chan_138:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[4]);
-    printk("%s:lvl_adj_5g_chan_155:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[5]);
-}
-
 void get_userconfig_txpwr_loss(txpwr_loss_conf_t *txpwr_loss)
 {
     txpwr_loss->loss_enable      = nvram_info.txpwr_loss.loss_enable;
@@ -1312,9 +898,22 @@
 void set_txpwr_loss_ofst(s8_l value)
 {
     nvram_info.txpwr_loss.loss_enable = 1;
-    nvram_info.txpwr_loss.loss_value += value;
+    nvram_info.txpwr_loss.loss_value = value;
     printk("%s:value:%d\r\n",      __func__, value);
 }
+#ifdef CONFIG_SET_AP_PS
+int get_userconfig_set_ap_ps_lvl(ap_ps_conf_t *ap_ps_lvl)
+{
+    ap_ps_lvl->ap_ps_clk = nvram_info.ap_ps_lvl.ap_ps_clk;
+    printk("%s:ap_ps_clk:%d\r\n",     __func__, ap_ps_lvl->ap_ps_clk);
+    if ((ap_ps_lvl->ap_ps_clk > 0) && (ap_ps_lvl->ap_ps_clk <= AP_PS_CLK_5)){
+        return ap_ps_lvl->ap_ps_clk;
+    } else {
+        printk("%s:set ap_ps_clk default AP_PS_CLK_5\r\n",     __func__);
+        return AP_PS_CLK_5;
+    }
+}
+#endif
 void get_userconfig_txpwr_ofst(txpwr_ofst_conf_t *txpwr_ofst)
 {
     txpwr_ofst->enable       = nvram_info.txpwr_ofst.enable;
@@ -1336,31 +935,6 @@
     printk("%s:chan_142_165:%d\r\n", __func__, txpwr_ofst->chan_142_165);
 }
 
-void get_userconfig_txpwr_ofst2x_in_fdrv(txpwr_ofst2x_conf_t *txpwr_ofst2x)
-{
-    int type, ch_grp;
-    *txpwr_ofst2x = nvram_info.txpwr_ofst2x;
-    printk("%s:enable      :%d\r\n", __func__, txpwr_ofst2x->enable);
-    printk("pwrofst2x 2.4g: [0]:11b, [1]:ofdm_highrate, [2]:ofdm_lowrate\n"
-        "  chan=" "\t1-4" "\t5-9" "\t10-13");
-    for (type = 0; type < 3; type++) {
-        printk("\n  [%d] =", type);
-        for (ch_grp = 0; ch_grp < 3; ch_grp++) {
-            printk("\t%d", txpwr_ofst2x->pwrofst2x_tbl_2g4[type][ch_grp]);
-        }
-    }
-    printk("\npwrofst2x 5g: [0]:ofdm_lowrate, [1]:ofdm_highrate, [2]:ofdm_midrate\n"
-        "  chan=" "\t36-50" "\t51-64" "\t98-114" "\t115-130" "\t131-146" "\t147-166");
-    for (type = 0; type < 3; type++) {
-        printk("\n  [%d] =", type);
-        for (ch_grp = 0; ch_grp < 6; ch_grp++) {
-            printk("\t%d", txpwr_ofst2x->pwrofst2x_tbl_5g[type][ch_grp]);
-        }
-    }
-    printk("\n");
-}
-
-
 void get_userconfig_xtal_cap(xtal_cap_conf_t *xtal_cap)
 {
     *xtal_cap = nvram_info.xtal_cap;
@@ -1489,273 +1063,8 @@
         nvram_info.xtal_cap.xtal_cap = rwnx_atoi(value);
     } else if (!strcmp(command, "xtal_cap_fine")) {
         nvram_info.xtal_cap.xtal_cap_fine = rwnx_atoi(value);
-    } else {
-        printk("invalid cmd: %s\n", command);
-    }
-}
-
-void rwnx_plat_nvram_set_value_v3(char *command, char *value)
-{
-    //TODO send command
-    printk("%s:command=%s value=%s\n", __func__, command, value);
-    if (!strcmp(command, "enable")) {
-        nvram_info.txpwr_lvl.enable = rwnx_atoi(value);
-        nvram_info.txpwr_lvl_v3.enable = rwnx_atoi(value);
-    } else if (!strcmp(command, "dsss")) {
-        nvram_info.txpwr_lvl.dsss = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofdmlowrate_2g4")) {
-        nvram_info.txpwr_lvl.ofdmlowrate_2g4 = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofdm64qam_2g4")) {
-        nvram_info.txpwr_lvl.ofdm64qam_2g4 = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofdm256qam_2g4")) {
-        nvram_info.txpwr_lvl.ofdm256qam_2g4 = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofdm1024qam_2g4")) {
-        nvram_info.txpwr_lvl.ofdm1024qam_2g4 = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofdmlowrate_5g")) {
-        nvram_info.txpwr_lvl.ofdmlowrate_5g = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofdm64qam_5g")) {
-        nvram_info.txpwr_lvl.ofdm64qam_5g = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofdm256qam_5g")) {
-        nvram_info.txpwr_lvl.ofdm256qam_5g = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofdm1024qam_5g")) {
-        nvram_info.txpwr_lvl.ofdm1024qam_5g = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11b_11ag_1m_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[0] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11b_11ag_2m_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[1] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11b_11ag_5m5_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[2] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11b_11ag_11m_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[3] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11b_11ag_6m_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[4] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11b_11ag_9m_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[5] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11b_11ag_12m_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[6] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11b_11ag_18m_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[7] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11b_11ag_24m_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[8] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11b_11ag_36m_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[9] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11b_11ag_48m_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[10] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11b_11ag_54m_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[11] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs0_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[0] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs1_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[1] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs2_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[2] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs3_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[3] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs4_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[4] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs5_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[5] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs6_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[6] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs7_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[7] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs8_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[8] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs9_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[9] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs0_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[0] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs1_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[1] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs2_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[2] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs3_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[3] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs4_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[4] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs5_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[5] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs6_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[6] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs7_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[7] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs8_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[8] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs9_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[9] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs10_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[10] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs11_2g4")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[11] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11a_1m_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[0] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11a_2m_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[1] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11a_5m5_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[2] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11a_11m_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[3] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11a_6m_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[4] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11a_9m_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[5] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11a_12m_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[6] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11a_18m_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[7] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11a_24m_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[8] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11a_36m_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[9] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11a_48m_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[10] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11a_54m_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[11] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs0_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[0] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs1_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[1] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs2_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[2] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs3_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[3] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs4_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[4] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs5_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[5] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs6_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[6] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs7_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[7] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs8_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[8] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11n_11ac_mcs9_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[9] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs0_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[0] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs1_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[1] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs2_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[2] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs3_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[3] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs4_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[4] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs5_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[5] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs6_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[6] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs7_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[7] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs8_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[8] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs9_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[9] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs10_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[10] = rwnx_atoi(value);
-    } else if (!strcmp(command,     "lvl_11ax_mcs11_5g")) {
-        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[11] = rwnx_atoi(value);
-    } else if (!strcmp(command, "lvl_adj_enable")) {
-        nvram_info.txpwr_lvl_adj.enable = rwnx_atoi(value);
-    } else if (!strcmp(command, "lvl_adj_2g4_chan_1_4")) {
-        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[0] = rwnx_atoi(value);
-    } else if (!strcmp(command, "lvl_adj_2g4_chan_5_9")) {
-        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[1] = rwnx_atoi(value);
-    } else if (!strcmp(command, "lvl_adj_2g4_chan_10_13")) {
-        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[2] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_42")) {
-        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[0] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_58")) {
-        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[1] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_106")) {
-        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[2] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_122")) {
-        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[3] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_138")) {
-        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[4] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_155")) {
-        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[5] = rwnx_atoi(value);
-    } else if (!strcmp(command, "loss_enable")) {
-        nvram_info.txpwr_loss.loss_enable = rwnx_atoi(value);
-    } else if (!strcmp(command, "loss_value")) {
-        nvram_info.txpwr_loss.loss_value = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_enable")) {
-        nvram_info.txpwr_ofst.enable = rwnx_atoi(value);
-		nvram_info.txpwr_ofst2x.enable = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_chan_1_4")) {
-        nvram_info.txpwr_ofst.chan_1_4 = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_chan_5_9")) {
-        nvram_info.txpwr_ofst.chan_5_9 = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_chan_10_13")) {
-        nvram_info.txpwr_ofst.chan_10_13 = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_chan_36_64")) {
-        nvram_info.txpwr_ofst.chan_36_64 = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_chan_100_120")) {
-        nvram_info.txpwr_ofst.chan_100_120 = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_chan_122_140")) {
-        nvram_info.txpwr_ofst.chan_122_140 = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_chan_142_165")) {
-        nvram_info.txpwr_ofst.chan_142_165 = rwnx_atoi(value);
-	} else if (!strcmp(command, "ofst_2g4_11b_chan_1_4")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][0] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_2g4_11b_chan_5_9")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][1] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_2g4_11b_chan_10_13")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][2] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_1_4")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][0] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_5_9")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][1] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_10_13")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][2] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_1_4")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][0] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_5_9")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][1] = rwnx_atoi(value);
-	} else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_10_13")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][0] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_42")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][0] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_58")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][1] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_106")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][2] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_122")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][3] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_138")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][4] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_155")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][5] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_42")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][0] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_58")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][1] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_106")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][2] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_122")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][3] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_138")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][4] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_155")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][5] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_42")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][0] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_58")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][1] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_106")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][2] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_122")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][3] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_138")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][4] = rwnx_atoi(value);
-    } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_155")) {
-        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][5] = rwnx_atoi(value);
-    } else if (!strcmp(command, "xtal_enable")) {
-        nvram_info.xtal_cap.enable = rwnx_atoi(value);
-    } else if (!strcmp(command, "xtal_cap")) {
-        nvram_info.xtal_cap.xtal_cap = rwnx_atoi(value);
-    } else if (!strcmp(command, "xtal_cap_fine")) {
-        nvram_info.xtal_cap.xtal_cap_fine = rwnx_atoi(value);
+    } else if (!strcmp(command, "ap_ps_lvl")) {
+        nvram_info.ap_ps_lvl.ap_ps_clk = rwnx_atoi(value);
     } else {
         printk("invalid cmd: %s\n", command);
     }
@@ -1823,71 +1132,6 @@
     }
 }
 
-void rwnx_plat_userconfig_parsing3(char *buffer, int size)
-{
-    int i = 0;
-    int parse_state = 0;
-    char command[64];
-    char value[100];
-    int char_counter = 0;
-
-    memset(command, 0, 64);
-    memset(value, 0, 100);
-
-    for (i = 0; i < size; i++) {
-        //Send command or print nvram log when char is \r or \n
-        if (buffer[i] == 0x0a || buffer[i] == 0x0d) {
-            if (command[0] != 0 && value[0] != 0) {
-                if (parse_state == PRINT) {
-                    printk("%s:%s\r\n", __func__, value);
-                } else if (parse_state == GET_VALUE) {
-                    rwnx_plat_nvram_set_value_v3(command, value);
-                }
-            }
-            //Reset command value and char_counter
-            memset(command, 0, 64);
-            memset(value, 0, 100);
-            char_counter = 0;
-            parse_state = INIT;
-            continue;
-        }
-
-        //Switch parser state
-        if (parse_state == INIT) {
-            if (buffer[i] == '#') {
-                parse_state = PRINT;
-                continue;
-            } else if (buffer[i] == 0x0a || buffer[i] == 0x0d) {
-                parse_state = INIT;
-                continue;
-            } else {
-                parse_state = CMD;
-            }
-        }
-
-        //Fill data to command and value
-        if (parse_state == PRINT) {
-            command[0] = 0x01;
-            value[char_counter] = buffer[i];
-            char_counter++;
-        } else if (parse_state == CMD) {
-            if (command[0] != 0 && buffer[i] == '=') {
-                parse_state = GET_VALUE;
-                char_counter = 0;
-                continue;
-            }
-            command[char_counter] = buffer[i];
-            char_counter++;
-        } else if (parse_state == GET_VALUE) {
-            if(buffer[i] != 0x2D && (buffer[i] < 0x30 || buffer[i] > 0x39)) {
-                continue;
-            }
-            value[char_counter] = buffer[i];
-            char_counter++;
-        }
-    }
-}
-
 /**
  * rwnx_plat_userconfig_load  ---Load aic_userconfig.txt
  *@filename name of config
@@ -1917,35 +1161,6 @@
     printk("userconfig download complete\n\n");
     return 0;
 }
-
-int	rwnx_plat_userconfig_load_8800d80(struct rwnx_hw *rwnx_hw)
-{
-    int size;
-    u32 *dst=NULL;
-    char *filename = FW_USERCONFIG_NAME_8800D80;
-
-    printk("userconfig file path:%s \r\n", filename);
-
-    /* load file */
-    size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
-    if (size <= 0) {
-            printk("wrong size of firmware file\n");
-            dst = NULL;
-            return 0;
-    }
-
-	/* Copy the file on the Embedded side */
-    printk("### Load file done: %s, size=%d\n", filename, size);
-
-	rwnx_plat_userconfig_parsing3((char *)dst, size);
-
-    rwnx_release_firmware_common(&dst);
-
-    printk("userconfig download complete\n\n");
-    return 0;
-
-}
-
 #endif
 
 #ifndef CONFIG_ROM_PATCH_EN
@@ -2496,27 +1711,22 @@
 	*head = NULL;
 	return 0;
 }
-
 struct aicbt_patch_table *aicbt_patch_table_alloc(const char *filename)
 {
 	uint8_t *rawdata = NULL, *p;
 	int size;
 	struct aicbt_patch_table *head = NULL, *new = NULL, *cur = NULL;
-
-	/* load aic firmware */
 	size = rwnx_load_firmware((u32 **)&rawdata, filename, NULL);
 	if (size <= 0) {
 		printk("wrong size of firmware file\n");
 		goto err;
 	}
-
 	p = rawdata;
 	if (memcmp(p, AICBT_PT_TAG, sizeof(AICBT_PT_TAG) < 16 ? sizeof(AICBT_PT_TAG) : 16)) {
 		printk("TAG err\n");
 		goto err;
 	}
 	p += 16;
-
 	while (p - rawdata < size) {
 		new = (struct aicbt_patch_table *)kmalloc(sizeof(struct aicbt_patch_table),GFP_KERNEL);
 		memset(new, 0, sizeof(struct aicbt_patch_table));
@@ -2527,18 +1737,14 @@
 			cur->next = new;
 			cur = cur->next;
 		}
-
 		cur->name = (char *)kmalloc(sizeof(char) * 16, GFP_KERNEL);
 		memset(cur->name, 0, sizeof(char) * 16);
 		memcpy(cur->name, p, 16);
 		p += 16;
-
 		cur->type = *(uint32_t *)p;
 		p += 4;
-
 		cur->len = *(uint32_t *)p;
 		p += 4;
-
 		if((cur->type )  >= 1000 ) {//Temp Workaround
 			cur->len = 0;
 		}else{
@@ -2554,14 +1760,12 @@
 	kfree(rawdata);
 #endif
 	return head;
-
 err:
 	aicbt_patch_table_free(&head);
 	if (rawdata)
 		kfree(rawdata);
 	return NULL;
 }
-
 int aicbt_patch_info_unpack(struct aicbt_patch_info_t *patch_info, struct aicbt_patch_table *head_t)
 {
     if (AICBT_PT_INF == head_t->type) {
@@ -2573,7 +1777,6 @@
     }
     return 0;
 }
-
 int aicbt_patch_trap_data_load(struct aic_sdio_dev *sdiodev, struct aicbt_patch_table *head)
 {
 	struct aicbt_patch_info_t patch_info = {
@@ -2590,12 +1793,6 @@
     if(head == NULL){
         return -1;
     }
-
-	/*if(sdiodev->chipid == PRODUCT_ID_AIC8801){
-		patch_info.addr_adid  = FW_RAM_ADID_BASE_ADDR;
-		patch_info.addr_patch = FW_RAM_PATCH_BASE_ADDR;
-	}
-	else if(sdiodev->chipid == PRODUCT_ID_AIC8800DC){*/
 		if(aicbsp_info.chip_rev == CHIP_REV_U01){
 			patch_info.addr_adid = RAM_8800DC_U01_ADID_ADDR;
 		}else if(aicbsp_info.chip_rev == CHIP_REV_U02){
@@ -2613,31 +1810,13 @@
             if (rwnx_send_dbg_mem_write_req(sdiodev->rwnx_hw, patch_info.adid_flag_addr, patch_info.adid_flag))
                 return -1;
         }
-	/*} else if(sdiodev->chipid == PRODUCT_ID_AIC8800D80){
-        if (aicbsp_info.chip_rev == CHIP_REV_U01) {
-		    patch_info.addr_adid = FW_RAM_ADID_BASE_ADDR_8800D80;
-		    patch_info.addr_patch = FW_RAM_PATCH_BASE_ADDR_8800D80;
-        } else if (aicbsp_info.chip_rev == CHIP_REV_U02 || aicbsp_info.chip_rev == CHIP_REV_U03) {
-            patch_info.addr_adid = FW_RAM_ADID_BASE_ADDR_8800D80_U02;
-		    patch_info.addr_patch = FW_RAM_PATCH_BASE_ADDR_8800D80_U02;
-        }
-        aicbt_patch_info_unpack(&patch_info, head);
-        if(patch_info.info_len == 0) {
-            printk("%s, aicbt_patch_info_unpack fail\n", __func__);
-            return -1;
-        }
-	}*/
-
 	printk("addr_adid %x addr_patch %x \n",patch_info.addr_adid, patch_info.addr_patch);
-
 	if (rwnx_plat_bin_fw_upload_2(sdiodev->rwnx_hw, patch_info.addr_adid, aicbsp_firmware_list[aicbsp_info.cpmode].bt_adid))
 		return -1;
 	if (rwnx_plat_bin_fw_upload_2(sdiodev->rwnx_hw, patch_info.addr_patch, aicbsp_firmware_list[aicbsp_info.cpmode].bt_patch))
 		return -1;
 	return 0;
-
 }
-
 int aicbt_patch_table_load(struct aic_sdio_dev *sdiodev, struct aicbt_patch_table *head)
 {
 	struct aicbt_patch_table *p;
@@ -2646,35 +1825,29 @@
     if(head == NULL){
         return -1;
     }
-
 	printk("aicbt_patch_table_load begin \n");
-
     for (p = head; p != NULL; p = p->next) {
     	data = p->data;
     	if (AICBT_PT_BTMODE == p->type) {
     		*(data + 1)  = aicbsp_info.hwinfo < 0;
     		*(data + 3)  = aicbsp_info.hwinfo;
     		*(data + 5)  = (sdiodev->chipid == PRODUCT_ID_AIC8800DC?aicbsp_info.cpmode:0);//0;//aicbsp_info.cpmode;
-
     		*(data + 7)  = aicbt_info[sdiodev->chipid].btmode;
     		*(data + 9)  = aicbt_info[sdiodev->chipid].btport;
     		*(data + 11) = aicbt_info[sdiodev->chipid].uart_baud;
     		*(data + 13) = aicbt_info[sdiodev->chipid].uart_flowctrl;
     		*(data + 15) = aicbt_info[sdiodev->chipid].lpm_enable;
     		*(data + 17) = aicbt_info[sdiodev->chipid].txpwr_lvl;
-
             printk("%s bt btmode[%d]:%d \r\n", __func__, sdiodev->chipid, aicbt_info[sdiodev->chipid].btmode);
     		printk("%s bt uart_baud[%d]:%d \r\n", __func__, sdiodev->chipid, aicbt_info[sdiodev->chipid].uart_baud);
     		printk("%s bt uart_flowctrl[%d]:%d \r\n", __func__, sdiodev->chipid, aicbt_info[sdiodev->chipid].uart_flowctrl);
     		printk("%s bt lpm_enable[%d]:%d \r\n", __func__, sdiodev->chipid, aicbt_info[sdiodev->chipid].lpm_enable);
     		printk("%s bt tx_pwr[%d]:%d \r\n", __func__, sdiodev->chipid, aicbt_info[sdiodev->chipid].txpwr_lvl);
     	}
-
     	if (AICBT_PT_VER == p->type) {
     		printk("aicbsp: bt patch version: %s\n", (char *)p->data);
     		continue;
     	}
-
     	for (i = 0; i < p->len; i++) {
     		ret = rwnx_send_dbg_mem_write_req(sdiodev->rwnx_hw, *data, *(data + 1));
     		if (ret != 0)
@@ -2684,12 +1857,9 @@
     	if (p->type == AICBT_PT_PWRON)
     		udelay(500);
     }
-
 	printk("aicbt_patch_table_load end \n");
-	///aicbt_patch_table_free(&head);
 	return 0;
 }
-
 int aicbt_init(struct aic_sdio_dev *sdiodev)
 {
     int ret = 0;
@@ -2698,27 +1868,21 @@
         printk("aicbt_patch_table_alloc fail\n");
         return -1;
     }
-
     if (aicbt_patch_trap_data_load(sdiodev, head)) {
 		printk("aicbt_patch_trap_data_load fail\n");
         ret = -1;
 		goto err;
 	}
-
 	if (aicbt_patch_table_load(sdiodev, head)) {
 		 printk("aicbt_patch_table_load fail\n");
         ret = -1;
 		goto err;
 	}
-
 err:
 	aicbt_patch_table_free(&head);
 	return ret;
 }
-
 #endif
-
-
 /**
  * rwnx_plat_fmac_load() - Load FW code
  *
@@ -2809,7 +1973,6 @@
 				printk("apply dpd bin fail: %d\n", ret);
 				return ret;
 			}
-
 		}
 #endif
 		else
@@ -2826,7 +1989,7 @@
         }
         #endif
     } else if (testmode == 1) {
-    	printk("patch load\n");
+			printk("patch load\n");
 		aicwf_plat_patch_load_8800dc(rwnx_hw);
 		if (ret) {
 			printk("patch load fail: %d\n", ret);
@@ -2846,16 +2009,16 @@
 #endif
 		printk("%s load rftest bin\n", __func__);
         if (chip_sub_id == 0) {
-            ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, ROM_FMAC_PATCH_ADDR_U01, RWNX_MAC_RF_PATCH_NAME);
+        ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, ROM_FMAC_PATCH_ADDR_U01, RWNX_MAC_RF_PATCH_NAME);
         }
         if (!ret) {
-			ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, RAM_LMAC_FW_ADDR, RWNX_MAC_FW_RF_BASE_NAME);
+            ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, RAM_LMAC_FW_ADDR, RWNX_MAC_FW_RF_BASE_NAME);
         	}
 		if (ret) {
 			printk("rftest bin load fail: %d\n", ret);
 			return ret;
 		}
-		        }
+        }
 	else if (testmode == 4) {
                 #if (defined(CONFIG_DPD) && !defined(CONFIG_FORCE_DPD_CALIB))
 					if (is_file_exist(FW_DPDRESULT_NAME_8800DC) == 0) {
@@ -2879,32 +2042,10 @@
 					}
                 #endif
 					return 1; // exit calib mode
-				}
+    }
 
     return ret;
 }
-
-static int rwnx_plat_patch_load_d80(struct rwnx_hw *rwnx_hw){
-	int ret = 0;
-	
-	if (rwnx_plat_bin_fw_upload_2(rwnx_hw, RAM_FMAC_FW_ADDR, aicbsp_firmware_list[aicbsp_info.cpmode].wl_fw)) {
-		printk("8800d80 download wifi fw fail\n");
-		return -1;
-	}
-
-	if (aicwifi_patch_config_8800d80(rwnx_hw)) {
-		printk("aicwifi_patch_config_8800d80 fail\n");
-		return -1;
-	}
-
-	if (aicwifi_sys_config_8800d80(rwnx_hw)) {
-		printk("aicwifi_patch_config_8800d80 fail\n");
-		return -1;
-	}
-
-	return 0;
-}
-
 #ifdef CONFIG_DPD
 #define RAM_LMAC_FW_ADDR               0x00150000
 int aicwf_plat_calib_load_8800dc(struct rwnx_hw *rwnx_hw)
@@ -2961,7 +2102,7 @@
     if (testmode == 1) {
         cfg_base = RAM_LMAC_FW_ADDR + 0x0164;
     }
-        ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0x14, &cfm);
+    ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0x14, &cfm);
     if (ret) {
         printk("rf misc ram[0x%x] rd fail: %d\n", cfg_base + 0x14, ret);
         return ret;
@@ -2988,7 +2129,7 @@
         printk("load calib bin fail: %d\n", ret);
         return ret;
     }
-        fw_addr = 0x00130009;
+    fw_addr = 0x00130009;
     boot_type = 4;//HOST_START_APP_FNCALL;
     printk("Start app: %08x, %d\n", fw_addr, boot_type);
     ret = rwnx_send_dbg_start_app_req(rwnx_hw, fw_addr, boot_type);
@@ -3008,7 +2149,7 @@
             return ret;
         }
         misc_ram_addr = cfm.memdata;
-                ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, bit_mask);
+        ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, bit_mask);
         ram_word_cnt = (MEMBER_SIZE(rf_misc_ram_t, bit_mask) + MEMBER_SIZE(rf_misc_ram_t, reserved)) / 4;
         for (i = 0; i < ram_word_cnt; i++) {
             ret = rwnx_send_dbg_mem_read_req(rwnx_hw, ram_base_addr + i * 4, &cfm);
@@ -3018,7 +2159,7 @@
             }
             dpd_res->bit_mask[i] = cfm.memdata;
         }
-                ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, dpd_high);
+        ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, dpd_high);
         ram_word_cnt = MEMBER_SIZE(rf_misc_ram_t, dpd_high) / 4;
         for (i = 0; i < ram_word_cnt; i++) {
             ret = rwnx_send_dbg_mem_read_req(rwnx_hw, ram_base_addr + i * 4, &cfm);
@@ -3028,7 +2169,7 @@
             }
             dpd_res->dpd_high[i] = cfm.memdata;
         }
-                ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, loft_res);
+        ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, loft_res);
         ram_word_cnt = MEMBER_SIZE(rf_misc_ram_t, loft_res) / 4;
         for (i = 0; i < ram_word_cnt; i++) {
             ret = rwnx_send_dbg_mem_read_req(rwnx_hw, ram_base_addr + i * 4, &cfm);
@@ -3053,16 +2194,16 @@
         printk("void dpd_res, bypass it.\n");
         return 0;
     }
-    if (testmode == 1) {
-        cfg_base = RAM_LMAC_FW_ADDR + 0x0164;
-    }
+	if (testmode == 1) {
+		cfg_base = RAM_LMAC_FW_ADDR + 0x0164;
+	}
     if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0x14, &cfm))) {
         printk("rf misc ram[0x%x] rd fail: %d\n", cfg_base + 0x14, ret);
         return ret;
     }
     misc_ram_addr = cfm.memdata;
     printk("misc_ram_addr: %x\n", misc_ram_addr);
-        printk("bit_mask[0]=%x\n", dpd_res->bit_mask[0]);
+    printk("bit_mask[0]=%x\n", dpd_res->bit_mask[0]);
     ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, bit_mask);
     ram_byte_cnt = MEMBER_SIZE(rf_misc_ram_t, bit_mask) + MEMBER_SIZE(rf_misc_ram_t, reserved);
     ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, ram_base_addr, ram_byte_cnt, (u32 *)&dpd_res->bit_mask[0]);
@@ -3070,7 +2211,7 @@
         printk("bit_mask wr fail: %x, ret:%d\r\n", ram_base_addr, ret);
         return ret;
     }
-        printk("dpd_high[0]=%x\n", dpd_res->dpd_high[0]);
+    printk("dpd_high[0]=%x\n", dpd_res->dpd_high[0]);
     ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, dpd_high);
     ram_byte_cnt = MEMBER_SIZE(rf_misc_ram_t, dpd_high);
     ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, ram_base_addr, ram_byte_cnt, (u32 *)&dpd_res->dpd_high[0]);
@@ -3078,7 +2219,7 @@
         printk("dpd_high wr fail: %x, ret:%d\r\n", ram_base_addr, ret);
         return ret;
     }
-        printk("loft_res[0]=%x\n", dpd_res->loft_res[0]);
+    printk("loft_res[0]=%x\n", dpd_res->loft_res[0]);
     ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, loft_res);
     ram_byte_cnt = MEMBER_SIZE(rf_misc_ram_t, loft_res);
     ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, ram_base_addr, ram_byte_cnt, (u32 *)&dpd_res->loft_res[0]);
@@ -3096,7 +2237,7 @@
     u32 *dst=NULL;
     char *filename = FW_DPDRESULT_NAME_8800DC;
     printk("dpd_res file path:%s \r\n", filename);
-        size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
+    size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
     if (size <= 0) {
         printk("wrong size of dpd_res file\n");
         dst = NULL;
@@ -3432,11 +2573,9 @@
     if (rwnx_plat->enabled)
         return 0;
 
-
 	#ifdef CONFIG_BT_SUPPORT
 		ret = aicbt_init(rwnx_hw->sdiodev);
 	#endif
-
     #if 0
     if (rwnx_platform_reset(rwnx_plat))
         return -1;
@@ -3490,27 +2629,15 @@
     #endif
     #endif
     #ifdef CONFIG_ROM_PATCH_EN
-	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-	    ret = rwnx_plat_patch_load(rwnx_hw);
-	    if (ret) {
-			printk("DCDW patch load return %d\n", ret);
-	        return ret;
-	    }
-	}else if(rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
-		ret = rwnx_plat_patch_load_d80(rwnx_hw);
-		if (ret) {
-			printk("D80 patch load return %d\n", ret);
-	        return ret;
-	    }
-	}
+    ret = rwnx_plat_patch_load(rwnx_hw);
+    if (ret) {
+		printk("patch load return %d\n", ret);
+        return ret;
+    }
     #endif
 
     #ifdef CONFIG_LOAD_USERCONFIG
-	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
-    	rwnx_plat_userconfig_load(rwnx_hw);
-	}else if(rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
-		rwnx_plat_userconfig_load_8800d80(rwnx_hw);
-	}
+    rwnx_plat_userconfig_load(rwnx_hw);
     #endif
 
     rwnx_plat->enabled = true;
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_platform.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_platform.h
index 3b51404..5eb56e4 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_platform.h
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_platform.h
@@ -93,7 +93,6 @@
 #define RWNX_FCU_FW_NAME                "fcuram.bin"
 
 #define FW_USERCONFIG_NAME              "aic_userconfig.txt"
-#define FW_USERCONFIG_NAME_8800D80         "aic_userconfig_8800d80.txt"
 
 #ifdef CONFIG_VENDOR_SPECIFIED_FW_PATH
 #define VENDOR_SPECIFIED_FW_PATH    CONFIG_VENDOR_SPECIFIED_FW_PATH
@@ -112,17 +111,11 @@
 	AICBT_PT_AF,
 	AICBT_PT_VER,
 };
-
 enum aicbt_btport_type {
 	AICBT_BTPORT_NULL,
 	AICBT_BTPORT_MB,
 	AICBT_BTPORT_UART,
 };
-
-/*  btmode
- * used for force bt mode,if not AICBSP_MODE_NULL
- * efuse valid and vendor_info will be invalid, even has beed set valid
-*/
 enum aicbt_btmode_type {
 	AICBT_BTMODE_BT_ONLY_SW = 0x0,    // bt only mode with switch
 	AICBT_BTMODE_BT_WIFI_COMBO,       // wifi/bt combo mode
@@ -132,36 +125,27 @@
 	AICBT_BTMODE_BT_ONLY_COANT,       // bt only mode with no external switch
 	AICBT_MODE_NULL = 0xFF,           // invalid value
 };
-
-/*  uart_baud
- * used for config uart baud when btport set to uart,
- * otherwise meaningless
-*/
 enum aicbt_uart_baud_type {
 	AICBT_UART_BAUD_115200     = 115200,
 	AICBT_UART_BAUD_921600     = 921600,
 	AICBT_UART_BAUD_1_5M       = 1500000,
 	AICBT_UART_BAUD_3_25M      = 3250000,
 };
-
 enum aicbt_uart_flowctrl_type {
 	AICBT_UART_FLOWCTRL_DISABLE = 0x0,    // uart without flow ctrl
 	AICBT_UART_FLOWCTRL_ENABLE,           // uart with flow ctrl
 };
-
 enum aicbsp_cpmode_type {
 	AICBSP_CPMODE_WORK,
 	AICBSP_CPMODE_TEST,
 	AICBSP_CPMODE_MAX,
 };
-
 enum chip_rev {
 	CHIP_REV_U01 = 1,
 	CHIP_REV_U02 = 3,
 	CHIP_REV_U03 = 7,
 	CHIP_REV_U04 = 7,
 };
-
 #define RAM_FMAC_FW_ADDR                    0x00120000
 #define FW_RAM_ADID_BASE_ADDR               0x00161928
 #define FW_RAM_ADID_BASE_ADDR_U03           0x00161928
@@ -177,18 +161,13 @@
 #define FW_RAM_PATCH_BASE_ADDR_8800D80      0x0020B2B0
 #define FW_RAM_ADID_BASE_ADDR_8800D80_U02   0x00201940
 #define FW_RAM_PATCH_BASE_ADDR_8800D80_U02  0x0020b43c
-
 #define AICBT_PT_TAG                "AICBT_PT_TAG"
-///aic bt tx pwr lvl :lsb->msb: first byte, min pwr lvl; second byte, max pwr lvl;
-///pwr lvl:20(min), 30 , 40 , 50 , 60(max)
 #define AICBT_TXPWR_LVL            0x00006020
 #define AICBT_TXPWR_LVL_8800dc            0x00006f2f
 #define AICBT_TXPWR_LVL_8800d80           0x00006f2f
-
 #define AICBSP_HWINFO_DEFAULT       (-1)
 #define AICBSP_CPMODE_DEFAULT       AICBSP_CPMODE_WORK
 #define AICBSP_FWLOG_EN_DEFAULT     0
-
 #define AICBT_BTMODE_DEFAULT_8800d80    AICBT_BTMODE_BT_ONLY_COANT
 #define AICBT_BTMODE_DEFAULT            AICBT_BTMODE_BT_WIFI_COMBO
 #define AICBT_BTPORT_DEFAULT            AICBT_BTPORT_UART
@@ -198,7 +177,6 @@
 #define AICBT_TXPWR_LVL_DEFAULT         AICBT_TXPWR_LVL
 #define AICBT_TXPWR_LVL_DEFAULT_8800dc  AICBT_TXPWR_LVL_8800dc
 #define AICBT_TXPWR_LVL_DEFAULT_8800d80 AICBT_TXPWR_LVL_8800d80
-
 struct aicbt_patch_table {
 	char     *name;
 	uint32_t type;
@@ -206,7 +184,6 @@
 	uint32_t len;
 	struct aicbt_patch_table *next;
 };
-
 struct aicbt_info_t {
 	uint32_t btmode;
 	uint32_t btport;
@@ -215,7 +192,6 @@
 	uint32_t lpm_enable;
 	uint32_t txpwr_lvl;
 };
-
 struct aicbt_patch_info_t {
 	uint32_t info_len;
 	uint32_t adid_addrinf;
@@ -227,7 +203,6 @@
 	uint32_t adid_flag_addr;
 	uint32_t adid_flag;
 };
-
 struct aicbsp_firmware {
 	const char *desc;
 	const char *bt_adid;
@@ -235,7 +210,6 @@
 	const char *bt_table;
 	const char *wl_fw;
 };
-
 struct aicbsp_info_t {
 	int hwinfo;
 	int hwinfo_r;
@@ -244,7 +218,6 @@
 	bool fwlog_en;
 	uint8_t irqf;
 };
-
 /**
  * Type of memory to access (cf rwnx_plat.get_address)
  *
@@ -333,13 +306,13 @@
 #ifdef CONFIG_LOAD_USERCONFIG
 void get_userconfig_txpwr_lvl(txpwr_lvl_conf_t *txpwr_lvl);
 void get_userconfig_txpwr_lvl_v2_in_fdrv(txpwr_lvl_conf_v2_t *txpwr_lvl_v2);
-void get_userconfig_txpwr_lvl_v3_in_fdrv(txpwr_lvl_conf_v3_t *txpwr_lvl_v3);
-void get_userconfig_txpwr_lvl_adj_in_fdrv(txpwr_lvl_adj_conf_t *txpwr_lvl_adj);
 void get_userconfig_txpwr_loss(txpwr_loss_conf_t *txpwr_loss);
 void get_userconfig_txpwr_ofst(txpwr_ofst_conf_t *txpwr_ofst);
-void get_userconfig_txpwr_ofst2x_in_fdrv(txpwr_ofst2x_conf_t *txpwr_ofst2x);
 void get_userconfig_xtal_cap(xtal_cap_conf_t *xtal_cap);
 void set_txpwr_loss_ofst(s8_l value);
+#ifdef CONFIG_SET_AP_PS
+int get_userconfig_set_ap_ps_lvl(ap_ps_conf_t *ap_ps_lvl);
+#endif
 #endif
 int aicwf_patch_table_load(struct rwnx_hw *rwnx_hw, char *filename);
 int rwnx_load_firmware(u32 **fw_buf, const char *name, struct device *device);
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_rx.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_rx.c
index cec249e..19f2422 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_rx.c
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_rx.c
@@ -393,18 +393,18 @@
 	if (fast_from_driver) {
 		rx_skb->data -= 14;
 		rx_skb->len += 14;
-			if(((unsigned int)rx_skb->data) % 4 == 0)
+		if(((unsigned int)rx_skb->data) % 4 == 0)
 		{
-						memmove(rx_skb->data - 2, rx_skb->data, rx_skb->len);
+			memmove(rx_skb->data - 2, rx_skb->data, rx_skb->len);
 			rx_skb->data = rx_skb->data - 2;
 		}
-		rx_skb->dev = rwnx_vif->ndev;
+	rx_skb->dev = rwnx_vif->ndev;
 		ret = fast_from_driver(rx_skb,rx_skb->dev);
 		if(!ret)
 		{
 			rx_skb->data += 14;
 			rx_skb->len -= 14;
-	//printk("forward\n");
+    //printk("forward\n");
 		#if 0
 		    struct iphdr *iphead = (struct iphdr *)(rx_skb->data);
 		    struct tcphdr *tcph;
@@ -417,48 +417,48 @@
                     printk("RX1:%x,%x\n", ntohs(tcph->source), ntohs(tcph->dest));
 	        }
 		#endif
-            			netif_rx(rx_skb);
+			netif_rx(rx_skb);
 		}
 	} else {
 		netif_rx(rx_skb);
 	}
 #else
-	rx_skb->protocol = eth_type_trans(rx_skb, rwnx_vif->ndev);	
-        memset(rx_skb->cb, 0, sizeof(rx_skb->cb));
+	rx_skb->protocol = eth_type_trans(rx_skb, rwnx_vif->ndev);
+	memset(rx_skb->cb, 0, sizeof(rx_skb->cb));
 
-			do {
+    do {
 		size_t offset = (size_t)rx_skb->data&3;
 		if(offset == 0)
-			break;
-
-		if(skb_headroom(rx_skb) < offset)
-			break;
-
-		u8_l *newhead = skb_push(rx_skb, offset);
-		memmove(newhead, newhead + offset, rx_skb->len - offset);
-		skb_trim(rx_skb, rx_skb->len - offset);
-	} while(0);
+            break;
+    
+        if(skb_headroom(rx_skb) < offset)
+            break;
+    
+        u8_l *newhead = skb_push(rx_skb, offset);
+        memmove(newhead, newhead + offset, rx_skb->len - offset);
+        skb_trim(rx_skb, rx_skb->len - offset);
+    } while(0);
 
 	#if 0 //modify by aic
-    netif_receive_skb(rx_skb);
+	netif_receive_skb(rx_skb);
 	#else
-    if (in_interrupt()) {
-        netif_rx(rx_skb);
-    } else {
-    /*
-    * If the receive is not processed inside an ISR, the softirqd must be woken explicitly to service the NET_RX_SOFTIRQ.
-    * * In 2.6 kernels, this is handledby netif_rx_ni(), but in earlier kernels, we need to do it manually.
-    */
+	if (in_interrupt()) {
+		netif_rx(rx_skb);
+	} else {
+	/*
+	* If the receive is not processed inside an ISR, the softirqd must be woken explicitly to service the NET_RX_SOFTIRQ.
+	* * In 2.6 kernels, this is handledby netif_rx_ni(), but in earlier kernels, we need to do it manually.
+	*/
 	#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
-        netif_rx_ni(rx_skb);
+		netif_rx_ni(rx_skb);
 	#else
-        ulong flags;
-        netif_rx(rx_skb);
-        local_irq_save(flags);
-        RAISE_RX_SOFTIRQ();
-        local_irq_restore(flags);
+		ulong flags;
+		netif_rx(rx_skb);
+		local_irq_save(flags);
+		RAISE_RX_SOFTIRQ();
+		local_irq_restore(flags);
 	#endif
-    }
+	}
 	#endif
 #endif
 }
@@ -626,7 +626,7 @@
 				//printk("will not happen\n");
 			aicwf_fast_from_driver(rx_skb, rwnx_vif);
 
-//		printk("NET:%d\n", rx_skb->len);
+		//		printk("NET:%d\n", rx_skb->len);
                 
             /*
             * If the receive is not processed inside an ISR, the softirqd must be woken explicitly to service the NET_RX_SOFTIRQ.
@@ -697,20 +697,20 @@
 	const u8* ie;
 	u32 len;
     if (ieee80211_is_assoc_req(mgmt->frame_control) && rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) {
-        printk("ASSOC_REQ: sta_idx %d MAC %pM\n", rwnx_vif->ap.aic_index, mgmt->sa);
-        sta->sta_idx = rwnx_vif->ap.aic_index;
-        len = skb->len - (mgmt->u.assoc_req.variable - skb->data);
+    printk("ASSOC_REQ: sta_idx %d MAC %pM\n", rwnx_vif->ap.aic_index, mgmt->sa);
+	sta->sta_idx = rwnx_vif->ap.aic_index;
+	len = skb->len - (mgmt->u.assoc_req.variable - skb->data);
         #ifdef CONFIG_HE_FOR_OLD_KERNEL
         struct ieee80211_he_cap_elem *he;
-        ie = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, mgmt->u.assoc_req.variable, len);
-        if(ie && ie[1] >= sizeof(*he) + 1){
+	ie = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, mgmt->u.assoc_req.variable, len);
+		if(ie && ie[1] >= sizeof(*he) + 1){
             printk("assoc_req: find he\n");
-            sta->he = true;
-        }
-        else{
+			sta->he = true;
+			}
+		else{
             printk("assoc_req: no find he\n");
-            sta->he = false;
-        }
+			sta->he = false;
+			}
         #endif
         #ifdef CONFIG_VHT_FOR_OLD_KERNEL
         struct ieee80211_vht_cap *vht;
@@ -1268,10 +1268,10 @@
     skb->ip_summed = CHECKSUM_UNNECESSARY;
     skb->pkt_type = PACKET_OTHERHOST;
     skb->protocol = htons(ETH_P_802_2);
+
 #ifdef CONFIG_FILTER_TCP_ACK
 	filter_rx_tcp_ack(rwnx_hw,skb->data, cpu_to_le16(skb->len));
 #endif
-
     netif_receive_skb(skb);
 
     return 0;
@@ -1507,11 +1507,11 @@
         arpoffload_proc(skb, rwnx_vif);
     }
 #endif
-//u8_l offset = (u8_l)(4 - (unsigned long)skb->data%4);
+		//u8_l offset = (u8_l)(4 - (unsigned long)skb->data%4);
 	
 	
 	aicwf_fast_from_driver(skb, rwnx_vif);
-/*
+    /*
     * If the receive is not processed inside an ISR, the softirqd must be woken explicitly to service the NET_RX_SOFTIRQ.
     * * In 2.6 kernels, this is handledby netif_rx_ni(), but in earlier kernels, we need to do it manually.
     */
@@ -2178,9 +2178,9 @@
 						skb_put(defrag_info->skb, skb->len);
 						memcpy(defrag_info->skb->data, skb->data, skb->len);
 						defrag_info->frm_len = skb->len;
-//printk("first:%p,%d\r\n", defrag_info, defrag_info->frm_len);
+						//printk("first:%p,%d\r\n", defrag_info, defrag_info->frm_len);
 						defrag_info->rwnx_hw = rwnx_hw;
-												list_add_tail(&defrag_info->list, &rwnx_hw->defrag_list);
+						list_add_tail(&defrag_info->list, &rwnx_hw->defrag_list);
 						spin_unlock_bh(&rwnx_hw->defrag_lock);
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
 						init_timer(&defrag_info->defrag_timer);
@@ -2205,7 +2205,7 @@
 							}
 						}
 						if (!defrag_info) 
-							spin_unlock_bh(&rwnx_hw->defrag_lock);
+						spin_unlock_bh(&rwnx_hw->defrag_lock);
 						else {
 							if (defrag_info->next_fn != frag_num) {
 								printk("discard:%d:%d\n", defrag_info->next_fn, frag_num);
@@ -2312,10 +2312,10 @@
                     dev_kfree_skb(skb);
             }
 	    } else if( (rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ) {
-#if 0
-				skb_reset_mac_header(skb);
-				eth = eth_hdr(skb);
-				//printk("da:%pM, %x,%x, len=%d\n", eth->h_dest, skb->data[12], skb->data[13], skb->len);
+            #if 0
+            skb_reset_mac_header(skb);
+            eth = eth_hdr(skb);
+            //printk("da:%pM, %x,%x, len=%d\n", eth->h_dest, skb->data[12], skb->data[13], skb->len);
 
                 if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
                     /* broadcast pkt need to be forwared to upper layer and resent
@@ -2356,7 +2356,7 @@
 #else
 				if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb, hw_rxhdr))
 					dev_kfree_skb(skb);
-#endif
+                #endif
 			}
 #else
             if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb, hw_rxhdr))
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_tx.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_tx.c
index 7ff46fa..3424522 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_tx.c
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800/rwnx_tx.c
@@ -136,7 +136,7 @@
 
         rwnx_txq_sta_start(sta, RWNX_TXQ_STOP_STA_PS, rwnx_hw);
         spin_unlock_bh(&rwnx_hw->tx_lock);
-
+        
 #if 0
         if (sta->ps.pkt_ready[LEGACY_PS_ID])
             rwnx_set_traffic_status(rwnx_hw, sta, false, LEGACY_PS_ID);
@@ -178,9 +178,8 @@
     //         sta->mac_addr))
     //    return;
     if(!sta->ps.active) {
-       return;
+    	return;
     }
-
 #ifdef CREATE_TRACE_POINTS
     trace_ps_traffic_req(sta, pkt_req, ps_id);
 #endif
@@ -689,7 +688,7 @@
         printk("need cfm ethertype:%8x,user_idx=%d, skb=%p\n", sw_txhdr->desc.host.ethertype, rwnx_hw->sdio_env.txdesc_free_idx[0], skb);
     } else {
         sw_txhdr->need_cfm = 0;
-        sw_txhdr->desc.host.status_desc_addr = 0;
+            sw_txhdr->desc.host.status_desc_addr = 0;
 
         //sw_txhdr->rwnx_vif->net_stats.tx_packets++;
         //sw_txhdr->rwnx_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
@@ -704,7 +703,7 @@
         aicwf_sdio_host_txdesc_push(&(rwnx_hw->sdio_env), 0, (long)skb);
     } else {
         sw_txhdr->need_cfm = 0;
-        txhdr->desc.host.status_desc_addr = 0;
+            txhdr->desc.host.status_desc_addr = 0;
 
         //sw_txhdr->rwnx_vif->net_stats.tx_packets++;
         //sw_txhdr->rwnx_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
@@ -722,7 +721,7 @@
         printk("need cfm ethertype:%8x,user_idx=%d, skb=%p\n", sw_txhdr->desc.host.ethertype, rwnx_hw->usb_env.txdesc_free_idx[0], skb);
     } else {
         sw_txhdr->need_cfm = 0;
-		sw_txhdr->desc.host.status_desc_addr = 0;
+            sw_txhdr->desc.host.status_desc_addr = 0;
 
         sw_txhdr->rwnx_vif->net_stats.tx_packets++;
         sw_txhdr->rwnx_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
@@ -1039,12 +1038,8 @@
     return res;
 }
 #endif /* CONFIG_RWNX_AMSDUS_TX */
-#ifdef CONFIG_FILTER_TCP_ACK
-/* return:
- *      0, msg buf freed by the real driver
- *      others, skb need free by the caller,remember not use msg->skb!
- */
 
+#ifdef CONFIG_FILTER_TCP_ACK
 int intf_tx(struct rwnx_hw *priv,struct msg_buf *msg)
 {
 		struct rwnx_vif *rwnx_vif = msg->rwnx_vif;
@@ -1057,28 +1052,22 @@
 		int headroom;
 		int max_headroom;
 		int hdr_pads;
-
 		u16 frame_len;
 		u16 frame_oft;
 		u8 tid;
 		struct sk_buff *skb=msg->skb;
 		struct ethhdr eth_t;
-
 		move_tcpack_msg(rwnx_hw,msg);
 		kfree(msg);
-
 			memcpy(&eth_t, skb->data, sizeof(struct ethhdr));
-				/* Retrieve the pointer to the Ethernet data */
 #ifdef CONFIG_SDIO_AGGR
 			skb_pull(skb, 14);
 #else
 			skb_pull(skb, 12);
 #endif
-
 			sk_pacing_shift_update(skb->sk, rwnx_hw->tcp_pacing_shift);
 			max_headroom = sizeof(struct rwnx_txhdr);
 			u32 allign = (long)(skb->data) & 0x3;
-			/* check whether the current skb can be used */
 			if (skb_shared(skb) || (skb_headroom(skb) < max_headroom) ||
 		#ifndef CONFIG_SDIO_AGGR
 				(allign!=0) ||
@@ -1088,39 +1077,25 @@
 															 GFP_ATOMIC);
 					if (unlikely(newskb == NULL))
 						goto free;
-
 					dev_kfree_skb_any(skb);
 					skb = newskb;
 			}
-
-			/* Get the STA id and TID information */
 			sta = rwnx_get_tx_priv(rwnx_vif, skb, &tid);
 			if (!sta){
-				//printk("NO sta:%pM\n", eth_t.h_dest);
 				goto free;
 			}
-
 			txq = rwnx_txq_sta_get(sta, tid, rwnx_hw);
 			if (txq->idx == TXQ_INACTIVE){
-			//printk("staidx=%d\n", sta->sta_idx);
-				//printk("Inactive:%pM\n", eth_t.h_dest);
 				goto free;
 			}
 #ifdef CONFIG_RWNX_AMSDUS_TX
 			if (rwnx_amsdu_add_subframe(rwnx_hw, skb, sta, txq))
 				return NETDEV_TX_OK;
 #endif
-
-			//hdr_pads	= RWNX_SWTXHDR_ALIGN_PADS((long)skb->data);
 			headroom  = sizeof(struct rwnx_txhdr) ;//+ hdr_pads;
 			skb_push(skb, headroom);
-
 			txhdr = (struct rwnx_txhdr *)skb->data;
 			sw_txhdr = &txhdr->sw_hdr;
-			//sw_txhdr = kmem_cache_alloc(rwnx_hw->sw_txhdr_cache, GFP_ATOMIC);
-			//if (unlikely(sw_txhdr == NULL))
-			//	  goto free;
-			//txhdr->sw_hdr = sw_txhdr;
 #ifdef CONFIG_SDIO_AGGR
 			desc = &sw_txhdr->desc;
 			frame_len = (u16)skb->len - headroom;	//- 2;// - sizeof(*eth);
@@ -1128,22 +1103,17 @@
 			desc = &txhdr->desc;
 			frame_len = (u16)skb->len - headroom - 2;// - sizeof(*eth);
 #endif
-
 			sw_txhdr->txq		= txq;
 			sw_txhdr->frame_len = frame_len;
 			sw_txhdr->rwnx_sta	= sta;
 			sw_txhdr->rwnx_vif	= rwnx_vif;
 			sw_txhdr->skb		= skb;
 			sw_txhdr->headroom	= headroom;
-			//sw_txhdr->map_len   = skb->len - offsetof(struct rwnx_txhdr, hw_hdr);
-
 			sw_txhdr->is_dhcp	= 0;
-
 #ifdef CONFIG_RWNX_AMSDUS_TX
 			sw_txhdr->amsdu.len = 0;
 			sw_txhdr->amsdu.nb = 0;
 #endif
-			// Fill-in the descriptor
 			memcpy(&desc->host.eth_dest_addr, eth_t.h_dest, ETH_ALEN);
 			memcpy(&desc->host.eth_src_addr, eth_t.h_source, ETH_ALEN);
 			desc->host.ethertype = eth_t.h_proto;
@@ -1157,68 +1127,41 @@
 				desc->host.flags = TXU_CNTRL_USE_4ADDR;
 			else
 				desc->host.flags = 0;
-
 #if 0
 			if ((rwnx_vif->tdls_status == TDLS_LINK_ACTIVE) &&
 				rwnx_vif->sta.tdls_sta &&
 				(memcmp(desc->host.eth_dest_addr.array, rwnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN) == 0)) {
 				desc->host.flags |= TXU_CNTRL_TDLS;
 				rwnx_vif->sta.tdls_sta->tdls.last_tid = desc->host.tid;
-				//rwnx_vif->sta.tdls_sta->tdls.last_sn = desc->host.sn;
 			}
 #endif
-
 			if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_MESH_POINT) {
 				if (rwnx_vif->is_resending) {
 					desc->host.flags |= TXU_CNTRL_MESH_FWD;
 				}
 			}
-
 #ifdef CONFIG_RWNX_SPLIT_TX_BUF
 			desc->host.packet_len[0] = frame_len;
 #else
 			desc->host.packet_len = frame_len;
 #endif
-
 			txhdr->hw_hdr.cfm.status.value = 0;
-
 			if (unlikely(rwnx_prep_tx(rwnx_hw, txhdr))) {
 				printk("TX_BUSY:%pM\n", eth_t.h_dest);
-				//kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
 				skb_pull(skb, headroom);
 				dev_kfree_skb_any(skb);
 				return NETDEV_TX_BUSY;
 			}
-
-			/* Fill-in TX descriptor */
-		/*
-			frame_oft = sizeof(struct rwnx_txhdr) - offsetof(struct rwnx_txhdr, hw_hdr)
-						+ hdr_pads;// + sizeof(*eth);
-		*/
-		/*
-#ifdef CONFIG_RWNX_SPLIT_TX_BUF
-			desc->host.packet_addr[0] = sw_txhdr->dma_addr + frame_oft;
-			desc->host.packet_cnt = 1;
-#else
-			desc->host.packet_addr = sw_txhdr->dma_addr + frame_oft;
-#endif
-			desc->host.status_desc_addr = sw_txhdr->dma_addr;
-		*/
-
 			spin_lock_bh(&rwnx_hw->tx_lock);
 			if (rwnx_txq_queue_skb(skb, txq, rwnx_hw, false))
 				rwnx_hwq_process(rwnx_hw, txq->hwq);
 			spin_unlock_bh(&rwnx_hw->tx_lock);
-
 		return 0;//NETDEV_TX_OK;
-
 	free:
 		dev_kfree_skb_any(skb);
-
 		return 0;//NETDEV_TX_OK;
 	}
 #endif
-
 /**
  * netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
  *                               struct net_device *dev);
@@ -1310,7 +1253,7 @@
 #endif
 
     memcpy(&eth_t, skb->data, sizeof(struct ethhdr));
-		#ifdef CONFIG_SDIO_AGGR
+#ifdef CONFIG_SDIO_AGGR
 	skb_pull(skb, 14);
 #else
 	skb_pull(skb, 12);
@@ -1352,7 +1295,7 @@
         return NETDEV_TX_OK;
 #endif
 
-/* Retrieve the pointer to the Ethernet data */
+    /* Retrieve the pointer to the Ethernet data */
     //hdr_pads  = RWNX_SWTXHDR_ALIGN_PADS((long)skb->data);  
     headroom  = sizeof(struct rwnx_txhdr) ;//+ hdr_pads;
     skb_push(skb, headroom);
@@ -1426,7 +1369,7 @@
     txhdr->hw_hdr.cfm.status.value = 0;
 
     if (unlikely(rwnx_prep_tx(rwnx_hw, txhdr))) {
-		printk("TX_BUSY:%pM\n", eth_t.h_dest);
+	printk("TX_BUSY:%pM\n", eth_t.h_dest);
         //kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
         skb_pull(skb, headroom);
         dev_kfree_skb_any(skb);
@@ -1670,7 +1613,7 @@
     #else
     desc = &txhdr->desc;
     #endif
-    desc->host.ethertype = 0;
+	desc->host.ethertype = 0;
     desc->host.staid = (sta) ? sta->sta_idx : 0xFF;
     desc->host.vif_idx = vif->vif_index;
     desc->host.tid = 0xFF;
@@ -2065,7 +2008,7 @@
 #ifdef AICWF_USB_SUPPORT
     if (rwnx_hw->usbdev->state == USB_DOWN_ST) {
         headroom = sw_txhdr->headroom;
-                skb_pull(skb, headroom);
+        skb_pull(skb, headroom);
         consume_skb(skb);
         return 0;
     }
@@ -2073,7 +2016,7 @@
 #ifdef AICWF_SDIO_SUPPORT
     if(rwnx_hw->sdiodev->bus_if->state == BUS_DOWN_ST) {
         headroom = sw_txhdr->headroom;
-                skb_pull(skb, headroom);
+        skb_pull(skb, headroom);
         consume_skb(skb);
         return 0;
     }
@@ -2105,7 +2048,7 @@
         cfg80211_mgmt_tx_status(&sw_txhdr->rwnx_vif->wdev,
 	#else
 	if (sw_txhdr->rwnx_vif->up && sw_txhdr->rwnx_vif->ndev && sw_txhdr->rwnx_vif->ndev->reg_state == NETREG_REGISTERED)
-        	cfg80211_mgmt_tx_status(sw_txhdr->rwnx_vif->ndev,
+        cfg80211_mgmt_tx_status(sw_txhdr->rwnx_vif->ndev,
 	#endif
                                 (unsigned long)skb,
                                 (skb->data + sw_txhdr->headroom),
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/Kconfig b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/Kconfig
new file mode 100755
index 0000000..e20ffc3
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/Kconfig
@@ -0,0 +1,8 @@
+#menu "AIC8800 WLAN support"
+
+config AIC8800D80L
+	tristate "AIC8800D80L Wireless driver"
+	depends on MMC
+	---help---
+		Enable AIC8800D80L  kernel driver.
+#endmenu
\ No newline at end of file
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/Makefile b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/Makefile
new file mode 100755
index 0000000..40c1e43
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/Makefile
@@ -0,0 +1,325 @@
+KDIR  := /lib/modules/$(shell uname -r)/build
+PWD   := $(shell pwd)
+
+RWNX_VERS_NUM=6.4.3.0
+
+CONFIG_PLATFORM_NANOPI_M4 ?= n
+CONFIG_PLATFORM_ALLWINNER ?= n
+CONFIG_PLATFORM_INGENIC_T20 ?= n
+CONFIG_PLATFORM_UBUNTU ?= n
+
+FAST_FROM_DRIVER = y
+HANDLE_TX_THREAD_ZTE = y
+HANDLE_RX_THREAD_ZTE = y
+
+CONFIG_ANDROID_PLATFORM ?= n
+ifeq ($(CONFIG_PLATFORM_NANOPI_M4), y)
+CONFIG_ANDROID_PLATFORM = y
+endif
+ifeq ($(CONFIG_PLATFORM_ALLWINNER), y)
+CONFIG_ANDROID_PLATFORM = y
+endif
+# Support of bootrom start
+CONFIG_START_FROM_BOOTROM = y
+
+# Support of pmic setting, new version bootrom avaliable
+CONFIG_PMIC_SETTING ?= y
+
+# Select DCDC_VRF, check your board
+CONFIG_VRF_DCDC_MODE = y
+
+# ROM patch enabled option
+CONFIG_ROM_PATCH_EN ?=y
+
+# FPGA verification
+CONFIG_FPGA_VERIFICATION ?=n
+
+CONFIG_VHT_FOR_OLD_KERNEL ?= y
+# Support HE on old version kernel
+CONFIG_HE_FOR_OLD_KERNEL ?= y
+ifeq ($(CONFIG_PLATFORM_ALLWINNER), y)
+CONFIG_HE_FOR_OLD_KERNEL = y
+endif
+
+# Support chip with mcu
+CONFIG_MCU_INTEGRATED ?= n
+CONFIG_MCU_MESSAGE ?= n
+ifeq ($(CONFIG_MCU_INTEGRATED), y)
+CONFIG_PMIC_SETTING = n
+else
+CONFIG_MCU_MESSAGE ?= n
+endif
+
+# Support coex with bt
+CONFIG_COEX ?= y
+
+#
+# WAITING FOR KCONFIG {
+#
+CONFIG_RWNX_FULLMAC ?= y
+CONFIG_RWNX_FHOST ?= n
+
+#
+# DEBUG OPTIONS
+CONFIG_RWNX_UM_HELPER_DFLT ?= "/dini/dini_bin/rwnx_umh.sh"
+CONFIG_AIC_FW_PATH = "/vendor/etc/firmware"
+export CONFIG_AIC_FW_PATH
+
+#
+# FW ARCH:
+CONFIG_RWNX_SDM ?= n
+CONFIG_RWNX_TL4 ?= n
+
+# IPC version
+CONFIG_RWNX_OLD_IPC ?= n
+
+# Support of P2P DebugFS for enabling/disabling NoA and OppPS
+CONFIG_RWNX_P2P_DEBUGFS ?= n
+ccflags-y := -DCONFIG_RWNX_FULLMAC
+# FW path
+ifeq ($(CONFIG_PLATFORM_NANOPI_M4), y) # NANOPI_M4
+CONFIG_VENDOR_SPECIFIED_FW_PATH ?= "/vendor/firmware"
+else # NANOPI_M4
+ifeq ($(CONFIG_PLATFORM_ALLWINNER), y) # ALLWINNER
+CONFIG_VENDOR_SPECIFIED_FW_PATH ?= "/vendor/etc/firmware"
+else # ALLWINNER
+ifeq ($(CONFIG_PLATFORM_INGENIC_T20), y) # INGENTIC_T20
+CONFIG_VENDOR_SPECIFIED_FW_PATH ?= "/lib/firmware"
+else # INGENTIC_T20
+ifeq ($(CONFIG_PLATFORM_UBUNTU), y) # PC
+CONFIG_VENDOR_SPECIFIED_FW_PATH ?= "/lib/firmware"
+CONFIG_VENDOR_SUBDIR_NAME ?= "aic8818"
+endif # PC
+endif # INGENTIC_T20
+endif # ALLWINNER
+endif # NANOPI_M4
+CONFIG_VENDOR_SPECIFIED_FW_PATH ?= "/etc/firmware"
+ifneq ($(CONFIG_VENDOR_SPECIFIED_FW_PATH),)
+subdir-ccflags-y += -DCONFIG_VENDOR_SPECIFIED_FW_PATH=\"$(CONFIG_VENDOR_SPECIFIED_FW_PATH)\"
+endif
+ifneq ($(CONFIG_VENDOR_SUBDIR_NAME),)
+subdir-ccflags-y += -DCONFIG_VENDOR_SUBDIR_NAME=\"$(CONFIG_VENDOR_SUBDIR_NAME)\"
+endif
+
+#
+# } // WAITING FOR KCONFIG
+#
+
+# Enable A-MSDU support (need FW support)
+## Select this if FW is compiled with AMSDU support
+CONFIG_RWNX_SPLIT_TX_BUF ?= n
+## Select this TO send AMSDU
+CONFIG_RWNX_AMSDUS_TX ?= n
+
+# Enable BFMER support (need FW support)
+CONFIG_RWNX_BFMER ?= n
+
+CONFIG_SDIO_SUPPORT =y
+CONFIG_USB_SUPPORT =n
+CONFIG_RX_REORDER ?=y
+CONFIG_ARP_OFFLOAD =y
+CONFIG_USE_5G =y
+CONFIG_RADAR_OR_IR_DETECT =n
+CONFIG_DOWNLOAD_FW =n
+CONFIG_LOAD_USERCONFIG ?=y
+CONFIG_CHIP_REBOOT ?=n
+CONFIG_BT_SUPPORT ?=n
+CONFIG_RFTEST=y
+CONFIG_RFTEST_USB_BT=n
+CONFIG_RFTEST_UART_BT=y
+CONFIG_USB_BT =y
+CONFIG_MAC_RANDOM_IF_NO_MAC_IN_EFUSE ?= y
+CONFIG_SDIO_PWRCTRL ?= n
+CONFIG_WPA3_FOR_OLD_KERNEL ?= n
+CONFIG_USB_MSG_OUT_EP =y
+CONFIG_USB_MSG_IN_EP =y
+CONFIG_USB_TX_AGGR=n
+CONFIG_SDIO_AGGR=y
+CONFIG_SET_VENDOR_EXTENSION_IE = n
+CONFIG_LESS_SKB = y
+CONFIG_CREATE_TRACE_POINTS = n
+CONFIG_TX_NETIF_FLOWCTRL = y
+CONFIG_AGGRESSIVE_TX = n
+CONFIG_ONE_TXQ = y
+CONFIG_TXRX_THREAD_PRIO = y
+CONFIG_DPD = y
+CONFIG_FORCE_DPD_CALIB = y
+CONFIG_TEMP_PW = y
+CONFIG_FILTER_TCP_ACK =y
+
+# Support of MU-MIMO transmission (need FW support)
+ifeq ($(CONFIG_RWNX_BFMER), y)
+CONFIG_RWNX_MUMIMO_TX ?= n
+else
+CONFIG_RWNX_MUMIMO_TX = n
+endif
+
+# Enable handling of radar event
+CONFIG_RWNX_RADAR ?= n
+
+# Enable HW queue for Broadcast/Multicast traffic (need FW support)
+CONFIG_RWNX_BCMC ?= y
+
+# Enable Monitor+Data interface support (need FW support)
+CONFIG_RWNX_MON_DATA =y
+CONFIG_RWNX_MON_XMIT ?= n
+
+# extra DEBUG config
+CONFIG_RWNX_SW_PROFILING ?= n
+CONFIG_RWNX_DBG ?= y
+CONFIG_DEBUG_FS_AIC = n
+
+obj-y += aic8818_fdrv.o
+aic8818_fdrv-y := rwnx_msg_tx.o       \
+               rwnx_msg_rx.o          \
+               rwnx_utils.o           \
+               rwnx_cmds.o            \
+               rwnx_irqs.o            \
+               rwnx_cfgfile.o         \
+               rwnx_strs.o            \
+               rwnx_rx.o              \
+               rwnx_tx.o              \
+               rwnx_txq.o             \
+               rwnx_main.o            \
+               rwnx_mod_params.o      \
+               rwnx_mesh.o            \
+               rwnx_platform.o	      \
+               rwnx_pci.o             \
+               rwnx_dini.o            \
+               rwnx_v7.o              \
+               ipc_host.o             \
+               rwnx_tdls.o            \
+               regdb.o				  \
+               wifi_dev_aic88.o		  \
+               aic88-generic-wlan.o   \
+               aicwf_mem_prealloc.o
+
+aic8818_fdrv-$(CONFIG_RWNX_RADAR)       += rwnx_radar.o
+aic8818_fdrv-$(CONFIG_DEBUG_FS_AIC)     += rwnx_debugfs.o
+aic8818_fdrv-$(CONFIG_DEBUG_FS_AIC)     += rwnx_fw_trace.o
+aic8818_fdrv-$(CONFIG_NL80211_TESTMODE) += rwnx_testmode.o
+aic8818_fdrv-$(CONFIG_RWNX_BFMER)       += rwnx_bfmer.o
+aic8818_fdrv-$(CONFIG_RWNX_MUMIMO_TX)   += rwnx_mu_group.o
+aic8818_fdrv-$(CONFIG_SDIO_SUPPORT)     += sdio_host.o
+aic8818_fdrv-$(CONFIG_SDIO_SUPPORT)     += aicwf_txrxif.o
+aic8818_fdrv-$(CONFIG_SDIO_SUPPORT)     += aicwf_sdio.o
+aic8818_fdrv-$(CONFIG_FILTER_TCP_ACK)   += aicwf_tcp_ack.o
+
+
+aic8818_fdrv-$(CONFIG_USB_SUPPORT)      += usb_host.o
+aic8818_fdrv-$(CONFIG_USB_SUPPORT)      += aicwf_txrxif.o
+aic8818_fdrv-$(CONFIG_USB_SUPPORT)      += aicwf_usb.o
+
+ccflags-$(CONFIG_DEBUG_FS_AIC) += -DCONFIG_RWNX_DEBUGFS
+ccflags-$(CONFIG_DEBUG_FS_AIC) += -DCONFIG_RWNX_UM_HELPER_DFLT=\"$(CONFIG_RWNX_UM_HELPER_DFLT)\"
+ccflags-$(CONFIG_RWNX_P2P_DEBUGFS) += -DCONFIG_RWNX_P2P_DEBUGFS
+
+# FW VARS
+subdir-ccflags-y += -DNX_VIRT_DEV_MAX=4
+subdir-ccflags-y += -DNX_REMOTE_STA_MAX=32    #U01: 8   U02: 32
+subdir-ccflags-y += -DNX_MU_GROUP_MAX=62
+subdir-ccflags-y += -DNX_TXDESC_CNT=64
+subdir-ccflags-y += -DNX_TX_MAX_RATES=4
+subdir-ccflags-y += -DNX_CHAN_CTXT_CNT=3
+
+# FW ARCH:
+ccflags-$(CONFIG_RWNX_SDM) += -DCONFIG_RWNX_SDM
+ccflags-$(CONFIG_RWNX_TL4) += -DCONFIG_RWNX_TL4
+ccflags-$(CONFIG_RWNX_OLD_IPC) += -DCONFIG_RWNX_OLD_IPC
+ccflags-$(CONFIG_PLATFORM_NANOPI_M4) += -DCONFIG_NANOPI_M4
+ccflags-$(CONFIG_PLATFORM_INGENIC_T20) += -DCONFIG_INGENIC_T20
+ccflags-$(CONFIG_PLATFORM_ALLWINNER) += -DCONFIG_PLATFORM_ALLWINNER
+subdir-ccflags-$(CONFIG_ANDROID_PLATFORM) += -DCONFIG_ANDROID_PLATFORM
+ccflags-$(CONFIG_START_FROM_BOOTROM) += -DCONFIG_START_FROM_BOOTROM
+ccflags-$(CONFIG_PMIC_SETTING) += -DCONFIG_PMIC_SETTING
+ccflags-$(CONFIG_VRF_DCDC_MODE) += -DCONFIG_VRF_DCDC_MODE
+ccflags-$(CONFIG_ROM_PATCH_EN) += -DCONFIG_ROM_PATCH_EN
+ccflags-$(CONFIG_FPGA_VERIFICATION) += -DCONFIG_FPGA_VERIFICATION
+ccflags-$(CONFIG_VHT_FOR_OLD_KERNEL) += -DCONFIG_VHT_FOR_OLD_KERNEL
+ccflags-$(CONFIG_PLATFORM_ALLWINNER) += -DCONFIG_HE_FOR_OLD_KERNEL
+ccflags-$(CONFIG_HE_FOR_OLD_KERNEL) += -DCONFIG_HE_FOR_OLD_KERNEL
+
+ccflags-y += -I$(src)/.
+ccflags-$(CONFIG_RWNX_RADAR) += -DCONFIG_RWNX_RADAR
+ccflags-$(CONFIG_RWNX_MON_DATA) += -DCONFIG_RWNX_MON_DATA
+ccflags-$(CONFIG_RWNX_MON_XMIT) += -DCONFIG_RWNX_MON_XMIT
+ccflags-$(CONFIG_RWNX_BFMER) += -DCONFIG_RWNX_BFMER
+ccflags-$(CONFIG_RWNX_SPLIT_TX_BUF) += -DCONFIG_RWNX_SPLIT_TX_BUF
+ifeq ($(CONFIG_RWNX_SPLIT_TX_BUF), y)
+ccflags-$(CONFIG_RWNX_AMSDUS_TX) += -DCONFIG_RWNX_AMSDUS_TX
+endif
+ccflags-$(CONFIG_RWNX_DBG) += -DCONFIG_RWNX_DBG
+ccflags-$(CONFIG_RWNX_SW_PROFILING) += -DCONFIG_RWNX_SW_PROFILING
+ccflags-$(CONFIG_RWNX_MUMIMO_TX) += -DCONFIG_RWNX_MUMIMO_TX
+ccflags-$(CONFIG_CREATE_TRACE_POINTS) += -DCREATE_TRACE_POINTS
+ccflags-$(CONFIG_RFTEST) += -DCONFIG_RFTEST
+ccflags-$(CONFIG_RFTEST_USB_BT) += -DCONFIG_RFTEST_USB_BT
+ccflags-$(CONFIG_RFTEST_UART_BT) += -DCONFIG_RFTEST_UART_BT
+ccflags-$(CONFIG_WPA3_FOR_OLD_KERNEL) += -DCONFIG_WPA3_FOR_OLD_KERNEL
+ccflags-$(CONFIG_SET_VENDOR_EXTENSION_IE) += -DCONFIG_SET_VENDOR_EXTENSION_IE
+ccflags-$(CONFIG_TX_NETIF_FLOWCTRL) += -DCONFIG_TX_NETIF_FLOWCTRL
+ccflags-$(CONFIG_AGGRESSIVE_TX)  += -DCONFIG_AGGRESSIVE_TX
+ccflags-$(CONFIG_ONE_TXQ)  += -DCONFIG_ONE_TXQ
+ccflags-$(CONFIG_TXRX_THREAD_PRIO) += -DCONFIG_TXRX_THREAD_PRIO
+ccflags-$(CONFIG_DPD)  += -DCONFIG_DPD
+ccflags-$(CONFIG_FORCE_DPD_CALIB) += -DCONFIG_FORCE_DPD_CALIB -DCONFIG_DPD
+ccflags-$(CONFIG_TEMP_PW) += -DCONFIG_TEMP_PW
+ccflags-$(CONFIG_FILTER_TCP_ACK) += -DCONFIG_FILTER_TCP_ACK
+
+ifeq ($(CONFIG_LESS_SKB), y)
+ccflags-y += -DLESS_SKB
+#KBUILD_EXTRA_SYMBOLS += $(PWD)/../prealloc/Module.symvers
+endif
+
+ifeq ($(CONFIG_SDIO_SUPPORT), y)
+ccflags-y += -DAICWF_SDIO_SUPPORT
+ccflags-$(CONFIG_SDIO_PWRCTRL) += -DCONFIG_SDIO_PWRCTRL
+ccflags-$(CONFIG_SDIO_AGGR) += -DCONFIG_SDIO_AGGR
+endif
+
+ifeq ($(CONFIG_USB_SUPPORT), y)
+ccflags-y += -DAICWF_USB_SUPPORT
+ccflags-$(CONFIG_USB_MSG_OUT_EP) += -DCONFIG_USB_MSG_OUT_EP
+ccflags-$(CONFIG_USB_MSG_IN_EP) += -DCONFIG_USB_MSG_IN_EP
+ccflags-$(CONFIG_USB_TX_AGGR) += -DCONFIG_USB_TX_AGGR
+endif
+
+ifeq ($(CONFIG_RWNX_MUMIMO_TX), y)
+ccflags-y += -DCONFIG_USER_MAX=2
+else
+ccflags-y += -DCONFIG_USER_MAX=1
+endif
+
+ifeq ($(CONFIG_RWNX_BCMC), y)
+ccflags-y += -DNX_TXQ_CNT=5
+else
+ccflags-y += -DNX_TXQ_CNT=4
+endif
+
+# For old kernel (<=3.19)
+ifeq ($(shell test $(VERSION) -lt 4 -a "$(CONFIG_VENDOR_RWNX)" = y ; echo $$?),0)
+subdir-ccflags-y += -DCONFIG_VENDOR_RWNX_VHT_NO80
+endif
+
+ccflags-$(CONFIG_RX_REORDER) += -DAICWF_RX_REORDER
+ccflags-$(CONFIG_ARP_OFFLOAD) += -DAICWF_ARP_OFFLOAD
+ccflags-$(CONFIG_USE_5G) += -DUSE_5G
+ccflags-$(CONFIG_RADAR_DETECT) += -DRADAR_OR_IR_DETECT
+ccflags-$(CONFIG_DOWNLOAD_FW)  += -DCONFIG_DOWNLOAD_FW
+ccflags-$(CONFIG_LOAD_USERCONFIG) += -DCONFIG_LOAD_USERCONFIG
+ccflags-$(HANDLE_TX_THREAD_ZTE)  += -DHANDLE_TX_THREAD_ZTE
+ccflags-$(HANDLE_RX_THREAD_ZTE)  += -DHANDLE_RX_THREAD_ZTE
+ccflags-$(FAST_FROM_DRIVER)  += -DFAST_FROM_DRIVER
+ccflags-$(CONFIG_CHIP_REBOOT) += -DCONFIG_CHIP_REBOOT
+ccflags-$(CONFIG_BT_SUPPORT) += -DCONFIG_BT_SUPPORT
+ccflags-$(CONFIG_USB_BT)  += -DCONFIG_USB_BT
+ccflags-$(CONFIG_MAC_RANDOM_IF_NO_MAC_IN_EFUSE) += -DCONFIG_MAC_RANDOM_IF_NO_MAC_IN_EFUSE
+
+#ldflags-y += --strip-debug
+
+all: modules
+modules:
+	make -C $(KDIR) M=$(PWD) modules
+clean:
+	$(MAKE) -C $(KDIR) M=$(PWD) clean
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aic88-generic-wlan.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aic88-generic-wlan.c
new file mode 100755
index 0000000..7f592b2
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aic88-generic-wlan.c
@@ -0,0 +1,79 @@
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#include <asm/io.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
+#include <linux/printk.h>
+#include <linux/err.h>
+#else
+#include <config/printk.h>
+#endif
+
+
+extern int ssvdevice_init(void);
+extern void ssvdevice_exit(void);
+
+extern int  wifi_dev_init(void);
+extern void  wifi_dev_exit(void);
+
+//extern int ssv_prealloc_init(void);
+//extern void  ssv_prealloc_exit(void);
+
+//extern void ssv6x5x_wifi_enable(int bval);
+extern void aic8800_wifi_enable(int bval);
+extern int aicwf_prealloc_init(void);
+extern void aicwf_prealloc_exit(void);
+int initWlan(void)
+{
+    int ret=0;
+    printk(KERN_ERR "qqq wlan.c initWlan@@@\n");
+	aic8800_wifi_enable(1);
+    ret = aicwf_prealloc_init();
+    if (ret)
+        return ret;
+    //ssv6x5x_wifi_enable(1);
+    //ret = ssv_prealloc_init();
+    // if (ret) 
+	//return ret;
+	 
+    ret = wifi_dev_init();
+    return ret;
+	
+}
+
+void exitWlan(void)
+{
+    //ssvdevice_exit();
+    wifi_dev_exit();
+    aicwf_prealloc_exit();
+    //ssv_prealloc_exit();
+    return;
+}
+
+static int generic_wifi_init_module(void)
+{
+	return initWlan();
+}
+
+static void generic_wifi_exit_module(void)
+{
+	exitWlan();
+}
+
+EXPORT_SYMBOL(generic_wifi_init_module);
+EXPORT_SYMBOL(generic_wifi_exit_module);
+
+//#ifdef CONFIG_SSV6X5X //CONFIG_SSV6XXX=y
+module_init(generic_wifi_init_module);
+//late_initcall(generic_wifi_init_module);
+//#else //CONFIG_SSV6XXX=m or =n
+//module_init(generic_wifi_init_module);
+//#endif
+module_exit(generic_wifi_exit_module);
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_mem_prealloc.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_mem_prealloc.c
new file mode 100755
index 0000000..11271ee
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_mem_prealloc.c
@@ -0,0 +1,134 @@
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include "aicwf_mem_prealloc.h"
+
+//#define MOD_AUTHOR      "Aicsemi, Ltd"
+//#define MOD_DESCRIPTION "Pre-allocated memory for RivieraWaves 11nac driver"
+//#define MOD_LICENSE     "Dual BSD/GPL"
+
+#ifdef LESS_SKB
+struct aicwf_rx_buff_list aic_rx_buff_list;
+EXPORT_SYMBOL(aic_rx_buff_list);
+
+int aic_rxbuff_num_max = 30;
+EXPORT_SYMBOL(aic_rxbuff_num_max);
+module_param(aic_rxbuff_num_max, int, 0644);
+MODULE_PARM_DESC(aic_rxbuff_num_max, "Max length of prealloc rx buff list");
+
+int aic_rxbuff_size = (64 * 512);
+EXPORT_SYMBOL(aic_rxbuff_size);
+module_param(aic_rxbuff_size, int, 0644);
+MODULE_PARM_DESC(aic_rxbuff_size, "Max size of prealloc rx buff");
+
+struct rx_buff *aicwf_prealloc_rxbuff_alloc(spinlock_t *lock) 
+{
+    unsigned long flags;
+    struct rx_buff *rxbuff = NULL;
+
+    spin_lock_irqsave(lock, flags);
+    if (list_empty(&aic_rx_buff_list.rxbuff_list)) {
+        spin_unlock_irqrestore(lock, flags);
+        printk("%s %d, rxbuff list is empty\n", __func__, __LINE__);
+        return NULL;
+    } else {
+		rxbuff = list_first_entry(&aic_rx_buff_list.rxbuff_list,
+					   struct rx_buff, queue);
+		list_del_init(&rxbuff->queue);
+        atomic_dec(&aic_rx_buff_list.rxbuff_list_len);
+	}
+    spin_unlock_irqrestore(lock, flags);
+//    printk("len:%d\n", aic_rx_buff_list.rxbuff_list_len);
+    memset(rxbuff->data, 0, aic_rxbuff_size);
+    rxbuff->len = 0;
+    rxbuff->start = NULL;
+    rxbuff->read = NULL;
+    rxbuff->end = NULL;
+
+    return rxbuff;
+}
+EXPORT_SYMBOL(aicwf_prealloc_rxbuff_alloc);
+
+void aicwf_prealloc_rxbuff_free(struct rx_buff *rxbuff, spinlock_t *lock)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(lock, flags);
+	list_add_tail(&rxbuff->queue, &aic_rx_buff_list.rxbuff_list);
+	atomic_inc(&aic_rx_buff_list.rxbuff_list_len);
+    spin_unlock_irqrestore(lock, flags);
+}
+EXPORT_SYMBOL(aicwf_prealloc_rxbuff_free);
+#endif
+
+struct sk_buff *tx_aggr_buf;
+int aicwf_prealloc_init(void)
+{
+    printk("%s, enter\n", __func__);
+
+    tx_aggr_buf = dev_alloc_skb(1536*32);
+    if(!tx_aggr_buf) {
+        printk("pre alloc tx_aggr_buf failed!\n");
+        return -1;
+    }
+
+#ifdef LESS_SKB
+    struct rx_buff *rxbuff;
+    int i = 0;
+
+    INIT_LIST_HEAD(&aic_rx_buff_list.rxbuff_list);
+    
+	for (i = 0 ; i < aic_rxbuff_num_max ; i++) {
+        rxbuff = kzalloc(sizeof(struct rx_buff), GFP_KERNEL);
+        if (rxbuff) {
+            rxbuff->data = kzalloc(aic_rxbuff_size, GFP_KERNEL);
+            if (rxbuff->data == NULL) {
+                printk("failed to alloc rxbuff data\n");
+                kfree(rxbuff);
+                continue;
+            }
+            rxbuff->len = 0;
+            rxbuff->start = NULL;
+            rxbuff->read = NULL;
+            rxbuff->end = NULL;
+            list_add_tail(&rxbuff->queue, &aic_rx_buff_list.rxbuff_list);
+            atomic_inc(&aic_rx_buff_list.rxbuff_list_len);
+        }
+    }
+
+	printk("pre alloc rxbuff list len: %d\n", aic_rx_buff_list.rxbuff_list_len);
+#endif
+    return 0;
+}
+EXPORT_SYMBOL(aicwf_prealloc_init);
+
+void aicwf_prealloc_exit(void)
+{
+    printk("%s enter\n", __func__);
+
+    if (tx_aggr_buf)
+        dev_kfree_skb(tx_aggr_buf);
+
+#ifdef LESS_SKB
+    struct rx_buff *rxbuff;
+    struct rx_buff *pos;
+    
+	printk("free pre alloc rxbuff list %d\n", aic_rx_buff_list.rxbuff_list_len);
+    list_for_each_entry_safe(rxbuff, pos, &aic_rx_buff_list.rxbuff_list, queue) {
+        list_del_init(&rxbuff->queue);
+        kfree(rxbuff->data);
+        kfree(rxbuff);
+    }
+#endif
+}
+EXPORT_SYMBOL(aicwf_prealloc_exit);
+
+//module_init(aicwf_prealloc_init);
+//module_exit(aicwf_prealloc_exit);
+
+//MODULE_AUTHOR(MOD_AUTHOR);
+//MODULE_DESCRIPTION(MOD_DESCRIPTION);
+//MODULE_LICENSE(MOD_LICENSE);
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_mem_prealloc.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_mem_prealloc.h
new file mode 100755
index 0000000..ba73f7c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_mem_prealloc.h
@@ -0,0 +1,22 @@
+#ifndef _AICWF_MEM_PREALLOC_H_
+#define _AICWF_MEM_PREALLOC_H_
+
+struct rx_buff {
+    struct list_head queue;
+    unsigned char *data;
+    u32 len;
+    uint8_t *start;
+    uint8_t *end;
+    uint8_t *read;
+};
+
+struct aicwf_rx_buff_list {
+    struct list_head rxbuff_list;
+    int rxbuff_list_len;
+};
+
+struct rx_buff *aicwf_prealloc_rxbuff_alloc(spinlock_t *lock);
+void aicwf_prealloc_rxbuff_free(struct rx_buff *rxbuff, spinlock_t *lock);
+
+
+#endif /* _AICWF_MEM_PREALLOC_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_sdio.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_sdio.c
new file mode 100755
index 0000000..f67ee05
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_sdio.c
@@ -0,0 +1,2314 @@
+/**
+ * aicwf_sdmmc.c
+ *
+ * SDIO function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/semaphore.h>
+#include <linux/debugfs.h>
+#include <linux/kthread.h>
+#include "aicwf_txrxif.h"
+#include "aicwf_sdio.h"
+#include "sdio_host.h"
+#include "rwnx_defs.h"
+#include "rwnx_platform.h"
+#include "rwnx_msg_tx.h"
+#ifdef CONFIG_INGENIC_T20
+#include "mach/jzmmc.h"
+#endif /* CONFIG_INGENIC_T20 */
+extern uint8_t scanning;
+extern bool_l func_flag;
+
+#ifdef CONFIG_PLATFORM_ALLWINNER
+void platform_wifi_power_off(void);
+#endif
+
+int tx_aggr_counter = 32;
+module_param_named(tx_aggr_counter, tx_aggr_counter, int, 0644);
+
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+int tx_fc_low_water = AICWF_SDIO_TX_LOW_WATER;
+module_param_named(tx_fc_low_water, tx_fc_low_water, int, 0644);
+
+int tx_fc_high_water = AICWF_SDIO_TX_HIGH_WATER;
+module_param_named(tx_fc_high_water, tx_fc_high_water, int, 0644);
+#endif
+
+/* SDIO Device ID */
+#define SDIO_VENDOR_ID_AIC8801              0x5449
+#define SDIO_VENDOR_ID_AIC8800DC            0xc8a1
+#define SDIO_VENDOR_ID_AIC8800D80           0xc8a1
+
+#define SDIO_DEVICE_ID_AIC8801				0x0145
+#define SDIO_DEVICE_ID_AIC8800DC			0xc08d
+#define SDIO_DEVICE_ID_AIC8800D80           0x0082
+
+uint8_t crc8_ponl_107(uint8_t *p_buffer, uint16_t cal_size)
+{
+    uint8_t i;
+    uint8_t crc = 0;
+    if (cal_size==0) {
+        return crc;
+    }
+    while (cal_size--) {
+        for (i = 0x80; i > 0; i /= 2) {
+            if (crc & 0x80)  {
+                crc *= 2;
+                crc ^= 0x07; //polynomial X8 + X2 + X + 1,(0x107)
+            } else {
+                crc *= 2;
+            }
+            if ((*p_buffer) & i) {
+                crc ^= 0x07;
+            }
+        }
+        p_buffer++;
+    }
+
+    return crc;
+}
+
+int aicwf_sdio_readb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 *val)
+{
+    int ret;
+    sdio_claim_host(sdiodev->func);
+    *val = sdio_readb(sdiodev->func, regaddr, &ret);
+    sdio_release_host(sdiodev->func);
+    return ret;
+}
+
+int aicwf_sdio_readb_func2(struct aic_sdio_dev *sdiodev, uint regaddr, u8 *val)
+{
+    int ret;
+    sdio_claim_host(sdiodev->func_msg);
+    *val = sdio_readb(sdiodev->func_msg, regaddr, &ret);
+    sdio_release_host(sdiodev->func_msg);
+    return ret;
+}
+
+int aicwf_sdio_writeb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val)
+{
+    int ret;
+    sdio_claim_host(sdiodev->func);
+    sdio_writeb(sdiodev->func, val, regaddr, &ret);
+    sdio_release_host(sdiodev->func);
+    return ret;
+}
+
+
+int aicwf_sdio_writeb_func2(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val)
+{
+    int ret;
+    sdio_claim_host(sdiodev->func_msg);
+    sdio_writeb(sdiodev->func_msg, val, regaddr, &ret);
+    sdio_release_host(sdiodev->func_msg);
+    return ret;
+}
+
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+void aicwf_sdio_tx_netif_flowctrl(struct rwnx_hw *rwnx_hw, bool state)
+{
+    struct rwnx_vif *rwnx_vif;
+    list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+        if (! rwnx_vif->up)
+            continue;
+        if (state)
+            netif_tx_stop_all_queues(rwnx_vif->ndev);//netif_stop_queue(rwnx_vif->ndev);
+        else
+            netif_tx_wake_all_queues(rwnx_vif->ndev);//netif_wake_queue(rwnx_vif->ndev);
+    }
+}
+#endif
+
+int aicwf_sdio_flow_ctrl_msg(struct aic_sdio_dev *sdiodev)
+{
+    int ret = -1;
+    u8 fc_reg = 0;
+    u32 count = 0;
+
+    while (true) {
+        ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.flow_ctrl_reg, &fc_reg);
+        if (ret) {
+            return -1;
+        }
+
+        if (sdiodev->chipid == PRODUCT_ID_AIC8801 || sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
+            sdiodev->chipid == PRODUCT_ID_AIC8800DW) {
+            fc_reg = fc_reg & SDIOWIFI_FLOWCTRL_MASK_REG;
+        }
+
+        if (fc_reg != 0) {
+            ret = fc_reg;
+            if(ret > tx_aggr_counter){
+				ret = tx_aggr_counter;
+			}
+            return ret;
+        } else {
+            if (count >= FLOW_CTRL_RETRY_COUNT) {
+                ret = -fc_reg;
+                break;
+            }
+            count++;
+            if (count < 30)
+                udelay(200);
+            else if(count < 40)
+                msleep(2);
+            else
+                msleep(10);
+        }
+    }
+
+    return ret;
+}
+
+
+int aicwf_sdio_flow_ctrl(struct aic_sdio_dev *sdiodev)
+{
+    int ret = -1;
+    u8 fc_reg = 0;
+    u32 count = 0;
+
+    while (true) {
+        ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.flow_ctrl_reg, &fc_reg);
+        if (ret) {
+            return -1;
+        }
+
+        if (sdiodev->chipid == PRODUCT_ID_AIC8801 || sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
+            sdiodev->chipid == PRODUCT_ID_AIC8800DW) {
+            fc_reg = fc_reg & SDIOWIFI_FLOWCTRL_MASK_REG;
+        }
+
+        if ( fc_reg > 2) {
+            ret = fc_reg;
+            if(ret > tx_aggr_counter){
+				ret = tx_aggr_counter;
+			}
+            return ret;
+        } else {
+            if (count >= FLOW_CTRL_RETRY_COUNT) {
+                ret = -fc_reg;
+                break;
+            }
+            count++;
+            if (count < 30)
+                udelay(200);
+            else if(count < 40)
+                msleep(2);
+            else
+                msleep(10);
+        }
+    }
+
+    return ret;
+}
+
+int aicwf_sdio_send_msg(struct aic_sdio_dev *sdiodev, u8 *buf, uint count)
+{
+    int ret = 0;
+if (func_flag){
+    sdio_claim_host(sdiodev->func_msg);
+    ret = sdio_writesb(sdiodev->func_msg, sdiodev->sdio_reg.wr_fifo_addr, buf, count);
+    sdio_release_host(sdiodev->func_msg);
+} else {
+    sdio_claim_host(sdiodev->func);
+    ret = sdio_writesb(sdiodev->func, sdiodev->sdio_reg.wr_fifo_addr, buf, count);
+    sdio_release_host(sdiodev->func);
+}
+    return ret;
+}
+
+
+int aicwf_sdio_send_pkt(struct aic_sdio_dev *sdiodev, u8 *buf, uint count)
+{
+    int ret = 0;
+
+    sdio_claim_host(sdiodev->func);
+    ret = sdio_writesb(sdiodev->func, sdiodev->sdio_reg.wr_fifo_addr, buf, count);
+    sdio_release_host(sdiodev->func);
+
+    return ret;
+}
+
+#ifdef LESS_SKB
+int aicwf_sdio_recv_pkt(struct aic_sdio_dev *sdiodev, struct rx_buff *rxbuff,
+    u32 size, u8 msg)
+{
+    int ret;
+
+    if ((!rxbuff->data) || (!size)) {
+        return -EINVAL;;
+    }
+
+    if (func_flag){
+        if(!msg) {
+            sdio_claim_host(sdiodev->func);
+            ret = sdio_readsb(sdiodev->func, rxbuff->data, sdiodev->sdio_reg.rd_fifo_addr, size);
+            sdio_release_host(sdiodev->func);
+        } else {
+            sdio_claim_host(sdiodev->func_msg);
+            ret = sdio_readsb(sdiodev->func_msg, rxbuff->data, sdiodev->sdio_reg.rd_fifo_addr, size);
+            sdio_release_host(sdiodev->func_msg);
+        }
+    } else{
+            sdio_claim_host(sdiodev->func);
+            ret = sdio_readsb(sdiodev->func, rxbuff->data, sdiodev->sdio_reg.rd_fifo_addr, size);
+            sdio_release_host(sdiodev->func);
+    }
+
+    if (ret < 0) {
+        return ret;
+    }
+    rxbuff->len = size;
+    return ret;
+}
+#else
+int aicwf_sdio_recv_pkt(struct aic_sdio_dev *sdiodev, struct sk_buff *skbbuf,
+    u32 size, u8 msg)
+{
+    int ret;
+
+    if ((!skbbuf) || (!size)) {
+        return -EINVAL;;
+    }
+
+if (func_flag){
+    if(!msg) {
+        sdio_claim_host(sdiodev->func);
+        ret = sdio_readsb(sdiodev->func, skbbuf->data, sdiodev->sdio_reg.rd_fifo_addr, size);
+        sdio_release_host(sdiodev->func);
+    } else {
+        sdio_claim_host(sdiodev->func_msg);
+        ret = sdio_readsb(sdiodev->func_msg, skbbuf->data, sdiodev->sdio_reg.rd_fifo_addr, size);
+        sdio_release_host(sdiodev->func_msg);
+    }
+} else{
+        sdio_claim_host(sdiodev->func);
+        ret = sdio_readsb(sdiodev->func, skbbuf->data, sdiodev->sdio_reg.rd_fifo_addr, size);
+        sdio_release_host(sdiodev->func);
+}
+    if (ret < 0) {
+        return ret;
+    }
+    skbbuf->len = size;
+
+    return ret;
+}
+
+#endif
+
+static int aicwf_sdio_chipmatch(struct aic_sdio_dev *sdio_dev, uint16_t vid, uint16_t did){
+
+	if(vid == SDIO_VENDOR_ID_AIC8801 && did == SDIO_DEVICE_ID_AIC8801){
+		sdio_dev->chipid = PRODUCT_ID_AIC8801;
+		printk("%s USE AIC8801\r\n", __func__);
+		return 0;
+	}else if(vid == SDIO_VENDOR_ID_AIC8800DC && did == SDIO_DEVICE_ID_AIC8800DC){
+		sdio_dev->chipid = PRODUCT_ID_AIC8800DC;
+		printk("%s USE AIC8800DC\r\n", __func__);
+		return 0;
+	}else if(vid == SDIO_VENDOR_ID_AIC8800D80 && did == SDIO_DEVICE_ID_AIC8800D80){
+		sdio_dev->chipid = PRODUCT_ID_AIC8800D80;
+		func_flag = false;
+		printk("%s USE AIC8800D80\r\n", __func__);
+		return 0;
+	}else{
+		return -1;
+	}
+}
+
+struct aic_sdio_dev *g_sdiodev;
+static int aicwf_sdio_probe(struct sdio_func *func,
+    const struct sdio_device_id *id)
+{
+    struct mmc_host *host;
+    struct aic_sdio_dev *sdiodev;
+    struct aicwf_bus *bus_if;
+    int err = -ENODEV;
+
+    sdio_dbg("%s:%d\n", __func__, func->num);
+    sdio_dbg("Class=%x\n", func->class);
+    sdio_dbg("sdio vendor ID: 0x%04x\n", func->vendor);
+    sdio_dbg("sdio device ID: 0x%04x\n", func->device);
+    sdio_dbg("Function#: %d\n", func->num);
+    host = func->card->host;
+    if(func->num != 1) {
+        return err;
+    }
+
+    bus_if = kzalloc(sizeof(struct aicwf_bus), GFP_KERNEL);
+    if (!bus_if) {
+        sdio_err("alloc bus fail\n");
+        return -ENOMEM;
+    }
+
+    sdiodev = kzalloc(sizeof(struct aic_sdio_dev), GFP_KERNEL);
+    if (!sdiodev) {
+        sdio_err("alloc sdiodev fail\n");
+        kfree(bus_if);
+        return -ENOMEM;
+    }
+
+	err = aicwf_sdio_chipmatch(sdiodev, func->vendor, func->device);
+    if (err < 0) {
+        sdio_err("sdio chipmatch fail\n");
+    }
+
+    sdiodev->func = func;
+    if(sdiodev->chipid == PRODUCT_ID_AIC8800DC || sdiodev->chipid == PRODUCT_ID_AIC8800DW){
+		sdiodev->func_msg = func->card->sdio_func[1];
+	}
+    sdiodev->bus_if = bus_if;
+    bus_if->bus_priv.sdio = sdiodev;
+    if(sdiodev->chipid == PRODUCT_ID_AIC8800DC || sdiodev->chipid == PRODUCT_ID_AIC8800DW){
+		dev_set_drvdata(&sdiodev->func_msg->dev, bus_if);
+		printk("the device is PRODUCT_ID_AIC8800DC \n");
+	}
+
+    dev_set_drvdata(&func->dev, bus_if);
+    sdiodev->dev = &func->dev;
+
+	if (sdiodev->chipid != PRODUCT_ID_AIC8800D80) {
+		err = aicwf_sdio_func_init(sdiodev);
+    } else {
+        err = aicwf_sdiov3_func_init(sdiodev);
+    }
+    if (err < 0) {
+        sdio_err("sdio func init fail\n");
+        goto fail;
+    }
+
+    if (aicwf_sdio_bus_init(sdiodev) == NULL) {
+		sdio_err("sdio bus init fail\n");
+		err = -1;
+		goto fail;
+	}
+
+    host->caps |= MMC_CAP_NONREMOVABLE;
+    aicwf_rwnx_sdio_platform_init(sdiodev);
+    aicwf_hostif_ready();
+
+    g_sdiodev = sdiodev;
+
+    return 0;
+fail:
+	aicwf_sdio_func_deinit(sdiodev);
+	dev_set_drvdata(&func->dev, NULL);
+	kfree(sdiodev);
+	kfree(bus_if);
+	aicwf_hostif_fail();
+	return err;
+}
+
+extern atomic_t skb_used;
+static void aicwf_sdio_remove(struct sdio_func *func)
+{
+    struct mmc_host *host;
+    struct aicwf_bus *bus_if = NULL;
+    struct aic_sdio_dev *sdiodev = NULL;
+
+    sdio_dbg("%s\n", __func__);
+
+#ifdef CONFIG_TEMP_PW
+	if (timer_pending(&g_rwnx_plat->sdiodev->tp_timer)) {
+		printk("%s del_timer\n", __func__);
+		del_timer_sync(&g_rwnx_plat->sdiodev->tp_timer);
+	}
+	cancel_work_sync(&g_rwnx_plat->sdiodev->tp_work);
+#endif
+
+    host = func->card->host;
+    host->caps &= ~MMC_CAP_NONREMOVABLE;
+    bus_if = dev_get_drvdata(&func->dev);
+    if (!bus_if) {
+        return;
+    }
+
+    sdiodev = bus_if->bus_priv.sdio;
+    if (!sdiodev) {
+        return;
+    }
+    sdiodev->bus_if->state = BUS_DOWN_ST;
+    aicwf_sdio_release(sdiodev);
+    aicwf_sdio_func_deinit(sdiodev);
+    dev_set_drvdata(&sdiodev->func->dev, NULL);
+
+	printk("skb_used=%d\n", skb_used);
+	int i;
+	for(i=0; i<NX_NB_TXQ;i++){
+		if(skb_queue_len(&sdiodev->rwnx_hw->txq[i].sk_list)!= 0)
+			printk("i=%d, len=%d\n",skb_queue_len(&sdiodev->rwnx_hw->txq[i].sk_list));
+		}
+    wiphy_free(sdiodev->rwnx_hw->wiphy);
+    kfree(sdiodev);
+    kfree(bus_if);
+    sdio_dbg("%s done\n", __func__);
+#ifdef CONFIG_PLATFORM_ALLWINNER
+    platform_wifi_power_off();
+#endif
+}
+
+static int aicwf_sdio_suspend(struct device *dev)
+{
+    int ret = 0;
+    struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+    struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+    mmc_pm_flag_t sdio_flags;
+    struct rwnx_vif *rwnx_vif, *tmp;
+
+    sdio_dbg("%s\n", __func__);
+    list_for_each_entry_safe(rwnx_vif, tmp, &sdiodev->rwnx_hw->vifs, list) {
+        if(rwnx_vif->ndev)
+            netif_device_detach(rwnx_vif->ndev);
+    }
+
+    sdio_flags = sdio_get_host_pm_caps(sdiodev->func);
+    if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
+        return -EINVAL;
+    }
+    ret = sdio_set_host_pm_flags(sdiodev->func, MMC_PM_KEEP_POWER);
+    if (ret) {
+        return ret;
+    }
+
+    while (sdiodev->state == SDIO_ACTIVE_ST) {
+        if (down_interruptible(&sdiodev->tx_priv->txctl_sema)) {
+            continue;
+        }
+        #if defined(CONFIG_SDIO_PWRCTRL)
+        aicwf_sdio_pwr_stctl(sdiodev, SDIO_SLEEP_ST);
+        #endif
+        up(&sdiodev->tx_priv->txctl_sema);
+        break;
+    }
+
+    return 0;
+}
+
+static int aicwf_sdio_resume(struct device *dev)
+{
+    struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+    struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+    struct rwnx_vif *rwnx_vif, *tmp;
+
+    sdio_dbg("%s\n", __func__);
+    list_for_each_entry_safe(rwnx_vif, tmp, &sdiodev->rwnx_hw->vifs, list) {
+        if(rwnx_vif->ndev)
+            netif_device_attach(rwnx_vif->ndev);
+    }
+
+    return 0;
+}
+
+static const struct sdio_device_id aicwf_sdmmc_ids[] = {
+    {SDIO_DEVICE(SDIO_VENDOR_ID_AIC, SDIO_DEVICE_ID_AIC)},
+    {SDIO_DEVICE(SDIO_VENDOR_ID_AIC8800D80, SDIO_DEVICE_ID_AIC8800D80)},
+    { },
+};
+
+MODULE_DEVICE_TABLE(sdio, aicwf_sdmmc_ids);
+
+static const struct dev_pm_ops aicwf_sdio_pm_ops = {
+    SET_SYSTEM_SLEEP_PM_OPS(aicwf_sdio_suspend, aicwf_sdio_resume)
+};
+
+static struct sdio_driver aicwf_sdio_driver = {
+    .probe = aicwf_sdio_probe,
+    .remove = aicwf_sdio_remove,
+    .name = AICWF_SDIO_NAME,
+    .id_table = aicwf_sdmmc_ids,
+    .drv = {
+        .pm = &aicwf_sdio_pm_ops,
+    },
+};
+
+#ifdef CONFIG_NANOPI_M4
+    extern int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq);
+    extern unsigned  aic_max_freqs;
+    extern struct mmc_host* aic_host_drv;
+    extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
+    extern void mmc_release_host(struct mmc_host *host);
+#endif
+#ifdef CONFIG_PLATFORM_ALLWINNER
+extern void sunxi_mmc_rescan_card(unsigned ids);
+extern void sunxi_wlan_set_power(int on);
+extern int sunxi_wlan_get_bus_index(void);
+
+int platform_wifi_power_on(void)
+{
+	int ret=0;
+	int wlan_bus_index=sunxi_wlan_get_bus_index();
+	if(wlan_bus_index < 0)
+		return wlan_bus_index;
+
+	sunxi_wlan_set_power(1);
+	mdelay(1000);
+	sunxi_mmc_rescan_card(wlan_bus_index);
+
+	printk("platform_wifi_power_on");
+
+	return ret;
+}
+
+void platform_wifi_power_off(void)
+{
+	int wlan_bus_index = sunxi_wlan_get_bus_index();
+    if(wlan_bus_index < 0) {
+		printk("no wlan_bus_index\n");
+		return ;
+	}
+	printk("power_off\n");
+	sunxi_wlan_set_power(0);
+    mdelay(100);
+    //sunxi_mmc_rescan_card(wlan_bus_index);
+
+    printk("platform_wifi_power_off");
+}
+#endif
+void aicwf_sdio_register(void)
+{
+#ifdef CONFIG_PLATFORM_NANOPI
+    extern_wifi_set_enable(0);
+    mdelay(200);
+    extern_wifi_set_enable(1);
+    mdelay(200);
+    sdio_reinit();
+#endif /*CONFIG_PLATFORM_NANOPI*/
+
+#ifdef CONFIG_INGENIC_T20
+    jzmmc_manual_detect(1, 1);
+#endif /* CONFIG_INGENIC_T20 */
+
+#ifdef CONFIG_NANOPI_M4
+    if(aic_host_drv->card == NULL){
+        __mmc_claim_host(aic_host_drv,NULL);
+        printk("aic: >>>mmc_rescan_try_freq, aic_max_freqs=%d\n",aic_max_freqs);
+        #if 1 // limit clock for fpga
+        aic_host_drv->f_max = 2000000;
+        #endif
+        printk("f_min=%d,f_max=%d,f_init=%d,ocr_avail=%x,ocr_avail_sdio=%x\r\n",
+            aic_host_drv->f_min,aic_host_drv->f_max,aic_host_drv->f_init, aic_host_drv->ocr_avail,aic_host_drv->ocr_avail_sdio);
+        printk("caps=0x%x,caps2=0x%x,ocr_avail_sd=%x,ocr_avail_mmc=%x\r\n",
+            aic_host_drv->caps,aic_host_drv->caps2, aic_host_drv->ocr_avail_sd,aic_host_drv->ocr_avail_mmc);
+        mmc_rescan_try_freq(aic_host_drv,aic_max_freqs);
+        mmc_release_host(aic_host_drv);
+    }
+#endif
+#ifdef CONFIG_PLATFORM_ALLWINNER
+    platform_wifi_power_on();
+#endif
+    if (sdio_register_driver(&aicwf_sdio_driver)) {
+
+    } else {
+    	//may add mmc_rescan here
+    }
+}
+
+void aicwf_sdio_exit(void)
+{
+    if(g_rwnx_plat && g_rwnx_plat->enabled)
+        rwnx_platform_deinit(g_rwnx_plat->sdiodev->rwnx_hw);
+
+	sdio_unregister_driver(&aicwf_sdio_driver);
+	func_flag = true;
+
+#ifdef CONFIG_PLATFORM_AMLOGIC
+	extern_wifi_set_enable(0);
+#endif /*CONFIG_PLATFORM_AMLOGIC*/
+
+#if 0
+#ifdef CONFIG_PLATFORM_ROCKCHIP
+	rockchip_wifi_set_carddetect(0);
+	mdelay(200);
+	rockchip_wifi_power(0);
+	mdelay(200);
+#endif /*CONFIG_PLATFORM_ROCKCHIP*/
+#endif
+
+	if(g_rwnx_plat){
+		kfree(g_rwnx_plat);
+	}
+}
+
+#if defined(CONFIG_SDIO_PWRCTRL)
+int aicwf_sdio_wakeup(struct aic_sdio_dev *sdiodev)
+{
+    int ret = 0;
+    int read_retry;
+    int write_retry = 20;
+	int wakeup_reg_val = 0;
+
+    if (sdiodev->chipid == PRODUCT_ID_AIC8801 ||
+        sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
+        sdiodev->chipid == PRODUCT_ID_AIC8800DW) {
+        wakeup_reg_val = 1;
+    } else if (sdiodev->chipid == PRODUCT_ID_AIC8800D80) {
+        wakeup_reg_val = 0x11;
+    }
+
+    if (sdiodev->state == SDIO_SLEEP_ST) {
+        down(&sdiodev->pwrctl_wakeup_sema);
+        if (sdiodev->rwnx_hw->vif_started) {
+            if(sdiodev->state == SDIO_ACTIVE_ST) {
+                up(&sdiodev->pwrctl_wakeup_sema);
+                return 0;
+            }
+            sdio_dbg("w\n");
+            while(write_retry) {
+                ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, wakeup_reg_val);
+                if (ret) {
+                    txrx_err("sdio wakeup fail\n");
+                    ret = -1;
+                } else {
+                    read_retry=10;
+                    while (read_retry) {
+                        u8 val;
+                        ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.sleep_reg, &val);
+                        if (ret==0 && val&0x10) {
+                            break;
+                        }
+                        read_retry--;
+                        udelay(200);
+                    }
+                    if(read_retry != 0)
+                        break;
+                }
+                sdio_dbg("write retry:  %d \n",write_retry);
+                write_retry--;
+                udelay(100);
+            }
+        }
+
+        sdiodev->state = SDIO_ACTIVE_ST;
+        aicwf_sdio_pwrctl_timer(sdiodev, sdiodev->active_duration);
+        up(&sdiodev->pwrctl_wakeup_sema);
+    }
+    return ret;
+}
+
+extern u8 dhcped;
+int aicwf_sdio_sleep_allow(struct aic_sdio_dev *sdiodev)
+{
+    int ret = 0;
+    struct aicwf_bus *bus_if = sdiodev->bus_if;
+    struct rwnx_hw *rwnx_hw = sdiodev->rwnx_hw;
+
+    if (bus_if->state == BUS_DOWN_ST) {
+        ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x10);
+        if (ret) {
+            sdio_err("Write sleep fail!\n");
+    }
+        aicwf_sdio_pwrctl_timer(sdiodev, 0);
+        return ret;
+    }
+
+    sdio_info("sleep: %d, %d\n", sdiodev->state, scanning);
+    if (sdiodev->state == SDIO_ACTIVE_ST  && !scanning && !rwnx_hw->is_p2p_alive \
+                && !rwnx_hw->is_p2p_connected && dhcped) {
+        down(&sdiodev->pwrctl_wakeup_sema);
+        if (rwnx_hw->vif_started) {
+            sdio_dbg("s\n");
+            ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x10);
+            if (ret)
+               	sdio_err("Write sleep fail!\n");
+        }
+        sdiodev->state = SDIO_SLEEP_ST;
+        aicwf_sdio_pwrctl_timer(sdiodev, 0);
+        up(&sdiodev->pwrctl_wakeup_sema);
+    }
+    else{
+        aicwf_sdio_pwrctl_timer(sdiodev, sdiodev->active_duration);
+    }
+
+    return ret;
+}
+
+int aicwf_sdio_pwr_stctl(struct aic_sdio_dev *sdiodev, uint target)
+{
+    int ret = 0;
+
+    if (sdiodev->bus_if->state == BUS_DOWN_ST) {
+        return -1;
+    }
+
+    if (sdiodev->state == target) {
+        if (target == SDIO_ACTIVE_ST) {
+            aicwf_sdio_pwrctl_timer(sdiodev, sdiodev->active_duration);
+        }
+        return ret;
+    }
+
+    switch (target) {
+    case SDIO_ACTIVE_ST:
+        aicwf_sdio_wakeup(sdiodev);
+        break;
+    case SDIO_SLEEP_ST:
+        aicwf_sdio_sleep_allow(sdiodev);
+        break;
+    }
+
+    return ret;
+}
+#endif
+
+int aicwf_sdio_txpkt(struct aic_sdio_dev *sdiodev, struct sk_buff *pkt)
+{
+    int ret = 0;
+    u8 *frame;
+    u32 len = 0;
+    struct aicwf_bus *bus_if = dev_get_drvdata(sdiodev->dev);
+
+    if (bus_if->state == BUS_DOWN_ST) {
+        sdio_dbg("tx bus is down!\n");
+        return -EINVAL;
+    }
+
+    frame = (u8 *) (pkt->data);
+    len = pkt->len;
+    len = (len + SDIOWIFI_FUNC_BLOCKSIZE - 1) / SDIOWIFI_FUNC_BLOCKSIZE * SDIOWIFI_FUNC_BLOCKSIZE;
+    ret = aicwf_sdio_send_pkt(sdiodev, pkt->data, len);
+    if (ret)
+        sdio_err("aicwf_sdio_send_pkt fail%d\n", ret);
+
+    return ret;
+}
+
+static int aicwf_sdio_intr_get_len_bytemode(struct aic_sdio_dev *sdiodev, u8 *byte_len)
+{
+    int ret = 0;
+
+    if (!byte_len)
+        return -EBADE;
+
+    if (sdiodev->bus_if->state == BUS_DOWN_ST) {
+        *byte_len = 0;
+    } else {
+        ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.bytemode_len_reg, byte_len);
+        sdiodev->rx_priv->data_len = (*byte_len)*4;
+    }
+
+    return ret;
+}
+
+static void aicwf_sdio_bus_stop(struct device *dev)
+{
+    struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+    struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+    int ret;
+
+    #if defined(CONFIG_SDIO_PWRCTRL)
+    aicwf_sdio_pwrctl_timer(sdiodev, 0);
+    #endif
+    if(timer_pending(&sdiodev->rwnx_hw->p2p_alive_timer)){
+        ret = del_timer(&sdiodev->rwnx_hw->p2p_alive_timer);}
+    sdio_dbg("%s\n",__func__);
+    #if defined(CONFIG_SDIO_PWRCTRL)
+    if (sdiodev->pwrctl_tsk) {
+        complete(&sdiodev->pwrctrl_trgg);
+        kthread_stop(sdiodev->pwrctl_tsk);
+        sdiodev->pwrctl_tsk = NULL;
+    }
+
+    sdio_dbg("%s:pwrctl stopped\n",__func__);
+    #endif
+    bus_if->state = BUS_DOWN_ST;
+    ret = down_interruptible(&sdiodev->tx_priv->txctl_sema);
+    if (ret)
+       sdio_err("down txctl_sema fail\n");
+
+    #if defined(CONFIG_SDIO_PWRCTRL)
+    aicwf_sdio_pwr_stctl(sdiodev, SDIO_SLEEP_ST);
+    #endif
+    if (!ret)
+        up(&sdiodev->tx_priv->txctl_sema);
+    spin_lock_bh(&sdiodev->tx_priv->txqlock);
+    aicwf_txframe_queue_flush(&sdiodev->tx_priv->txq);
+    atomic_set(&sdiodev->tx_priv->tx_pktcnt, 0);
+    spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+    sdio_dbg("exit %s\n",__func__);
+}
+
+#ifdef LESS_SKB
+struct rx_buff *aicwf_sdio_readframes(struct aic_sdio_dev *sdiodev, u8 msg)
+{
+    int ret = 0;
+    u32 size = 0;
+    struct aicwf_bus *bus_if = dev_get_drvdata(sdiodev->dev);
+    struct rx_buff* rxbuff;
+    
+    if (bus_if->state == BUS_DOWN_ST) {
+        sdio_dbg("bus down\n");
+        return NULL;
+    }
+#if 0
+    rxbuff = kmalloc(sizeof(struct rx_buff), GFP_KERNEL);
+    if(rxbuff == NULL){
+        printk("failed to alloc rxbuff\n");
+        return NULL;
+    }
+
+    size = sdiodev->rx_priv->data_len;
+	//printk("sdall:%d\n", atomic_read(&skb_used));
+
+    rxbuff->data = kmalloc(size,GFP_KERNEL);
+    if (rxbuff->data == NULL) {
+        printk("failed to alloc rxbuff data\n");
+        kfree(rxbuff);
+        return NULL;
+    }
+#else
+    size = sdiodev->rx_priv->data_len;
+    rxbuff = aicwf_prealloc_rxbuff_alloc(&sdiodev->rx_priv->rxbuff_lock);
+    if(rxbuff == NULL){
+        printk("failed to alloc rxbuff\n");
+        return NULL;
+    }
+#endif
+    rxbuff->len = 0;
+    rxbuff->start = rxbuff->data;
+    rxbuff->read = rxbuff->start;
+    rxbuff->end = rxbuff->data + size;
+
+    ret = aicwf_sdio_recv_pkt(sdiodev, rxbuff, size, msg);
+    if (ret) {
+        printk("%s %d, sdio recv pkt fail", __func__, ret);
+#if 0
+        rxbuff_free(rxbuff);
+#else
+        aicwf_prealloc_rxbuff_free(rxbuff, &sdiodev->rx_priv->rxbuff_lock);
+#endif
+        return NULL;
+    }
+
+    return rxbuff;
+}
+#else
+struct sk_buff *aicwf_sdio_readframes(struct aic_sdio_dev *sdiodev, u8 msg)
+{
+    int ret = 0;
+    u32 size = 0;
+    struct sk_buff *skb = NULL;
+    struct aicwf_bus *bus_if = dev_get_drvdata(sdiodev->dev);
+
+    if (bus_if->state == BUS_DOWN_ST) {
+        sdio_dbg("bus down\n");
+        return NULL;
+    }
+
+    size = sdiodev->rx_priv->data_len;
+    skb =  __dev_alloc_skb(size, GFP_KERNEL);
+    if (!skb) {
+        return NULL;
+    }
+
+    ret = aicwf_sdio_recv_pkt(sdiodev, skb, size, msg);
+    if (ret) {
+        printk("%s %d, recv pkt error\n", __func__, ret);
+        dev_kfree_skb(skb);
+        skb = NULL;
+    }
+
+    return skb;
+}
+
+#endif
+
+static int aicwf_sdio_tx_msg(struct aic_sdio_dev *sdiodev)
+{
+    int err = 0;
+    u16 len;
+    u8 *payload = sdiodev->tx_priv->cmd_buf;
+    u16 payload_len = sdiodev->tx_priv->cmd_len;
+    u8 adjust_str[4] = {0, 0, 0, 0};
+    int adjust_len = 0;
+    int buffer_cnt = 0;
+    u8 retry = 0;
+
+    len = payload_len;
+    if ((len % TX_ALIGNMENT) != 0) {
+        adjust_len = roundup(len, TX_ALIGNMENT);
+        memcpy(payload+payload_len, adjust_str, (adjust_len - len));
+        payload_len += (adjust_len - len);
+    }
+    len = payload_len;
+
+    //link tail is necessary
+    if ((len % SDIOWIFI_FUNC_BLOCKSIZE) != 0) {
+        memset(payload+payload_len, 0, TAIL_LEN);
+        payload_len += TAIL_LEN;
+        len = (payload_len/SDIOWIFI_FUNC_BLOCKSIZE + 1) * SDIOWIFI_FUNC_BLOCKSIZE;
+    } else
+        len = payload_len;
+
+	if(sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+		buffer_cnt = aicwf_sdio_flow_ctrl_msg(sdiodev);
+		while ((buffer_cnt <= 0 || (buffer_cnt > 0 && len > (buffer_cnt * BUFFER_SIZE))) && retry < 10) {
+			retry++;
+			buffer_cnt = aicwf_sdio_flow_ctrl_msg(sdiodev);
+			printk("buffer_cnt = %d\n", buffer_cnt);
+		}
+	}else if(sdiodev->chipid == PRODUCT_ID_AIC8800DC ||sdiodev->chipid == PRODUCT_ID_AIC8800DW){
+		if (!func_flag){
+		    buffer_cnt = aicwf_sdio_flow_ctrl_msg(sdiodev);
+		    while ((buffer_cnt <= 0 || (buffer_cnt > 0 && len > (buffer_cnt * BUFFER_SIZE))) && retry < 10) {
+		        retry++;
+		        buffer_cnt = aicwf_sdio_flow_ctrl_msg(sdiodev);
+		    }
+		}
+	}
+    printk("sdio txmsg: %d\n", buffer_cnt);
+    down(&sdiodev->tx_priv->cmd_txsema);
+	if(sdiodev->chipid == PRODUCT_ID_AIC8800DC ||sdiodev->chipid == PRODUCT_ID_AIC8800DW){
+		if(((!func_flag) && (buffer_cnt > 0 && len <= (buffer_cnt * BUFFER_SIZE))) || func_flag) {
+		    //err = aicwf_sdio_send_pkt(sdiodev, payload, len);
+		    err = aicwf_sdio_send_msg(sdiodev, payload, len);
+		    if (err) {
+		        sdio_err("aicwf_sdio_send_pkt fail%d\n", err);
+		    }
+		}else {
+		    sdio_err("tx msg fc retry fail\n");
+		    up(&sdiodev->tx_priv->cmd_txsema);
+		    return -1;
+		}
+	}else if (sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+		if (buffer_cnt > 0 && len < (buffer_cnt * BUFFER_SIZE)) {
+			err = aicwf_sdio_send_pkt(sdiodev, payload, len);
+			if (err) {
+				sdio_err("aicwf_sdio_send_pkt fail%d\n", err);
+			}
+		} else {
+			sdio_err("tx msg fc retry fail:%d, %d\n", buffer_cnt, len);
+			up(&sdiodev->tx_priv->cmd_txsema);
+			return -1;
+		}
+	}
+	
+    sdiodev->tx_priv->cmd_txstate = false;
+    if (!err)
+        sdiodev->tx_priv->cmd_tx_succ= true;
+    else
+        sdiodev->tx_priv->cmd_tx_succ= false;
+
+    up(&sdiodev->tx_priv->cmd_txsema);
+
+    return err;
+}
+
+#ifndef CONFIG_SDIO_AGGR
+static void aicwf_sdio_send_single_pkt(struct sk_buff *skb, struct aic_sdio_dev *sdiodev){
+    struct aicwf_tx_priv *tx_priv=sdiodev->tx_priv;
+    struct rwnx_txhdr *txhdr = (struct rwnx_txhdr *)(skb->data);
+    u8 *startptr=((u8 *)txhdr->sdio_hdr);//sdiohdr
+    u8 *payload = ((u8 *)txhdr->sdio_hdr);
+    int payload_len=0;
+    u8 adjust_str[4] = {0, 0, 0, 0};
+    int allign_len = 0;
+    u8 *old_tail=NULL;
+    int headroom;
+    
+    txhdr->sdio_hdr[2] = 0x01; //data
+    txhdr->sdio_hdr[3] = 0; //reserved
+   
+	struct txdesc_api *test =&txhdr->desc;
+    payload_len=(skb->tail-startptr);
+    
+    if (payload_len & (TX_ALIGNMENT - 1)) {//payload_len include sdiohdr
+        old_tail = skb->tail;
+        allign_len = roundup(payload_len, TX_ALIGNMENT)-payload_len;
+        skb_put(skb, allign_len);
+        memset(old_tail, 0, allign_len);//expand to word
+    }
+    
+    startptr[0] = ((skb->tail-startptr-4) & 0xff);
+    startptr[1] = (((skb->tail-startptr-4) >> 8)&0x0f);
+    
+    if(( (skb->tail-startptr) % SDIOWIFI_FUNC_BLOCKSIZE)!=0){
+    	old_tail=skb->tail;	
+    	skb_put(skb, 4);
+    	memset(old_tail, 0, 4);
+    }
+    
+    int len=((skb->tail-startptr) + SDIOWIFI_FUNC_BLOCKSIZE - 1) / SDIOWIFI_FUNC_BLOCKSIZE * SDIOWIFI_FUNC_BLOCKSIZE;
+
+    int ret = aicwf_sdio_send_pkt(sdiodev, (u8 *)(long)&txhdr->sdio_hdr , len);
+    if (ret < 0) {
+        sdio_err("fail to send single pkt!\n");
+    }
+    
+    if(!txhdr->sw_hdr.need_cfm){
+        headroom = txhdr->sw_hdr.headroom;
+        //kmem_cache_free(txhdr->sw_hdr->rwnx_vif->rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+        skb_pull(skb, headroom);
+        consume_skb(skb);
+    }
+
+}
+#endif//CONFIG_SDIO_AGGR
+static void aicwf_sdio_tx_process(struct aic_sdio_dev *sdiodev)
+{
+    int err = 0;
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+    unsigned long flags;
+#endif
+
+    if (sdiodev->bus_if->state == BUS_DOWN_ST) {
+        sdio_err("Bus is down\n");
+        return;
+    }
+
+    #if defined(CONFIG_SDIO_PWRCTRL)
+    aicwf_sdio_pwr_stctl(sdiodev, SDIO_ACTIVE_ST);
+    #endif
+
+    //config
+    sdio_info("send cmd\n");
+    if (sdiodev->tx_priv->cmd_txstate) {
+        if (down_interruptible(&sdiodev->tx_priv->txctl_sema)) {
+            txrx_err("txctl down bus->txctl_sema fail\n");
+            return;
+        }
+        if (sdiodev->state != SDIO_ACTIVE_ST) {
+            txrx_err("state err\n");
+            up(&sdiodev->tx_priv->txctl_sema);
+            txrx_err("txctl up bus->txctl_sema fail\n");
+            return;
+        }
+
+        err = aicwf_sdio_tx_msg(sdiodev);
+        up(&sdiodev->tx_priv->txctl_sema);
+        if (waitqueue_active(&sdiodev->tx_priv->cmd_txdone_wait))
+            wake_up(&sdiodev->tx_priv->cmd_txdone_wait);
+    }
+
+    //data
+    sdio_info("send data\n");
+    if (down_interruptible(&sdiodev->tx_priv->txctl_sema)) {
+        txrx_err("txdata down bus->txctl_sema\n");
+        return;
+    }
+
+    if (sdiodev->state != SDIO_ACTIVE_ST) {
+        txrx_err("sdio state err\n");
+        up(&sdiodev->tx_priv->txctl_sema);
+        return;
+    }
+
+    if (!aicwf_is_framequeue_empty(&sdiodev->tx_priv->txq)){
+        sdiodev->tx_priv->fw_avail_bufcnt = aicwf_sdio_flow_ctrl(sdiodev);
+    }
+
+    #ifdef CONFIG_SDIO_AGGR
+	while (!aicwf_is_framequeue_empty(&sdiodev->tx_priv->txq)) {
+		if(sdiodev->bus_if->state == BUS_DOWN_ST){
+			break;
+		}
+	        if (sdiodev->tx_priv->fw_avail_bufcnt <= DATA_FLOW_CTRL_THRESH) {
+        	    if (sdiodev->tx_priv->cmd_txstate)
+                	break;
+            	    sdiodev->tx_priv->fw_avail_bufcnt = aicwf_sdio_flow_ctrl(sdiodev);
+        	} else {
+            		if (sdiodev->tx_priv->cmd_txstate) {
+                		aicwf_sdio_send(sdiodev->tx_priv, 1);
+                		break;
+            		} else {
+                		aicwf_sdio_send(sdiodev->tx_priv, 0);
+            		}
+        	}
+	}
+    #else
+    while (!aicwf_is_framequeue_empty(&sdiodev->tx_priv->txq)) {
+        if(sdiodev->tx_priv->cmd_txstate || (sdiodev->bus_if->state == BUS_DOWN_ST)){
+            break;
+	    }
+
+        if(sdiodev->tx_priv->fw_avail_bufcnt <= DATA_FLOW_CTRL_THRESH) {
+			sdiodev->tx_priv->fw_avail_bufcnt = aicwf_sdio_flow_ctrl(sdiodev);
+        } else {
+    	    struct sk_buff *skb=NULL;
+
+            spin_lock_bh(&sdiodev->tx_priv->txqlock);
+            skb = aicwf_txframe_dequeue(&sdiodev->tx_priv->txq);
+            if (skb == NULL) {
+                sdio_err("txq no pkt\n");
+                spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+                break;
+            }
+            atomic_dec(&sdiodev->tx_priv->tx_pktcnt);
+            spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+
+            aicwf_sdio_send_single_pkt(skb, sdiodev);
+            sdiodev->tx_priv->fw_avail_bufcnt--;
+        }
+    }
+
+    #ifdef CONFIG_TX_NETIF_FLOWCTRL
+        spin_lock_irqsave(&sdiodev->tx_flow_lock, flags);
+        if (atomic_read(&sdiodev->tx_priv->tx_pktcnt) < tx_fc_low_water) {
+            //printk("sdiodev->tx_priv->tx_pktcnt < tx_fc_low_water:%d %d\r\n",
+			//    atomic_read(&sdiodev->tx_priv->tx_pktcnt), tx_fc_low_water);
+            if (sdiodev->flowctrl) {
+                sdiodev->flowctrl = 0;
+                aicwf_sdio_tx_netif_flowctrl(sdiodev->rwnx_hw, false);
+            }
+        }
+        spin_unlock_irqrestore(&sdiodev->tx_flow_lock, flags);
+    #endif
+    #endif
+
+    up(&sdiodev->tx_priv->txctl_sema);
+}
+
+static int aicwf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
+{
+    uint prio;
+    int ret = -EBADE;
+    struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+    struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+    unsigned long flags;
+#endif
+
+    prio = (pkt->priority & 0xF);
+    if(prio > (AICWF_TXQ_CNT - 1)) {
+        //printk("prio eceed: %d\n", prio);
+        prio = 0;
+    }
+    spin_lock_bh(&sdiodev->tx_priv->txqlock);
+    if (!aicwf_txframe_enq(sdiodev->dev, &sdiodev->tx_priv->txq, pkt, prio)) {
+        struct rwnx_txhdr *txhdr = (struct rwnx_txhdr *)pkt->data;
+        int headroom = txhdr->sw_hdr.headroom;
+        //kmem_cache_free(txhdr->sw_hdr->rwnx_vif->rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+        skb_pull(pkt, headroom);
+        consume_skb(pkt);
+        spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+        ret = -ENOSR;
+        goto flowctrl;
+    } else {
+        ret = 0;
+    }
+
+    if (bus_if->state != BUS_UP_ST) {
+        sdio_err("bus_if stopped\n");
+        spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+        return -1;
+    }
+
+    atomic_inc(&sdiodev->tx_priv->tx_pktcnt);
+    spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+#ifdef HANDLE_TX_THREAD_ZTE
+//#ifdef CONFIG_SDIO_AGGR
+    if (atomic_read(&sdiodev->tx_priv->tx_pktcnt) == 1)
+//#endif
+        complete(&bus_if->bustx_trgg);
+#else
+    tasklet_schedule(&sdiodev->tx_priv->tx_tasklet);
+#endif
+
+	flowctrl:
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+    spin_lock_irqsave(&sdiodev->tx_flow_lock, flags);
+    if (atomic_read(&sdiodev->tx_priv->tx_pktcnt) >= tx_fc_high_water) {
+        //printk("sdiodev->tx_priv->tx_pktcnt >= tx_fc_high_water:%d %d\r\n",
+		//	atomic_read(&sdiodev->tx_priv->tx_pktcnt), tx_fc_high_water);
+        if (!sdiodev->flowctrl) {
+            sdiodev->flowctrl = 1;
+            aicwf_sdio_tx_netif_flowctrl(sdiodev->rwnx_hw, true);
+        }
+    }
+    spin_unlock_irqrestore(&sdiodev->tx_flow_lock, flags);
+#endif
+
+    return ret;
+}
+
+static int aicwf_sdio_bus_txmsg(struct device *dev, u8 *msg, uint msglen)
+{
+    int ret = -1;
+    struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+    struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+
+    down(&sdiodev->tx_priv->cmd_txsema);
+    sdiodev->tx_priv->cmd_txstate = true;
+    sdiodev->tx_priv->cmd_tx_succ = false;
+    sdiodev->tx_priv->cmd_buf = msg;
+    sdiodev->tx_priv->cmd_len = msglen;
+    up(&sdiodev->tx_priv->cmd_txsema);
+
+    if (bus_if->state != BUS_UP_ST) {
+        sdio_err("bus has stop\n");
+        return -1;
+    }
+
+#ifdef HANDLE_TX_THREAD_ZTE
+    complete(&bus_if->bustx_trgg);
+#else
+        tasklet_schedule(&sdiodev->tx_priv->tx_tasklet);
+#endif
+#if 0
+if (!func_flag){
+    if (sdiodev->tx_priv->cmd_txstate) {
+        int timeout = msecs_to_jiffies(CMD_TX_TIMEOUT);
+        ret = wait_event_interruptible_timeout(sdiodev->tx_priv->cmd_txdone_wait, \
+                                            !(sdiodev->tx_priv->cmd_txstate), timeout);
+    }
+
+    if (!sdiodev->tx_priv->cmd_txstate && sdiodev->tx_priv->cmd_tx_succ) {
+        ret = 0;
+    } else {
+        sdio_err("send faild:%d, %d,%x\n", sdiodev->tx_priv->cmd_txstate, sdiodev->tx_priv->cmd_tx_succ, ret);
+        ret = -EIO;
+    }
+
+    return ret;
+}
+#endif
+    return 0;
+}
+
+#ifdef CONFIG_SDIO_AGGR
+int aicwf_sdio_send(struct aicwf_tx_priv *tx_priv, u8 txnow)
+{
+	struct sk_buff *pkt;
+	struct aic_sdio_dev *sdiodev = tx_priv->sdiodev;
+	u32 aggr_len = 0;
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+    unsigned long flags;
+#endif
+	aggr_len = (tx_priv->tail - tx_priv->head);
+	if (((atomic_read(&tx_priv->aggr_count) == 0) && (aggr_len != 0))
+		|| ((atomic_read(&tx_priv->aggr_count) != 0) && (aggr_len == 0))) {
+		if (aggr_len > 0)
+			aicwf_sdio_aggrbuf_reset(tx_priv);
+		goto done;
+	}
+
+	if (atomic_read(&tx_priv->aggr_count) == (tx_priv->fw_avail_bufcnt - 2)) {
+		if (atomic_read(&tx_priv->aggr_count) > 0) {
+			tx_priv->fw_avail_bufcnt -= atomic_read(&tx_priv->aggr_count);
+			aicwf_sdio_aggr_send(tx_priv); //send and check the next pkt;
+		}
+	} else {
+		spin_lock_bh(&sdiodev->tx_priv->txqlock);
+		pkt = aicwf_txframe_dequeue(&sdiodev->tx_priv->txq);
+		if (pkt == NULL) {
+			sdio_err("txq no pkt\n");
+			spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+			goto done;
+		}
+		atomic_dec(&sdiodev->tx_priv->tx_pktcnt);
+		spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+        
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+        spin_lock_irqsave(&sdiodev->tx_flow_lock, flags);
+        if (atomic_read(&sdiodev->tx_priv->tx_pktcnt) < tx_fc_low_water) {
+            //printk("sdiodev->tx_priv->tx_pktcnt < tx_fc_low_water:%d %d\r\n",
+			//    atomic_read(&sdiodev->tx_priv->tx_pktcnt), tx_fc_low_water);
+            if (sdiodev->flowctrl) {
+                sdiodev->flowctrl = 0;
+                aicwf_sdio_tx_netif_flowctrl(sdiodev->rwnx_hw, false);
+            }
+        }
+        spin_unlock_irqrestore(&sdiodev->tx_flow_lock, flags);
+#endif
+
+		if (tx_priv == NULL || tx_priv->tail == NULL || pkt == NULL)
+			txrx_err("null error\n");
+		if (aicwf_sdio_aggr(tx_priv, pkt)) {
+			aicwf_sdio_aggrbuf_reset(tx_priv);
+			sdio_err("add aggr pkts failed!\n");
+			goto done;
+		}
+
+		//when aggr finish or there is cmd to send, just send this aggr pkt to fw
+		if ((int)atomic_read(&sdiodev->tx_priv->tx_pktcnt) == 0 || txnow || (atomic_read(&tx_priv->aggr_count) == (tx_priv->fw_avail_bufcnt - 2))) {
+			tx_priv->fw_avail_bufcnt -= atomic_read(&tx_priv->aggr_count);
+			aicwf_sdio_aggr_send(tx_priv);
+		} else
+			goto done;
+	}
+
+done:
+	return 0;
+}
+
+int aicwf_sdio_aggr(struct aicwf_tx_priv *tx_priv, struct sk_buff *pkt)
+{
+    struct rwnx_txhdr *txhdr = (struct rwnx_txhdr *)pkt->data;
+    u8 *start_ptr = tx_priv->tail;
+    u8 sdio_header[4];
+    u8 adjust_str[4] = {0, 0, 0, 0};
+    u32 curr_len = 0;
+    int allign_len = 0;
+    int headroom;
+
+    sdio_header[0] =((pkt->len - sizeof(struct rwnx_txhdr) + sizeof(struct txdesc_api)) & 0xff);
+    sdio_header[1] =(((pkt->len - sizeof(struct rwnx_txhdr) + sizeof(struct txdesc_api)) >> 8)&0x0f);
+    sdio_header[2] = 0x01; //data
+	if (tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8801 || 
+        tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
+        tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8800DW)
+        sdio_header[3] = 0; //reserved
+    else if (tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8800D80)
+	    sdio_header[3] = crc8_ponl_107(&sdio_header[0], 3); // crc8
+
+
+    memcpy(tx_priv->tail, (u8 *)&sdio_header, sizeof(sdio_header));
+    tx_priv->tail += sizeof(sdio_header);
+    //payload
+    memcpy(tx_priv->tail, (u8 *)(long)&txhdr->sw_hdr.desc, sizeof(struct txdesc_api));
+    tx_priv->tail += sizeof(struct txdesc_api); //hostdesc
+    memcpy(tx_priv->tail, (u8 *)((u8 *)txhdr + txhdr->sw_hdr.headroom), pkt->len-txhdr->sw_hdr.headroom);
+    tx_priv->tail += (pkt->len - txhdr->sw_hdr.headroom);
+
+    //word alignment
+    curr_len = tx_priv->tail - tx_priv->head;
+    if (curr_len & (TX_ALIGNMENT - 1)) {
+        allign_len = roundup(curr_len, TX_ALIGNMENT)-curr_len;
+        memcpy(tx_priv->tail, adjust_str, allign_len);
+        tx_priv->tail += allign_len;
+    }
+
+	if (tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8801 || 
+		tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
+		tx_priv->sdiodev->chipid == PRODUCT_ID_AIC8800DW) {
+	    start_ptr[0] = ((tx_priv->tail - start_ptr - 4) & 0xff);
+	    start_ptr[1] = (((tx_priv->tail - start_ptr - 4)>>8) & 0x0f);
+	}
+    tx_priv->aggr_buf->dev = pkt->dev;
+
+    if(!txhdr->sw_hdr.need_cfm) {
+	headroom = txhdr->sw_hdr.headroom;
+        //kmem_cache_free(txhdr->sw_hdr->rwnx_vif->rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+        skb_pull(pkt, headroom);
+        consume_skb(pkt);
+    }
+
+    atomic_inc(&tx_priv->aggr_count);
+    return 0;
+}
+
+void aicwf_sdio_aggr_send(struct aicwf_tx_priv *tx_priv)
+{
+    struct sk_buff *tx_buf = tx_priv->aggr_buf;
+    int ret = 0;
+    int curr_len = 0;
+
+    //link tail is necessary
+    curr_len = tx_priv->tail - tx_priv->head;
+    if ((curr_len % TXPKT_BLOCKSIZE) != 0) {
+        memset(tx_priv->tail, 0, TAIL_LEN);
+        tx_priv->tail += TAIL_LEN;
+    }
+
+    tx_buf->len = tx_priv->tail - tx_priv->head;
+    ret = aicwf_sdio_txpkt(tx_priv->sdiodev, tx_buf);
+    if (ret < 0) {
+        sdio_err("fail to send aggr pkt!\n");
+    }
+
+    aicwf_sdio_aggrbuf_reset(tx_priv);
+}
+
+void aicwf_sdio_aggrbuf_reset(struct aicwf_tx_priv *tx_priv)
+{
+    struct sk_buff *aggr_buf = tx_priv->aggr_buf;
+
+    tx_priv->tail = tx_priv->head;
+    aggr_buf->len = 0;
+    atomic_set(&tx_priv->aggr_count, 0);
+}
+#endif
+static int aicwf_sdio_bus_start(struct device *dev)
+{
+    struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+    struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+    int ret = 0;
+
+	if(sdiodev->chipid == PRODUCT_ID_AIC8800DC || sdiodev->chipid == PRODUCT_ID_AIC8800DW){
+
+	    sdio_claim_host(sdiodev->func);
+#if 0
+	    sdio_claim_irq(sdiodev->func, aicwf_sdio_hal_irqhandler);
+	    sdio_claim_irq(sdiodev->func_msg, aicwf_sdio_hal_irqhandler_func2);
+#else
+	    //since we have func2 we don't register irq handler
+	    sdio_claim_irq(sdiodev->func, NULL);
+	    sdio_claim_irq(sdiodev->func_msg, NULL);
+
+	    sdiodev->func->irq_handler = (sdio_irq_handler_t *)aicwf_sdio_hal_irqhandler;
+	    sdiodev->func_msg->irq_handler = (sdio_irq_handler_t *)aicwf_sdio_hal_irqhandler_func2;
+#endif 
+
+	    sdio_release_host(sdiodev->func);
+
+	    //enable sdio interrupt
+	    ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x07);
+
+	    if (ret != 0)
+	        sdio_err("intr register failed:%d\n", ret);
+
+	    //enable sdio interrupt
+	    ret = aicwf_sdio_writeb_func2(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x07);
+
+	    if (ret != 0)
+	        sdio_err("func2 intr register failed:%d\n", ret);
+
+	}else if(sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+		sdio_claim_host(sdiodev->func);
+		sdio_claim_irq(sdiodev->func, aicwf_sdio_hal_irqhandler);
+
+        sdio_f0_writeb(sdiodev->func, 0x07, 0x04, &ret);
+        if (ret) {
+            sdio_err("set func0 int en fail %d\n", ret);
+        }
+        sdio_release_host(sdiodev->func);
+		//enable sdio interrupt
+		ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x07);
+		if (ret != 0)
+			sdio_err("intr register failed:%d\n", ret);
+	}
+    bus_if->state = BUS_UP_ST;
+
+    return ret;
+}
+
+
+static inline void aic_thread_wait_stop(void)
+{
+	set_current_state(TASK_INTERRUPTIBLE);
+	while (!kthread_should_stop()) {
+		schedule();
+		set_current_state(TASK_INTERRUPTIBLE);
+	}
+	__set_current_state(TASK_RUNNING);
+}
+
+#ifdef CONFIG_TXRX_THREAD_PRIO
+//#include "linux/sched/types.h"
+//#include "linux/sched/rt.h"
+int bustx_thread_prio = 36;
+module_param_named(bustx_thread_prio, bustx_thread_prio, int, 0644);
+//module_param(bustx_thread_prio, int, 0);
+int busrx_thread_prio = 37;
+module_param_named(busrx_thread_prio, busrx_thread_prio, int, 0644);
+//module_param(busrx_thread_prio, int, 0);
+#endif
+
+#ifdef HANDLE_TX_THREAD_ZTE
+int sdio_bustx_thread(void *data)
+{
+    struct aicwf_bus *bus = (struct aicwf_bus *) data;
+    struct aic_sdio_dev *sdiodev = bus->bus_priv.sdio;
+
+#ifdef CONFIG_TXRX_THREAD_PRIO
+    if (bustx_thread_prio > 0) {
+        struct sched_param param;
+        param.sched_priority = (bustx_thread_prio < MAX_RT_PRIO)?bustx_thread_prio:(MAX_RT_PRIO-1);
+        sched_setscheduler(current, SCHED_FIFO, &param);
+    }
+#endif
+    while (1) {
+        if (!wait_for_completion_interruptible(&bus->bustx_trgg)) {
+	    if(sdiodev->bus_if->state == BUS_DOWN_ST)
+                break;
+
+            while((int)(atomic_read(&sdiodev->tx_priv->tx_pktcnt) > 0) || (sdiodev->tx_priv->cmd_txstate == true)) {
+                aicwf_sdio_tx_process(sdiodev);
+                if(sdiodev->bus_if->state == BUS_DOWN_ST)
+                    break;
+            }
+        }
+    }
+    aic_thread_wait_stop();
+
+    return 0;
+}
+#else
+void sdio_bustx_tasklet(void *data)
+{
+    struct aicwf_bus *bus = (struct aicwf_bus *) data;
+    struct aic_sdio_dev *sdiodev = bus->bus_priv.sdio;
+
+    do {
+        if(sdiodev->bus_if->state == BUS_DOWN_ST)
+            break;
+
+        if((int)(atomic_read(&sdiodev->tx_priv->tx_pktcnt) > 0) || (sdiodev->tx_priv->cmd_txstate == true)) {
+                //printk("tp\n");
+                aicwf_sdio_tx_process(sdiodev);
+        }
+        else
+            break;
+    } while(1);
+
+    //printk("tpe\n");
+}
+
+#endif
+
+#ifdef HANDLE_RX_THREAD_ZTE
+int sdio_busrx_thread(void *data)
+{
+    struct aicwf_rx_priv *rx_priv = (struct aicwf_rx_priv *)data;
+    struct aicwf_bus *bus_if = rx_priv->sdiodev->bus_if;
+
+#ifdef CONFIG_TXRX_THREAD_PRIO
+    if (busrx_thread_prio > 0) {
+        struct sched_param param;
+        param.sched_priority = (busrx_thread_prio < MAX_RT_PRIO)?busrx_thread_prio:(MAX_RT_PRIO-1);
+        sched_setscheduler(current, SCHED_FIFO, &param);
+    }
+#endif
+    while (1) {
+        if (!wait_for_completion_interruptible(&bus_if->busrx_trgg)) {
+            if(bus_if->state == BUS_DOWN_ST)
+                break;
+            aicwf_process_rxframes(rx_priv);
+        }
+    }
+    aic_thread_wait_stop();
+    return 0;
+}
+#else
+void sdio_busrx_tasklet(void *data)
+{
+    struct aicwf_bus *bus_if = (struct aicwf_bus *)data;
+    struct aicwf_rx_priv *rx_priv = bus_if->bus_priv.sdio->rx_priv;
+
+    do {
+        if(bus_if->state == BUS_DOWN_ST)
+            break;
+        //printk("rp\n");
+	aicwf_process_rxframes(rx_priv);
+	//printk("R\n");
+	msleep(5);
+	//printk("rpe\n");       
+    } while(0);
+}
+
+#endif
+
+#if defined(CONFIG_SDIO_PWRCTRL)
+static int aicwf_sdio_pwrctl_thread(void *data)
+{
+    struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *) data;
+
+    while (1) {
+        if (!wait_for_completion_interruptible(&sdiodev->pwrctrl_trgg)) {
+            if(sdiodev->bus_if->state == BUS_DOWN_ST)
+                break;
+	    if (sdiodev->state == SDIO_ACTIVE_ST) {
+                if((int)(atomic_read(&sdiodev->tx_priv->tx_pktcnt) <= 0) && (sdiodev->tx_priv->cmd_txstate == false) && \
+                    atomic_read(&sdiodev->rx_priv->rx_cnt)==0)
+                        aicwf_sdio_pwr_stctl(sdiodev, SDIO_SLEEP_ST);
+                else
+                    aicwf_sdio_pwrctl_timer(sdiodev, sdiodev->active_duration);
+            }
+        } else {
+                continue;
+        }
+    }
+
+    aic_thread_wait_stop();
+    return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+static void aicwf_sdio_bus_pwrctl(ulong data)
+#else
+static void aicwf_sdio_bus_pwrctl(struct timer_list *t)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+    struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *) data;
+#else
+    struct aic_sdio_dev *sdiodev = from_timer(sdiodev, t, timer);
+#endif
+
+    if (sdiodev->bus_if->state == BUS_DOWN_ST) {
+        sdio_err("bus down\n");
+        return;
+    }
+
+    if (sdiodev->pwrctl_tsk){
+        complete(&sdiodev->pwrctrl_trgg);
+    }
+}
+#endif
+
+#ifdef LESS_SKB
+static void aicwf_sdio_enq_rxpkt(struct aic_sdio_dev *sdiodev, struct rx_buff *pkt)
+#else
+static void aicwf_sdio_enq_rxpkt(struct aic_sdio_dev *sdiodev, struct sk_buff *pkt)
+#endif
+{
+    struct aicwf_rx_priv* rx_priv = sdiodev->rx_priv;
+    unsigned long flags = 0;
+
+    spin_lock_irqsave(&rx_priv->rxqlock, flags);
+    #ifdef LESS_SKB
+    if (!aicwf_rxbuff_enqueue(sdiodev->dev, &rx_priv->rxq, pkt)) {
+        spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+        printk("%s %d, enqueue rxq fail\n", __func__, __LINE__);
+#if 0
+        rxbuff_free(pkt);
+#else
+        aicwf_prealloc_rxbuff_free(pkt, &rx_priv->rxbuff_lock);
+#endif
+        return;
+    }
+    #else
+#if 1
+    if (!aicwf_rxframe_enqueue(sdiodev->dev, &rx_priv->rxq, pkt)) {
+        spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+        aicwf_dev_skb_free(pkt);
+        return;
+    }
+#else 
+    skb_queue_tail(&rx_priv->rxq.queuelist[0], pkt);
+#endif
+    #endif
+    spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+
+    atomic_inc(&rx_priv->rx_cnt);
+}
+
+#define SDIO_OTHER_INTERRUPT (0x1ul << 7)
+
+void aicwf_sdio_hal_irqhandler(struct sdio_func *func)
+{
+    struct aicwf_bus *bus_if = dev_get_drvdata(&func->dev);
+    struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+    u8 intstatus = 0;
+    u8 byte_len = 0;
+    #ifdef LESS_SKB
+    struct rx_buff *pkt = NULL;
+    #else
+    struct sk_buff *pkt = NULL;
+    #endif
+    int ret;
+    u8 trigg = 0;
+
+    if (!bus_if || bus_if->state == BUS_DOWN_ST) {
+        sdio_err("bus err\n");
+        return;
+    }
+    //printk("sdio func1 irq\n");
+
+#ifdef LESS_SKB
+    if (list_empty(&aic_rx_buff_list.rxbuff_list)) {
+        printk("%s %d, rxbuff list is empty\n", __func__, __LINE__);
+        return;
+    } 
+#endif
+
+    if (sdiodev->chipid == PRODUCT_ID_AIC8801 || sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
+        sdiodev->chipid == PRODUCT_ID_AIC8800DW) {
+		ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.block_cnt_reg, &intstatus);
+		while(ret || (intstatus & SDIO_OTHER_INTERRUPT)) {
+		    sdio_err("ret=%d, intstatus=%x\r\n",ret, intstatus);
+		    ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.block_cnt_reg, &intstatus);
+		}
+		sdiodev->rx_priv->data_len = intstatus * SDIOWIFI_FUNC_BLOCKSIZE;
+
+		if (intstatus > 0) {
+		    if(intstatus < 64) {
+		        pkt = aicwf_sdio_readframes(sdiodev, 0);
+		    } else {
+		        aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len);//byte_len must<= 128
+		        sdio_info("byte mode len=%d\r\n", byte_len);
+		        pkt = aicwf_sdio_readframes(sdiodev, 0);
+		    }
+		} else {
+#ifndef CONFIG_PLATFORM_ALLWINNER
+		    sdio_err("Interrupt but no data\n");
+#endif
+		}
+
+		if (pkt)
+		    aicwf_sdio_enq_rxpkt(sdiodev, pkt);
+
+		if (atomic_read(&sdiodev->rx_priv->rx_cnt) == 1)
+		    complete(&bus_if->busrx_trgg);
+
+	}else if (sdiodev->chipid == PRODUCT_ID_AIC8800D80) {
+        do {
+            ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.misc_int_status_reg, &intstatus);
+            if (!ret) {
+                break;
+            }
+            sdio_err("ret=%d, intstatus=%x\r\n",ret, intstatus);
+        } while (1);
+        if (intstatus & SDIO_OTHER_INTERRUPT) {
+            u8 int_pending;
+            ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.sleep_reg, &int_pending);
+            if (ret < 0) {
+                sdio_err("reg:%d read failed!\n", sdiodev->sdio_reg.sleep_reg);
+            }
+            int_pending &= ~0x01; // dev to host soft irq
+            ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.sleep_reg, int_pending);
+            if (ret < 0) {
+                sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.sleep_reg);
+            }
+        }
+
+        if (intstatus > 0) {
+            uint8_t intmaskf2 = intstatus | (0x1UL << 3);
+            if (intmaskf2 > 120U) { // func2
+                if (intmaskf2 == 127U) { // byte mode
+                    //aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len, 1);//byte_len must<= 128
+                    aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len);//byte_len must<= 128
+                    sdio_info("byte mode len=%d\r\n", byte_len);
+                    //pkt = aicwf_sdio_readframes(sdiodev, 1);
+                    pkt = aicwf_sdio_readframes(sdiodev,0);
+                } else { // block mode
+                    sdiodev->rx_priv->data_len = (intstatus & 0x7U) * SDIOWIFI_FUNC_BLOCKSIZE;
+                    //pkt = aicwf_sdio_readframes(sdiodev, 1);
+                    pkt = aicwf_sdio_readframes(sdiodev,0);
+                }
+            } else { // func1
+                if (intstatus == 120U) { // byte mode
+                    //aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len, 0);//byte_len must<= 128
+                    aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len);//byte_len must<= 128
+                    sdio_info("byte mode len=%d\r\n", byte_len);
+                    //pkt = aicwf_sdio_readframes(sdiodev, 0);
+                    pkt = aicwf_sdio_readframes(sdiodev,0);
+                } else { // block mode
+                    sdiodev->rx_priv->data_len = (intstatus & 0x7FU) * SDIOWIFI_FUNC_BLOCKSIZE;
+                    //pkt = aicwf_sdio_readframes(sdiodev, 0);
+                    pkt = aicwf_sdio_readframes(sdiodev,0);
+                }
+    		}
+        } else {
+    #ifndef CONFIG_PLATFORM_ALLWINNER
+            //sdio_err("Interrupt but no data\n");
+    #endif
+        }
+
+        if (pkt)
+            aicwf_sdio_enq_rxpkt(sdiodev, pkt);
+
+        if(atomic_read(&sdiodev->rx_priv->rx_cnt) == 1){
+            complete(&bus_if->busrx_trgg);
+        }
+    }
+}
+
+
+void aicwf_sdio_hal_irqhandler_func2(struct sdio_func *func)
+{
+    struct aicwf_bus *bus_if = dev_get_drvdata(&func->dev);
+    struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+    u8 intstatus = 0;
+    u8 byte_len = 0;
+#ifdef LESS_SKB
+    struct rx_buff *pkt = NULL;
+#else
+    struct sk_buff *pkt = NULL;
+#endif
+    int ret;
+
+    if (!bus_if || bus_if->state == BUS_DOWN_ST) {
+        sdio_err("bus err\n");
+        return;
+    }
+
+    //printk("sdio func2 irq\n");
+
+#ifdef LESS_SKB
+    if (list_empty(&aic_rx_buff_list.rxbuff_list)) {
+        printk("%s %d, rxbuff list is empty\n", __func__, __LINE__);
+        return;
+    } 
+#endif
+
+    ret = aicwf_sdio_readb_func2(sdiodev, sdiodev->sdio_reg.block_cnt_reg, &intstatus);
+
+    while(ret || (intstatus & SDIO_OTHER_INTERRUPT)) {
+        sdio_err("ret=%d, intstatus=%x\r\n",ret, intstatus);
+        ret = aicwf_sdio_readb_func2(sdiodev, sdiodev->sdio_reg.block_cnt_reg, &intstatus);
+    }
+    sdiodev->rx_priv->data_len = intstatus * SDIOWIFI_FUNC_BLOCKSIZE;
+    if (intstatus > 0) {
+        if(intstatus < 64) {
+            pkt = aicwf_sdio_readframes(sdiodev, 1);
+        } else {
+            sdio_info("byte mode len=%d\r\n", byte_len);
+
+            aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len);//byte_len must<= 128
+            pkt = aicwf_sdio_readframes(sdiodev, 1);
+        }
+    } else {
+	#ifndef CONFIG_PLATFORM_ALLWINNER
+        sdio_err("Interrupt but no data\n");
+	#endif
+    }
+
+    if (pkt)
+        aicwf_sdio_enq_rxpkt(sdiodev, pkt);
+
+    complete(&bus_if->busrx_trgg);
+}
+
+#if defined(CONFIG_SDIO_PWRCTRL)
+void aicwf_sdio_pwrctl_timer(struct aic_sdio_dev *sdiodev, uint duration)
+{
+    uint timeout;
+
+    if (sdiodev->bus_if->state == BUS_DOWN_ST && duration)
+        return;
+
+    spin_lock_bh(&sdiodev->pwrctl_lock);
+    if (!duration) {
+        if (timer_pending(&sdiodev->timer))
+            del_timer_sync(&sdiodev->timer);
+    } else {
+        sdiodev->active_duration = duration;
+        timeout = msecs_to_jiffies(sdiodev->active_duration);
+        mod_timer(&sdiodev->timer, jiffies + timeout);
+    }
+    spin_unlock_bh(&sdiodev->pwrctl_lock);
+}
+#endif
+
+static struct aicwf_bus_ops aicwf_sdio_bus_ops = {
+    .stop = aicwf_sdio_bus_stop,
+    .start = aicwf_sdio_bus_start,
+    .txdata = aicwf_sdio_bus_txdata,
+    .txmsg = aicwf_sdio_bus_txmsg,
+};
+
+void aicwf_sdio_release_func2(struct aic_sdio_dev *sdiodev)
+{
+    int ret = 0;
+    sdio_dbg("%s\n", __func__);
+    sdio_claim_host(sdiodev->func_msg);
+    //disable sdio interrupt
+    ret = aicwf_sdio_writeb_func2(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x0);
+    if (ret < 0) {
+        sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.intr_config_reg);
+    }
+    sdio_release_irq(sdiodev->func_msg);
+    sdio_release_host(sdiodev->func_msg);
+
+}
+void aicwf_sdio_release(struct aic_sdio_dev *sdiodev)
+{
+    struct aicwf_bus *bus_if;
+    struct aicwf_rx_priv* rx_priv = NULL;
+    int ret;
+    sdio_dbg("%s\n", __func__);
+
+    bus_if = dev_get_drvdata(sdiodev->dev);
+    bus_if->state = BUS_DOWN_ST;
+
+    sdio_claim_host(sdiodev->func);
+    //disable sdio interrupt
+    ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x0);
+    if (ret < 0) {
+        sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.intr_config_reg);
+    }
+    sdio_release_irq(sdiodev->func);
+    sdio_release_host(sdiodev->func);
+
+    if (sdiodev->dev)
+        aicwf_bus_deinit(sdiodev->dev);
+
+    aicwf_tx_deinit(sdiodev->tx_priv);
+    rx_priv = sdiodev->rx_priv;
+    if(rx_priv != NULL)
+        aicwf_rx_deinit(rx_priv);
+#if 1
+    if (sdiodev->cmd_mgr.cmd_thread) {
+        complete_all(&sdiodev->cmd_mgr.cmdproc_trgg);
+        kthread_stop(sdiodev->cmd_mgr.cmd_thread);
+        sdiodev->cmd_mgr.cmd_thread = NULL;
+    }
+#endif
+    #ifndef CMD_WORKQUEUE
+    //tasklet_kill(&sdiodev->cmd_mgr.cmd_tasklet);
+    #endif
+
+    rwnx_cmd_mgr_deinit(&sdiodev->cmd_mgr);
+    sdio_dbg("exit %s\n", __func__);
+}
+
+#define FEATURE_SDIO_PHASE          2          // 0: default, 2: 180
+int sdio_phase = FEATURE_SDIO_PHASE;
+module_param(sdio_phase, int, 0644);
+MODULE_PARM_DESC(sdio_phase, "sdio_phase:0: default, 2: 180");
+
+void aicwf_sdio_reg_init(struct aic_sdio_dev *sdiodev)
+{
+    sdio_dbg("%s\n", __func__);
+
+    if(sdiodev->chipid == PRODUCT_ID_AIC8801 || sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
+       sdiodev->chipid == PRODUCT_ID_AIC8800DW){
+        sdiodev->sdio_reg.bytemode_len_reg =       SDIOWIFI_BYTEMODE_LEN_REG;
+        sdiodev->sdio_reg.intr_config_reg =        SDIOWIFI_INTR_CONFIG_REG;
+        sdiodev->sdio_reg.sleep_reg =              SDIOWIFI_SLEEP_REG;
+        sdiodev->sdio_reg.wakeup_reg =             SDIOWIFI_WAKEUP_REG;
+        sdiodev->sdio_reg.flow_ctrl_reg =          SDIOWIFI_FLOW_CTRL_REG;
+        sdiodev->sdio_reg.register_block =         SDIOWIFI_REGISTER_BLOCK;
+        sdiodev->sdio_reg.bytemode_enable_reg =    SDIOWIFI_BYTEMODE_ENABLE_REG;
+        sdiodev->sdio_reg.block_cnt_reg =          SDIOWIFI_BLOCK_CNT_REG;
+        sdiodev->sdio_reg.rd_fifo_addr =           SDIOWIFI_RD_FIFO_ADDR;
+        sdiodev->sdio_reg.wr_fifo_addr =           SDIOWIFI_WR_FIFO_ADDR;
+	} else if (sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+        sdiodev->sdio_reg.bytemode_len_reg =       SDIOWIFI_BYTEMODE_LEN_REG_V3;
+        sdiodev->sdio_reg.intr_config_reg =        SDIOWIFI_INTR_ENABLE_REG_V3;
+        sdiodev->sdio_reg.sleep_reg =              SDIOWIFI_INTR_PENDING_REG_V3;
+        sdiodev->sdio_reg.wakeup_reg =             SDIOWIFI_INTR_TO_DEVICE_REG_V3;
+        sdiodev->sdio_reg.flow_ctrl_reg =          SDIOWIFI_FLOW_CTRL_Q1_REG_V3;
+        sdiodev->sdio_reg.bytemode_enable_reg =    SDIOWIFI_BYTEMODE_ENABLE_REG_V3;
+        sdiodev->sdio_reg.misc_int_status_reg =    SDIOWIFI_MISC_INT_STATUS_REG_V3;
+        sdiodev->sdio_reg.rd_fifo_addr =           SDIOWIFI_RD_FIFO_ADDR_V3;
+        sdiodev->sdio_reg.wr_fifo_addr =           SDIOWIFI_WR_FIFO_ADDR_V3;
+    }
+}
+
+int aicwf_sdio_func_init(struct aic_sdio_dev *sdiodev)
+{
+    struct mmc_host *host;
+    u8 block_bit0 = 0x1;
+    u8 byte_mode_disable = 0x1;//1: no byte mode
+    int ret = 0;
+    host = sdiodev->func->card->host;
+
+    sdio_claim_host(sdiodev->func);
+#if 1//SDIO PHASE SETTING
+	sdiodev->func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+	sdio_f0_writeb(sdiodev->func, sdio_phase, 0x13, &ret);
+	if (ret < 0) {
+		sdio_err("write func0 fail %d\n", ret);
+		return ret;
+	}
+#endif
+    ret = sdio_set_block_size(sdiodev->func, SDIOWIFI_FUNC_BLOCKSIZE);
+    if (ret < 0) {
+        sdio_err("set blocksize fail %d\n", ret);
+        sdio_release_host(sdiodev->func);
+        return ret;
+    }
+    ret = sdio_enable_func(sdiodev->func);
+    if (ret < 0) {
+        sdio_release_host(sdiodev->func);
+        sdio_err("enable func fail %d.\n", ret);
+    }
+
+
+    #if 1 // limit clock for fpga
+    host->ios.clock = 78000000;
+    #else
+    host->ios.clock = 60000000;
+    #endif
+    host->ops->set_ios(host, &host->ios);
+    sdio_release_host(sdiodev->func);
+
+    sdio_claim_host(sdiodev->func_msg);
+    ret = sdio_set_block_size(sdiodev->func_msg, SDIOWIFI_FUNC_BLOCKSIZE);
+    if (ret < 0) {
+        sdio_err("set func2 blocksize fail %d\n", ret);
+        sdio_release_host(sdiodev->func_msg);
+        return ret;
+    }
+    ret = sdio_enable_func(sdiodev->func_msg);
+    if (ret < 0) {
+        sdio_err("enable func2 fail %d.\n", ret);
+    }
+    sdio_release_host(sdiodev->func_msg);
+
+    sdio_dbg("8818 Set SDIO Clock %d MHz\n",host->ios.clock/1000000);
+
+    ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_REGISTER_BLOCK, block_bit0);
+    if (ret < 0) {
+        sdio_err("reg:%d write failed!\n", SDIOWIFI_REGISTER_BLOCK);
+        return ret;
+    }
+
+    //1: no byte mode
+    ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_BYTEMODE_ENABLE_REG, byte_mode_disable);
+    if (ret < 0) {
+        sdio_err("reg:%d write failed!\n", SDIOWIFI_BYTEMODE_ENABLE_REG);
+        return ret;
+    }
+
+    ret = aicwf_sdio_writeb_func2(sdiodev, SDIOWIFI_REGISTER_BLOCK, block_bit0);
+    if (ret < 0) {
+        sdio_err("reg:%d write failed!\n", SDIOWIFI_REGISTER_BLOCK);
+        return ret;
+    }
+
+    //1: no byte mode
+    ret = aicwf_sdio_writeb_func2(sdiodev, SDIOWIFI_BYTEMODE_ENABLE_REG, byte_mode_disable);
+    if (ret < 0) {
+        sdio_err("reg:%d write failed!\n", SDIOWIFI_BYTEMODE_ENABLE_REG);
+        return ret;
+    }
+
+    return ret;
+}
+
+int aicwf_sdiov3_func_init(struct aic_sdio_dev *sdiodev)
+{
+	struct mmc_host *host;
+	u8 byte_mode_disable = 0x1;//1: no byte mode
+	int ret = 0;
+	uint32_t sdio_clock = 100000000;
+    u8 val;
+	u8 val1 = 0;
+	//struct aicbsp_feature_t feature;
+
+	//aicbsp_get_feature(&feature, NULL);
+    aicwf_sdio_reg_init(sdiodev);
+
+	host = sdiodev->func->card->host;
+
+	sdio_claim_host(sdiodev->func);
+	sdiodev->func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+
+	ret = sdio_set_block_size(sdiodev->func, SDIOWIFI_FUNC_BLOCKSIZE);
+	if (ret < 0) {
+		sdio_err("set blocksize fail %d\n", ret);
+		sdio_release_host(sdiodev->func);
+		return ret;
+	}
+	ret = sdio_enable_func(sdiodev->func);
+	if (ret < 0) {
+        sdio_err("enable func fail %d.\n", ret);
+		sdio_release_host(sdiodev->func);
+		return ret;
+	}
+
+    sdio_f0_writeb(sdiodev->func, 0x7F, 0xF2, &ret);
+    if (ret) {
+        sdio_err("set fn0 0xF2 fail %d\n", ret);
+        sdio_release_host(sdiodev->func);
+        return ret;
+    }
+#if 1
+    if (host->ios.timing == MMC_TIMING_UHS_DDR50) {
+        val = 0x21;//0x20;//0x1D;//0x5;
+    } else {
+        val = 0x01;//0x19;//0x1;
+    }
+    val |= SDIOCLK_FREE_RUNNING_BIT;
+    sdio_f0_writeb(sdiodev->func, val, 0xF0, &ret);
+    if (ret) {
+        sdio_err("set iopad ctrl fail %d\n", ret);
+        sdio_release_host(sdiodev->func);
+        return ret;
+    }
+    sdio_f0_writeb(sdiodev->func, 0x0, 0xF8, &ret);
+    if (ret) {
+        sdio_err("set iopad delay2 fail %d\n", ret);
+        sdio_release_host(sdiodev->func);
+        return ret;
+    }
+    sdio_f0_writeb(sdiodev->func, 0x00, 0xF1, &ret);
+	
+    if (ret) {
+        sdio_err("set iopad delay1 fail %d\n", ret);
+        sdio_release_host(sdiodev->func);
+        return ret;
+    }
+	sdio_f0_writeb(sdiodev->func, 0x20, 0xF3, &ret);
+	 if (ret) {
+        sdio_err("set iopad delay3 fail %d\n", ret);
+        sdio_release_host(sdiodev->func);
+        return ret;
+    }
+    msleep(1);
+#if 0//SDIO CLOCK SETTING
+	if ((sdio_clock > 0) && (host->ios.timing != MMC_TIMING_UHS_DDR50)) {
+		host->ios.clock = sdio_clock;
+		host->ops->set_ios(host, &host->ios);
+		sdio_dbg("Set SDIO Clock %d MHz\n", host->ios.clock/1000000);
+	}
+#endif
+#endif
+	sdio_dbg("Set SDIO Clock %d MHz\n", host->ios.clock/1000000);
+
+	sdio_release_host(sdiodev->func);
+
+	//1: no byte mode
+	ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.bytemode_enable_reg, byte_mode_disable);
+	if (ret < 0) {
+		sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.bytemode_enable_reg);
+		return ret;
+	}
+
+	ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, 0x11);
+	if (ret < 0) {
+		sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.wakeup_reg);
+		return ret;
+	}
+	
+#if 1
+	mdelay(5);
+	ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.sleep_reg, &val1);
+	if (ret < 0) {
+		sdio_err("reg:%d read failed!\n", sdiodev->sdio_reg.sleep_reg);
+		return ret;
+	}
+
+	if(!(val1 & 0x10)){
+		sdio_err("wakeup fail\n");
+	}else{
+		sdio_err("sdio ready\n");
+	}
+#endif
+
+	return ret;
+}
+
+void aicwf_sdio_func_deinit(struct aic_sdio_dev *sdiodev)
+{
+    sdio_claim_host(sdiodev->func);
+    sdio_disable_func(sdiodev->func);
+    sdio_release_host(sdiodev->func);
+}
+
+#ifdef CONFIG_TEMP_PW
+extern void set_txpwr_ctrl(struct aic_sdio_dev *sdiodev, s8_l value);
+
+void aicwf_temp_worker(struct work_struct *work)
+{
+	struct rwnx_hw *rwnx_hw;
+	struct mm_set_vendor_hwconfig_cfm cfm;
+	struct aic_sdio_dev *sdiodev = container_of(work, struct aic_sdio_dev, tp_work);
+	rwnx_hw = sdiodev->rwnx_hw;
+
+	rwnx_send_get_temp_req(rwnx_hw, &cfm);
+
+	set_txpwr_ctrl(sdiodev, cfm.chip_temp_cfm.degree);
+
+	mod_timer(&sdiodev->tp_timer, jiffies + msecs_to_jiffies(TEMP_GET_INTERVAL));
+}
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+static void aicwf_temp_timer(ulong data)
+#else
+static void aicwf_temp_timer(struct timer_list *t)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+	struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *) data;
+#else
+	struct aic_sdio_dev *sdiodev = from_timer(sdiodev, t, tp_timer);
+#endif
+
+	if (!work_pending(&sdiodev->tp_work))
+		schedule_work(&sdiodev->tp_work);
+
+	return;
+}
+#endif
+
+void *aicwf_sdio_bus_init(struct aic_sdio_dev *sdiodev)
+{
+    int ret;
+    struct aicwf_bus *bus_if;
+    struct aicwf_rx_priv *rx_priv;
+    struct aicwf_tx_priv *tx_priv;
+
+    #if defined(CONFIG_SDIO_PWRCTRL)
+    spin_lock_init(&sdiodev->pwrctl_lock);
+    sema_init(&sdiodev->pwrctl_wakeup_sema, 1);
+    #endif
+
+    bus_if = sdiodev->bus_if;
+    bus_if->dev = sdiodev->dev;
+    bus_if->ops = &aicwf_sdio_bus_ops;
+    bus_if->state = BUS_DOWN_ST;
+    #if defined(CONFIG_SDIO_PWRCTRL)
+    sdiodev->state = SDIO_SLEEP_ST;
+    sdiodev->active_duration = SDIOWIFI_PWR_CTRL_INTERVAL;
+    #else
+    sdiodev->state = SDIO_ACTIVE_ST;
+    #endif
+
+    rx_priv = aicwf_rx_init(sdiodev);
+    if(!rx_priv) {
+        sdio_err("rx init fail\n");
+        goto fail;
+    }
+    sdiodev->rx_priv = rx_priv;
+
+    tx_priv = aicwf_tx_init(sdiodev);
+    if(!tx_priv) {
+        sdio_err("tx init fail\n");
+        goto fail;
+    }
+    sdiodev->tx_priv = tx_priv;
+    aicwf_frame_queue_init(&tx_priv->txq, AICWF_TXQ_CNT, TXQLEN);
+    spin_lock_init(&tx_priv->txqlock);
+    sema_init(&tx_priv->txctl_sema,1);
+    sema_init(&tx_priv->cmd_txsema, 1);
+    init_waitqueue_head(&tx_priv->cmd_txdone_wait);
+    atomic_set(&tx_priv->tx_pktcnt, 0);
+
+#ifdef CONFIG_TEMP_PW
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+		init_timer(&sdiodev->tp_timer);
+		sdiodev->tp_timer.data = (ulong) sdiodev;
+		sdiodev->tp_timer.function = aicwf_temp_timer;
+#else
+		timer_setup(&sdiodev->tp_timer, aicwf_temp_timer, 0);
+#endif
+		INIT_WORK(&sdiodev->tp_work, aicwf_temp_worker);
+		mod_timer(&sdiodev->tp_timer, jiffies + msecs_to_jiffies(10 * 1000));
+		sdiodev->range = 0;
+#endif
+
+#if defined(CONFIG_SDIO_PWRCTRL)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+    init_timer(&sdiodev->timer);
+    sdiodev->timer.data = (ulong) sdiodev;
+    sdiodev->timer.function = aicwf_sdio_bus_pwrctl;
+#else
+    timer_setup(&sdiodev->timer, aicwf_sdio_bus_pwrctl, 0);
+#endif
+    init_completion(&sdiodev->pwrctrl_trgg);
+#ifdef AICWF_SDIO_SUPPORT
+    sdiodev->pwrctl_tsk = kthread_run(aicwf_sdio_pwrctl_thread, sdiodev, "aicwf_pwrctl");
+#endif
+    if (IS_ERR(sdiodev->pwrctl_tsk)) {
+        sdiodev->pwrctl_tsk = NULL;
+    }
+#endif
+
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+    sdiodev->flowctrl = 0;
+    spin_lock_init(&sdiodev->tx_flow_lock);
+#endif
+
+    ret = aicwf_bus_init(0, sdiodev->dev);
+    if (ret < 0) {
+        sdio_err("bus init fail\n");
+        goto fail;
+    }
+
+    ret  = aicwf_bus_start(bus_if);
+    if (ret != 0) {
+        sdio_err("bus start fail\n");
+        goto fail;
+    }
+
+    return sdiodev;
+
+fail:
+    aicwf_sdio_release(sdiodev);
+    return NULL;
+}
+
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_sdio.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_sdio.h
new file mode 100755
index 0000000..db3f13e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_sdio.h
@@ -0,0 +1,206 @@
+/**
+ * aicwf_sdio.h
+ *
+ * SDIO function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+#ifndef _AICWF_SDMMC_H_
+#define _AICWF_SDMMC_H_
+
+#ifdef AICWF_SDIO_SUPPORT
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/ieee80211.h>
+#include <linux/semaphore.h>
+#include "rwnx_cmds.h"
+#define AICWF_SDIO_NAME                 "aicwf_sdio"
+#define SDIOWIFI_FUNC_BLOCKSIZE         512
+
+#define SDIO_VENDOR_ID_AIC              0xC8A1
+#define SDIO_DEVICE_ID_AIC              0xC08D
+
+#define SDIOWIFI_BYTEMODE_LEN_REG       0x02
+#define SDIOWIFI_INTR_CONFIG_REG	    0x04
+#define SDIOWIFI_SLEEP_REG	            0x05
+#define SDIOWIFI_WAKEUP_REG             0x09
+#define SDIOWIFI_FLOW_CTRL_REG          0x0A
+#define SDIOWIFI_REGISTER_BLOCK         0x0B
+#define SDIOWIFI_BYTEMODE_ENABLE_REG    0x11
+#define SDIOWIFI_BLOCK_CNT_REG          0x12
+#define SDIOWIFI_FLOWCTRL_MASK_REG      0x7F
+#define SDIOWIFI_WR_FIFO_ADDR			    0x07
+#define SDIOWIFI_RD_FIFO_ADDR			    0x08
+#define SDIO_VENDOR_ID_AIC8800D80           0xc8a1
+#define SDIO_DEVICE_ID_AIC8800D80           0x0082
+#define SDIOWIFI_INTR_ENABLE_REG_V3         0x00
+#define SDIOWIFI_INTR_PENDING_REG_V3        0x01
+#define SDIOWIFI_INTR_TO_DEVICE_REG_V3      0x02
+#define SDIOWIFI_FLOW_CTRL_Q1_REG_V3        0x03
+#define SDIOWIFI_MISC_INT_STATUS_REG_V3     0x04
+#define SDIOWIFI_BYTEMODE_LEN_REG_V3        0x05
+#define SDIOWIFI_BYTEMODE_LEN_MSB_REG_V3    0x06
+#define SDIOWIFI_BYTEMODE_ENABLE_REG_V3     0x07
+#define SDIOWIFI_MISC_CTRL_REG_V3           0x08
+#define SDIOWIFI_FLOW_CTRL_Q2_REG_V3        0x09
+#define SDIOWIFI_CLK_TEST_RESULT_REG_V3     0x0A
+#define SDIOWIFI_RD_FIFO_ADDR_V3            0x0F
+#define SDIOWIFI_WR_FIFO_ADDR_V3            0x10
+
+#define SDIOCLK_FREE_RUNNING_BIT 	(1<<6)
+#define SDIOWIFI_PWR_CTRL_INTERVAL      30
+#define FLOW_CTRL_RETRY_COUNT           50
+#define BUFFER_SIZE                     1536
+#define TAIL_LEN                        4
+#define TXQLEN                          (2048*4)
+
+#define SDIO_SLEEP_ST                    0
+#define SDIO_ACTIVE_ST                   1
+
+#define TEMP_GET_INTERVAL                (60 * 1000)   //time interval
+#define TEMP_THD_1                       80            //temperature 1 (℃)
+#define TEMP_THD_2                       100           //temperature 2 (℃)
+#define TEMP_STEP_1                      4             //step (dB)
+#define TEMP_STEP_2                      8             //step (dB)
+
+
+
+#define DATA_FLOW_CTRL_THRESH 2
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+#define AICWF_SDIO_TX_LOW_WATER          100
+#define AICWF_SDIO_TX_HIGH_WATER         500
+#endif
+
+typedef enum {
+    SDIO_TYPE_DATA         = 0X00,
+    SDIO_TYPE_CFG          = 0X10,
+    SDIO_TYPE_CFG_CMD_RSP  = 0X11,
+    SDIO_TYPE_CFG_DATA_CFM = 0X12
+} sdio_type;
+
+enum AICWF_IC{
+	PRODUCT_ID_AIC8801	=	0,
+	PRODUCT_ID_AIC8800DC,
+	PRODUCT_ID_AIC8800DW,
+	PRODUCT_ID_AIC8800D80
+};
+
+struct rwnx_hw;
+
+#ifdef LESS_SKB
+struct rx_buff {
+    struct list_head queue;
+    unsigned char *data;
+    u32 len;
+    u8_l *start;
+    u8_l *end;
+    u8_l *read;
+};
+#endif
+
+struct aic_sdio_reg {
+    u8 bytemode_len_reg;
+    u8 intr_config_reg;
+    u8 sleep_reg;
+    u8 wakeup_reg;
+    u8 flow_ctrl_reg;
+    u8 flowctrl_mask_reg;
+    u8 register_block;
+    u8 bytemode_enable_reg;
+    u8 block_cnt_reg;
+    u8 misc_int_status_reg;
+    u8 rd_fifo_addr;
+    u8 wr_fifo_addr;
+};
+struct aic_sdio_dev {
+    struct rwnx_hw *rwnx_hw;
+    struct sdio_func *func;
+    struct sdio_func *func_msg;
+    struct device *dev;
+    struct aicwf_bus *bus_if;
+    struct rwnx_cmd_mgr cmd_mgr;
+
+    struct aicwf_rx_priv *rx_priv;
+    struct aicwf_tx_priv *tx_priv;
+    u32 state;
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+    u8 flowctrl;
+    spinlock_t tx_flow_lock;
+#endif
+
+#ifdef CONFIG_TEMP_PW
+	struct timer_list tp_timer;
+	struct work_struct tp_work;
+	u8 range;
+#endif
+
+    #if defined(CONFIG_SDIO_PWRCTRL)
+    //for sdio pwr ctrl
+    struct timer_list timer;
+    uint active_duration;
+    struct completion pwrctrl_trgg;
+    struct task_struct *pwrctl_tsk;
+    spinlock_t pwrctl_lock;
+    struct semaphore pwrctl_wakeup_sema;
+    #endif
+	u16 chipid;
+	struct aic_sdio_reg sdio_reg;
+};
+int aicwf_sdio_writeb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val);
+void aicwf_sdio_hal_irqhandler(struct sdio_func *func);
+void aicwf_sdio_hal_irqhandler_func2(struct sdio_func *func);
+
+#ifdef CONFIG_TEMP_PW
+void aicwf_temp_worker(struct work_struct *work);
+#endif
+
+#if defined(CONFIG_SDIO_PWRCTRL)
+void aicwf_sdio_pwrctl_timer(struct aic_sdio_dev *sdiodev, uint duration);
+int aicwf_sdio_pwr_stctl(struct  aic_sdio_dev *sdiodev, uint target);
+#endif
+int aicwf_sdio_func_init(struct aic_sdio_dev *sdiodev);
+int aicwf_sdiov3_func_init(struct aic_sdio_dev *sdiodev);
+void aicwf_sdio_func_deinit(struct aic_sdio_dev *sdiodev);
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+void aicwf_sdio_tx_netif_flowctrl(struct rwnx_hw *rwnx_hw, bool state);
+#endif
+int aicwf_sdio_flow_ctrl(struct aic_sdio_dev *sdiodev);
+int aicwf_sdio_flow_ctrl_msg(struct aic_sdio_dev *sdiodev);
+#ifdef LESS_SKB
+int aicwf_sdio_recv_pkt(struct aic_sdio_dev *sdiodev, struct rx_buff *rxbuff, u32 size, u8 msg);
+#else
+int aicwf_sdio_recv_pkt(struct aic_sdio_dev *sdiodev, struct sk_buff *skbbuf, u32 size, u8 msg);
+#endif
+int aicwf_sdio_send_pkt(struct aic_sdio_dev *sdiodev, u8 *buf, uint count);
+void *aicwf_sdio_bus_init(struct aic_sdio_dev *sdiodev);
+void aicwf_sdio_release(struct aic_sdio_dev *sdiodev);
+void aicwf_sdio_release_func2(struct aic_sdio_dev *sdiodev);
+void aicwf_sdio_exit(void);
+void aicwf_sdio_register(void);
+int aicwf_sdio_txpkt(struct aic_sdio_dev *sdiodev, struct sk_buff *pkt);
+#ifdef HANDLE_TX_THREAD_ZTE
+int sdio_bustx_thread(void *data);
+#else
+void sdio_bustx_tasklet(void *data);
+#endif
+#ifdef HANDLE_RX_THREAD_ZTE
+int sdio_busrx_thread(void *data);
+#else
+void sdio_busrx_tasklet(void *data);
+#endif
+int aicwf_sdio_aggr(struct aicwf_tx_priv *tx_priv, struct sk_buff *pkt);
+int aicwf_sdio_send(struct aicwf_tx_priv *tx_priv, u8 txnow);
+void aicwf_sdio_aggr_send(struct aicwf_tx_priv *tx_priv);
+void aicwf_sdio_aggrbuf_reset(struct aicwf_tx_priv* tx_priv);
+extern void aicwf_hostif_ready(void);
+extern void aicwf_hostif_fail(void);
+#ifdef CONFIG_PLATFORM_NANOPI
+extern void extern_wifi_set_enable(int is_on);
+extern void sdio_reinit(void);
+#endif /*CONFIG_PLATFORM_NANOPI*/
+uint8_t crc8_ponl_107(uint8_t *p_buffer, uint16_t cal_size);
+
+#endif /* AICWF_SDIO_SUPPORT */
+
+#endif /*_AICWF_SDMMC_H_*/
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_tcp_ack.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_tcp_ack.c
new file mode 100755
index 0000000..27fe5f7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_tcp_ack.c
@@ -0,0 +1,633 @@
+#include"aicwf_tcp_ack.h"

+//#include"rwnx_tx.h"

+//#include "aicwf_tcp_ack.h"

+#include"rwnx_defs.h"

+extern int intf_tx(struct rwnx_hw *priv,struct msg_buf *msg);

+struct msg_buf *intf_tcp_alloc_msg(struct msg_buf *msg)

+{

+	//printk("%s \n",__func__);

+	int len=sizeof(struct msg_buf) ;

+	msg = kzalloc(len , GFP_KERNEL);

+	if(!msg)

+		printk("%s: alloc failed \n", __func__);

+	memset(msg,0,len);

+	return msg;

+}

+						

+void intf_tcp_drop_msg(struct rwnx_hw *priv,

+					    struct msg_buf *msg)

+{

+	//printk("%s \n",__func__);

+	if (msg->skb)

+		dev_kfree_skb_any(msg->skb);

+

+	kfree(msg);

+}

+

+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) 

+void tcp_ack_timeout(unsigned long data)

+#else

+void tcp_ack_timeout(struct timer_list *t)

+#endif

+{

+	//printk("%s \n",__func__);

+	struct tcp_ack_info *ack_info;

+	struct msg_buf *msg;

+	struct tcp_ack_manage *ack_m = NULL;

+

+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) 

+	ack_info = (struct tcp_ack_info *)data;

+#else

+	ack_info = container_of(t,struct tcp_ack_info,timer);

+#endif

+

+	ack_m = container_of(ack_info, struct tcp_ack_manage,

+			     ack_info[ack_info->ack_info_num]);

+

+	write_seqlock_bh(&ack_info->seqlock);

+	msg = ack_info->msgbuf;

+	if (ack_info->busy && msg && !ack_info->in_send_msg) {

+		ack_info->msgbuf = NULL;

+		ack_info->drop_cnt = 0;

+		ack_info->in_send_msg = msg;

+		write_sequnlock_bh(&ack_info->seqlock);

+		intf_tx(ack_m->priv, msg);//send skb

+		//ack_info->in_send_msg = NULL;//add by dwx

+		//write_sequnlock_bh(&ack_info->seqlock);

+		//intf_tx(ack_m->priv, msg);

+		return;

+	}

+	write_sequnlock_bh(&ack_info->seqlock);

+}

+

+void tcp_ack_init(struct rwnx_hw *priv)

+{

+	int i;

+	struct tcp_ack_info *ack_info;

+	struct tcp_ack_manage *ack_m = &priv->ack_m;

+

+	printk("%s \n",__func__);

+	memset(ack_m, 0, sizeof(struct tcp_ack_manage));

+	ack_m->priv = priv;

+	spin_lock_init(&ack_m->lock);

+	atomic_set(&ack_m->max_drop_cnt, TCP_ACK_DROP_CNT);

+	ack_m->last_time = jiffies;

+	ack_m->timeout = msecs_to_jiffies(ACK_OLD_TIME);

+

+	for (i = 0; i < TCP_ACK_NUM; i++) {

+		ack_info = &ack_m->ack_info[i];

+		ack_info->ack_info_num = i;

+		seqlock_init(&ack_info->seqlock);

+		ack_info->last_time = jiffies;

+		ack_info->timeout = msecs_to_jiffies(ACK_OLD_TIME);

+

+		#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) 

+			setup_timer(&ack_info->timer, tcp_ack_timeout,

+				    (unsigned long)ack_info);

+		#else

+			timer_setup(&ack_info->timer,tcp_ack_timeout,0);

+		#endif

+	}

+

+	atomic_set(&ack_m->enable, 1);

+	ack_m->ack_winsize = MIN_WIN;

+}

+

+void tcp_ack_deinit(struct rwnx_hw *priv)

+{

+	int i;

+	struct tcp_ack_manage *ack_m = &priv->ack_m;

+	struct msg_buf *drop_msg = NULL;

+

+	printk("%s \n",__func__);

+	atomic_set(&ack_m->enable, 0);

+

+	for (i = 0; i < TCP_ACK_NUM; i++) {

+		drop_msg = NULL;

+

+		write_seqlock_bh(&ack_m->ack_info[i].seqlock);

+		del_timer(&ack_m->ack_info[i].timer);

+		drop_msg = ack_m->ack_info[i].msgbuf;

+		ack_m->ack_info[i].msgbuf = NULL;

+		write_sequnlock_bh(&ack_m->ack_info[i].seqlock);

+

+		if (drop_msg)

+			intf_tcp_drop_msg(priv, drop_msg);//drop skb

+	}

+}

+

+int tcp_check_quick_ack(unsigned char *buf,

+				      struct tcp_ack_msg *msg)

+{

+	int ip_hdr_len;

+	unsigned char *temp;

+	struct ethhdr *ethhdr;

+	struct iphdr *iphdr;

+	struct tcphdr *tcphdr;

+

+	ethhdr = (struct ethhdr *)buf;

+	if (ethhdr->h_proto != htons(ETH_P_IP))

+		return 0;

+	iphdr = (struct iphdr *)(ethhdr + 1);

+	if (iphdr->version != 4 || iphdr->protocol != IPPROTO_TCP)

+		return 0;

+	ip_hdr_len = iphdr->ihl * 4;

+	temp = (unsigned char *)(iphdr) + ip_hdr_len;

+	tcphdr = (struct tcphdr *)temp;

+	/* TCP_FLAG_ACK */

+	if (!(temp[13] & 0x10))

+		return 0;

+

+	if (temp[13] & 0x8) {

+		msg->saddr = iphdr->daddr;

+		msg->daddr = iphdr->saddr;

+		msg->source = tcphdr->dest;

+		msg->dest = tcphdr->source;

+		msg->seq = ntohl(tcphdr->seq);

+		return 1;

+	}

+

+	return 0;

+}

+

+int is_drop_tcp_ack(struct tcphdr *tcphdr, int tcp_tot_len,

+				unsigned short *win_scale)

+{

+	//printk("%s \n",__func__);

+	int drop = 1;

+	int len = tcphdr->doff * 4;

+	unsigned char *ptr;

+

+	if(tcp_tot_len > len) {

+		drop = 0;

+	} else {

+		len -= sizeof(struct tcphdr);

+		ptr = (unsigned char *)(tcphdr + 1);

+

+		while ((len > 0) && drop) {

+			int opcode = *ptr++;

+			int opsize;

+

+			switch (opcode) {

+			case TCPOPT_EOL:

+				break;

+			case TCPOPT_NOP:

+				len--;

+				continue;

+			default:

+				opsize = *ptr++;

+				if (opsize < 2)

+					break;

+				if (opsize > len)

+					break;

+

+				switch (opcode) {

+				/* TODO: Add other ignore opt */

+				case TCPOPT_TIMESTAMP:

+					break;

+				case TCPOPT_WINDOW:

+					if (*ptr < 15)

+						*win_scale = (1 << (*ptr));

+					printk("%d\n",*win_scale);

+					break;

+				default:

+					drop = 2;

+				}

+

+				ptr += opsize - 2;

+				len -= opsize;

+			}

+		}

+	}

+

+	return drop;

+}

+

+

+/* flag:0 for not tcp ack

+ *	1 for ack which can be drop

+ *	2 for other ack whith more info

+ */

+

+int tcp_check_ack(unsigned char *buf,

+				struct tcp_ack_msg *msg,

+				unsigned short *win_scale)

+{

+	int ret;

+	int ip_hdr_len;

+	int tcp_tot_len;

+	unsigned char *temp;

+	struct ethhdr *ethhdr;

+	struct iphdr *iphdr;

+	struct tcphdr *tcphdr;

+

+	ethhdr =(struct ethhdr *)buf;

+	if (ethhdr->h_proto != htons(ETH_P_IP))

+		return 0;

+

+	iphdr = (struct iphdr *)(ethhdr + 1);

+	if (iphdr->version != 4 || iphdr->protocol != IPPROTO_TCP)

+		return 0;

+

+	ip_hdr_len = iphdr->ihl * 4;

+	temp = (unsigned char *)(iphdr) + ip_hdr_len;

+	tcphdr = (struct tcphdr *)temp;

+	/* TCP_FLAG_ACK */

+	if (!(temp[13] & 0x10))

+		return 0;

+

+	tcp_tot_len = ntohs(iphdr->tot_len) - ip_hdr_len;// tcp total len

+	ret = is_drop_tcp_ack(tcphdr, tcp_tot_len, win_scale);

+	//printk("is drop:%d \n",ret);

+

+	if (ret > 0) {

+		msg->saddr = iphdr->saddr;

+		msg->daddr = iphdr->daddr;

+		msg->source = tcphdr->source;

+		msg->dest = tcphdr->dest;

+		msg->seq = ntohl(tcphdr->ack_seq);

+		msg->win = ntohs(tcphdr->window);

+	}

+	

+	return ret;

+}

+

+/* return val: -1 for not match, others for match */

+int tcp_ack_match(struct tcp_ack_manage *ack_m,

+				struct tcp_ack_msg *ack_msg)

+{

+	int i, ret = -1;

+	unsigned start;

+	struct tcp_ack_info *ack_info;

+	struct tcp_ack_msg *ack;

+

+	for (i = 0; ((ret < 0) && (i < TCP_ACK_NUM)); i++) {

+		ack_info = &ack_m->ack_info[i];

+		do {

+			start = read_seqbegin(&ack_info->seqlock);

+			ret = -1;

+

+			ack = &ack_info->ack_msg;

+			if (ack_info->busy &&

+			    ack->dest == ack_msg->dest &&

+			    ack->source == ack_msg->source &&

+			    ack->saddr == ack_msg->saddr &&

+			    ack->daddr == ack_msg->daddr)

+				ret = i;

+		} while(read_seqretry(&ack_info->seqlock, start));

+	}

+

+	return ret;

+}

+

+

+void tcp_ack_update(struct tcp_ack_manage *ack_m)

+{

+	int i;

+	struct tcp_ack_info *ack_info;

+

+	if (time_after(jiffies, ack_m->last_time + ack_m->timeout)) {

+		spin_lock_bh(&ack_m->lock);

+		ack_m->last_time = jiffies;

+		for (i = TCP_ACK_NUM - 1; i >= 0; i--) {

+			ack_info = &ack_m->ack_info[i];

+			write_seqlock_bh(&ack_info->seqlock);

+			if (ack_info->busy &&

+			    time_after(jiffies, ack_info->last_time +

+				       ack_info->timeout)) {

+				ack_m->free_index = i;

+				ack_m->max_num--;

+				ack_info->busy = 0;

+			}

+			write_sequnlock_bh(&ack_info->seqlock);

+		}

+		spin_unlock_bh(&ack_m->lock);

+	}

+}

+

+/* return val: -1 for no index, others for index */

+int tcp_ack_alloc_index(struct tcp_ack_manage *ack_m)

+{

+	int i, ret = -1;

+	struct tcp_ack_info *ack_info;

+	unsigned start;

+

+	spin_lock_bh(&ack_m->lock);

+	if (ack_m->max_num == TCP_ACK_NUM) {

+		spin_unlock_bh(&ack_m->lock);

+		return -1;

+	}

+

+	if (ack_m->free_index >= 0) {

+		i = ack_m->free_index;

+		ack_m->free_index = -1;

+		ack_m->max_num++;

+		spin_unlock_bh(&ack_m->lock);

+		return i;

+	}

+

+	for (i = 0; ((ret < 0) && (i < TCP_ACK_NUM)); i++) {

+		ack_info = &ack_m->ack_info[i];

+		do {

+			start = read_seqbegin(&ack_info->seqlock);

+			ret = -1;

+			if (!ack_info->busy) {

+				ack_m->free_index = -1;

+				ack_m->max_num++;

+				ret = i;

+			}

+		} while(read_seqretry(&ack_info->seqlock, start));

+	}

+	spin_unlock_bh(&ack_m->lock);

+

+	return ret;

+}

+

+

+/* return val: 0 for not handle tx, 1 for handle tx */

+int tcp_ack_handle(struct msg_buf *new_msgbuf,

+			  struct tcp_ack_manage *ack_m,

+			  struct tcp_ack_info *ack_info,

+			  struct tcp_ack_msg *ack_msg,

+			  int type)

+{

+	int quick_ack = 0;

+	struct tcp_ack_msg *ack;

+	int ret = 0;

+	struct msg_buf *drop_msg = NULL;

+

+	//printk("%s %d",__func__,type);

+	write_seqlock_bh(&ack_info->seqlock);

+

+	ack_info->last_time = jiffies;

+	ack = &ack_info->ack_msg;

+

+	if (type == 2) {

+		if (U32_BEFORE(ack->seq, ack_msg->seq)) {

+			ack->seq = ack_msg->seq;

+			if (ack_info->psh_flag &&

+			    !U32_BEFORE(ack_msg->seq,

+					       ack_info->psh_seq)) {

+				ack_info->psh_flag = 0;

+			}

+

+			if (ack_info->msgbuf) {

+				//printk("%lx \n",ack_info->msgbuf);

+				drop_msg = ack_info->msgbuf;

+				ack_info->msgbuf = NULL;

+				del_timer(&ack_info->timer);

+			}else{

+				//printk("msgbuf is NULL \n");

+			}

+

+			ack_info->in_send_msg = NULL;

+			ack_info->drop_cnt = atomic_read(&ack_m->max_drop_cnt);

+		} else {

+			printk("%s before abnormal ack: %d, %d\n",

+			       __func__, ack->seq, ack_msg->seq);

+			drop_msg = new_msgbuf;

+			ret = 1;

+		}

+	} else if (U32_BEFORE(ack->seq, ack_msg->seq)) {

+		if (ack_info->msgbuf) {

+			drop_msg = ack_info->msgbuf;

+			ack_info->msgbuf = NULL;

+		}

+

+		if (ack_info->psh_flag &&

+		    !U32_BEFORE(ack_msg->seq, ack_info->psh_seq)) {

+			ack_info->psh_flag = 0;

+			quick_ack = 1;

+		} else {

+			ack_info->drop_cnt++;

+		}

+

+		ack->seq = ack_msg->seq;

+

+		if (quick_ack || (!ack_info->in_send_msg &&

+				  (ack_info->drop_cnt >=

+				   atomic_read(&ack_m->max_drop_cnt)))) {

+			ack_info->drop_cnt = 0;

+			ack_info->in_send_msg = new_msgbuf;

+			del_timer(&ack_info->timer);

+		} else {

+			ret = 1;

+			ack_info->msgbuf = new_msgbuf;

+			if (!timer_pending(&ack_info->timer))

+				mod_timer(&ack_info->timer,

+					  (jiffies + msecs_to_jiffies(5)));

+		}

+	} else {

+		printk("%s before ack: %d, %d\n",

+		       __func__, ack->seq, ack_msg->seq);

+		drop_msg = new_msgbuf;

+		ret = 1;

+	}

+

+	write_sequnlock_bh(&ack_info->seqlock);

+

+	if (drop_msg)

+		intf_tcp_drop_msg(ack_m->priv, drop_msg);// drop skb

+

+	return ret;

+}

+

+int tcp_ack_handle_new(struct msg_buf *new_msgbuf,

+			  struct tcp_ack_manage *ack_m,

+			  struct tcp_ack_info *ack_info,

+			  struct tcp_ack_msg *ack_msg,

+			  int type)

+{

+	int quick_ack = 0;

+	struct tcp_ack_msg *ack;

+	int ret = 0;

+	struct msg_buf *drop_msg = NULL;

+	struct msg_buf * send_msg = NULL;

+	//printk("",);

+	write_seqlock_bh(&ack_info->seqlock);

+

+        ack_info->last_time = jiffies;

+        ack = &ack_info->ack_msg;

+

+	if(U32_BEFORE(ack->seq, ack_msg->seq)){

+		if (ack_info->msgbuf) {

+			drop_msg = ack_info->msgbuf;

+			ack_info->msgbuf = NULL;

+			//ack_info->drop_cnt++;

+		}

+

+		if (ack_info->psh_flag &&

+		    !U32_BEFORE(ack_msg->seq, ack_info->psh_seq)) {

+			ack_info->psh_flag = 0;

+			quick_ack = 1;

+		} else {

+			ack_info->drop_cnt++;

+		}

+

+		ack->seq = ack_msg->seq;

+

+		if(quick_ack || (!ack_info->in_send_msg &&

+				  (ack_info->drop_cnt >=

+				   atomic_read(&ack_m->max_drop_cnt)))){

+			ack_info->drop_cnt = 0;

+			send_msg = new_msgbuf;

+			ack_info->in_send_msg = send_msg;

+			del_timer(&ack_info->timer);

+		}else{

+			ret = 1;

+			ack_info->msgbuf = new_msgbuf;

+			if (!timer_pending(&ack_info->timer))

+				mod_timer(&ack_info->timer,

+					  (jiffies + msecs_to_jiffies(5)));

+		}

+		

+		//ret = 1;

+	}else {

+		printk("%s before ack: %d, %d\n",

+		       __func__, ack->seq, ack_msg->seq);

+		drop_msg = new_msgbuf;

+		ret = 1;

+	}

+

+	/*if(send_msg){

+		intf_tx(ack_m->priv,send_msg);

+		ack_info->in_send_msg=NULL;

+	}*/

+

+	//ack_info->in_send_msg=NULL;

+	

+	write_sequnlock_bh(&ack_info->seqlock);

+

+    	/*if(send_msg){

+            intf_tx(ack_m->priv,send_msg);

+            //ack_info->in_send_msg=NULL;

+    	}*/

+

+	if (drop_msg)

+		intf_tcp_drop_msg(ack_m->priv, drop_msg);// drop skb

+

+	return ret;

+

+}

+

+void filter_rx_tcp_ack(struct rwnx_hw *priv,

+			      unsigned char *buf, unsigned plen)

+{

+	int index;

+	struct tcp_ack_msg ack_msg;

+	struct tcp_ack_info *ack_info;

+	struct tcp_ack_manage *ack_m = &priv->ack_m;

+

+	if (!atomic_read(&ack_m->enable))

+		return;

+

+	if ((plen > MAX_TCP_ACK) ||

+	    !tcp_check_quick_ack(buf, &ack_msg))

+		return;

+

+	index = tcp_ack_match(ack_m, &ack_msg);

+	if (index >= 0) {

+		ack_info = ack_m->ack_info + index;

+		write_seqlock_bh(&ack_info->seqlock);

+		ack_info->psh_flag = 1;

+		ack_info->psh_seq = ack_msg.seq;

+		write_sequnlock_bh(&ack_info->seqlock);

+	}

+}

+

+/* return val: 0 for not filter, 1 for filter */

+int filter_send_tcp_ack(struct rwnx_hw *priv,

+			       struct msg_buf *msgbuf,

+			       unsigned char *buf, unsigned int plen)

+{

+	//printk("%s \n",__func__);

+	int ret = 0;

+	int index, drop;

+	unsigned short win_scale = 0;

+	unsigned int win = 0;

+	struct tcp_ack_msg ack_msg;

+	struct tcp_ack_msg *ack;

+	struct tcp_ack_info *ack_info;

+	struct tcp_ack_manage *ack_m = &priv->ack_m;

+

+	if (plen > MAX_TCP_ACK)

+		return 0;

+

+	tcp_ack_update(ack_m);

+	drop = tcp_check_ack(buf, &ack_msg, &win_scale);

+	//printk("drop:%d win_scale:%d",drop,win_scale);

+	if (!drop && (0 == win_scale))

+		return 0;

+

+	index = tcp_ack_match(ack_m, &ack_msg);

+	if (index >= 0) {

+		ack_info = ack_m->ack_info + index;

+		if ((0 != win_scale) &&

+			(ack_info->win_scale != win_scale)) {

+			write_seqlock_bh(&ack_info->seqlock);

+			ack_info->win_scale = win_scale;

+			write_sequnlock_bh(&ack_info->seqlock);

+		}

+

+		if (drop > 0 && atomic_read(&ack_m->enable)) {

+			win = ack_info->win_scale * ack_msg.win;

+			if ((win_scale!=0) && (win < (ack_m->ack_winsize * SIZE_KB)))

+			{	

+				drop = 2;

+				printk("%d %d %d",win_scale,win,(ack_m->ack_winsize * SIZE_KB));

+			}

+			ret = tcp_ack_handle_new(msgbuf, ack_m, ack_info,

+						&ack_msg, drop);

+		}

+

+		goto out;

+	}

+

+	index = tcp_ack_alloc_index(ack_m);

+	if (index >= 0) {

+		write_seqlock_bh(&ack_m->ack_info[index].seqlock);

+		ack_m->ack_info[index].busy = 1;

+		ack_m->ack_info[index].psh_flag = 0;

+		ack_m->ack_info[index].last_time = jiffies;

+		ack_m->ack_info[index].drop_cnt =

+			atomic_read(&ack_m->max_drop_cnt);

+		ack_m->ack_info[index].win_scale =

+			(win_scale != 0) ? win_scale : 1;

+		

+		//ack_m->ack_info[index].msgbuf = NULL;

+		//ack_m->ack_info[index].in_send_msg = NULL;

+		ack = &ack_m->ack_info[index].ack_msg;

+		ack->dest = ack_msg.dest;

+		ack->source = ack_msg.source;

+		ack->saddr = ack_msg.saddr;

+		ack->daddr = ack_msg.daddr;

+		ack->seq = ack_msg.seq;

+		write_sequnlock_bh(&ack_m->ack_info[index].seqlock);

+	}

+

+out:

+	return ret;

+}

+

+void move_tcpack_msg(struct rwnx_hw *priv,

+			    struct msg_buf *msg)

+{

+	struct tcp_ack_info *ack_info;

+	struct tcp_ack_manage *ack_m = &priv->ack_m;

+	int i = 0;

+

+	if (!atomic_read(&ack_m->enable))

+		return;

+

+	//if (msg->len > MAX_TCP_ACK)

+	//	return;

+

+	for (i = 0; i < TCP_ACK_NUM; i++) {

+		ack_info = &ack_m->ack_info[i];

+		write_seqlock_bh(&ack_info->seqlock);

+		if (ack_info->busy && (ack_info->in_send_msg == msg))

+			ack_info->in_send_msg = NULL;

+		write_sequnlock_bh(&ack_info->seqlock);

+	}

+}

+

diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_tcp_ack.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_tcp_ack.h
new file mode 100755
index 0000000..edfdcc1
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_tcp_ack.h
@@ -0,0 +1,111 @@
+#ifndef _AICWF_TCP_ACK_H_

+#define _AICWF_TCP_ACK_H_

+

+#include <linux/if_ether.h>

+#include <linux/tcp.h>

+#include <linux/ip.h>

+#include <linux/in.h>

+#include <linux/moduleparam.h>

+#include <net/tcp.h>

+#include <linux/timer.h>

+

+

+#define TCP_ACK_NUM  32

+#define TCP_ACK_EXIT_VAL		0x800

+#define TCP_ACK_DROP_CNT		10

+

+#define ACK_OLD_TIME	4000

+#define U32_BEFORE(a, b)	((__s32)((__u32)a - (__u32)b) <= 0)

+

+#define MAX_TCP_ACK 200

+/*min window size in KB, it's 256KB*/

+#define MIN_WIN 256

+#define SIZE_KB 1024

+

+

+struct msg_buf {

+	//struct list_head list;

+	struct sk_buff *skb;

+	struct rwnx_vif *rwnx_vif;

+

+	/* data just tx cmd use,not include the head */

+	/*void *data;

+	void *tran_data;

+	unsigned long pcie_addr;

+	u8 type;

+	u8 mode;

+	u16 len;

+	unsigned long timeout;*/

+	/* marlin 2 */

+	/*unsigned int fifo_id;

+	struct sprdwl_msg_list *msglist;*/

+	/* marlin 3 */

+	/*unsigned char buffer_type;

+	struct sprdwl_xmit_msg_list *xmit_msg_list;

+	unsigned char msg_type;

+

+	unsigned long last_time;

+	u8 ctxt_id;*/

+

+};

+

+struct tcp_ack_msg {

+	u16 source;

+	u16 dest;

+	s32 saddr;

+	s32 daddr;

+	u32 seq;

+	u16 win;

+};

+

+

+struct tcp_ack_info {

+	int ack_info_num;

+	int busy;

+	int drop_cnt;

+	int psh_flag;

+	u32 psh_seq;

+	u16 win_scale;

+	/* seqlock for ack info */

+	seqlock_t seqlock;

+	unsigned long last_time;

+	unsigned long timeout;

+	struct timer_list timer;

+	struct msg_buf *msgbuf;

+	struct msg_buf *in_send_msg;

+	struct tcp_ack_msg ack_msg;

+};

+

+struct tcp_ack_manage {

+	/* 1 filter */

+	atomic_t enable;

+	int max_num;

+	int free_index;

+	unsigned long last_time;

+	unsigned long timeout;

+	atomic_t max_drop_cnt;

+	/* lock for tcp ack alloc and free */

+	spinlock_t lock;

+	struct rwnx_hw *priv;

+	struct tcp_ack_info ack_info[TCP_ACK_NUM];

+	/*size in KB*/

+	unsigned int ack_winsize;

+};

+

+struct msg_buf *intf_tcp_alloc_msg(struct msg_buf *msg);

+

+void tcp_ack_init(struct rwnx_hw *priv);

+

+void tcp_ack_deinit(struct rwnx_hw *priv);

+

+

+int is_drop_tcp_ack(struct tcphdr *tcphdr, int tcp_tot_len, unsigned short *win_scale);

+

+int is_tcp_ack(struct sk_buff *skb, unsigned short *win_scale);

+

+int filter_send_tcp_ack(struct rwnx_hw *priv, struct msg_buf *msgbuf,unsigned char *buf, unsigned int plen);

+

+void filter_rx_tcp_ack(struct rwnx_hw *priv,unsigned char *buf, unsigned plen);

+

+void move_tcpack_msg(struct rwnx_hw *priv, struct msg_buf * msg);

+#endif

diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_txrxif.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_txrxif.c
new file mode 100755
index 0000000..07d3471
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_txrxif.c
@@ -0,0 +1,1133 @@
+/**
+ * aicwf_bus.c
+ *
+ * bus function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+#include <linux/kthread.h>
+#include <linux/netdevice.h>
+#include <linux/printk.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/semaphore.h>
+#include <linux/debugfs.h>
+#include <linux/atomic.h>
+#include <linux/vmalloc.h>
+#include "lmac_msg.h"
+#include "aicwf_txrxif.h"
+#include "rwnx_platform.h"
+#include "rwnx_defs.h"
+#include "rwnx_msg_rx.h"
+#include "rwnx_rx.h"
+#ifdef AICWF_SDIO_SUPPORT
+#include "sdio_host.h"
+#endif
+
+int dbg_level=DEBUG_DEBUG_LEVEL;
+struct txframeq_stat sta_txframeq_stat[NX_REMOTE_STA_MAX][8];
+
+#ifdef LESS_SKB
+void aicwf_rxframe_queue_init_2(struct rx_frame_queue *pq, int max_len)
+{
+    //int prio;
+
+    memset(pq, 0, offsetof(struct rx_frame_queue, queuelist) + (sizeof(struct list_head)));
+    pq->qmax = (u16)max_len;
+    INIT_LIST_HEAD(&pq->queuelist);
+#if 0
+    memset(pq, 0, offsetof(struct rx_frame_queue, queuelist) + (sizeof(struct list_head) * num_prio));
+    pq->num_prio = (u16)num_prio;
+    pq->qmax = (u16)max_len;
+
+    for (prio = 0; prio < num_prio; prio++) {
+        INIT_LIST_HEAD(&pq->queuelist[prio]);
+    }
+#endif
+}
+
+extern struct aic_sdio_dev *g_sdiodev;
+void rxbuff_queue_flush(struct rx_frame_queue *pq)
+{
+
+    //int prio;
+    struct list_head *pos;
+    struct list_head *n;
+    struct list_head *head;
+    struct rx_buff *tempbuf = NULL;
+
+    head = &pq->queuelist;
+    list_for_each_safe(pos, n, head) {
+        tempbuf = list_entry(pos, struct rx_buff, queue);
+        list_del_init(&tempbuf->queue);
+#if 0
+        rxbuff_free(tempbuf);
+#else
+        aicwf_prealloc_rxbuff_free(tempbuf, &g_sdiodev->rx_priv->rxbuff_lock);
+#endif
+        pq->qcnt--;
+    }
+}
+#endif
+
+int aicwf_bus_init(uint bus_hdrlen, struct device *dev)
+{
+    int ret = 0;
+    struct aicwf_bus *bus_if;
+
+    if (!dev) {
+        txrx_err("device not found\n");
+        return -1;
+    }
+    bus_if = dev_get_drvdata(dev);
+    bus_if->cmd_buf = kzalloc(CMD_BUF_MAX, GFP_KERNEL);
+    if(!bus_if->cmd_buf) {
+        ret = -ENOMEM;
+        txrx_err("proto_attach failed\n");
+        goto fail;
+    }
+    memset(bus_if->cmd_buf, '\0', CMD_BUF_MAX);
+
+#ifdef HANDLE_TX_THREAD_ZTE
+    init_completion(&bus_if->bustx_trgg);
+#else
+    tasklet_init(&bus_if->bus_priv.sdio->tx_priv->tx_tasklet,
+         (void(*)(unsigned long))sdio_bustx_tasklet,
+         (unsigned long)bus_if);
+#endif
+#ifdef HANDLE_RX_THREAD_ZTE
+    init_completion(&bus_if->busrx_trgg);
+#ifdef CONFIG_USB_MSG_IN_EP
+    init_completion(&bus_if->msg_busrx_trgg);
+#endif
+#ifdef AICWF_SDIO_SUPPORT
+    bus_if->bustx_thread = kthread_run(sdio_bustx_thread, (void *)bus_if, "aicwf_bustx_thread");
+    bus_if->busrx_thread = kthread_run(sdio_busrx_thread, (void *)bus_if->bus_priv.sdio->rx_priv, "aicwf_busrx_thread");
+#endif
+#else
+    tasklet_init(&bus_if->bus_priv.sdio->rx_priv->rx_tasklet,
+         (void(*)(unsigned long))sdio_busrx_tasklet,
+         (unsigned long)bus_if);
+#endif//HANDLE_RX_THREAD_ZTE
+#ifdef AICWF_USB_SUPPORT
+    bus_if->bustx_thread = kthread_run(usb_bustx_thread, (void *)bus_if, "aicwf_bustx_thread");
+    bus_if->busrx_thread = kthread_run(usb_busrx_thread, (void *)bus_if->bus_priv.usb->rx_priv, "aicwf_busrx_thread");
+#ifdef CONFIG_USB_MSG_IN_EP
+    bus_if->msg_busrx_thread = kthread_run(usb_msg_busrx_thread, (void *)bus_if->bus_priv.usb->rx_priv, "aicwf_msg_busrx_thread");
+#endif
+#endif
+
+    if (IS_ERR(bus_if->bustx_thread)) {
+        bus_if->bustx_thread  = NULL;
+        txrx_err("aicwf_bustx_thread run fail\n");
+        goto fail;
+    }
+
+    if (IS_ERR(bus_if->busrx_thread)) {
+        bus_if->busrx_thread  = NULL;
+        txrx_err("aicwf_bustx_thread run fail\n");
+        goto fail;
+    }
+
+#ifdef CONFIG_USB_MSG_IN_EP
+    if (IS_ERR(bus_if->msg_busrx_thread)) {
+        bus_if->msg_busrx_thread  = NULL;
+        txrx_err("aicwf_msg_busrx_thread run fail\n");
+        goto fail;
+    }
+#endif
+
+    return ret;
+fail:
+    aicwf_bus_deinit(dev);
+
+    return ret;
+}
+
+void aicwf_bus_deinit(struct device *dev)
+{
+    struct aicwf_bus *bus_if;
+#ifdef AICWF_USB_SUPPORT
+    struct aic_usb_dev *usb;
+#endif
+#ifdef AICWF_SDIO_SUPPORT
+    struct aic_sdio_dev *sdiodev;
+#endif
+
+    if (!dev) {
+        txrx_err("device not found\n");
+        return;
+    }
+    printk("%s", __func__);
+    bus_if = dev_get_drvdata(dev);
+    aicwf_bus_stop(bus_if);
+
+#ifdef AICWF_USB_SUPPORT
+    usb =bus_if->bus_priv.usb;
+    if(g_rwnx_plat->enabled)
+        rwnx_platform_deinit(usb->rwnx_hw);
+#endif
+#ifdef AICWF_SDIO_SUPPORT
+    sdiodev =bus_if->bus_priv.sdio;
+    if(g_rwnx_plat->enabled)
+        rwnx_platform_deinit(sdiodev->rwnx_hw);
+#endif
+
+    if (bus_if->cmd_buf) {
+        kfree(bus_if->cmd_buf);
+        bus_if->cmd_buf = NULL;
+    }
+
+
+#ifdef HANDLE_TX_THREAD_ZTE
+    if (bus_if->bustx_thread) {
+        complete_all(&bus_if->bustx_trgg);
+        kthread_stop(bus_if->bustx_thread);
+        bus_if->bustx_thread = NULL;
+    }
+#else
+    tasklet_kill(&bus_if->bus_priv.sdio->tx_priv->tx_tasklet);
+#endif
+    printk("exit %s\n", __func__);
+}
+
+void aicwf_frame_tx(void *dev, struct sk_buff *skb)
+{
+#ifdef AICWF_SDIO_SUPPORT
+    struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *)dev;
+    unsigned long flags;
+
+    if (sdiodev->bus_if->state == BUS_DOWN_ST) {
+        txrx_err("bus down\n");
+        #ifdef CONFIG_TX_NETIF_FLOWCTRL
+        spin_lock_irqsave(&sdiodev->tx_flow_lock, flags);
+        if (!sdiodev->flowctrl) {
+            sdiodev->flowctrl = 1;
+            aicwf_sdio_tx_netif_flowctrl(sdiodev->rwnx_hw, true);
+        }
+        spin_unlock_irqrestore(&sdiodev->tx_flow_lock, flags);
+        #endif
+        dev_kfree_skb(skb);
+        return;
+    }
+    aicwf_bus_txdata(sdiodev->bus_if, skb);
+#else
+    struct aic_usb_dev *usbdev = (struct aic_usb_dev *)dev;
+
+    if (!usbdev->state) {
+        txrx_err("down\n");
+        aicwf_usb_tx_flowctrl(usbdev->rwnx_hw, true);
+        dev_kfree_skb(skb);
+        return;
+    }
+    aicwf_bus_txdata(usbdev->bus_if, skb);
+#endif
+}
+
+extern struct sk_buff *tx_aggr_buf;
+struct aicwf_tx_priv* aicwf_tx_init(void *arg)
+{
+    struct aicwf_tx_priv* tx_priv;
+
+    tx_priv = kzalloc(sizeof(struct aicwf_tx_priv), GFP_KERNEL);
+    if (!tx_priv)
+        return NULL;
+
+#ifdef AICWF_SDIO_SUPPORT
+    tx_priv->sdiodev = (struct aic_sdio_dev *)arg;
+#else
+    tx_priv->usbdev = (struct aic_usb_dev *)arg;
+#endif
+
+    atomic_set(&tx_priv->aggr_count, 0);
+    tx_priv->aggr_buf = tx_aggr_buf;    //tx_priv->aggr_buf = dev_alloc_skb(MAX_AGGR_TXPKT_LEN);
+    if(!tx_priv->aggr_buf) {
+        txrx_err("Alloc bus->txdata_buf failed!\n");
+        kfree(tx_priv);
+        return NULL;
+    }
+    tx_priv->head = tx_priv->aggr_buf->data;
+    tx_priv->tail = tx_priv->aggr_buf->data;
+
+    return tx_priv;
+}
+
+void aicwf_tx_deinit(struct aicwf_tx_priv* tx_priv)
+{
+    if (tx_priv && tx_priv->aggr_buf)
+        tx_priv->aggr_buf = NULL;   //dev_kfree_skb(tx_priv->aggr_buf);
+
+    kfree(tx_priv);
+    tx_priv = NULL;
+}
+
+#ifdef AICWF_SDIO_SUPPORT
+
+#ifdef LESS_SKB
+static bool aicwf_another_ptk_1(struct rx_buff *buffer)
+{
+    u8 *read = buffer->read;
+    u16 aggr_len = 0;
+
+    BUG_ON((read - buffer->start)%4 != 0);
+
+    if(read == NULL || read >= buffer->end) {
+        return false;
+    }
+
+    aggr_len = (*read | (*(read + 1) << 8));
+    if(aggr_len == 0) {
+        return false;
+    }
+
+    return true;
+}
+#else
+static bool aicwf_another_ptk(struct sk_buff *skb)
+{
+    u8 *data;
+    u16 aggr_len = 0;
+
+    if(skb->data == NULL || skb->len == 0) {
+        return false;
+    }
+    data = skb->data;
+    aggr_len = (*skb->data | (*(skb->data + 1) << 8));
+    if(aggr_len == 0) {
+        return false;
+    }
+    //printk("another\n");
+    return true;
+}
+#endif
+
+
+#endif
+
+int aicwf_process_rxframes(struct aicwf_rx_priv *rx_priv)
+{
+#ifdef AICWF_SDIO_SUPPORT
+    int ret = 0;
+    unsigned long flags = 0;
+    struct sk_buff *skb = NULL;
+    u16 pkt_len = 0;
+    struct sk_buff *skb_inblock = NULL;
+    u16 aggr_len = 0, adjust_len = 0;
+    u8 *data = NULL;
+	u8_l *msg = NULL;
+    struct rx_buff *buffer = NULL;
+
+#ifdef LESS_SKB
+    while (1) {
+        spin_lock_irqsave(&rx_priv->rxqlock, flags);
+        if( !rx_priv->rxq.qcnt) {
+            spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
+            break;
+        }
+        buffer = rxbuff_dequeue(&rx_priv->rxq);
+        spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
+        if (buffer == NULL) {
+            txrx_err("skb_error\r\n");
+            break;
+        }
+
+        while(aicwf_another_ptk_1(buffer)) {
+            data = buffer->read;
+            pkt_len = (*data | (*(data + 1) << 8));
+
+            if((data[2] & SDIO_TYPE_CFG) != SDIO_TYPE_CFG) { // type : data
+                aggr_len = pkt_len + RX_HWHRD_LEN;
+
+                if (aggr_len & (RX_ALIGNMENT - 1))
+                    adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+                else
+                    adjust_len = aggr_len;
+
+                skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_KERNEL);
+                if(skb_inblock == NULL){
+                    txrx_err("no more space! skip\n");
+                    buffer->read = buffer->read + adjust_len;
+                    continue;
+                }
+
+                skb_put(skb_inblock, aggr_len);
+                memcpy(skb_inblock->data, data, aggr_len);
+                rwnx_rxdataind_aicwf(rx_priv->sdiodev->rwnx_hw, skb_inblock, (void *)rx_priv);
+                buffer->read = buffer->read + adjust_len;
+            }
+            else { //  type : config
+                aggr_len = pkt_len;
+
+                if (aggr_len & (RX_ALIGNMENT - 1))
+                    adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+                else
+                    adjust_len = aggr_len;
+
+                msg = kmalloc(aggr_len+4, GFP_KERNEL);
+				if(msg == NULL){
+                   txrx_err("no more space for msg!\n");
+#if 0
+                   rxbuff_free(buffer);
+#else
+                   aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
+#endif
+                   return -EBADE;
+                }
+                memcpy(msg, data, aggr_len + 4);
+				if(((*(msg + 2) & 0x7f) == SDIO_TYPE_CFG_CMD_RSP) && (rx_priv->sdiodev->bus_if->state != BUS_DOWN_ST))
+                    rwnx_rx_handle_msg(rx_priv->sdiodev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
+
+                if((*(msg + 2) & 0x7f) == SDIO_TYPE_CFG_DATA_CFM)
+                    aicwf_sdio_host_tx_cfm_handler(&(rx_priv->sdiodev->rwnx_hw->sdio_env), (u32 *)(msg + 4));
+                buffer->read = buffer->read + (adjust_len + 4);
+				kfree(msg);
+            }
+        }
+
+#if 0
+        rxbuff_free(buffer);
+#else
+        aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
+#endif
+
+        atomic_dec(&rx_priv->rx_cnt);
+    }
+#else
+    while (1) {
+        spin_lock_irqsave(&rx_priv->rxqlock, flags);
+        if(aicwf_is_framequeue_empty(&rx_priv->rxq)) {
+            spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
+            break;
+        }
+#if 1
+        skb = aicwf_frame_dequeue(&rx_priv->rxq);
+#else
+        skb = skb_dequeue(&rx_priv->rxq.queuelist[0]);
+#endif
+        spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
+        if (skb == NULL) {
+            txrx_err("skb_error\r\n");
+            break;
+        }
+
+	#if 1
+        pkt_len = (*skb->data | (*(skb->data + 1) << 8));
+        
+        if((skb->data[2] & SDIO_TYPE_CFG) != SDIO_TYPE_CFG) { // type : data
+            //printk("data:%x, %x\n", skb->data[0], skb->data[1]);
+            aggr_len = pkt_len + RX_HWHRD_LEN;
+            skb_trim(skb, aggr_len);
+            rwnx_rxdataind_aicwf(rx_priv->sdiodev->rwnx_hw, skb, (void *)rx_priv);
+        }else{ //  type : config
+            //printk("config:%p, %p\n", skb, skb->data);
+            if(((*(skb->data + 2) & 0x7f) == SDIO_TYPE_CFG_CMD_RSP) && (rx_priv->sdiodev->bus_if->state != BUS_DOWN_ST))
+                    rwnx_rx_handle_msg(rx_priv->sdiodev->rwnx_hw, (struct ipc_e2a_msg *)(skb->data + 4));
+
+            if((*(skb->data + 2) & 0x7f) == SDIO_TYPE_CFG_DATA_CFM)
+                    aicwf_sdio_host_tx_cfm_handler(&(rx_priv->sdiodev->rwnx_hw->sdio_env), (u32 *)(skb->data + 4));
+            
+            dev_kfree_skb(skb);//jingsu
+        }
+
+        #else
+        while(aicwf_another_ptk(skb)) {
+            data = skb->data;
+            pkt_len = (*skb->data | (*(skb->data + 1) << 8));
+
+            if((skb->data[2] & SDIO_TYPE_CFG) != SDIO_TYPE_CFG) { // type : data
+                aggr_len = pkt_len + RX_HWHRD_LEN;
+
+                if (aggr_len & (RX_ALIGNMENT - 1))
+                    adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+                else
+                    adjust_len = aggr_len;
+
+                skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_KERNEL);
+                if(skb_inblock == NULL){
+                    txrx_err("no more space! skip\n");
+                    skb_pull(skb, adjust_len);
+                    continue;
+                }
+
+                skb_put(skb_inblock, aggr_len);
+                memcpy(skb_inblock->data, data, aggr_len);
+                rwnx_rxdataind_aicwf(rx_priv->sdiodev->rwnx_hw, skb_inblock, (void *)rx_priv);
+                skb_pull(skb, adjust_len);
+            }
+            else { //  type : config
+                aggr_len = pkt_len;
+
+                if (aggr_len & (RX_ALIGNMENT - 1))
+                    adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+                else
+                    adjust_len = aggr_len;
+
+                msg = kmalloc(aggr_len+4, GFP_KERNEL);
+                if(msg == NULL){
+                    txrx_err("no more space for msg!\n");
+                    aicwf_dev_skb_free(skb);
+                    return -EBADE;
+                }
+
+                memcpy(msg, data, aggr_len + 4);
+                if((*(msg + 2) & 0x7f) == SDIO_TYPE_CFG_CMD_RSP)
+                    rwnx_rx_handle_msg(rx_priv->sdiodev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
+
+                if((*(msg + 2) & 0x7f) == SDIO_TYPE_CFG_DATA_CFM)
+                    aicwf_sdio_host_tx_cfm_handler(&(rx_priv->sdiodev->rwnx_hw->sdio_env), (u32 *)(msg + 4));
+                skb_pull(skb, adjust_len+4);
+                kfree(msg);
+            }
+        }
+
+        dev_kfree_skb(skb);
+
+        #endif
+		//printk("sdfree:%d\n", atomic_read(&skb_used));
+        atomic_dec(&rx_priv->rx_cnt);
+    }
+
+    #if defined(CONFIG_SDIO_PWRCTRL)
+    aicwf_sdio_pwr_stctl(rx_priv->sdiodev, SDIO_ACTIVE_ST);
+    #endif
+#endif//LESS_SKB
+
+    return ret;
+#else //AICWF_USB_SUPPORT
+    int ret = 0;
+    unsigned long flags = 0;
+    struct sk_buff *skb = NULL; /* Packet for event or data frames */
+    u16 pkt_len = 0;
+    struct sk_buff *skb_inblock = NULL;
+    u16 aggr_len = 0, adjust_len = 0;
+    u8 *data = NULL;
+    u8_l *msg = NULL;
+
+    while (1) {
+        spin_lock_irqsave(&rx_priv->rxqlock, flags);
+        if(aicwf_is_framequeue_empty(&rx_priv->rxq)) {
+            usb_info("no more rxdata\n");
+            spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
+            break;
+        }
+        skb = aicwf_frame_dequeue(&rx_priv->rxq);
+        spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+        if (skb == NULL) {
+            txrx_err("skb_error\r\n");
+            break;
+        }
+        data = skb->data;
+        pkt_len = (*skb->data | (*(skb->data + 1) << 8));
+        //printk("p:%d, s:%d , %x\n", pkt_len, skb->len, data[2]);
+#if 0 //amsdu > 1600
+	if (pkt_len > 1600) {
+            dev_kfree_skb(skb);
+            atomic_dec(&rx_priv->rx_cnt);
+                continue;
+        }
+#endif
+
+        if((skb->data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
+            aggr_len = pkt_len + RX_HWHRD_LEN;
+            if (aggr_len & (RX_ALIGNMENT - 1))
+                adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+            else
+                adjust_len = aggr_len;
+
+            skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_KERNEL);//8 is for ccmp mic or wep icv
+            if(skb_inblock == NULL){
+                txrx_err("no more space! skip!\n");
+                skb_pull(skb, adjust_len);
+                continue;
+            }
+
+            skb_put(skb_inblock, aggr_len);
+            memcpy(skb_inblock->data, data, aggr_len);
+            rwnx_rxdataind_aicwf(rx_priv->usbdev->rwnx_hw, skb_inblock, (void *)rx_priv);
+            ///TODO: here need to add rx data process
+
+            skb_pull(skb, adjust_len);
+        }
+        else { //  type : config
+            aggr_len = pkt_len;
+            if (aggr_len & (RX_ALIGNMENT - 1))
+                adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+            else
+                adjust_len = aggr_len;
+
+            msg = kmalloc(aggr_len+4, GFP_KERNEL);
+            if(msg == NULL){
+            txrx_err("no more space for msg!\n");
+                aicwf_dev_skb_free(skb);
+                return -EBADE;
+            }
+            memcpy(msg, data, aggr_len + 4);
+            if(((*(msg + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP) && (rx_priv->usbdev->bus_if->state != USB_DOWN_ST))
+                rwnx_rx_handle_msg(rx_priv->usbdev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
+
+            if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_DATA_CFM)
+                aicwf_usb_host_tx_cfm_handler(&(rx_priv->usbdev->rwnx_hw->usb_env), (u32 *)(msg + 4));
+            skb_pull(skb, adjust_len+4);
+            kfree(msg);
+        }
+
+        dev_kfree_skb(skb);
+        atomic_dec(&rx_priv->rx_cnt);
+    }
+
+    return ret;
+#endif //AICWF_SDIO_SUPPORT
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+int aicwf_process_msg_rxframes(struct aicwf_rx_priv *rx_priv)
+{
+    int ret = 0;
+    unsigned long flags = 0;
+    struct sk_buff *skb = NULL; /* Packet for event or data frames */
+    u16 pkt_len = 0;
+    struct sk_buff *skb_inblock = NULL;
+    u16 aggr_len = 0, adjust_len = 0;
+    u8 *data = NULL;
+    u8_l *msg = NULL;
+
+    while (1) {
+        spin_lock_irqsave(&rx_priv->msg_rxqlock, flags);
+        if(aicwf_is_framequeue_empty(&rx_priv->msg_rxq)) {
+            usb_info("no more rxmsg\n");
+            spin_unlock_irqrestore(&rx_priv->msg_rxqlock,flags);
+            break;
+        }
+        skb = aicwf_frame_dequeue(&rx_priv->msg_rxq);
+        spin_unlock_irqrestore(&rx_priv->msg_rxqlock, flags);
+        if (skb == NULL) {
+            txrx_err("skb_error\r\n");
+            break;
+        }
+        data = skb->data;
+        pkt_len = (*skb->data | (*(skb->data + 1) << 8));
+        //printk("p:%d, s:%d , %x\n", pkt_len, skb->len, data[2]);
+#if 0 //amsdu > 1600
+	if (pkt_len > 1600) {
+            dev_kfree_skb(skb);
+            atomic_dec(&rx_priv->rx_cnt);
+                continue;
+        }
+#endif
+
+        if((skb->data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
+            aggr_len = pkt_len + RX_HWHRD_LEN;
+            if (aggr_len & (RX_ALIGNMENT - 1))
+                adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+            else
+                adjust_len = aggr_len;
+
+            skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_KERNEL);//8 is for ccmp mic or wep icv
+            if(skb_inblock == NULL){
+                txrx_err("no more space! skip!\n");
+                skb_pull(skb, adjust_len);
+                continue;
+            }
+
+            skb_put(skb_inblock, aggr_len);
+            memcpy(skb_inblock->data, data, aggr_len);
+            rwnx_rxdataind_aicwf(rx_priv->usbdev->rwnx_hw, skb_inblock, (void *)rx_priv);
+            ///TODO: here need to add rx data process
+
+            skb_pull(skb, adjust_len);
+        }
+        else { //  type : config
+            aggr_len = pkt_len;
+            if (aggr_len & (RX_ALIGNMENT - 1))
+                adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+            else
+                adjust_len = aggr_len;
+
+            msg = kmalloc(aggr_len+4, GFP_KERNEL);
+            if(msg == NULL){
+            txrx_err("no more space for msg!\n");
+                aicwf_dev_skb_free(skb);
+                return -EBADE;
+            }
+            memcpy(msg, data, aggr_len + 4);
+            if(((*(msg + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP) && (rx_priv->usbdev->bus_if->state != USB_DOWN_ST))
+                rwnx_rx_handle_msg(rx_priv->usbdev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
+
+            if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_DATA_CFM)
+                aicwf_usb_host_tx_cfm_handler(&(rx_priv->usbdev->rwnx_hw->usb_env), (u32 *)(msg + 4));
+            skb_pull(skb, adjust_len+4);
+            kfree(msg);
+        }
+
+        dev_kfree_skb(skb);
+        atomic_dec(&rx_priv->msg_rx_cnt);
+    }
+
+    return ret;
+}
+#endif
+
+static struct recv_msdu *aicwf_rxframe_queue_init(struct list_head *q, int qsize)
+{
+    int i;
+    struct recv_msdu *req, *reqs;
+
+    reqs = vmalloc(qsize*sizeof(struct recv_msdu));
+    if (reqs == NULL)
+        return NULL;
+
+    req = reqs;
+    for (i = 0; i < qsize; i++) {
+        INIT_LIST_HEAD(&req->rxframe_list);
+        list_add(&req->rxframe_list, q);
+        req->len = 0;
+        req++;
+    }
+
+    return reqs;
+}
+
+struct aicwf_rx_priv *aicwf_rx_init(void *arg)
+{
+    struct aicwf_rx_priv* rx_priv;
+    rx_priv = kzalloc(sizeof(struct aicwf_rx_priv), GFP_KERNEL);
+    if (!rx_priv)
+        return NULL;
+
+#ifdef AICWF_SDIO_SUPPORT
+    rx_priv->sdiodev = (struct aic_sdio_dev *)arg;
+    #ifdef LESS_SKB
+    aicwf_rxframe_queue_init_2(&rx_priv->rxq, MAX_RXQLEN);
+    #else
+    aicwf_frame_queue_init(&rx_priv->rxq, 1, MAX_RXQLEN);
+    #endif
+#else
+    rx_priv->usbdev = (struct aic_usb_dev *)arg;
+    aicwf_frame_queue_init(&rx_priv->rxq, 1, MAX_RXQLEN);
+#endif
+
+    spin_lock_init(&rx_priv->rxqlock);
+
+#ifdef LESS_SKB
+    spin_lock_init(&rx_priv->rxbuff_lock);
+#endif
+    atomic_set(&rx_priv->rx_cnt, 0);
+#ifdef CONFIG_USB_MSG_IN_EP
+    aicwf_frame_queue_init(&rx_priv->msg_rxq, 1, MAX_RXQLEN);
+    spin_lock_init(&rx_priv->msg_rxqlock);
+    atomic_set(&rx_priv->msg_rx_cnt, 0);
+#endif
+
+#ifdef AICWF_RX_REORDER
+    INIT_LIST_HEAD(&rx_priv->rxframes_freequeue);
+    spin_lock_init(&rx_priv->freeq_lock);
+    rx_priv->recv_frames = aicwf_rxframe_queue_init(&rx_priv->rxframes_freequeue, MAX_REORD_RXFRAME);
+    if (!rx_priv->recv_frames) {
+        txrx_err("no enough buffer for free recv frame queue!\n");
+        kfree(rx_priv);
+        return NULL;
+    }
+    spin_lock_init(&rx_priv->stas_reord_lock);
+    INIT_LIST_HEAD(&rx_priv->stas_reord_list);
+#endif
+
+    return rx_priv;
+}
+
+
+static void aicwf_recvframe_queue_deinit(struct list_head *q)
+{
+    struct recv_msdu *req, *next;
+
+    list_for_each_entry_safe(req, next, q, rxframe_list) {
+        list_del_init(&req->rxframe_list);
+    }
+}
+
+void aicwf_rx_deinit(struct aicwf_rx_priv* rx_priv)
+{
+#ifdef AICWF_RX_REORDER
+    struct reord_ctrl_info *reord_info, *tmp;
+
+    txrx_dbg("%s\n", __func__);
+
+    spin_lock_bh(&rx_priv->stas_reord_lock);
+    list_for_each_entry_safe(reord_info, tmp,
+        &rx_priv->stas_reord_list, list) {
+        reord_deinit_sta(rx_priv, reord_info);
+    }
+    spin_unlock_bh(&rx_priv->stas_reord_lock);
+#endif
+
+    txrx_dbg("stio rx thread\n");
+#ifdef AICWF_SDIO_SUPPORT
+#ifdef HANDLE_RX_THREAD_ZTE
+    if (rx_priv->sdiodev->bus_if->busrx_thread) {
+        complete_all(&rx_priv->sdiodev->bus_if->busrx_trgg);
+        kthread_stop(rx_priv->sdiodev->bus_if->busrx_thread);
+        rx_priv->sdiodev->bus_if->busrx_thread = NULL;
+    }
+#else
+    tasklet_kill(&rx_priv->sdiodev->rx_priv->rx_tasklet);
+#endif
+#endif
+#ifdef AICWF_USB_SUPPORT
+    if (rx_priv->usbdev->bus_if->busrx_thread) {
+        complete(&rx_priv->usbdev->bus_if->busrx_trgg);
+        kthread_stop(rx_priv->usbdev->bus_if->busrx_thread);
+        rx_priv->usbdev->bus_if->busrx_thread = NULL;
+    }
+#ifdef CONFIG_USB_MSG_IN_EP
+    if (rx_priv->usbdev->bus_if->msg_busrx_thread) {
+        complete(&rx_priv->usbdev->bus_if->msg_busrx_trgg);
+        kthread_stop(rx_priv->usbdev->bus_if->msg_busrx_thread);
+        rx_priv->usbdev->bus_if->msg_busrx_thread = NULL;
+    }
+#endif
+#endif
+
+    #ifdef LESS_SKB
+    rxbuff_queue_flush(&rx_priv->rxq);
+    #else
+ 
+
+    aicwf_frame_queue_flush(&rx_priv->rxq);
+	#endif
+#ifdef CONFIG_USB_MSG_IN_EP
+    aicwf_frame_queue_flush(&rx_priv->msg_rxq);
+#endif
+#ifdef AICWF_RX_REORDER
+    aicwf_recvframe_queue_deinit(&rx_priv->rxframes_freequeue);
+    if (rx_priv->recv_frames)
+        vfree(rx_priv->recv_frames);
+#endif
+
+    kfree(rx_priv);
+    rx_priv = NULL;
+
+    txrx_dbg("exit %s\n", __func__);
+}
+
+#ifdef LESS_SKB
+void rxbuff_free(struct rx_buff *rxbuff)
+{
+   kfree(rxbuff->data);
+   kfree(rxbuff);
+}
+
+struct rx_buff *rxbuff_queue_penq(struct rx_frame_queue *pq, struct rx_buff *p)
+{
+
+    struct list_head *q;
+    if (pq->qcnt >= pq->qmax)
+        return NULL;
+
+    q = &pq->queuelist;
+    list_add_tail(&p->queue,q);
+    pq->qcnt++;
+
+    return p;
+}
+
+struct rx_buff *rxbuff_dequeue(struct rx_frame_queue *pq)
+{
+    struct rx_buff *p = NULL;
+
+    if (pq->qcnt == 0) {
+        printk("%s %d, rxq is empty\n", __func__, __LINE__);
+        return NULL;
+    }
+
+    if(list_empty(&pq->queuelist)) {
+        printk("%s %d, rxq is empty\n", __func__, __LINE__);
+        return NULL;
+    } else {
+        p = list_first_entry(&pq->queuelist, struct rx_buff, queue);
+        list_del_init(&p->queue);
+        pq->qcnt--;
+    }
+
+    return p;
+}
+
+bool aicwf_rxbuff_enqueue(struct device *dev, struct rx_frame_queue *rxq, struct rx_buff *pkt)
+{
+    struct rx_buff *p = NULL;
+
+    if ((rxq == NULL) || (pkt == NULL)) {
+        printk("%s %d, rxq or pkt is NULL\n", __func__, __LINE__);
+        return false;
+    }
+
+    if (rxq->qcnt < rxq->qmax) {
+        if (rxbuff_queue_penq(rxq, pkt)) {
+            return true;
+        } else {
+            printk("%s %d, rxbuff enqueue fail\n", __func__, __LINE__);
+            return false;
+        }
+    } else {
+        printk("%s %d, rxq or pkt is full\n", __func__, __LINE__);
+        return false;
+    }
+}
+#endif
+
+bool aicwf_rxframe_enqueue(struct device *dev, struct frame_queue *q, struct sk_buff *pkt)
+{
+    return aicwf_frame_enq(dev, q, pkt, 0);
+}
+
+
+void aicwf_dev_skb_free(struct sk_buff *skb)
+{
+    if (!skb)
+        return;
+
+    dev_kfree_skb_any(skb);
+}
+
+static struct sk_buff *aicwf_frame_queue_penq(struct frame_queue *pq, int prio, struct sk_buff *p)
+{
+    struct sk_buff_head *q;
+
+    if (pq->queuelist[prio].qlen >= pq->qmax)
+        return NULL;
+
+    q = &pq->queuelist[prio];
+    __skb_queue_tail(q, p);
+    pq->qcnt++;
+    if (pq->hi_prio < prio)
+        pq->hi_prio = (u16)prio;
+
+    return p;
+}
+
+static struct sk_buff *aicwf_txframe_queue_penq(struct frame_queue *pq, int prio, struct sk_buff *p)
+{
+    struct sk_buff_head *q;
+    struct rwnx_txhdr *txhdr;
+    u8_l sta_idx = 0;
+
+    if (pq->queuelist[prio].qlen >= pq->qmax)
+        return NULL;
+
+	txhdr = (struct rwnx_txhdr *)p->data;
+	if(txhdr->sw_hdr.rwnx_sta)
+		sta_idx = txhdr->sw_hdr.rwnx_sta->sta_idx;
+
+	if(prio == 8 || sta_idx >= NX_REMOTE_STA_MAX) {
+	    q = &pq->queuelist[prio];
+	    __skb_queue_tail(q, p);
+	    pq->qcnt++;
+	    if (pq->hi_prio < prio)
+	        pq->hi_prio = (u16)prio;
+
+	   //printk("e:%p, %d, %d, pq:%d,%d\n", p, prio, sta_idx, pq->qcnt,pq->queuelist[prio].qlen);
+	} else {
+		q = &pq->queuelist[prio];
+
+		pq->qcnt++;
+		if (pq->hi_prio < prio)
+			pq->hi_prio = (u16)prio;
+
+		if(sta_txframeq_stat[sta_idx][prio].stat_cnt == 0)
+		    __skb_queue_tail(q, p);
+		else {
+	            __skb_queue_after(q, sta_txframeq_stat[sta_idx][prio].skb, p);
+		    //printk("qaft:%p,%p\n", p, sta_txframeq_stat[sta_idx][prio].skb);
+		}
+		sta_txframeq_stat[sta_idx][prio].stat_cnt++;
+		if(sta_txframeq_stat[sta_idx][prio].stat_cnt == 4) {
+			sta_txframeq_stat[sta_idx][prio].stat_cnt = 0;
+		}
+		sta_txframeq_stat[sta_idx][prio].skb = p;
+	
+		//printk("en:%d,%d,%d,%p, pq:%d,%d\n", sta_idx, prio, 
+		//		sta_txframeq_stat[sta_idx][prio].stat_cnt, p, pq->qcnt,pq->queuelist[prio].qlen);
+	}
+	
+    return p;
+}
+
+void aicwf_frame_queue_flush(struct frame_queue *pq)
+{
+    int prio;
+    struct sk_buff_head *q;
+    struct sk_buff *p, *next;
+
+    printk("%s enter\n", __func__);
+
+    for (prio = 0; prio < pq->num_prio; prio++)
+    {
+        q = &pq->queuelist[prio];
+        skb_queue_walk_safe(q, p, next) {
+            skb_unlink(p, q);
+            aicwf_dev_skb_free(p);
+            pq->qcnt--;
+        }
+    }
+}
+
+void aicwf_txframe_queue_flush(struct frame_queue *pq)
+{
+    int prio;
+    struct sk_buff_head *q;
+    struct sk_buff *p, *next;
+    u8 i,j;
+
+    printk("%s enter\n", __func__);
+
+    for (prio = 0; prio < pq->num_prio; prio++)
+    {
+        q = &pq->queuelist[prio];
+        skb_queue_walk_safe(q, p, next) {
+            skb_unlink(p, q);
+            aicwf_dev_skb_free(p);
+            pq->qcnt--;
+        }
+    }
+
+    for(i=0; i<NX_REMOTE_STA_MAX; i++) {
+		for(j=0; j<8; j++) {
+			sta_txframeq_stat[i][j].skb = NULL;
+			sta_txframeq_stat[i][j].stat_cnt = 0;
+		}
+    }
+}
+
+void aicwf_frame_queue_init(struct frame_queue *pq, int num_prio, int max_len)
+{
+    int prio;
+
+    memset(pq, 0, offsetof(struct frame_queue, queuelist) + (sizeof(struct sk_buff_head) * num_prio));
+    pq->num_prio = (u16)num_prio;
+    pq->qmax = (u16)max_len;
+
+    for (prio = 0; prio < num_prio; prio++) {
+        skb_queue_head_init(&pq->queuelist[prio]);
+    }
+}
+
+struct sk_buff *aicwf_frame_queue_peek_tail(struct frame_queue *pq, int *prio_out)
+{
+    int prio;
+
+    if (pq->qcnt == 0)
+        return NULL;
+
+    for (prio = 0; prio < pq->hi_prio; prio++)
+        if (!skb_queue_empty(&pq->queuelist[prio]))
+            break;
+
+    if (prio_out)
+        *prio_out = prio;
+
+    return skb_peek_tail(&pq->queuelist[prio]);
+}
+
+bool aicwf_is_framequeue_empty(struct frame_queue *pq)
+{
+    int prio, len = 0;
+
+    for (prio = 0; prio <= pq->hi_prio; prio++)
+        len += pq->queuelist[prio].qlen;
+
+    if(len > 0)
+        return false;
+    else
+        return true;
+}
+
+struct sk_buff *aicwf_frame_dequeue(struct frame_queue *pq)
+{
+    struct sk_buff_head *q;
+    struct sk_buff *p;
+    int prio;
+
+    if (pq->qcnt == 0)
+        return NULL;
+
+    while ((prio = pq->hi_prio) > 0 && skb_queue_empty(&pq->queuelist[prio]))
+        pq->hi_prio--;
+
+    q = &pq->queuelist[prio];
+    p = __skb_dequeue(q);
+    if (p == NULL)
+        return NULL;
+
+    pq->qcnt--;
+
+    return p;
+}
+
+struct sk_buff *aicwf_txframe_dequeue(struct frame_queue *pq)
+{
+    struct sk_buff_head *q;
+    struct sk_buff *p;
+    int prio;
+    struct rwnx_txhdr *txhdr;
+    u8_l sta_idx = 0;
+
+    if (pq->qcnt == 0) {
+	   //printk("txframe: %d, %d\n", pq->qcnt, aicwf_is_framequeue_empty(pq));
+	   return NULL;
+    }
+
+    while ((prio = pq->hi_prio) > 0 && skb_queue_empty(&pq->queuelist[prio]))
+        pq->hi_prio--;
+
+    q = &pq->queuelist[prio];
+    p = __skb_dequeue(q);
+    if (p == NULL)
+        return NULL;
+	else {
+		txhdr = (struct rwnx_txhdr *)p->data;
+		if(txhdr->sw_hdr.rwnx_sta)
+			sta_idx = txhdr->sw_hdr.rwnx_sta->sta_idx;
+		if(prio < 8 && sta_idx < NX_REMOTE_STA_MAX) {
+			//printk("de: %d,%d,%p,%d\n", sta_idx, sta_txframeq_stat[sta_idx][prio].stat_cnt, p,pq->queuelist[prio].qlen);
+			if(p == sta_txframeq_stat[sta_idx][prio].skb) {
+				sta_txframeq_stat[sta_idx][prio].skb = NULL;
+				sta_txframeq_stat[sta_idx][prio].stat_cnt = 0;
+			}
+		}
+	}
+    pq->qcnt--;
+    //printk("d: %p, %d, %d, pq:%d,%d\n", p, prio, sta_idx, pq->qcnt,pq->queuelist[prio].qlen);
+
+    return p;
+}
+
+static struct sk_buff *aicwf_skb_dequeue_tail(struct frame_queue *pq, int prio)
+{
+    struct sk_buff_head *q = &pq->queuelist[prio];
+    struct sk_buff *p = skb_dequeue_tail(q);
+
+    if (!p)
+        return NULL;
+
+    pq->qcnt--;
+    return p;
+}
+
+bool aicwf_frame_enq(struct device *dev, struct frame_queue *q, struct sk_buff *pkt, int prio)
+{
+	if (q->queuelist[prio].qlen < q->qmax && q->qcnt < q->qmax) {
+        aicwf_frame_queue_penq(q, prio, pkt);
+        return true;
+    } else
+		return false;
+}
+
+bool aicwf_txframe_enq(struct device *dev, struct frame_queue *q, struct sk_buff *pkt, int prio)
+{
+	if (q->queuelist[prio].qlen < q->qmax && q->qcnt < q->qmax) {
+        	aicwf_txframe_queue_penq(q, prio, pkt);
+        	return true;
+    	} else
+		return false;
+}
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_txrxif.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_txrxif.h
new file mode 100755
index 0000000..3b00e37
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_txrxif.h
@@ -0,0 +1,314 @@
+/**
+ * aicwf_txrxif.h
+ *
+ * bus function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+#ifndef _AICWF_TXRXIF_H_
+#define _AICWF_TXRXIF_H_
+
+#include <linux/skbuff.h>
+#include <linux/sched.h>
+#include "ipc_shared.h"
+#ifdef AICWF_SDIO_SUPPORT
+#include "aicwf_sdio.h"
+#else
+#include "aicwf_usb.h"
+#endif
+
+#define CMD_BUF_MAX                 1536
+#define TXPKT_BLOCKSIZE             512
+#define MAX_AGGR_TXPKT_LEN          (1536*32)//(1536*64)
+#define CMD_TX_TIMEOUT              5000
+#define TX_ALIGNMENT                4
+#define AICWF_TXQ_CNT				9
+
+#ifdef CONFIG_USB_TX_AGGR
+#define MAX_USB_AGGR_TXPKT_LEN          (1536*15)
+#endif
+
+#define RX_HWHRD_LEN                60 //58->60 word allined
+#define CCMP_OR_WEP_INFO            8
+#define MAX_RXQLEN                  2000
+#define RX_ALIGNMENT                4
+
+#define DEBUG_ERROR_LEVEL           0
+#define DEBUG_DEBUG_LEVEL           1
+#define DEBUG_INFO_LEVEL            2
+
+extern int dbg_level;
+#define txrx_err(fmt, ...)          aicwf_dbg(DEBUG_ERROR_LEVEL, "<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__) //pr_err("txrx_err:<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define sdio_err(fmt, ...)          aicwf_dbg(DEBUG_ERROR_LEVEL, "<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__) //pr_err("sdio_err:<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define usb_err(fmt, ...)           aicwf_dbg(DEBUG_ERROR_LEVEL, "<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__) //pr_err("usb_err:<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__)
+
+#define txrx_dbg(fmt, ...)          aicwf_dbg(DEBUG_DEBUG_LEVEL, "<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__) //printk("txrx: " fmt, ##__VA_ARGS__)
+#define sdio_dbg(fmt, ...)          aicwf_dbg(DEBUG_DEBUG_LEVEL, "<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__) //printk("aicsdio: " fmt, ##__VA_ARGS__)
+#define usb_dbg(fmt, ...)           aicwf_dbg(DEBUG_DEBUG_LEVEL, "<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__) //printk("aicusb: " fmt, ##__VA_ARGS__)
+
+#define txrx_info(fmt, ...)         aicwf_dbg(DEBUG_INFO_LEVEL, "<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__) //printk("aicsdio: " fmt, ##__VA_ARGS__)
+#define sdio_info(fmt, ...)         aicwf_dbg(DEBUG_INFO_LEVEL, "<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__) //printk("aicsdio: " fmt, ##__VA_ARGS__)
+#define usb_info(fmt, ...)          aicwf_dbg(DEBUG_INFO_LEVEL, "<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__) //printk("aicusb: " fmt, ##__VA_ARGS__)
+
+#define aicwf_dbg(level, format, ...) \
+	do { \
+		if(level <= dbg_level) { \
+			switch(level) { \
+				case DEBUG_ERROR_LEVEL: \
+					printk("E: " format, ##__VA_ARGS__); \
+					break; \
+				case DEBUG_DEBUG_LEVEL: \
+					printk("D: " format, ##__VA_ARGS__); \
+					break; \
+				case DEBUG_INFO_LEVEL: \
+					printk("I: " format, ##__VA_ARGS__); \
+                                        break; \
+			} \
+		} \
+	} while(0) \
+
+
+enum aicwf_bus_state {
+    BUS_DOWN_ST,
+    BUS_UP_ST
+};
+
+struct txframeq_stat
+{
+    struct sk_buff *skb;
+    uint16_t stat_cnt;
+};
+
+struct aicwf_bus_ops {
+    int (*start) (struct device * dev);
+    void (*stop) (struct device * dev);
+    int (*txdata) (struct device * dev, struct sk_buff * skb);
+    int (*txmsg) (struct device * dev, u8 * msg, uint len);
+};
+
+struct frame_queue {
+    u16              num_prio;
+    u16              hi_prio;
+    u16              qmax;      /* max number of queued frames */
+    u16              qcnt;
+    struct sk_buff_head queuelist[AICWF_TXQ_CNT];
+};
+
+#ifdef LESS_SKB
+struct rx_frame_queue {
+    u16              qmax;      /* max number of queued frames */
+    u16              qcnt;
+    struct list_head queuelist;
+};
+#endif
+
+struct aicwf_bus {
+    union {
+        struct aic_sdio_dev *sdio;
+        struct aic_usb_dev *usb;
+    } bus_priv;
+    struct device *dev;
+    struct aicwf_bus_ops *ops;
+    enum aicwf_bus_state state;
+    u8 *cmd_buf;
+    struct completion bustx_trgg;
+    struct completion busrx_trgg;
+#ifdef CONFIG_USB_MSG_IN_EP
+    struct completion msg_busrx_trgg;
+#endif
+#ifdef HANDLE_TX_THREAD_ZTE
+    struct task_struct *bustx_thread;
+#endif
+#ifdef HANDLE_RX_THREAD_ZTE
+    struct task_struct *busrx_thread;
+#endif
+#ifdef CONFIG_USB_MSG_IN_EP
+    struct task_struct *msg_busrx_thread;
+#endif
+};
+
+struct aicwf_tx_priv {
+#ifdef AICWF_SDIO_SUPPORT
+    struct aic_sdio_dev *sdiodev;
+    int fw_avail_bufcnt;
+    //for cmd tx
+    u8 *cmd_buf;
+    uint cmd_len;
+    bool cmd_txstate;
+    bool cmd_tx_succ;
+    struct semaphore cmd_txsema;
+    wait_queue_head_t cmd_txdone_wait;
+    //for data tx
+    atomic_t tx_pktcnt;
+
+    struct frame_queue txq;
+    spinlock_t txqlock;
+    struct semaphore txctl_sema;
+#endif
+#ifdef AICWF_USB_SUPPORT
+    struct aic_usb_dev *usbdev;
+#ifdef CONFIG_USB_TX_AGGR
+    int fw_avail_bufcnt;
+
+    //for data tx
+    atomic_t tx_pktcnt;
+
+    struct frame_queue txq;
+    spinlock_t txqlock;
+    spinlock_t txdlock;
+#endif
+#endif
+    struct sk_buff *aggr_buf;
+    atomic_t aggr_count;
+    u8 *head;
+    u8 *tail;
+
+#ifndef HANDLE_TX_THREAD_ZTE
+    struct tasklet_struct tx_tasklet;
+#endif
+};
+
+
+#define DEFRAG_MAX_WAIT         40 //100
+#ifdef AICWF_RX_REORDER
+#define MAX_REORD_RXFRAME       250
+#define REORDER_UPDATE_TIME     50
+#define AICWF_REORDER_WINSIZE   64
+#define SN_LESS(a, b)           (((a-b)&0x800)!=0)
+#define SN_EQUAL(a, b)          (a == b)
+
+struct reord_ctrl {
+    struct aicwf_rx_priv *rx_priv;
+    u8 enable;
+    u16 ind_sn;
+    u8 wsize_b;
+    spinlock_t reord_list_lock;
+    struct list_head reord_list;
+    struct timer_list reord_timer;
+    struct work_struct reord_timer_work;
+};
+
+struct reord_ctrl_info {
+    u8 mac_addr[6];
+    struct reord_ctrl preorder_ctrl[8];
+    struct list_head list;
+};
+
+struct recv_msdu {
+	 struct sk_buff  *pkt;
+	 u8  tid;
+	 u16 seq_num;
+	 u8 forward;
+	 uint len;
+	 u8 *rx_data;
+	 //for pending rx reorder list
+	struct list_head reord_pending_list;
+	//for total frame list, when rxframe from busif, dequeue, when submit frame to net, enqueue
+	struct list_head rxframe_list;
+	struct reord_ctrl *preorder_ctrl;
+};
+#endif
+
+struct aicwf_rx_priv {
+#ifdef AICWF_SDIO_SUPPORT
+    struct aic_sdio_dev *sdiodev;
+
+    #ifdef LESS_SKB
+    struct rx_frame_queue rxq;
+    #else
+    struct frame_queue rxq;
+    #endif
+#endif
+#ifdef AICWF_USB_SUPPORT
+    struct aic_usb_dev *usbdev;
+    struct frame_queue rxq;
+#endif
+
+    void *rwnx_vif;
+    atomic_t rx_cnt;
+    u32 data_len;
+    spinlock_t rxqlock;
+
+#ifdef LESS_SKB
+    spinlock_t rxbuff_lock;
+#endif
+
+#ifndef HANDLE_RX_THREAD_ZTE
+        struct tasklet_struct rx_tasklet;
+#endif
+
+#ifdef CONFIG_USB_MSG_IN_EP
+    atomic_t msg_rx_cnt;
+    spinlock_t msg_rxqlock;
+    struct frame_queue msg_rxq;
+#endif
+
+#ifdef AICWF_RX_REORDER
+    spinlock_t freeq_lock;
+    struct list_head rxframes_freequeue;
+    struct list_head stas_reord_list;
+    spinlock_t stas_reord_lock;
+    struct recv_msdu *recv_frames;
+#endif
+};
+
+static inline int aicwf_bus_start(struct aicwf_bus *bus)
+{
+    return bus->ops->start(bus->dev);
+}
+
+static inline void aicwf_bus_stop(struct aicwf_bus *bus)
+{
+    bus->ops->stop(bus->dev);
+}
+
+static inline int aicwf_bus_txdata(struct aicwf_bus *bus, struct sk_buff *skb)
+{
+    return bus->ops->txdata(bus->dev, skb);
+}
+
+static inline int aicwf_bus_txmsg(struct aicwf_bus *bus, u8 *msg, uint len)
+{
+    return bus->ops->txmsg(bus->dev, msg, len);
+}
+
+static inline void aicwf_sched_timeout(u32 millisec)
+{
+    ulong timeout = 0, expires = 0;
+    expires = jiffies + msecs_to_jiffies(millisec);
+    timeout = millisec;
+
+    while (timeout) {
+        timeout = schedule_timeout(timeout);
+        if (time_after(jiffies, expires))
+            break;
+    }
+}
+#ifdef LESS_SKB
+void rxbuff_queue_flush(struct rx_frame_queue *pq);
+void aicwf_rxframe_queue_init_2(struct rx_frame_queue *pq, int max_len);
+void rxbuff_free(struct rx_buff *rxbuff);
+struct rx_buff *rxbuff_dequeue(struct rx_frame_queue *pq);
+bool aicwf_rxbuff_enqueue(struct device *dev, struct rx_frame_queue *rxq, struct rx_buff *pkt);
+#endif
+int aicwf_bus_init(uint bus_hdrlen, struct device *dev);
+void aicwf_bus_deinit(struct device *dev);
+void aicwf_tx_deinit(struct aicwf_tx_priv* tx_priv);
+void aicwf_rx_deinit(struct aicwf_rx_priv* rx_priv);
+struct aicwf_tx_priv* aicwf_tx_init(void *arg);
+struct aicwf_rx_priv* aicwf_rx_init(void *arg);
+void aicwf_frame_queue_init(struct frame_queue *pq, int num_prio, int max_len);
+void aicwf_frame_queue_flush(struct frame_queue *pq);
+void aicwf_txframe_queue_flush(struct frame_queue *pq);
+bool aicwf_frame_enq(struct device *dev, struct frame_queue *q, struct sk_buff *pkt, int prio);
+bool aicwf_txframe_enq(struct device *dev, struct frame_queue *q, struct sk_buff *pkt, int prio);
+bool aicwf_rxframe_enqueue(struct device *dev, struct frame_queue *q, struct sk_buff *pkt);
+bool aicwf_is_framequeue_empty(struct frame_queue *pq);
+void aicwf_frame_tx(void *dev, struct sk_buff *skb);
+void aicwf_dev_skb_free(struct sk_buff *skb);
+struct sk_buff *aicwf_frame_dequeue(struct frame_queue *pq);
+struct sk_buff *aicwf_txframe_dequeue(struct frame_queue *pq);
+struct sk_buff *aicwf_frame_queue_peek_tail(struct frame_queue *pq, int *prio_out);
+
+#endif /* _AICWF_TXRXIF_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_usb.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_usb.c
new file mode 100755
index 0000000..531ee0f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_usb.c
@@ -0,0 +1,1505 @@
+/**
+ * aicwf_usb.c
+ *
+ * USB function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+#include <linux/usb.h>
+#include <linux/kthread.h>
+#include "aicwf_txrxif.h"
+#include "aicwf_usb.h"
+#include "rwnx_tx.h"
+#include "rwnx_defs.h"
+#include "usb_host.h"
+#include "rwnx_platform.h"
+
+void aicwf_usb_tx_flowctrl(struct rwnx_hw *rwnx_hw, bool state)
+{
+    struct rwnx_vif *rwnx_vif;
+    list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+        if (! rwnx_vif->up)
+            continue;
+        if (state)
+            netif_stop_queue(rwnx_vif->ndev);
+        else
+            netif_wake_queue(rwnx_vif->ndev);
+    }
+}
+
+static struct aicwf_usb_buf *aicwf_usb_tx_dequeue(struct aic_usb_dev *usb_dev,
+    struct list_head *q, int *counter, spinlock_t *qlock)
+{
+    unsigned long flags;
+    struct aicwf_usb_buf *usb_buf;
+
+    spin_lock_irqsave(qlock, flags);
+    if (list_empty(q)) {
+        usb_buf = NULL;
+    } else {
+        usb_buf = list_first_entry(q, struct aicwf_usb_buf, list);
+        list_del_init(&usb_buf->list);
+        if (counter)
+            (*counter)--;
+    }
+    spin_unlock_irqrestore(qlock, flags);
+    return usb_buf;
+}
+
+static void aicwf_usb_tx_queue(struct aic_usb_dev *usb_dev,
+    struct list_head *q, struct aicwf_usb_buf *usb_buf, int *counter,
+    spinlock_t *qlock)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(qlock, flags);
+    list_add_tail(&usb_buf->list, q);
+    (*counter)++;
+    spin_unlock_irqrestore(qlock, flags);
+}
+
+static struct aicwf_usb_buf *aicwf_usb_rx_buf_get(struct aic_usb_dev *usb_dev)
+{
+    unsigned long flags;
+    struct aicwf_usb_buf *usb_buf;
+
+    spin_lock_irqsave(&usb_dev->rx_free_lock, flags);
+    if (list_empty(&usb_dev->rx_free_list)) {
+        usb_buf = NULL;
+    } else {
+        usb_buf = list_first_entry(&usb_dev->rx_free_list, struct aicwf_usb_buf, list);
+        list_del_init(&usb_buf->list);
+    }
+    spin_unlock_irqrestore(&usb_dev->rx_free_lock, flags);
+    return usb_buf;
+}
+
+static void aicwf_usb_rx_buf_put(struct aic_usb_dev *usb_dev, struct aicwf_usb_buf *usb_buf)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&usb_dev->rx_free_lock, flags);
+    list_add_tail(&usb_buf->list, &usb_dev->rx_free_list);
+    spin_unlock_irqrestore(&usb_dev->rx_free_lock, flags);
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+static struct aicwf_usb_buf *aicwf_usb_msg_rx_buf_get(struct aic_usb_dev *usb_dev)
+{
+    unsigned long flags;
+    struct aicwf_usb_buf *usb_buf;
+
+    spin_lock_irqsave(&usb_dev->msg_rx_free_lock, flags);
+    if (list_empty(&usb_dev->msg_rx_free_list)) {
+        usb_buf = NULL;
+    } else {
+        usb_buf = list_first_entry(&usb_dev->msg_rx_free_list, struct aicwf_usb_buf, list);
+        list_del_init(&usb_buf->list);
+    }
+    spin_unlock_irqrestore(&usb_dev->msg_rx_free_lock, flags);
+    return usb_buf;
+}
+
+static void aicwf_usb_msg_rx_buf_put(struct aic_usb_dev *usb_dev, struct aicwf_usb_buf *usb_buf)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&usb_dev->msg_rx_free_lock, flags);
+    list_add_tail(&usb_buf->list, &usb_dev->msg_rx_free_list);
+    spin_unlock_irqrestore(&usb_dev->msg_rx_free_lock, flags);
+}
+#endif
+
+static void aicwf_usb_tx_complete(struct urb *urb)
+{
+    unsigned long flags;
+    struct aicwf_usb_buf *usb_buf = (struct aicwf_usb_buf *) urb->context;
+    struct aic_usb_dev *usb_dev = usb_buf->usbdev;
+    struct sk_buff *skb;
+    u8 *buf;
+
+    #ifndef CONFIG_USB_TX_AGGR
+    if (usb_buf->cfm == false) {
+        skb = usb_buf->skb;
+    } else {
+        buf = (u8 *)usb_buf->skb;
+    }
+
+    if (usb_buf->cfm == false) {
+        dev_kfree_skb_any(skb);
+    } else {
+        kfree(buf);
+    }
+    usb_buf->skb = NULL;
+    #else
+    printk("tx com %d\n", usb_buf->aggr_cnt);
+    usb_buf->aggr_cnt = 0;
+    #endif
+    aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_free_list, usb_buf,
+                    &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+
+    spin_lock_irqsave(&usb_dev->tx_flow_lock, flags);
+    if (usb_dev->tx_free_count > AICWF_USB_TX_HIGH_WATER) {
+        if (usb_dev->tbusy) {
+            usb_dev->tbusy = false;
+            aicwf_usb_tx_flowctrl(usb_dev->rwnx_hw, false);
+        }
+    }
+    spin_unlock_irqrestore(&usb_dev->tx_flow_lock, flags);
+    }
+
+static void aicwf_usb_rx_complete(struct urb *urb)
+{
+    struct aicwf_usb_buf *usb_buf = (struct aicwf_usb_buf *) urb->context;
+    struct aic_usb_dev *usb_dev = usb_buf->usbdev;
+    struct aicwf_rx_priv* rx_priv = usb_dev->rx_priv;
+    struct sk_buff *skb = NULL;
+    unsigned long flags = 0;
+
+    skb = usb_buf->skb;
+    usb_buf->skb = NULL;
+
+    if (urb->actual_length > urb->transfer_buffer_length) {
+        aicwf_dev_skb_free(skb);
+        aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+        schedule_work(&usb_dev->rx_urb_work);
+        return;
+    }
+
+    if (urb->status != 0 || !urb->actual_length) {
+        aicwf_dev_skb_free(skb);
+        aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+        schedule_work(&usb_dev->rx_urb_work);
+        return;
+    }
+
+    if (usb_dev->state == USB_UP_ST) {
+        skb_put(skb, urb->actual_length);
+
+        spin_lock_irqsave(&rx_priv->rxqlock, flags);
+        if(!aicwf_rxframe_enqueue(usb_dev->dev, &rx_priv->rxq, skb)){
+            spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+            usb_err("rx_priv->rxq is over flow!!!\n");
+            aicwf_dev_skb_free(skb);
+            return;
+        }
+        spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+        atomic_inc(&rx_priv->rx_cnt);
+        complete(&rx_priv->usbdev->bus_if->busrx_trgg);
+        aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+
+        schedule_work(&usb_dev->rx_urb_work);
+    } else {
+        aicwf_dev_skb_free(skb);
+        aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+    }
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+static void aicwf_usb_msg_rx_complete(struct urb *urb)
+{
+    struct aicwf_usb_buf *usb_buf = (struct aicwf_usb_buf *) urb->context;
+    struct aic_usb_dev *usb_dev = usb_buf->usbdev;
+    struct aicwf_rx_priv* rx_priv = usb_dev->rx_priv;
+    struct sk_buff *skb = NULL;
+    unsigned long flags = 0;
+
+    skb = usb_buf->skb;
+    usb_buf->skb = NULL;
+
+    if (urb->actual_length > urb->transfer_buffer_length) {
+        aicwf_dev_skb_free(skb);
+        aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+        schedule_work(&usb_dev->msg_rx_urb_work);
+        return;
+    }
+
+    if (urb->status != 0 || !urb->actual_length) {
+        aicwf_dev_skb_free(skb);
+        aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+        schedule_work(&usb_dev->msg_rx_urb_work);
+        return;
+    }
+
+    if (usb_dev->state == USB_UP_ST) {
+        skb_put(skb, urb->actual_length);
+
+        spin_lock_irqsave(&rx_priv->msg_rxqlock, flags);
+        if(!aicwf_rxframe_enqueue(usb_dev->dev, &rx_priv->msg_rxq, skb)){
+            spin_unlock_irqrestore(&rx_priv->msg_rxqlock, flags);
+            usb_err("rx_priv->rxq is over flow!!!\n");
+            aicwf_dev_skb_free(skb);
+            return;
+        }
+        spin_unlock_irqrestore(&rx_priv->msg_rxqlock, flags);
+        atomic_inc(&rx_priv->msg_rx_cnt);
+        complete(&rx_priv->usbdev->bus_if->msg_busrx_trgg);
+        aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+
+        schedule_work(&usb_dev->msg_rx_urb_work);
+    } else {
+        aicwf_dev_skb_free(skb);
+        aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+    }
+}
+#endif
+
+static int aicwf_usb_submit_rx_urb(struct aic_usb_dev *usb_dev,
+                struct aicwf_usb_buf *usb_buf)
+{
+    struct sk_buff *skb;
+    int ret;
+
+    if (!usb_buf || !usb_dev)
+        return -1;
+
+    if (usb_dev->state != USB_UP_ST) {
+        usb_err("usb state is not up!\n");
+        aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+        return -1;
+    }
+
+    skb = __dev_alloc_skb(AICWF_USB_MAX_PKT_SIZE, GFP_KERNEL);
+    if (!skb) {
+        aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+        return -1;
+    }
+
+    usb_buf->skb = skb;
+
+    usb_fill_bulk_urb(usb_buf->urb,
+        usb_dev->udev,
+        usb_dev->bulk_in_pipe,
+        skb->data, skb_tailroom(skb), aicwf_usb_rx_complete, usb_buf);
+
+    usb_buf->usbdev = usb_dev;
+
+    usb_anchor_urb(usb_buf->urb, &usb_dev->rx_submitted);
+    ret = usb_submit_urb(usb_buf->urb, GFP_ATOMIC);
+    if (ret) {
+        usb_err("usb submit rx urb fail:%d\n", ret);
+        usb_unanchor_urb(usb_buf->urb);
+        aicwf_dev_skb_free(usb_buf->skb);
+        usb_buf->skb = NULL;
+        aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+
+        msleep(100);
+    }
+    return 0;
+}
+
+static void aicwf_usb_rx_submit_all_urb(struct aic_usb_dev *usb_dev)
+{
+    struct aicwf_usb_buf *usb_buf;
+
+    if (usb_dev->state != USB_UP_ST) {
+        usb_err("bus is not up=%d\n", usb_dev->state);
+        return;
+    }
+
+    while((usb_buf = aicwf_usb_rx_buf_get(usb_dev)) != NULL) {
+        if (aicwf_usb_submit_rx_urb(usb_dev, usb_buf)) {
+            usb_err("usb rx refill fail\n");
+            if (usb_dev->state != USB_UP_ST)
+                return;
+        }
+    }
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+static int aicwf_usb_submit_msg_rx_urb(struct aic_usb_dev *usb_dev,
+                struct aicwf_usb_buf *usb_buf)
+{
+    struct sk_buff *skb;
+    int ret;
+
+    if (!usb_buf || !usb_dev)
+        return -1;
+
+    if (usb_dev->state != USB_UP_ST) {
+        usb_err("usb state is not up!\n");
+        aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+        return -1;
+    }
+
+    skb = __dev_alloc_skb(AICWF_USB_MAX_PKT_SIZE, GFP_KERNEL);
+    if (!skb) {
+        aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+        return -1;
+    }
+
+    usb_buf->skb = skb;
+
+    usb_fill_bulk_urb(usb_buf->urb,
+        usb_dev->udev,
+        usb_dev->msg_in_pipe,
+        skb->data, skb_tailroom(skb), aicwf_usb_msg_rx_complete, usb_buf);
+
+    usb_buf->usbdev = usb_dev;
+
+    usb_anchor_urb(usb_buf->urb, &usb_dev->msg_rx_submitted);
+    ret = usb_submit_urb(usb_buf->urb, GFP_ATOMIC);
+    if (ret) {
+        usb_err("usb submit msg rx urb fail:%d\n", ret);
+        usb_unanchor_urb(usb_buf->urb);
+        aicwf_dev_skb_free(usb_buf->skb);
+        usb_buf->skb = NULL;
+        aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+
+        msleep(100);
+    }
+    return 0;
+}
+
+
+static void aicwf_usb_msg_rx_submit_all_urb(struct aic_usb_dev *usb_dev)
+{
+    struct aicwf_usb_buf *usb_buf;
+
+    if (usb_dev->state != USB_UP_ST) {
+        usb_err("bus is not up=%d\n", usb_dev->state);
+        return;
+    }
+
+    while((usb_buf = aicwf_usb_msg_rx_buf_get(usb_dev)) != NULL) {
+        if (aicwf_usb_submit_msg_rx_urb(usb_dev, usb_buf)) {
+            usb_err("usb msg rx refill fail\n");
+            if (usb_dev->state != USB_UP_ST)
+                return;
+        }
+    }
+}
+#endif
+
+static void aicwf_usb_rx_prepare(struct aic_usb_dev *usb_dev)
+{
+    aicwf_usb_rx_submit_all_urb(usb_dev);
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+static void aicwf_usb_msg_rx_prepare(struct aic_usb_dev *usb_dev)
+{
+    aicwf_usb_msg_rx_submit_all_urb(usb_dev);
+}
+#endif
+
+static void aicwf_usb_tx_prepare(struct aic_usb_dev *usb_dev)
+{
+    struct aicwf_usb_buf *usb_buf;
+
+    while(!list_empty(&usb_dev->tx_post_list)){
+        usb_buf = aicwf_usb_tx_dequeue(usb_dev, &usb_dev->tx_post_list,
+            &usb_dev->tx_post_count, &usb_dev->tx_post_lock);
+        #ifndef CONFIG_USB_TX_AGGR
+        if(usb_buf->skb) {
+            dev_kfree_skb(usb_buf->skb);
+            usb_buf->skb = NULL;
+        }
+        #endif
+        aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_free_list, usb_buf,
+                &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+    }
+}
+
+#ifdef CONFIG_USB_TX_AGGR
+int aicwf_usb_send_pkt(struct aic_usb_dev *usb_dev, u8 *buf, uint buf_len)
+{
+    int ret = 0;
+    u16 adjust_len = 0;
+    struct aicwf_usb_buf *usb_buf;
+    unsigned long flags;
+    u8 usb_header[4];
+    u8 adj_buf[4] = {0};
+    u16 index = 0;
+    bool need_cfm = false;
+
+    if (usb_dev->state != USB_UP_ST) {
+        usb_err("usb state is not up!\n");
+        return -EIO;
+    }
+
+    usb_buf = aicwf_usb_tx_dequeue(usb_dev, &usb_dev->tx_free_list,
+                        &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+    if (!usb_buf) {
+        usb_err("free:%d, post:%d\n", usb_dev->tx_free_count, usb_dev->tx_post_count);
+        ret = -ENOMEM;
+        goto flow_ctrl;
+    }
+
+    usb_buf->skb = (struct sk_buff *)buf;
+    usb_buf->usbdev = usb_dev;
+    if (need_cfm)
+        usb_buf->cfm = true;
+    else
+        usb_buf->cfm = false;
+    printk("%s len %d\n", __func__, buf_len);
+    print_hex_dump(KERN_ERR, "buf  ", DUMP_PREFIX_NONE, 16, 1, &buf[0], 32, false);
+    usb_fill_bulk_urb(usb_buf->urb, usb_dev->udev, usb_dev->bulk_out_pipe,
+                buf, buf_len, aicwf_usb_tx_complete, usb_buf);
+    usb_buf->urb->transfer_flags |= URB_ZERO_PACKET;
+
+    aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_post_list, usb_buf,
+                    &usb_dev->tx_post_count, &usb_dev->tx_post_lock);
+    ret = 0;
+
+    flow_ctrl:
+    spin_lock_irqsave(&usb_dev->tx_flow_lock, flags);
+    if (usb_dev->tx_free_count < AICWF_USB_TX_LOW_WATER) {
+        usb_dev->tbusy = true;
+        aicwf_usb_tx_flowctrl(usb_dev->rwnx_hw, true);
+    }
+    spin_unlock_irqrestore(&usb_dev->tx_flow_lock, flags);
+
+    return ret;
+}
+
+int aicwf_usb_aggr(struct aicwf_tx_priv *tx_priv, struct sk_buff *pkt)
+{
+    struct rwnx_txhdr *txhdr = (struct rwnx_txhdr *)pkt->data;
+    u8 *start_ptr = tx_priv->tail;
+    u8 usb_header[8];
+    u8 adjust_str[4] = {0, 0, 0, 0};
+    u32 curr_len = 0;
+    int allign_len = 0;
+    u32 data_len = (pkt->len - sizeof(struct rwnx_txhdr) + sizeof(struct txdesc_api)) + 4;
+
+    usb_header[0] =(data_len & 0xff);
+    usb_header[1] =((data_len >> 8)&0x0f);
+    usb_header[2] =(data_len & 0xff);
+    usb_header[3] =((data_len >> 8)&0x0f);
+
+    usb_header[4] =(data_len & 0xff);
+    usb_header[5] =((data_len >> 8)&0x0f);
+    usb_header[6] = 0x01; //data
+    usb_header[7] = 0; //reserved
+
+    memcpy(tx_priv->tail, (u8 *)&usb_header, sizeof(usb_header));
+    tx_priv->tail += sizeof(usb_header);
+    //payload
+    memcpy(tx_priv->tail, (u8 *)(long)&txhdr->sw_hdr.desc, sizeof(struct txdesc_api));
+    tx_priv->tail += sizeof(struct txdesc_api); //hostdesc
+    memcpy(tx_priv->tail, (u8 *)((u8 *)txhdr + txhdr->sw_hdr.headroom), pkt->len-txhdr->sw_hdr.headroom);
+    tx_priv->tail += (pkt->len - txhdr->sw_hdr.headroom);
+
+    //word alignment
+    curr_len = tx_priv->tail - tx_priv->head;
+    if (curr_len & (TX_ALIGNMENT - 1)) {
+        allign_len = roundup(curr_len, TX_ALIGNMENT)-curr_len;
+        memcpy(tx_priv->tail, adjust_str, allign_len);
+        tx_priv->tail += allign_len;
+    }
+
+    tx_priv->aggr_buf->dev = pkt->dev;
+
+    if(!txhdr->sw_hdr.need_cfm) {
+        //kmem_cache_free(txhdr->sw_hdr->rwnx_vif->rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+        skb_pull(pkt, txhdr->sw_hdr.headroom);
+        consume_skb(pkt);
+    }
+
+    atomic_inc(&tx_priv->aggr_count);
+    return 0;
+}
+
+int aicwf_usb_send(struct aicwf_tx_priv *tx_priv)
+{
+    struct sk_buff *pkt;
+    struct aic_usb_dev *usbdev = tx_priv->usbdev;
+    u32 aggr_len = 0;
+    int retry_times = 0;
+    int max_retry_times = 5;
+    struct aicwf_usb_buf *usb_buf;
+    int ret = 0;
+    int curr_len = 0;
+    unsigned long flags;
+
+    if (aicwf_is_framequeue_empty(&tx_priv->txq)) {
+        ret = -1;
+        printk("no buf to send\n");
+        return ret;
+    }
+
+    if (usbdev->state != USB_UP_ST) {
+        usb_err("usb state is not up!\n");
+        ret = -ENODEV;
+        return ret;
+    }
+    usb_buf = aicwf_usb_tx_dequeue(usbdev, &usbdev->tx_free_list,
+                        &usbdev->tx_free_count, &usbdev->tx_free_lock);
+    if (!usb_buf) {
+        usb_err("free:%d, post:%d\n", usbdev->tx_free_count, usbdev->tx_post_count);
+        ret = -ENOMEM;
+        return ret;
+    }
+
+    usb_buf->aggr_cnt = 0;
+    spin_lock_bh(&usbdev->tx_priv->txdlock);
+    tx_priv->head = usb_buf->skb->data;
+    tx_priv->tail = usb_buf->skb->data;
+
+    while (!aicwf_is_framequeue_empty(&usbdev->tx_priv->txq)) {
+        if (usbdev->state != USB_UP_ST) {
+            usb_err("usb state is not up, break!\n");
+            ret = -ENODEV;
+            break;
+        }
+
+        if (usb_buf->aggr_cnt == 10) {
+            break;
+        }
+        spin_lock_bh(&usbdev->tx_priv->txqlock);
+        pkt = aicwf_txframe_dequeue(&usbdev->tx_priv->txq);
+        if (pkt == NULL) {
+            usb_err("txq no pkt\n");
+            spin_unlock_bh(&usbdev->tx_priv->txqlock);
+            ret = -1;
+            return ret;
+        }
+        atomic_dec(&usbdev->tx_priv->tx_pktcnt);
+        spin_unlock_bh(&usbdev->tx_priv->txqlock);
+        if(tx_priv==NULL || tx_priv->tail==NULL || pkt==NULL) {
+            txrx_err("null error\n");
+        }
+        aicwf_usb_aggr(tx_priv, pkt);
+        usb_buf->aggr_cnt++;
+    }
+
+    struct sk_buff *tx_buf = usb_buf->skb;
+    u8* buf = tx_buf->data;
+
+    curr_len = tx_priv->tail - tx_priv->head;
+
+    printk("%s len %d, cnt %d\n", __func__,curr_len, usb_buf->aggr_cnt);
+    tx_buf->len = tx_priv->tail - tx_priv->head;
+    spin_unlock_bh(&usbdev->tx_priv->txdlock);
+    usb_fill_bulk_urb(usb_buf->urb, usbdev->udev, usbdev->bulk_out_pipe,
+                buf, curr_len, aicwf_usb_tx_complete, usb_buf);
+    usb_buf->urb->transfer_flags |= URB_ZERO_PACKET;
+
+    aicwf_usb_tx_queue(usbdev, &usbdev->tx_post_list, usb_buf,
+                    &usbdev->tx_post_count, &usbdev->tx_post_lock);
+
+    flow_ctrl:
+    spin_lock_irqsave(&usbdev->tx_flow_lock, flags);
+    if (usbdev->tx_free_count < AICWF_USB_TX_LOW_WATER) {
+        usbdev->tbusy = true;
+        aicwf_usb_tx_flowctrl(usbdev->rwnx_hw, true);
+    }
+    spin_unlock_irqrestore(&usbdev->tx_flow_lock, flags);
+
+    return ret;
+}
+
+#endif
+
+static void aicwf_usb_tx_process(struct aic_usb_dev *usb_dev)
+{
+
+#ifdef CONFIG_USB_TX_AGGR
+    if (!aicwf_is_framequeue_empty(&usb_dev->tx_priv->txq)) {
+        if (aicwf_usb_send(usb_dev->tx_priv)) {
+            printk("% no buf send\n", __func__);
+        }
+    }
+#endif
+
+    struct aicwf_usb_buf *usb_buf;
+    int ret = 0;
+    u8* data = NULL;
+
+    while(!list_empty(&usb_dev->tx_post_list)) {
+        if (usb_dev->state != USB_UP_ST) {
+            usb_err("usb state is not up!\n");
+            return;
+        }
+
+        usb_buf = aicwf_usb_tx_dequeue(usb_dev, &usb_dev->tx_post_list,
+                        &usb_dev->tx_post_count, &usb_dev->tx_post_lock);
+        if(!usb_buf) {
+            usb_err("can not get usb_buf from tx_post_list!\n");
+            return;
+        }
+        data = usb_buf->skb->data;
+
+        ret = usb_submit_urb(usb_buf->urb, GFP_ATOMIC);
+        if (ret) {
+            usb_err("aicwf_usb_bus_tx usb_submit_urb FAILED\n");
+            #ifdef CONFIG_USB_TX_AGGR
+            aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_post_list, usb_buf,
+                    &usb_dev->tx_post_count, &usb_dev->tx_post_lock);
+            break;
+            #else
+            goto fail;
+            #endif
+        }
+
+        continue;
+#ifndef CONFIG_USB_TX_AGGR
+fail:
+        dev_kfree_skb(usb_buf->skb);
+        usb_buf->skb = NULL;
+        aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_free_list, usb_buf,
+                    &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+#endif
+    }
+
+}
+
+int usb_bustx_thread(void *data)
+{
+    struct aicwf_bus *bus = (struct aicwf_bus *)data;
+    struct aic_usb_dev *usbdev = bus->bus_priv.usb;
+
+    while (1) {
+        if(kthread_should_stop()) {
+            usb_err("usb bustx thread stop\n");
+            break;
+        }
+        if (!wait_for_completion_interruptible(&bus->bustx_trgg)) {
+            if(usbdev->bus_if->state == BUS_DOWN_ST)
+                break;
+            #ifdef CONFIG_USB_TX_AGGR
+            if ((usbdev->tx_post_count > 0) || !aicwf_is_framequeue_empty(&usbdev->tx_priv->txq))
+            #else
+            if (usbdev->tx_post_count > 0)
+            #endif
+                aicwf_usb_tx_process(usbdev);
+        }
+    }
+
+    return 0;
+}
+
+int usb_busrx_thread(void *data)
+{
+    struct aicwf_rx_priv *rx_priv = (struct aicwf_rx_priv *)data;
+    struct aicwf_bus *bus_if = rx_priv->usbdev->bus_if;
+
+    while (1) {
+        if(kthread_should_stop()) {
+            usb_err("usb busrx thread stop\n");
+            break;
+        }
+        if (!wait_for_completion_interruptible(&bus_if->busrx_trgg)) {
+            if(bus_if->state == BUS_DOWN_ST)
+                break;
+            aicwf_process_rxframes(rx_priv);
+        }
+    }
+
+    return 0;
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+int usb_msg_busrx_thread(void *data)
+{
+    struct aicwf_rx_priv *rx_priv = (struct aicwf_rx_priv *)data;
+    struct aicwf_bus *bus_if = rx_priv->usbdev->bus_if;
+
+    while (1) {
+        if(kthread_should_stop()) {
+            usb_err("usb msg busrx thread stop\n");
+            break;
+        }
+        if (!wait_for_completion_interruptible(&bus_if->msg_busrx_trgg)) {
+            if(bus_if->state == BUS_DOWN_ST)
+                break;
+            aicwf_process_msg_rxframes(rx_priv);
+        }
+    }
+
+    return 0;
+}
+#endif
+
+static void aicwf_usb_send_msg_complete(struct urb *urb)
+{
+    struct aic_usb_dev *usb_dev = (struct aic_usb_dev *) urb->context;
+
+    usb_dev->msg_finished = true;
+    if (waitqueue_active(&usb_dev->msg_wait))
+        wake_up(&usb_dev->msg_wait);
+}
+
+static int aicwf_usb_bus_txmsg(struct device *dev, u8 *buf, u32 len)
+{
+    int ret = 0;
+    struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+    struct aic_usb_dev *usb_dev = bus_if->bus_priv.usb;
+
+    if (usb_dev->state != USB_UP_ST)
+        return -EIO;
+
+    if (buf == NULL || len == 0 || usb_dev->msg_out_urb == NULL)
+        return -EINVAL;
+
+    if (test_and_set_bit(0, &usb_dev->msg_busy)) {
+        usb_err("In a control frame option, can't tx!\n");
+        return -EIO;
+    }
+
+    usb_dev->msg_finished = false;
+
+#ifdef CONFIG_USB_MSG_OUT_EP
+    if (usb_dev->msg_out_pipe) {
+        usb_fill_bulk_urb(usb_dev->msg_out_urb,
+            usb_dev->udev,
+            usb_dev->msg_out_pipe,
+            buf, len, (usb_complete_t) aicwf_usb_send_msg_complete, usb_dev);
+    } else {
+        usb_fill_bulk_urb(usb_dev->msg_out_urb,
+            usb_dev->udev,
+            usb_dev->bulk_out_pipe,
+            buf, len, (usb_complete_t) aicwf_usb_send_msg_complete, usb_dev);
+    }
+#else
+    usb_fill_bulk_urb(usb_dev->msg_out_urb,
+        usb_dev->udev,
+        usb_dev->bulk_out_pipe,
+        buf, len, (usb_complete_t) aicwf_usb_send_msg_complete, usb_dev);
+#endif
+    usb_dev->msg_out_urb->transfer_flags |= URB_ZERO_PACKET;
+
+    ret = usb_submit_urb(usb_dev->msg_out_urb, GFP_ATOMIC);
+    if (ret) {
+        usb_err("usb_submit_urb failed %d\n", ret);
+        goto exit;
+    }
+
+    ret = wait_event_timeout(usb_dev->msg_wait,
+        usb_dev->msg_finished, msecs_to_jiffies(CMD_TX_TIMEOUT));
+    if (!ret) {
+        if (usb_dev->msg_out_urb)
+            usb_kill_urb(usb_dev->msg_out_urb);
+        usb_err("Txmsg wait timed out\n");
+        ret = -EIO;
+        goto exit;
+    }
+
+    if (usb_dev->msg_finished == false) {
+        usb_err("Txmsg timed out\n");
+        ret = -ETIMEDOUT;
+        goto exit;
+    }
+exit:
+    clear_bit(0, &usb_dev->msg_busy);
+    return ret;
+}
+
+
+static void aicwf_usb_free_urb(struct list_head *q, spinlock_t *qlock)
+{
+    struct aicwf_usb_buf *usb_buf, *tmp;
+    unsigned long flags;
+
+    spin_lock_irqsave(qlock, flags);
+    list_for_each_entry_safe(usb_buf, tmp, q, list) {
+        spin_unlock_irqrestore(qlock, flags);
+        if (!usb_buf->urb) {
+            usb_err("bad usb_buf\n");
+            spin_lock_irqsave(qlock, flags);
+            break;
+        }
+        #ifdef CONFIG_USB_TX_AGGR
+        if (usb_buf->skb) {
+            dev_kfree_skb(usb_buf->skb);
+        }
+        #endif
+        usb_free_urb(usb_buf->urb);
+        list_del_init(&usb_buf->list);
+        spin_lock_irqsave(qlock, flags);
+    }
+    spin_unlock_irqrestore(qlock, flags);
+}
+
+static int aicwf_usb_alloc_rx_urb(struct aic_usb_dev *usb_dev)
+{
+    int i;
+
+    for (i = 0; i < AICWF_USB_RX_URBS; i++) {
+        struct aicwf_usb_buf *usb_buf = &usb_dev->usb_rx_buf[i];
+
+        usb_buf->usbdev = usb_dev;
+        usb_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
+        if (!usb_buf->urb) {
+            usb_err("could not allocate rx data urb\n");
+            goto err;
+        }
+        list_add_tail(&usb_buf->list, &usb_dev->rx_free_list);
+    }
+    return 0;
+
+err:
+    aicwf_usb_free_urb(&usb_dev->rx_free_list, &usb_dev->rx_free_lock);
+    return -ENOMEM;
+}
+
+static int aicwf_usb_alloc_tx_urb(struct aic_usb_dev *usb_dev)
+{
+    int i;
+
+    for (i = 0; i < AICWF_USB_TX_URBS; i++) {
+        struct aicwf_usb_buf *usb_buf = &usb_dev->usb_tx_buf[i];
+
+        usb_buf->usbdev = usb_dev;
+        usb_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
+        if (!usb_buf->urb) {
+            usb_err("could not allocate tx data urb\n");
+            goto err;
+        }
+        #ifdef CONFIG_USB_TX_AGGR
+        usb_buf->skb = dev_alloc_skb(MAX_USB_AGGR_TXPKT_LEN);
+        #endif
+        list_add_tail(&usb_buf->list, &usb_dev->tx_free_list);
+        (usb_dev->tx_free_count)++;
+    }
+    return 0;
+
+err:
+    aicwf_usb_free_urb(&usb_dev->tx_free_list, &usb_dev->tx_free_lock);
+    return -ENOMEM;
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+static int aicwf_usb_alloc_msg_rx_urb(struct aic_usb_dev *usb_dev)
+{
+    int i;
+
+    for (i = 0; i < AICWF_USB_MSG_RX_URBS; i++) {
+        struct aicwf_usb_buf *usb_buf = &usb_dev->usb_msg_rx_buf[i];
+
+        usb_buf->usbdev = usb_dev;
+        usb_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
+        if (!usb_buf->urb) {
+            usb_err("could not allocate rx data urb\n");
+            goto err;
+        }
+        list_add_tail(&usb_buf->list, &usb_dev->msg_rx_free_list);
+    }
+    return 0;
+
+err:
+    aicwf_usb_free_urb(&usb_dev->msg_rx_free_list, &usb_dev->msg_rx_free_lock);
+    return -ENOMEM;
+}
+#endif
+
+static void aicwf_usb_state_change(struct aic_usb_dev *usb_dev, int state)
+{
+    int old_state;
+
+    if (usb_dev->state == state)
+        return;
+
+    old_state = usb_dev->state;
+    usb_dev->state = state;
+
+    if (state == USB_DOWN_ST) {
+        usb_dev->bus_if->state = BUS_DOWN_ST;
+    }
+    if (state == USB_UP_ST) {
+        usb_dev->bus_if->state = BUS_UP_ST;
+    }
+}
+
+#ifdef CONFIG_USB_TX_AGGR
+static int aicwf_usb_bus_txdata(struct device *dev, struct sk_buff *pkt)
+{
+    uint prio;
+    int ret = -EBADE;
+    struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+    struct aic_usb_dev *usbdev = bus_if->bus_priv.usb;
+
+    //printk("%s\n", __func__);
+    prio = (pkt->priority & 0xf);
+    spin_lock_bh(&usbdev->tx_priv->txqlock);
+    if (!aicwf_frame_enq(usbdev->dev, &usbdev->tx_priv->txq, pkt, prio)) {
+        aicwf_dev_skb_free(pkt);
+        spin_unlock_bh(&usbdev->tx_priv->txqlock);
+        return -ENOSR;
+    } else {
+        ret = 0;
+    }
+
+    if (bus_if->state != BUS_UP_ST) {
+        usb_err("bus_if stopped\n");
+        spin_unlock_bh(&usbdev->tx_priv->txqlock);
+        return -1;
+    }
+
+    atomic_inc(&usbdev->tx_priv->tx_pktcnt);
+    spin_unlock_bh(&usbdev->tx_priv->txqlock);
+    complete(&bus_if->bustx_trgg);
+
+    return ret;
+}
+
+#else
+static int aicwf_usb_bus_txdata(struct device *dev, struct sk_buff *skb)
+{
+    u8 *buf;
+    u16 buf_len = 0;
+    u16 adjust_len = 0;
+    struct aicwf_usb_buf *usb_buf;
+    int ret = 0;
+    unsigned long flags;
+    struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+    struct aic_usb_dev *usb_dev = bus_if->bus_priv.usb;
+    struct rwnx_txhdr *txhdr = (struct rwnx_txhdr *)skb->data;
+    struct rwnx_hw *rwnx_hw = usb_dev->rwnx_hw;
+    u8 usb_header[4];
+    u8 adj_buf[4] = {0};
+    u16 index = 0;
+    bool need_cfm = false;
+
+    if (usb_dev->state != USB_UP_ST) {
+        usb_err("usb state is not up!\n");
+        //kmem_cache_free(rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+        dev_kfree_skb_any(skb);
+        return -EIO;
+    }
+
+    usb_buf = aicwf_usb_tx_dequeue(usb_dev, &usb_dev->tx_free_list,
+                        &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+    if (!usb_buf) {
+        usb_err("free:%d, post:%d\n", usb_dev->tx_free_count, usb_dev->tx_post_count);
+        //kmem_cache_free(rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+        dev_kfree_skb_any(skb);
+        ret = -ENOMEM;
+        goto flow_ctrl;
+    }
+
+    if (txhdr->sw_hdr.need_cfm) {
+        need_cfm = true;
+        buf = kmalloc(skb->len, GFP_KERNEL);
+        index += sizeof(usb_header);
+        memcpy(&buf[index], (u8 *)(long)&txhdr->sw_hdr.desc, sizeof(struct txdesc_api));
+        index += sizeof(struct txdesc_api);
+        memcpy(&buf[index], &skb->data[txhdr->sw_hdr.headroom], skb->len - txhdr->sw_hdr.headroom);
+        index += skb->len - txhdr->sw_hdr.headroom;
+        buf_len = index;
+        if (buf_len & (TX_ALIGNMENT - 1)) {
+            adjust_len = roundup(buf_len, TX_ALIGNMENT)-buf_len;
+            memcpy(&buf[buf_len], adj_buf, adjust_len);
+            buf_len += adjust_len;
+        }
+        usb_header[0] =((buf_len) & 0xff);
+        usb_header[1] =(((buf_len) >> 8)&0x0f);
+        usb_header[2] = 0x01; //data
+        usb_header[3] = 0; //reserved
+        memcpy(&buf[0], usb_header, sizeof(usb_header));
+        usb_buf->skb = (struct sk_buff *)buf;
+    } else {
+        skb_pull(skb, txhdr->sw_hdr.headroom);
+        skb_push(skb, sizeof(struct txdesc_api));
+        memcpy(&skb->data[0], (u8 *)(long)&txhdr->sw_hdr.desc, sizeof(struct txdesc_api));
+        //kmem_cache_free(rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+
+        skb_push(skb, sizeof(usb_header));
+        usb_header[0] =((skb->len) & 0xff);
+        usb_header[1] =(((skb->len) >> 8)&0x0f);
+        usb_header[2] = 0x01; //data
+        usb_header[3] = 0; //reserved
+        memcpy(&skb->data[0], usb_header, sizeof(usb_header));
+
+        buf = skb->data;
+        buf_len = skb->len;
+
+        usb_buf->skb = skb;
+    }
+    usb_buf->usbdev = usb_dev;
+    if (need_cfm)
+        usb_buf->cfm = true;
+    else
+        usb_buf->cfm = false;
+    usb_fill_bulk_urb(usb_buf->urb, usb_dev->udev, usb_dev->bulk_out_pipe,
+                buf, buf_len, aicwf_usb_tx_complete, usb_buf);
+    usb_buf->urb->transfer_flags |= URB_ZERO_PACKET;
+
+    aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_post_list, usb_buf,
+                    &usb_dev->tx_post_count, &usb_dev->tx_post_lock);
+    complete(&bus_if->bustx_trgg);
+    ret = 0;
+
+    flow_ctrl:
+    spin_lock_irqsave(&usb_dev->tx_flow_lock, flags);
+    if (usb_dev->tx_free_count < AICWF_USB_TX_LOW_WATER) {
+        usb_dev->tbusy = true;
+        aicwf_usb_tx_flowctrl(usb_dev->rwnx_hw, true);
+    }
+    spin_unlock_irqrestore(&usb_dev->tx_flow_lock, flags);
+
+    return ret;
+}
+#endif
+static int aicwf_usb_bus_start(struct device *dev)
+{
+    struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+    struct aic_usb_dev *usb_dev = bus_if->bus_priv.usb;
+
+    if (usb_dev->state == USB_UP_ST)
+        return 0;
+
+    aicwf_usb_state_change(usb_dev, USB_UP_ST);
+    aicwf_usb_rx_prepare(usb_dev);
+    aicwf_usb_tx_prepare(usb_dev);
+#ifdef CONFIG_USB_MSG_IN_EP
+    aicwf_usb_msg_rx_prepare(usb_dev);
+#endif
+    return 0;
+}
+
+static void aicwf_usb_cancel_all_urbs(struct aic_usb_dev *usb_dev)
+{
+    struct aicwf_usb_buf *usb_buf, *tmp;
+    unsigned long flags;
+
+    if (usb_dev->msg_out_urb)
+        usb_kill_urb(usb_dev->msg_out_urb);
+
+    spin_lock_irqsave(&usb_dev->tx_post_lock, flags);
+    list_for_each_entry_safe(usb_buf, tmp, &usb_dev->tx_post_list, list) {
+        spin_unlock_irqrestore(&usb_dev->tx_post_lock, flags);
+        if (!usb_buf->urb) {
+            usb_err("bad usb_buf\n");
+            spin_lock_irqsave(&usb_dev->tx_post_lock, flags);
+            break;
+        }
+        usb_kill_urb(usb_buf->urb);
+        spin_lock_irqsave(&usb_dev->tx_post_lock, flags);
+    }
+    spin_unlock_irqrestore(&usb_dev->tx_post_lock, flags);
+
+    usb_kill_anchored_urbs(&usb_dev->rx_submitted);
+#ifdef CONFIG_USB_MSG_IN_EP
+    usb_kill_anchored_urbs(&usb_dev->msg_rx_submitted);
+#endif
+}
+
+static void aicwf_usb_bus_stop(struct device *dev)
+{
+    struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+    struct aic_usb_dev *usb_dev = bus_if->bus_priv.usb;
+
+    usb_dbg("%s\r\n", __func__);
+    if (usb_dev == NULL)
+        return;
+
+    if (usb_dev->state == USB_DOWN_ST)
+        return;
+
+    aicwf_usb_state_change(usb_dev, USB_DOWN_ST);
+    aicwf_usb_cancel_all_urbs(usb_dev);
+}
+
+static void aicwf_usb_deinit(struct aic_usb_dev *usbdev)
+{
+    cancel_work_sync(&usbdev->rx_urb_work);
+    aicwf_usb_free_urb(&usbdev->rx_free_list, &usbdev->rx_free_lock);
+    aicwf_usb_free_urb(&usbdev->tx_free_list, &usbdev->tx_free_lock);
+#ifdef CONFIG_USB_MSG_IN_EP
+    cancel_work_sync(&usbdev->msg_rx_urb_work);
+    aicwf_usb_free_urb(&usbdev->msg_rx_free_list, &usbdev->msg_rx_free_lock);
+#endif
+    usb_free_urb(usbdev->msg_out_urb);
+}
+
+static void aicwf_usb_rx_urb_work(struct work_struct *work)
+{
+    struct aic_usb_dev *usb_dev = container_of(work, struct aic_usb_dev, rx_urb_work);
+
+    aicwf_usb_rx_submit_all_urb(usb_dev);
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+static void aicwf_usb_msg_rx_urb_work(struct work_struct *work)
+{
+    struct aic_usb_dev *usb_dev = container_of(work, struct aic_usb_dev, msg_rx_urb_work);
+
+    aicwf_usb_msg_rx_submit_all_urb(usb_dev);
+}
+#endif
+
+static int aicwf_usb_init(struct aic_usb_dev *usb_dev)
+{
+    int ret = 0;
+
+    usb_dev->tbusy = false;
+    usb_dev->state = USB_DOWN_ST;
+
+    init_waitqueue_head(&usb_dev->msg_wait);
+    init_usb_anchor(&usb_dev->rx_submitted);
+#ifdef CONFIG_USB_MSG_IN_EP
+    init_usb_anchor(&usb_dev->msg_rx_submitted);
+#endif
+
+    spin_lock_init(&usb_dev->tx_free_lock);
+    spin_lock_init(&usb_dev->tx_post_lock);
+    spin_lock_init(&usb_dev->rx_free_lock);
+    spin_lock_init(&usb_dev->tx_flow_lock);
+#ifdef CONFIG_USB_MSG_IN_EP
+    spin_lock_init(&usb_dev->msg_rx_free_lock);
+#endif
+
+    INIT_LIST_HEAD(&usb_dev->rx_free_list);
+    INIT_LIST_HEAD(&usb_dev->tx_free_list);
+    INIT_LIST_HEAD(&usb_dev->tx_post_list);
+#ifdef CONFIG_USB_MSG_IN_EP
+    INIT_LIST_HEAD(&usb_dev->msg_rx_free_list);
+#endif
+
+    usb_dev->tx_free_count = 0;
+    usb_dev->tx_post_count = 0;
+
+    ret =  aicwf_usb_alloc_rx_urb(usb_dev);
+    if (ret) {
+        goto error;
+    }
+    ret =  aicwf_usb_alloc_tx_urb(usb_dev);
+    if (ret) {
+        goto error;
+    }
+#ifdef CONFIG_USB_MSG_IN_EP
+    ret =  aicwf_usb_alloc_msg_rx_urb(usb_dev);
+    if (ret) {
+        goto error;
+    }
+#endif
+
+    usb_dev->msg_out_urb = usb_alloc_urb(0, GFP_ATOMIC);
+    if (!usb_dev->msg_out_urb) {
+        usb_err("usb_alloc_urb (msg out) failed\n");
+        ret = ENOMEM;
+        goto error;
+    }
+
+    INIT_WORK(&usb_dev->rx_urb_work, aicwf_usb_rx_urb_work);
+#ifdef CONFIG_USB_MSG_IN_EP
+    INIT_WORK(&usb_dev->msg_rx_urb_work, aicwf_usb_msg_rx_urb_work);
+#endif
+
+    return ret;
+    error:
+    usb_err("failed!\n");
+    aicwf_usb_deinit(usb_dev);
+    return ret;
+}
+
+
+static int aicwf_parse_usb(struct aic_usb_dev *usb_dev, struct usb_interface *interface)
+{
+    struct usb_interface_descriptor *interface_desc;
+    struct usb_host_interface *host_interface;
+    struct usb_endpoint_descriptor *endpoint;
+    struct usb_device *usb = usb_dev->udev;
+    int i, endpoints;
+    u8 endpoint_num;
+    int ret = 0;
+
+    usb_dev->bulk_in_pipe = 0;
+    usb_dev->bulk_out_pipe = 0;
+#ifdef CONFIG_USB_MSG_OUT_EP
+    usb_dev->msg_out_pipe = 0;
+#endif
+#ifdef CONFIG_USB_MSG_IN_EP
+    usb_dev->msg_in_pipe = 0;
+#endif
+
+    host_interface = &interface->altsetting[0];
+    interface_desc = &host_interface->desc;
+    endpoints = interface_desc->bNumEndpoints;
+
+    /* Check device configuration */
+    if (usb->descriptor.bNumConfigurations != 1) {
+        usb_err("Number of configurations: %d not supported\n",
+                        usb->descriptor.bNumConfigurations);
+        ret = -ENODEV;
+        goto exit;
+    }
+
+    /* Check deviceclass */
+#ifndef CONFIG_USB_BT
+    if (usb->descriptor.bDeviceClass != 0x00) {
+        usb_err("DeviceClass %d not supported\n",
+            usb->descriptor.bDeviceClass);
+        ret = -ENODEV;
+        goto exit;
+    }
+#endif
+
+    /* Check interface number */
+#ifdef CONFIG_USB_BT
+    if (usb->actconfig->desc.bNumInterfaces != 3)
+#else
+    if (usb->actconfig->desc.bNumInterfaces != 1)
+#endif
+    {
+        usb_err("Number of interfaces: %d not supported\n",
+            usb->actconfig->desc.bNumInterfaces);
+        ret = -ENODEV;
+        goto exit;
+    }
+
+    if ((interface_desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
+        (interface_desc->bInterfaceSubClass != 0xff) ||
+        (interface_desc->bInterfaceProtocol != 0xff)) {
+        usb_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n",
+            interface_desc->bInterfaceNumber, interface_desc->bInterfaceClass,
+            interface_desc->bInterfaceSubClass, interface_desc->bInterfaceProtocol);
+        ret = -ENODEV;
+        goto exit;
+    }
+
+    for (i = 0; i < endpoints; i++) {
+        endpoint = &host_interface->endpoint[i].desc;
+        endpoint_num = usb_endpoint_num(endpoint);
+
+        if (usb_endpoint_dir_in(endpoint) &&
+            usb_endpoint_xfer_bulk(endpoint)) {
+            if (!usb_dev->bulk_in_pipe) {
+                usb_dev->bulk_in_pipe = usb_rcvbulkpipe(usb, endpoint_num);
+            }
+#ifdef CONFIG_USB_MSG_IN_EP
+            else if (!usb_dev->msg_in_pipe) {
+                usb_dev->msg_in_pipe = usb_rcvbulkpipe(usb, endpoint_num);
+            }
+#endif
+        }
+
+        if (usb_endpoint_dir_out(endpoint) &&
+            usb_endpoint_xfer_bulk(endpoint)) {
+            if (!usb_dev->bulk_out_pipe)
+            {
+                usb_dev->bulk_out_pipe = usb_sndbulkpipe(usb, endpoint_num);
+            }
+#ifdef CONFIG_USB_MSG_OUT_EP
+            else if (!usb_dev->msg_out_pipe) {
+                usb_dev->msg_out_pipe = usb_sndbulkpipe(usb, endpoint_num);
+            }
+#endif
+        }
+    }
+
+    if (usb_dev->bulk_in_pipe == 0) {
+        usb_err("No RX (in) Bulk EP found\n");
+        ret = -ENODEV;
+        goto exit;
+    }
+    if (usb_dev->bulk_out_pipe == 0) {
+        usb_err("No TX (out) Bulk EP found\n");
+        ret = -ENODEV;
+        goto exit;
+    }
+#ifdef CONFIG_USB_MSG_OUT_EP
+    if (usb_dev->msg_out_pipe == 0) {
+        usb_err("No TX Msg (out) Bulk EP found\n");
+    }
+#endif
+#ifdef CONFIG_USB_MSG_IN_EP
+    if (usb_dev->msg_in_pipe == 0) {
+        usb_err("No RX Msg (in) Bulk EP found\n");
+    }
+#endif
+
+    if (usb->speed == USB_SPEED_HIGH)
+        printk("Aic high speed USB device detected\n");
+    else
+        printk("Aic full speed USB device detected\n");
+
+    exit:
+    return ret;
+}
+
+
+
+static struct aicwf_bus_ops aicwf_usb_bus_ops = {
+    .start = aicwf_usb_bus_start,
+    .stop = aicwf_usb_bus_stop,
+    .txdata = aicwf_usb_bus_txdata,
+    .txmsg = aicwf_usb_bus_txmsg,
+};
+
+static int aicwf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+    int ret = 0;
+    struct usb_device *usb = interface_to_usbdev(intf);
+    struct aicwf_bus *bus_if ;
+    struct device *dev = NULL;
+    struct aicwf_rx_priv* rx_priv = NULL;
+    struct aic_usb_dev *usb_dev = NULL;
+    #ifdef CONFIG_USB_TX_AGGR
+    struct aicwf_tx_priv *tx_priv = NULL;
+    #endif
+
+    usb_dev = kzalloc(sizeof(struct aic_usb_dev), GFP_ATOMIC);
+    if (!usb_dev) {
+        return -ENOMEM;
+    }
+
+    usb_dev->udev = usb;
+    usb_dev->dev = &usb->dev;
+    usb_set_intfdata(intf, usb_dev);
+
+    ret = aicwf_parse_usb(usb_dev, intf);
+    if (ret) {
+        usb_err("aicwf_parse_usb err %d\n", ret);
+        goto out_free;
+    }
+
+    ret = aicwf_usb_init(usb_dev);
+    if (ret) {
+        usb_err("aicwf_usb_init err %d\n", ret);
+        goto out_free;
+    }
+
+    bus_if = kzalloc(sizeof(struct aicwf_bus), GFP_ATOMIC);
+    if (!bus_if) {
+        ret = -ENOMEM;
+        goto out_free_usb;
+    }
+
+    dev = usb_dev->dev;
+    bus_if->dev = dev;
+    usb_dev->bus_if = bus_if;
+    bus_if->bus_priv.usb = usb_dev;
+    dev_set_drvdata(dev, bus_if);
+
+    bus_if->ops = &aicwf_usb_bus_ops;
+
+    rx_priv = aicwf_rx_init(usb_dev);
+    if(!rx_priv) {
+        txrx_err("rx init failed\n");
+        ret = -1;
+        goto out_free_bus;
+    }
+    usb_dev->rx_priv = rx_priv;
+
+#ifdef CONFIG_USB_TX_AGGR
+    tx_priv = aicwf_tx_init(usb_dev);
+    if(!tx_priv) {
+        usb_err("tx init fail\n");
+        goto out_free_bus;
+    }
+    usb_dev->tx_priv = tx_priv;
+    aicwf_frame_queue_init(&tx_priv->txq, AICWF_TXQ_CNT, TXQLEN);
+    spin_lock_init(&tx_priv->txqlock);
+    spin_lock_init(&tx_priv->txdlock);
+#endif
+
+    ret = aicwf_bus_init(0, dev);
+    if (ret < 0) {
+        usb_err("aicwf_bus_init err %d\n", ret);
+        goto out_free_bus;
+    }
+
+    ret = aicwf_bus_start(bus_if);
+    if (ret < 0) {
+        usb_err("aicwf_bus_start err %d\n", ret);
+        goto out_free_bus;
+    }
+
+    aicwf_rwnx_usb_platform_init(usb_dev);
+    aicwf_hostif_ready();
+    return 0;
+
+out_free_bus:
+    aicwf_bus_deinit(dev);
+    kfree(bus_if);
+out_free_usb:
+    aicwf_usb_deinit(usb_dev);
+out_free:
+    usb_err("failed with errno %d\n", ret);
+    kfree(usb_dev);
+    usb_set_intfdata(intf, NULL);
+    return ret;
+}
+
+static void aicwf_usb_disconnect(struct usb_interface *intf)
+{
+    struct aic_usb_dev *usb_dev =
+            (struct aic_usb_dev *) usb_get_intfdata(intf);
+
+    if (!usb_dev)
+        return;
+
+    aicwf_bus_deinit(usb_dev->dev);
+    aicwf_usb_deinit(usb_dev);
+    rwnx_cmd_mgr_deinit(&usb_dev->cmd_mgr);
+
+    if (usb_dev->rx_priv)
+        aicwf_rx_deinit(usb_dev->rx_priv);
+    kfree(usb_dev->bus_if);
+    kfree(usb_dev);
+}
+
+static int aicwf_usb_suspend(struct usb_interface *intf, pm_message_t state)
+{
+    struct aic_usb_dev *usb_dev =
+        (struct aic_usb_dev *) usb_get_intfdata(intf);
+
+    aicwf_usb_state_change(usb_dev, USB_SLEEP_ST);
+    aicwf_bus_stop(usb_dev->bus_if);
+    return 0;
+}
+
+static int aicwf_usb_resume(struct usb_interface *intf)
+{
+    struct aic_usb_dev *usb_dev =
+        (struct aic_usb_dev *) usb_get_intfdata(intf);
+
+    if (usb_dev->state == USB_UP_ST)
+        return 0;
+
+    aicwf_bus_start(usb_dev->bus_if);
+    return 0;
+}
+
+static int aicwf_usb_reset_resume(struct usb_interface *intf)
+{
+    return aicwf_usb_resume(intf);
+}
+
+static struct usb_device_id aicwf_usb_id_table[] = {
+#ifndef CONFIG_USB_BT
+    {USB_DEVICE(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC)},
+#else
+    {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC, 0xff, 0xff, 0xff)},
+#endif
+    {}
+};
+
+MODULE_DEVICE_TABLE(usb, aicwf_usb_id_table);
+
+static struct usb_driver aicwf_usbdrvr = {
+    .name = KBUILD_MODNAME,
+    .probe = aicwf_usb_probe,
+    .disconnect = aicwf_usb_disconnect,
+    .id_table = aicwf_usb_id_table,
+    .suspend = aicwf_usb_suspend,
+    .resume = aicwf_usb_resume,
+    .reset_resume = aicwf_usb_reset_resume,
+    #if defined(CONFIG_ANDROID_PLATFORM)
+    .supports_autosuspend = 1,
+    #else
+    .supports_autosuspend = 0,
+    #endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+    .disable_hub_initiated_lpm = 1,
+#endif
+};
+
+void aicwf_usb_register(void)
+{
+    if (usb_register(&aicwf_usbdrvr) < 0) {
+        usb_err("usb_register failed\n");
+    }
+}
+
+void aicwf_usb_exit(void)
+{
+    if(g_rwnx_plat && g_rwnx_plat->enabled)
+        rwnx_platform_deinit(g_rwnx_plat->usbdev->rwnx_hw);
+    usb_deregister(&aicwf_usbdrvr);
+    kfree(g_rwnx_plat);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_usb.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_usb.h
new file mode 100755
index 0000000..980b670
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/aicwf_usb.h
@@ -0,0 +1,132 @@
+/**
+ * aicwf_usb.h
+ *
+ * USB function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+#ifndef _AICWF_USB_H_
+#define _AICWF_USB_H_
+
+#include <linux/usb.h>
+#include "rwnx_cmds.h"
+
+#ifdef AICWF_USB_SUPPORT
+
+/* USB Device ID */
+#define USB_VENDOR_ID_AIC                0xA69C
+
+#define USB_PRODUCT_ID_AIC               0x88DC
+
+#define AICWF_USB_RX_URBS               (200)
+#ifdef CONFIG_USB_MSG_IN_EP
+#define AICWF_USB_MSG_RX_URBS           (100)
+#endif
+#ifdef CONFIG_USB_TX_AGGR
+#define TXQLEN                          (2048*4)
+#define AICWF_USB_TX_URBS               (50)
+#else
+#define AICWF_USB_TX_URBS               (100)
+#endif
+#define AICWF_USB_TX_LOW_WATER          (AICWF_USB_TX_URBS/4)
+#define AICWF_USB_TX_HIGH_WATER         (AICWF_USB_TX_LOW_WATER*3)
+#define AICWF_USB_MAX_PKT_SIZE          (2048)
+
+typedef enum {
+    USB_TYPE_DATA         = 0X00,
+    USB_TYPE_CFG          = 0X10,
+    USB_TYPE_CFG_CMD_RSP  = 0X11,
+    USB_TYPE_CFG_DATA_CFM = 0X12
+} usb_type;
+
+enum aicwf_usb_state {
+    USB_DOWN_ST,
+    USB_UP_ST,
+    USB_SLEEP_ST
+};
+
+struct aicwf_usb_buf {
+    struct list_head list;
+    struct aic_usb_dev *usbdev;
+    struct urb *urb;
+    struct sk_buff *skb;
+    bool cfm;
+    #ifdef CONFIG_USB_TX_AGGR
+    u8 aggr_cnt;
+    #endif
+};
+
+struct aic_usb_dev {
+    struct rwnx_hw *rwnx_hw;
+    struct aicwf_bus *bus_if;
+    struct usb_device *udev;
+    struct device *dev;
+    struct aicwf_rx_priv* rx_priv;
+    enum aicwf_usb_state state;
+    struct rwnx_cmd_mgr cmd_mgr;
+
+#ifdef CONFIG_USB_TX_AGGR
+    struct aicwf_tx_priv *tx_priv;
+#endif
+
+    struct usb_anchor rx_submitted;
+    struct work_struct rx_urb_work;
+#ifdef CONFIG_USB_MSG_IN_EP
+    struct usb_anchor msg_rx_submitted;
+    struct work_struct msg_rx_urb_work;
+#endif
+
+    spinlock_t rx_free_lock;
+    spinlock_t tx_free_lock;
+    spinlock_t tx_post_lock;
+    spinlock_t tx_flow_lock;
+#ifdef CONFIG_USB_MSG_IN_EP
+    spinlock_t msg_rx_free_lock;
+#endif
+
+    struct list_head rx_free_list;
+    struct list_head tx_free_list;
+    struct list_head tx_post_list;
+#ifdef CONFIG_USB_MSG_IN_EP
+    struct list_head msg_rx_free_list;
+#endif
+
+    uint bulk_in_pipe;
+    uint bulk_out_pipe;
+#ifdef CONFIG_USB_MSG_OUT_EP
+    uint msg_out_pipe;
+#endif
+#ifdef CONFIG_USB_MSG_IN_EP
+    uint msg_in_pipe;
+#endif
+
+    int tx_free_count;
+    int tx_post_count;
+
+    struct aicwf_usb_buf usb_tx_buf[AICWF_USB_TX_URBS];
+    struct aicwf_usb_buf usb_rx_buf[AICWF_USB_RX_URBS];
+#ifdef CONFIG_USB_MSG_IN_EP
+    struct aicwf_usb_buf usb_msg_rx_buf[AICWF_USB_MSG_RX_URBS];
+#endif
+
+    int msg_finished;
+    wait_queue_head_t msg_wait;
+    ulong msg_busy;
+    struct urb *msg_out_urb;
+
+    bool tbusy;
+};
+
+extern void aicwf_usb_exit(void);
+extern void aicwf_usb_register(void);
+extern void aicwf_usb_tx_flowctrl(struct rwnx_hw *rwnx_hw, bool state);
+int usb_bustx_thread(void *data);
+int usb_busrx_thread(void *data);
+#ifdef CONFIG_USB_MSG_IN_EP
+int usb_msg_busrx_thread(void *data);
+#endif
+extern void aicwf_hostif_ready(void);
+
+#endif /* AICWF_USB_SUPPORT */
+#endif /* _AICWF_USB_H_       */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/compile_test.sh b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/compile_test.sh
new file mode 100755
index 0000000..b30227d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/compile_test.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+linux_dir=/net/rwlab-srv1/nx_share/linux
+ARCH=${ARCH:-x86}
+CROSS_COMPILE=${CROSS_COMPILE:-x86_64-poky-linux-}
+error=0
+
+if [ ! -d $linux_dir ]
+then
+    echo "Invalid path: ${linux_dir}" >&2
+    exit 1
+fi
+
+for version in $(find $linux_dir -maxdepth 2 -type d -name cevav7 | sort)
+do
+    echo ""
+    echo "#####################################################"
+    echo "Testing $version"
+    echo "#####################################################"
+    ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE KERNELDIR=$version \
+    CONFIG_RWNX_SOFTMAC=m CONFIG_RWNX_FULLMAC=m CONFIG_RWNX_FHOST=m make -j 8
+
+    if [ $? -ne  0 ]
+    then
+	((error++))
+    fi
+done
+
+exit $error
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/hal_desc.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/hal_desc.h
new file mode 100755
index 0000000..ce353b1
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/hal_desc.h
@@ -0,0 +1,368 @@
+/**
+ ******************************************************************************
+ *
+ * @file hal_desc.h
+ *
+ * @brief File containing the definition of HW descriptors.
+ *
+ * Contains the definition and structures used by HW
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _HAL_DESC_H_
+#define _HAL_DESC_H_
+
+#include "lmac_types.h"
+
+/* Rate and policy table */
+
+#define N_CCK  8
+#define N_OFDM 8
+#define N_HT   (8 * 2 * 2 * 4)
+#define N_VHT  (10 * 4 * 2 * 8)
+#define N_HE_SU (12 * 4 * 3 * 8)
+#define N_HE_MU (12 * 6 * 3 * 8)
+
+/* conversion table from NL80211 to MACHW enum */
+extern const int chnl2bw[];
+
+/* conversion table from MACHW to NL80211 enum */
+extern const int bw2chnl[];
+
+/* Rate cntrl info */
+#define MCS_INDEX_TX_RCX_OFT    0
+#define MCS_INDEX_TX_RCX_MASK   (0x7F << MCS_INDEX_TX_RCX_OFT)
+#define BW_TX_RCX_OFT           7
+#define BW_TX_RCX_MASK          (0x3 << BW_TX_RCX_OFT)
+#define SHORT_GI_TX_RCX_OFT     9
+#define SHORT_GI_TX_RCX_MASK    (0x1 << SHORT_GI_TX_RCX_OFT)
+#define PRE_TYPE_TX_RCX_OFT     10
+#define PRE_TYPE_TX_RCX_MASK    (0x1 << PRE_TYPE_TX_RCX_OFT)
+#define FORMAT_MOD_TX_RCX_OFT   11
+#define FORMAT_MOD_TX_RCX_MASK  (0x7 << FORMAT_MOD_TX_RCX_OFT)
+
+/* Values for formatModTx */
+#define FORMATMOD_NON_HT          0
+#define FORMATMOD_NON_HT_DUP_OFDM 1
+#define FORMATMOD_HT_MF           2
+#define FORMATMOD_HT_GF           3
+#define FORMATMOD_VHT             4
+#define FORMATMOD_HE_SU           5
+#define FORMATMOD_HE_MU           6
+#define FORMATMOD_HE_ER           7
+
+/* Values for navProtFrmEx */
+#define NAV_PROT_NO_PROT_BIT                 0
+#define NAV_PROT_SELF_CTS_BIT                1
+#define NAV_PROT_RTS_CTS_BIT                 2
+#define NAV_PROT_RTS_CTS_WITH_QAP_BIT        3
+#define NAV_PROT_STBC_BIT                    4
+
+/* THD MACCTRLINFO2 fields, used in  struct umacdesc umac.flags */
+/// WhichDescriptor definition - contains aMPDU bit and position value
+/// Offset of WhichDescriptor field in the MAC CONTROL INFO 2 word
+#define WHICHDESC_OFT                     19
+/// Mask of the WhichDescriptor field
+#define WHICHDESC_MSK                     (0x07 << WHICHDESC_OFT)
+/// Only 1 THD possible, describing an unfragmented MSDU
+#define WHICHDESC_UNFRAGMENTED_MSDU       (0x00 << WHICHDESC_OFT)
+/// THD describing the first MPDU of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_FIRST   (0x01 << WHICHDESC_OFT)
+/// THD describing intermediate MPDUs of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_INT     (0x02 << WHICHDESC_OFT)
+/// THD describing the last MPDU of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_LAST    (0x03 << WHICHDESC_OFT)
+/// THD for extra descriptor starting an AMPDU
+#define WHICHDESC_AMPDU_EXTRA             (0x04 << WHICHDESC_OFT)
+/// THD describing the first MPDU of an A-MPDU
+#define WHICHDESC_AMPDU_FIRST             (0x05 << WHICHDESC_OFT)
+/// THD describing intermediate MPDUs of an A-MPDU
+#define WHICHDESC_AMPDU_INT               (0x06 << WHICHDESC_OFT)
+/// THD describing the last MPDU of an A-MPDU
+#define WHICHDESC_AMPDU_LAST              (0x07 << WHICHDESC_OFT)
+
+/// aMPDU bit offset
+#define AMPDU_OFT                         21
+/// aMPDU bit
+#define AMPDU_BIT                         CO_BIT(AMPDU_OFT)
+
+enum {
+    HW_RATE_1MBPS   = 0,
+    HW_RATE_2MBPS   = 1,
+    HW_RATE_5_5MBPS = 2,
+    HW_RATE_11MBPS  = 3,
+    HW_RATE_6MBPS   = 4,
+    HW_RATE_9MBPS   = 5,
+    HW_RATE_12MBPS  = 6,
+    HW_RATE_18MBPS  = 7,
+    HW_RATE_24MBPS  = 8,
+    HW_RATE_36MBPS  = 9,
+    HW_RATE_48MBPS  = 10,
+    HW_RATE_54MBPS  = 11,
+    HW_RATE_MAX
+};
+
+union rwnx_mcs_index {
+    struct {
+        u32 mcs : 3;
+        u32 nss : 2;
+    } ht;
+    struct {
+        u32 mcs : 4;
+        u32 nss : 3;
+    } vht;
+    struct {
+        u32 mcs : 4;
+        u32 nss : 3;
+    } he;
+    u32 legacy : 7;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_rate_ctrl_info {
+    struct {
+        u32 mcsIndexTx      : 7;
+        u32 bwTx            : 2;
+        u32 giAndPreTypeTx  : 2;
+        u32 formatModTx     : 3;
+        u32 navProtFrmEx    : 3;
+        u32 mcsIndexProtTx  : 7;
+        u32 bwProtTx        : 2;
+        u32 formatModProtTx : 3;
+        u32 nRetry          : 3;
+    };
+    u32 value;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+struct rwnx_power_ctrl_info {
+    u32 txPwrLevelPT          : 8;
+    u32 txPwrLevelProtPT      : 8;
+    u32 reserved              :16;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_pol_phy_ctrl_info_1 {
+    struct {
+        u32 rsvd1     : 3;
+        u32 bfFrmEx   : 1;
+        u32 numExtnSS : 2;
+        u32 fecCoding : 1;
+        u32 stbc      : 2;
+        u32 rsvd2     : 5;
+        u32 nTx       : 3;
+        u32 nTxProt   : 3;
+    };
+    u32 value;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_pol_phy_ctrl_info_2 {
+    struct {
+        u32 antennaSet : 8;
+        u32 smmIndex   : 8;
+        u32 beamFormed : 1;
+    };
+    u32 value;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_pol_mac_ctrl_info_1 {
+    struct {
+        u32 keySRamIndex   : 10;
+        u32 keySRamIndexRA : 10;
+    };
+    u32 value;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_pol_mac_ctrl_info_2 {
+    struct {
+        u32 longRetryLimit  : 8;
+        u32 shortRetryLimit : 8;
+        u32 rtsThreshold    : 12;
+    };
+    u32 value;
+};
+
+#define POLICY_TABLE_PATTERN    0xBADCAB1E
+
+struct tx_policy_tbl {
+    /* Unique Pattern at the start of Policy Table */
+    u32 upatterntx;
+    /* PHY Control 1 Information used by MAC HW */
+    union rwnx_pol_phy_ctrl_info_1 phyctrlinfo_1;
+    /* PHY Control 2 Information used by MAC HW */
+    union rwnx_pol_phy_ctrl_info_2 phyctrlinfo_2;
+    /* MAC Control 1 Information used by MAC HW */
+    union rwnx_pol_mac_ctrl_info_1 macctrlinfo_1;
+    /* MAC Control 2 Information used by MAC HW */
+    union rwnx_pol_mac_ctrl_info_2 macctrlinfo_2;
+
+    union rwnx_rate_ctrl_info  ratectrlinfos[NX_TX_MAX_RATES];
+    struct rwnx_power_ctrl_info powerctrlinfos[NX_TX_MAX_RATES];
+};
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+/**
+ * struct rwnx_hw_txstatus - Bitfield of confirmation status
+ *
+ * @tx_done: packet has been processed by the firmware.
+ * @retry_required: packet has been transmitted but not acknoledged.
+ * Driver must repush it.
+ * @sw_retry_required: packet has not been transmitted (FW wasn't able to push
+ * it when it received it: not active channel ...). Driver must repush it.
+ * @acknowledged: packet has been acknowledged by peer
+ */
+union rwnx_hw_txstatus {
+    struct {
+        u32 tx_done            : 1;
+        u32 retry_required     : 1;
+        u32 sw_retry_required  : 1;
+        u32 acknowledged       : 1;
+        u32 reserved           :28;
+    };
+    u32 value;
+};
+
+/**
+ * struct tx_cfm_tag - Structure indicating the status and other
+ * information about the transmission
+ *
+ * @pn: PN that was used for the transmission
+ * @sn: Sequence number of the packet
+ * @timestamp: Timestamp of first transmission of this MPDU
+ * @credits: Number of credits to be reallocated for the txq that push this
+ * buffer (can be 0 or 1)
+ * @ampdu_size: Size of the ampdu in which the frame has been transmitted if
+ * this was the last frame of the a-mpdu, and 0 if the frame is not the last
+ * frame on a a-mdpu.
+ * 1 means that the frame has been transmitted as a singleton.
+ * @amsdu_size: Size, in bytes, allowed to create a-msdu.
+ * @status: transmission status
+ */
+struct tx_cfm_tag
+{
+    union rwnx_hw_txstatus status;
+};
+
+/**
+ * struct rwnx_hw_txhdr - Hardware part of tx header
+ *
+ * @cfm: Information updated by fw/hardware after sending a frame
+ */
+struct rwnx_hw_txhdr {
+    struct tx_cfm_tag cfm;
+};
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/* Modem */
+
+#define MDM_PHY_CONFIG_TRIDENT     0
+#define MDM_PHY_CONFIG_ELMA        1
+#define MDM_PHY_CONFIG_KARST       2
+
+// MODEM features (from reg_mdm_stat.h)
+/// MUMIMOTX field bit
+#define MDM_MUMIMOTX_BIT    ((u32)0x80000000)
+/// MUMIMOTX field position
+#define MDM_MUMIMOTX_POS    31
+/// MUMIMORX field bit
+#define MDM_MUMIMORX_BIT    ((u32)0x40000000)
+/// MUMIMORX field position
+#define MDM_MUMIMORX_POS    30
+/// BFMER field bit
+#define MDM_BFMER_BIT       ((u32)0x20000000)
+/// BFMER field position
+#define MDM_BFMER_POS       29
+/// BFMEE field bit
+#define MDM_BFMEE_BIT       ((u32)0x10000000)
+/// BFMEE field position
+#define MDM_BFMEE_POS       28
+/// LDPCDEC field bit
+#define MDM_LDPCDEC_BIT     ((u32)0x08000000)
+/// LDPCDEC field position
+#define MDM_LDPCDEC_POS     27
+/// LDPCENC field bit
+#define MDM_LDPCENC_BIT     ((u32)0x04000000)
+/// LDPCENC field position
+#define MDM_LDPCENC_POS     26
+/// CHBW field mask
+#define MDM_CHBW_MASK       ((u32)0x03000000)
+/// CHBW field LSB position
+#define MDM_CHBW_LSB        24
+/// CHBW field width
+#define MDM_CHBW_WIDTH      ((u32)0x00000002)
+/// DSSSCCK field bit
+#define MDM_DSSSCCK_BIT     ((u32)0x00800000)
+/// DSSSCCK field position
+#define MDM_DSSSCCK_POS     23
+/// VHT field bit
+#define MDM_VHT_BIT         ((u32)0x00400000)
+/// VHT field position
+#define MDM_VHT_POS         22
+/// HE field bit
+#define MDM_HE_BIT          ((u32)0x00200000)
+/// HE field position
+#define MDM_HE_POS          21
+/// ESS field bit
+#define MDM_ESS_BIT         ((u32)0x00100000)
+/// ESS field position
+#define MDM_ESS_POS         20
+/// RFMODE field mask
+#define MDM_RFMODE_MASK     ((u32)0x000F0000)
+/// RFMODE field LSB position
+#define MDM_RFMODE_LSB      16
+/// RFMODE field width
+#define MDM_RFMODE_WIDTH    ((u32)0x00000004)
+/// NSTS field mask
+#define MDM_NSTS_MASK       ((u32)0x0000F000)
+/// NSTS field LSB position
+#define MDM_NSTS_LSB        12
+/// NSTS field width
+#define MDM_NSTS_WIDTH      ((u32)0x00000004)
+/// NSS field mask
+#define MDM_NSS_MASK        ((u32)0x00000F00)
+/// NSS field LSB position
+#define MDM_NSS_LSB         8
+/// NSS field width
+#define MDM_NSS_WIDTH       ((u32)0x00000004)
+/// NTX field mask
+#define MDM_NTX_MASK        ((u32)0x000000F0)
+/// NTX field LSB position
+#define MDM_NTX_LSB         4
+/// NTX field width
+#define MDM_NTX_WIDTH       ((u32)0x00000004)
+/// NRX field mask
+#define MDM_NRX_MASK        ((u32)0x0000000F)
+/// NRX field LSB position
+#define MDM_NRX_LSB         0
+/// NRX field width
+#define MDM_NRX_WIDTH       ((u32)0x00000004)
+
+#define __MDM_PHYCFG_FROM_VERS(v)  (((v) & MDM_RFMODE_MASK) >> MDM_RFMODE_LSB)
+
+#define RIU_FCU_PRESENT_MASK       ((u32)0xFF000000)
+#define RIU_FCU_PRESENT_LSB        24
+
+#define __RIU_FCU_PRESENT(v)  (((v) & RIU_FCU_PRESENT_MASK) >> RIU_FCU_PRESENT_LSB == 5)
+
+/// AGC load version field mask
+#define RIU_AGC_LOAD_MASK          ((u32)0x00C00000)
+/// AGC load version field LSB position
+#define RIU_AGC_LOAD_LSB           22
+
+#define __RIU_AGCLOAD_FROM_VERS(v) (((v) & RIU_AGC_LOAD_MASK) >> RIU_AGC_LOAD_LSB)
+
+#define __FPGA_TYPE(v)             (((v) & 0xFFFF0000) >> 16)
+
+#define __MDM_MAJOR_VERSION(v)     (((v) & 0xFF000000) >> 24)
+#define __MDM_MINOR_VERSION(v)     (((v) & 0x00FF0000) >> 16)
+
+
+#endif // _HAL_DESC_H_
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/ipc_compat.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/ipc_compat.h
new file mode 100755
index 0000000..4601cfa
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/ipc_compat.h
@@ -0,0 +1,25 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ipc_compat.h
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _IPC_H_
+#define _IPC_H_
+
+#define __INLINE static __attribute__((__always_inline__)) inline
+
+#define __ALIGN4 __aligned(4)
+
+#define ASSERT_ERR(condition)                                                           \
+    do {                                                                                \
+        if (unlikely(!(condition))) {                                                   \
+            printk(KERN_ERR "%s:%d:ASSERT_ERR(" #condition ")\n", __FILE__,  __LINE__); \
+        }                                                                               \
+    } while(0)
+
+#endif /* _IPC_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/ipc_host.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/ipc_host.c
new file mode 100755
index 0000000..7534be4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/ipc_host.c
@@ -0,0 +1,771 @@
+/**
+ ******************************************************************************
+ *
+ * @file ipc_host.c
+ *
+ * @brief IPC module.
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ******************************************************************************
+ */
+
+/*
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+#ifndef __KERNEL__
+#include <stdio.h>
+#define REG_SW_SET_PROFILING(env, value)   do{  }while(0)
+#define REG_SW_CLEAR_PROFILING(env, value)   do{  }while(0)
+#define REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env)   do{  }while(0)
+#define REG_SW_SET_HOSTBUF_IDX_PROFILING(env, val)   do{  }while(0)
+#else
+#include <linux/spinlock.h>
+#include "rwnx_defs.h"
+#include "rwnx_prof.h"
+#endif
+
+#include "reg_ipc_app.h"
+#include "ipc_host.h"
+
+/*
+ * TYPES DEFINITION
+ ******************************************************************************
+ */
+
+const int nx_txdesc_cnt[] =
+{
+    NX_TXDESC_CNT0,
+    NX_TXDESC_CNT1,
+    NX_TXDESC_CNT2,
+    NX_TXDESC_CNT3,
+    #if NX_TXQ_CNT == 5
+    NX_TXDESC_CNT4,
+    #endif
+};
+
+const int nx_txdesc_cnt_msk[] =
+{
+    NX_TXDESC_CNT0 - 1,
+    NX_TXDESC_CNT1 - 1,
+    NX_TXDESC_CNT2 - 1,
+    NX_TXDESC_CNT3 - 1,
+    #if NX_TXQ_CNT == 5
+    NX_TXDESC_CNT4 - 1,
+    #endif
+};
+
+const int nx_txuser_cnt[] =
+{
+    CONFIG_USER_MAX,
+    CONFIG_USER_MAX,
+    CONFIG_USER_MAX,
+    CONFIG_USER_MAX,
+    #if NX_TXQ_CNT == 5
+    1,
+    #endif
+};
+
+
+/*
+ * FUNCTIONS DEFINITIONS
+ ******************************************************************************
+ */
+/**
+ * ipc_host_rxdesc_handler() - Handle the reception of a Rx Descriptor
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_RXDESC is set
+ */
+static void ipc_host_rxdesc_handler(struct ipc_host_env_tag *env)
+{
+    // For profiling
+    REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_RXDESC);
+
+    // LMAC has triggered an IT saying that a reception has occurred.
+    // Then we first need to check the validity of the current hostbuf, and the validity
+    // of the next hostbufs too, because it is likely that several hostbufs have been
+    // filled within the time needed for this irq handling
+    do {
+        #ifdef CONFIG_RWNX_FULLMAC
+        // call the external function to indicate that a RX descriptor is received
+        if (env->cb.recv_data_ind(env->pthis,
+                                  env->ipc_host_rxdesc_array[env->ipc_host_rxdesc_idx].hostid) != 0)
+        #else
+        // call the external function to indicate that a RX packet is received
+        if (env->cb.recv_data_ind(env->pthis,
+                                  env->ipc_host_rxbuf_array[env->ipc_host_rxbuf_idx].hostid) != 0)
+        #endif //(CONFIG_RWNX_FULLMAC)
+            break;
+
+    }while(1);
+
+    // For profiling
+    REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_RXDESC);
+}
+
+/**
+ * ipc_host_radar_handler() - Handle the reception of radar events
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_RADAR is set
+ */
+static void ipc_host_radar_handler(struct ipc_host_env_tag *env)
+{
+#ifdef CONFIG_RWNX_RADAR
+    // LMAC has triggered an IT saying that a radar event has been sent to upper layer.
+    // Then we first need to check the validity of the current msg buf, and the validity
+    // of the next buffers too, because it is likely that several buffers have been
+    // filled within the time needed for this irq handling
+    // call the external function to indicate that a RX packet is received
+    spin_lock_bh(&((struct rwnx_hw *)env->pthis)->radar.lock);
+    while (env->cb.recv_radar_ind(env->pthis,
+              env->ipc_host_radarbuf_array[env->ipc_host_radarbuf_idx].hostid) == 0)
+        ;
+    spin_unlock_bh(&((struct rwnx_hw *)env->pthis)->radar.lock);
+#endif /* CONFIG_RWNX_RADAR */
+}
+
+/**
+ * ipc_host_unsup_rx_vec_handler() - Handle the reception of unsupported rx vector
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_UNSUP_RX_VEC is set
+ */
+static void ipc_host_unsup_rx_vec_handler(struct ipc_host_env_tag *env)
+{
+    while (env->cb.recv_unsup_rx_vec_ind(env->pthis,
+              env->ipc_host_unsuprxvecbuf_array[env->ipc_host_unsuprxvecbuf_idx].hostid) == 0)
+        ;
+}
+
+/**
+ * ipc_host_msg_handler() - Handler for firmware message
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_MSG is set
+ */
+static void ipc_host_msg_handler(struct ipc_host_env_tag *env)
+{
+    // For profiling
+    REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_MSG);
+
+    // LMAC has triggered an IT saying that a message has been sent to upper layer.
+    // Then we first need to check the validity of the current msg buf, and the validity
+    // of the next buffers too, because it is likely that several buffers have been
+    // filled within the time needed for this irq handling
+    // call the external function to indicate that a RX packet is received
+    while (env->cb.recv_msg_ind(env->pthis,
+                    env->ipc_host_msgbuf_array[env->ipc_host_msge2a_idx].hostid) == 0)
+        ;
+
+
+    // For profiling
+    REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_MSG);
+
+}
+
+/**
+ * ipc_host_msgack_handler() - Handle the reception of message acknowledgement
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_MSG_ACK is set
+ */
+static void ipc_host_msgack_handler(struct ipc_host_env_tag *env)
+{
+    void *hostid = env->msga2e_hostid;
+
+    ASSERT_ERR(hostid);
+    ASSERT_ERR(env->msga2e_cnt == (((struct lmac_msg *)(&env->shared->msg_a2e_buf.msg))->src_id & 0xFF));
+
+    env->msga2e_hostid = NULL;
+    env->msga2e_cnt++;
+    env->cb.recv_msgack_ind(env->pthis, hostid);
+}
+
+/**
+ * ipc_host_dbg_handler() - Handle the reception of Debug event
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_DBG is set
+ */
+static void ipc_host_dbg_handler(struct ipc_host_env_tag *env)
+{
+    // For profiling
+    REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_DBG);
+
+    // LMAC has triggered an IT saying that a DBG message has been sent to upper layer.
+    // Then we first need to check the validity of the current buffer, and the validity
+    // of the next buffers too, because it is likely that several buffers have been
+    // filled within the time needed for this irq handling
+    // call the external function to indicate that a RX packet is received
+    while(env->cb.recv_dbg_ind(env->pthis,
+            env->ipc_host_dbgbuf_array[env->ipc_host_dbg_idx].hostid) == 0)
+        ;
+
+    // For profiling
+    REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_DBG);
+}
+
+/**
+ * ipc_host_tx_cfm_handler() - Handle the reception of TX confirmation
+ *
+ * @env: pointer to the IPC Host environment
+ * @queue_idx: index of the hardware on which the confirmation has been received
+ * @user_pos: index of the user position
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_TXCFM is set
+ */
+static void ipc_host_tx_cfm_handler(struct ipc_host_env_tag *env,
+                                    const int queue_idx, const int user_pos)
+{
+    // TX confirmation descriptors have been received
+    REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_TXCFM);
+    while (1)
+    {
+        // Get the used index and increase it. We do the increase before knowing if the
+        // current buffer is confirmed because the callback function may call the
+        // ipc_host_txdesc_get() in case flow control was enabled and the index has to be
+        // already at the good value to ensure that the test of FIFO full is correct
+        uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos]++;
+        uint32_t used_idx_mod = used_idx & nx_txdesc_cnt_msk[queue_idx];
+        void *host_id = env->tx_host_id[queue_idx][user_pos][used_idx_mod];
+
+        // Reset the host id in the array
+        env->tx_host_id[queue_idx][user_pos][used_idx_mod] = 0;
+
+        // call the external function to indicate that a TX packet is freed
+        if (host_id == 0)
+        {
+            // No more confirmations, so put back the used index at its initial value
+            env->txdesc_used_idx[queue_idx][user_pos] = used_idx;
+            break;
+        }
+
+        if (env->cb.send_data_cfm(env->pthis, host_id) != 0)
+        {
+            // No more confirmations, so put back the used index at its initial value
+            env->txdesc_used_idx[queue_idx][user_pos] = used_idx;
+            env->tx_host_id[queue_idx][user_pos][used_idx_mod] = host_id;
+            // and exit the loop
+            break;
+        }
+
+        REG_SW_SET_PROFILING_CHAN(env->pthis, SW_PROF_CHAN_CTXT_CFM_HDL_BIT);
+        REG_SW_CLEAR_PROFILING_CHAN(env->pthis, SW_PROF_CHAN_CTXT_CFM_HDL_BIT);
+    }
+
+    REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_TXCFM);
+}
+
+/**
+ ******************************************************************************
+ */
+bool ipc_host_tx_frames_pending(struct ipc_host_env_tag *env)
+{
+    int i, j;
+    bool tx_frames_pending = false;
+
+    for (i = 0; (i < IPC_TXQUEUE_CNT) && !tx_frames_pending; i++)
+    {
+        for (j = 0; j < nx_txuser_cnt[i]; j++)
+        {
+            uint32_t used_idx = env->txdesc_used_idx[i][j];
+            uint32_t free_idx = env->txdesc_free_idx[i][j];
+
+            // Check if this queue is empty or not
+            if (used_idx != free_idx)
+            {
+                // The queue is not empty, update the flag and exit
+                tx_frames_pending = true;
+                break;
+            }
+        }
+    }
+
+    return (tx_frames_pending);
+}
+
+/**
+ ******************************************************************************
+ */
+void *ipc_host_tx_flush(struct ipc_host_env_tag *env, const int queue_idx, const int user_pos)
+{
+    uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos];
+    void *host_id = env->tx_host_id[queue_idx][user_pos][used_idx & nx_txdesc_cnt_msk[queue_idx]];
+
+    // call the external function to indicate that a TX packet is freed
+    if (host_id != 0)
+    {
+        // Reset the host id in the array
+        env->tx_host_id[queue_idx][user_pos][used_idx & nx_txdesc_cnt_msk[queue_idx]] = 0;
+
+        // Increment the used index
+        env->txdesc_used_idx[queue_idx][user_pos]++;
+    }
+
+    return (host_id);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_init(struct ipc_host_env_tag *env,
+                  struct ipc_host_cb_tag *cb,
+                  struct ipc_shared_env_tag *shared_env_ptr,
+                  void *pthis)
+{
+    unsigned int i;
+    unsigned int size;
+    unsigned int * dst;
+
+    // Reset the environments
+    // Reset the IPC Shared memory
+#if 0
+    /* check potential platform bug on multiple stores */
+    memset(shared_env_ptr, 0, sizeof(struct ipc_shared_env_tag));
+#else
+    dst = (unsigned int *)shared_env_ptr;
+    size = (unsigned int)sizeof(struct ipc_shared_env_tag);
+    for (i=0; i < size; i+=4)
+    {
+        *dst++ = 0;
+    }
+#endif
+    // Reset the IPC Host environment
+    memset(env, 0, sizeof(struct ipc_host_env_tag));
+
+    // Initialize the shared environment pointer
+    env->shared = shared_env_ptr;
+
+    // Save the callbacks in our own environment
+    env->cb = *cb;
+
+    // Save the pointer to the register base
+    env->pthis = pthis;
+
+    // Initialize buffers numbers and buffers sizes needed for DMA Receptions
+    env->rx_bufnb = IPC_RXBUF_CNT;
+    #ifdef CONFIG_RWNX_FULLMAC
+    env->rxdesc_nb = IPC_RXDESC_CNT;
+    #endif //(CONFIG_RWNX_FULLMAC)
+    env->radar_bufnb = IPC_RADARBUF_CNT;
+    env->radar_bufsz = sizeof(struct radar_pulse_array_desc);
+    env->unsuprxvec_bufnb = IPC_UNSUPRXVECBUF_CNT;
+    env->unsuprxvec_bufsz = max(sizeof(struct rx_vector_desc), (size_t) RADIOTAP_HDR_MAX_LEN) +
+                            RADIOTAP_HDR_VEND_MAX_LEN +  UNSUP_RX_VEC_DATA_LEN;
+    env->ipc_e2amsg_bufnb = IPC_MSGE2A_BUF_CNT;
+    env->ipc_e2amsg_bufsz = sizeof(struct ipc_e2a_msg);
+    env->ipc_dbg_bufnb = IPC_DBGBUF_CNT;
+    env->ipc_dbg_bufsz = sizeof(struct ipc_dbg_msg);
+
+    for (i = 0; i < CONFIG_USER_MAX; i++)
+    {
+        // Initialize the pointers to the hostid arrays
+        env->tx_host_id[0][i] = env->tx_host_id0[i];
+        env->tx_host_id[1][i] = env->tx_host_id1[i];
+        env->tx_host_id[2][i] = env->tx_host_id2[i];
+        env->tx_host_id[3][i] = env->tx_host_id3[i];
+        #if NX_TXQ_CNT == 5
+        env->tx_host_id[4][i] = NULL;
+        #endif
+
+        // Initialize the pointers to the TX descriptor arrays
+        env->txdesc[0][i] = shared_env_ptr->txdesc0[i];
+        env->txdesc[1][i] = shared_env_ptr->txdesc1[i];
+        env->txdesc[2][i] = shared_env_ptr->txdesc2[i];
+        env->txdesc[3][i] = shared_env_ptr->txdesc3[i];
+        #if NX_TXQ_CNT == 5
+        env->txdesc[4][i] = NULL;
+        #endif
+    }
+
+    #if NX_TXQ_CNT == 5
+    env->tx_host_id[4][0] = env->tx_host_id4[0];
+    env->txdesc[4][0] = shared_env_ptr->txdesc4[0];
+    #endif
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_patt_addr_push(struct ipc_host_env_tag *env, uint32_t addr)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    // Copy the address
+    shared_env_ptr->pattern_addr = addr;
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_rxbuf_push(struct ipc_host_env_tag *env,
+#ifdef CONFIG_RWNX_FULLMAC
+                        uint32_t hostid,
+#endif
+                        uint32_t hostbuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env->pthis);
+    REG_SW_SET_HOSTBUF_IDX_PROFILING(env->pthis, env->ipc_host_rxbuf_idx);
+
+#ifdef CONFIG_RWNX_FULLMAC
+    // Copy the hostbuf (DMA address) in the ipc shared memory
+    shared_env_ptr->host_rxbuf[env->ipc_host_rxbuf_idx].hostid   = hostid;
+    shared_env_ptr->host_rxbuf[env->ipc_host_rxbuf_idx].dma_addr = hostbuf;
+#else
+    // Save the hostid and the hostbuf in global array
+    env->ipc_host_rxbuf_array[env->ipc_host_rxbuf_idx].hostid = hostid;
+    env->ipc_host_rxbuf_array[env->ipc_host_rxbuf_idx].dma_addr = hostbuf;
+
+    shared_env_ptr->host_rxbuf[env->ipc_host_rxbuf_idx] = hostbuf;
+#endif //(CONFIG_RWNX_FULLMAC)
+
+    // Signal to the embedded CPU that at least one buffer is available
+    ipc_app2emb_trigger_set(shared_env_ptr, IPC_IRQ_A2E_RXBUF_BACK);
+
+    // Increment the array index
+    env->ipc_host_rxbuf_idx = (env->ipc_host_rxbuf_idx +1)%IPC_RXBUF_CNT;
+
+    return (0);
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+/**
+ ******************************************************************************
+ */
+int ipc_host_rxdesc_push(struct ipc_host_env_tag *env, void *hostid,
+                         uint32_t hostbuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    // Reset the RX Descriptor DMA Address and increment the counter
+    env->ipc_host_rxdesc_array[env->ipc_host_rxdesc_idx].dma_addr = hostbuf;
+    env->ipc_host_rxdesc_array[env->ipc_host_rxdesc_idx].hostid = hostid;
+
+    shared_env_ptr->host_rxdesc[env->ipc_host_rxdesc_idx].dma_addr = hostbuf;
+
+    // Signal to the embedded CPU that at least one descriptor is available
+    ipc_app2emb_trigger_set(shared_env_ptr, IPC_IRQ_A2E_RXDESC_BACK);
+
+    env->ipc_host_rxdesc_idx = (env->ipc_host_rxdesc_idx + 1) % IPC_RXDESC_CNT;
+
+    return (0);
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_radarbuf_push(struct ipc_host_env_tag *env, void *hostid,
+                           uint32_t hostbuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    // Save the hostid and the hostbuf in global array
+    env->ipc_host_radarbuf_array[env->ipc_host_radarbuf_idx].hostid = hostid;
+    env->ipc_host_radarbuf_array[env->ipc_host_radarbuf_idx].dma_addr = hostbuf;
+
+    // Copy the hostbuf (DMA address) in the ipc shared memory
+    shared_env_ptr->radarbuf_hostbuf[env->ipc_host_radarbuf_idx] = hostbuf;
+
+    // Increment the array index
+    env->ipc_host_radarbuf_idx = (env->ipc_host_radarbuf_idx +1)%IPC_RADARBUF_CNT;
+
+    return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+
+int ipc_host_unsup_rx_vec_buf_push(struct ipc_host_env_tag *env,
+                                   void *hostid,
+                                   uint32_t hostbuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    env->ipc_host_unsuprxvecbuf_array[env->ipc_host_unsuprxvecbuf_idx].hostid = hostid;
+    env->ipc_host_unsuprxvecbuf_array[env->ipc_host_unsuprxvecbuf_idx].dma_addr = hostbuf;
+
+    // Copy the hostbuf (DMA address) in the ipc shared memory
+    shared_env_ptr->unsuprxvecbuf_hostbuf[env->ipc_host_unsuprxvecbuf_idx] = hostbuf;
+
+    // Increment the array index
+    env->ipc_host_unsuprxvecbuf_idx = (env->ipc_host_unsuprxvecbuf_idx + 1)%IPC_UNSUPRXVECBUF_CNT;
+
+    return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_msgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+                         uint32_t hostbuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    // Save the hostid and the hostbuf in global array
+    env->ipc_host_msgbuf_array[env->ipc_host_msge2a_idx].hostid = hostid;
+    env->ipc_host_msgbuf_array[env->ipc_host_msge2a_idx].dma_addr = hostbuf;
+
+    // Copy the hostbuf (DMA address) in the ipc shared memory
+    shared_env_ptr->msg_e2a_hostbuf_addr[env->ipc_host_msge2a_idx] = hostbuf;
+
+    // Increment the array index
+    env->ipc_host_msge2a_idx = (env->ipc_host_msge2a_idx +1)%IPC_MSGE2A_BUF_CNT;
+
+    return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_dbgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+                         uint32_t hostbuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    // Save the hostid and the hostbuf in global array
+    env->ipc_host_dbgbuf_array[env->ipc_host_dbg_idx].hostid = hostid;
+    env->ipc_host_dbgbuf_array[env->ipc_host_dbg_idx].dma_addr = hostbuf;
+
+    // Copy the hostbuf (DMA address) in the ipc shared memory
+    shared_env_ptr->dbg_hostbuf_addr[env->ipc_host_dbg_idx] = hostbuf;
+
+    // Increment the array index
+    env->ipc_host_dbg_idx = (env->ipc_host_dbg_idx +1)%IPC_DBGBUF_CNT;
+
+    return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_dbginfobuf_push(struct ipc_host_env_tag *env, uint32_t infobuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    // Copy the hostbuf (DMA address) in the ipc shared memory
+    shared_env_ptr->la_dbginfo_addr = infobuf;
+}
+
+/**
+ ******************************************************************************
+ */
+volatile struct txdesc_host *ipc_host_txdesc_get(struct ipc_host_env_tag *env, const int queue_idx, const int user_pos)
+{
+    volatile struct txdesc_host *txdesc_free;
+    uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos];
+    uint32_t free_idx = env->txdesc_free_idx[queue_idx][user_pos];
+
+    ASSERT_ERR(queue_idx < IPC_TXQUEUE_CNT);
+    ASSERT_ERR((free_idx - used_idx) <= nx_txdesc_cnt[queue_idx]);
+
+    // Check if a free descriptor is available
+    if (free_idx != (used_idx + nx_txdesc_cnt[queue_idx]))
+    {
+        // Get the pointer to the first free descriptor
+        txdesc_free = env->txdesc[queue_idx][user_pos] + (free_idx & nx_txdesc_cnt_msk[queue_idx]);
+    }
+    else
+    {
+        txdesc_free = NULL;
+    }
+
+    return txdesc_free;
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_txdesc_push(struct ipc_host_env_tag *env, const int queue_idx,
+                          const int user_pos, void *host_id)
+{
+    uint32_t free_idx = env->txdesc_free_idx[queue_idx][user_pos] & nx_txdesc_cnt_msk[queue_idx];
+    volatile struct txdesc_host *txdesc_pushed = env->txdesc[queue_idx][user_pos] + free_idx;
+
+
+    // Descriptor is now ready
+    txdesc_pushed->ready = 0xFFFFFFFF;
+
+    // Save the host id in the environment
+    env->tx_host_id[queue_idx][user_pos][free_idx] = host_id;
+
+    // Increment the index
+    env->txdesc_free_idx[queue_idx][user_pos]++;
+
+    // trigger interrupt!!!
+    //REG_SW_SET_PROFILING(env->pthis, CO_BIT(queue_idx+SW_PROF_IRQ_A2E_TXDESC_FIRSTBIT));
+    ipc_app2emb_trigger_setf(env->shared, CO_BIT(user_pos + queue_idx * CONFIG_USER_MAX +
+                                                 IPC_IRQ_A2E_TXDESC_FIRSTBIT));
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_irq(struct ipc_host_env_tag *env, uint32_t status)
+{
+    // Acknowledge the pending interrupts
+    ipc_emb2app_ack_clear(env->shared, status);
+    // And re-read the status, just to be sure that the acknowledgment is
+    // effective when we start the interrupt handling
+    ipc_emb2app_status_get(env->shared);
+
+    // Optimized for only one IRQ at a time
+    if (status & IPC_IRQ_E2A_RXDESC)
+    {
+        // handle the RX descriptor reception
+        ipc_host_rxdesc_handler(env);
+    }
+    if (status & IPC_IRQ_E2A_MSG_ACK)
+    {
+        ipc_host_msgack_handler(env);
+    }
+    if (status & IPC_IRQ_E2A_MSG)
+    {
+        ipc_host_msg_handler(env);
+    }
+    if (status & IPC_IRQ_E2A_TXCFM)
+    {
+        int i;
+
+#ifdef __KERNEL__
+        spin_lock_bh(&((struct rwnx_hw *)env->pthis)->tx_lock);
+#endif
+        // handle the TX confirmation reception
+        for (i = 0; i < IPC_TXQUEUE_CNT; i++)
+        {
+            int j = 0;
+#ifdef CONFIG_RWNX_MUMIMO_TX
+            for (; j < nx_txuser_cnt[i]; j++)
+#endif
+            {
+                uint32_t q_bit = CO_BIT(j + i * CONFIG_USER_MAX + IPC_IRQ_E2A_TXCFM_POS);
+                if (status & q_bit)
+                {
+                    // handle the confirmation
+                    ipc_host_tx_cfm_handler(env, i, j);
+                }
+            }
+        }
+#ifdef __KERNEL__
+        spin_unlock_bh(&((struct rwnx_hw *)env->pthis)->tx_lock);
+#endif
+    }
+    if (status & IPC_IRQ_E2A_RADAR)
+    {
+        // handle the radar event reception
+        ipc_host_radar_handler(env);
+    }
+
+    if (status & IPC_IRQ_E2A_UNSUP_RX_VEC)
+    {
+        // handle the unsupported rx vector reception
+        ipc_host_unsup_rx_vec_handler(env);
+    }
+
+    if (status & IPC_IRQ_E2A_DBG)
+    {
+        ipc_host_dbg_handler(env);
+    }
+
+    if (status & IPC_IRQ_E2A_TBTT_PRIM)
+    {
+        env->cb.prim_tbtt_ind(env->pthis);
+    }
+
+    if (status & IPC_IRQ_E2A_TBTT_SEC)
+    {
+        env->cb.sec_tbtt_ind(env->pthis);
+    }
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_msg_push(struct ipc_host_env_tag *env, void *msg_buf, uint16_t len)
+{
+    int i;
+    uint32_t *src, *dst;
+
+    REG_SW_SET_PROFILING(env->pthis, SW_PROF_IPC_MSGPUSH);
+
+    ASSERT_ERR(!env->msga2e_hostid);
+    ASSERT_ERR(round_up(len, 4) <= sizeof(env->shared->msg_a2e_buf.msg));
+
+    // Copy the message into the IPC MSG buffer
+#ifdef __KERNEL__
+    src = (uint32_t*)((struct rwnx_cmd *)msg_buf)->a2e_msg;
+#else
+    src = (uint32_t*) msg_buf;
+#endif
+    dst = (uint32_t*)&(env->shared->msg_a2e_buf.msg);
+
+    // Copy the message in the IPC queue
+    for (i=0; i<len; i+=4)
+    {
+        *dst++ = *src++;
+    }
+
+    env->msga2e_hostid = msg_buf;
+
+    // Trigger the irq to send the message to EMB
+    ipc_app2emb_trigger_set(env->shared, IPC_IRQ_A2E_MSG);
+
+    REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IPC_MSGPUSH);
+
+    return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_enable_irq(struct ipc_host_env_tag *env, uint32_t value)
+{
+    // Enable the handled interrupts
+    ipc_emb2app_unmask_set(env->shared, value);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_disable_irq(struct ipc_host_env_tag *env, uint32_t value)
+{
+    // Enable the handled interrupts
+    ipc_emb2app_unmask_clear(env->shared, value);
+}
+
+/**
+ ******************************************************************************
+ */
+uint32_t ipc_host_get_status(struct ipc_host_env_tag *env)
+{
+    volatile uint32_t status;
+
+    status = ipc_emb2app_status_get(env->shared);
+
+    return status;
+}
+
+/**
+ ******************************************************************************
+ */
+uint32_t ipc_host_get_rawstatus(struct ipc_host_env_tag *env)
+{
+    volatile uint32_t rawstatus;
+
+    rawstatus = ipc_emb2app_rawstatus_get(env->shared);
+
+    return rawstatus;
+}
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/ipc_host.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/ipc_host.h
new file mode 100755
index 0000000..c008a31
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/ipc_host.h
@@ -0,0 +1,508 @@
+/**
+ ******************************************************************************
+ *
+ * @file ipc_host.h
+ *
+ * @brief IPC module.
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ******************************************************************************
+ */
+#ifndef _IPC_HOST_H_
+#define _IPC_HOST_H_
+
+/*
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+#include "ipc_shared.h"
+#ifndef __KERNEL__
+#include "arch.h"
+#else
+#include "ipc_compat.h"
+#endif
+
+/*
+ * ENUMERATION
+ ******************************************************************************
+ */
+
+enum ipc_host_desc_status
+{
+    /// Descriptor is IDLE
+    IPC_HOST_DESC_IDLE      = 0,
+    /// Data can be forwarded
+    IPC_HOST_DESC_FORWARD,
+    /// Data has to be kept in UMAC memory
+    IPC_HOST_DESC_KEEP,
+    /// Delete stored packet
+    IPC_HOST_DESC_DELETE,
+    /// Update Frame Length status
+    IPC_HOST_DESC_LEN_UPDATE,
+};
+
+/**
+ ******************************************************************************
+ * @brief This structure is used to initialize the MAC SW
+ *
+ * The WLAN device driver provides functions call-back with this structure
+ ******************************************************************************
+ */
+struct ipc_host_cb_tag
+{
+    /// WLAN driver call-back function: send_data_cfm
+    int (*send_data_cfm)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: recv_data_ind
+    uint8_t (*recv_data_ind)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: recv_radar_ind
+    uint8_t (*recv_radar_ind)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: recv_unsup_rx_vec_ind
+    uint8_t (*recv_unsup_rx_vec_ind)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: recv_msg_ind
+    uint8_t (*recv_msg_ind)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: recv_msgack_ind
+    uint8_t (*recv_msgack_ind)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: recv_dbg_ind
+    uint8_t (*recv_dbg_ind)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: prim_tbtt_ind
+    void (*prim_tbtt_ind)(void *pthis);
+
+    /// WLAN driver call-back function: sec_tbtt_ind
+    void (*sec_tbtt_ind)(void *pthis);
+
+};
+
+/*
+ * Struct used to store information about host buffers (DMA Address and local pointer)
+ */
+struct ipc_hostbuf
+{
+    void    *hostid;     ///< ptr to hostbuf client (ipc_host client) structure
+    uint32_t dma_addr;   ///< ptr to real hostbuf dma address
+};
+
+/// Definition of the IPC Host environment structure.
+struct ipc_host_env_tag
+{
+    /// Structure containing the callback pointers
+    struct ipc_host_cb_tag cb;
+
+    /// Pointer to the shared environment
+    struct ipc_shared_env_tag *shared;
+
+    #ifdef CONFIG_RWNX_FULLMAC
+    // Array used to store the descriptor addresses
+    struct ipc_hostbuf ipc_host_rxdesc_array[IPC_RXDESC_CNT];
+    // Index of the host RX descriptor array (ipc_shared environment)
+    uint8_t ipc_host_rxdesc_idx;
+    /// Store the number of RX Descriptors
+    uint8_t rxdesc_nb;
+    #endif //(CONFIG_RWNX_FULLMAC)
+
+    /// Fields for Data Rx handling
+    // Index used for ipc_host_rxbuf_array to point to current buffer
+    uint8_t ipc_host_rxbuf_idx;
+    // Store the number of Rx Data buffers
+    uint32_t rx_bufnb;
+    // Store the size of the Rx Data buffers
+    uint32_t rx_bufsz;
+
+    /// Fields for Radar events handling
+    // Global array used to store the hostid and hostbuf addresses
+    struct ipc_hostbuf ipc_host_radarbuf_array[IPC_RADARBUF_CNT];
+    // Index used for ipc_host_rxbuf_array to point to current buffer
+    uint8_t ipc_host_radarbuf_idx;
+    // Store the number of radar event buffers
+    uint32_t radar_bufnb;
+    // Store the size of the radar event buffers
+    uint32_t radar_bufsz;
+
+    ///Fields for Unsupported frame handling
+    // Global array used to store the hostid and hostbuf addresses
+    struct ipc_hostbuf ipc_host_unsuprxvecbuf_array[IPC_UNSUPRXVECBUF_CNT];
+    // Index used for ipc_host_unsuprxvecbuf_array to point to current buffer
+    uint8_t ipc_host_unsuprxvecbuf_idx;
+    // Store the number of unsupported rx vector buffers
+    uint32_t unsuprxvec_bufnb;
+    // Store the size of unsupported rx vector buffers
+    uint32_t unsuprxvec_bufsz;
+
+    // Index used that points to the first free TX desc
+    uint32_t txdesc_free_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+    // Index used that points to the first used TX desc
+    uint32_t txdesc_used_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+    // Array storing the currently pushed host ids for the BK queue
+    void *tx_host_id0[CONFIG_USER_MAX][NX_TXDESC_CNT0];
+    // Array storing the currently pushed host ids for the BE queue
+    void *tx_host_id1[CONFIG_USER_MAX][NX_TXDESC_CNT1];
+    // Array storing the currently pushed host ids for the VI queue
+    void *tx_host_id2[CONFIG_USER_MAX][NX_TXDESC_CNT2];
+    // Array storing the currently pushed host ids for the VO queue
+    void *tx_host_id3[CONFIG_USER_MAX][NX_TXDESC_CNT3];
+    #if NX_TXQ_CNT == 5
+    // Array storing the currently pushed host ids for the BCN queue
+    void *tx_host_id4[1][NX_TXDESC_CNT4];
+    #endif
+    // Pointer to the different host ids arrays, per IPC queue
+    void **tx_host_id[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+    // Pointer to the different TX descriptor arrays, per IPC queue
+    volatile struct txdesc_host *txdesc[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+
+    /// Fields for Emb->App MSGs handling
+    // Global array used to store the hostid and hostbuf addresses for msg/ind
+    struct ipc_hostbuf ipc_host_msgbuf_array[IPC_MSGE2A_BUF_CNT];
+    // Index of the MSG E2A buffers array to point to current buffer
+    uint8_t ipc_host_msge2a_idx;
+    // Store the number of E2A MSG buffers
+    uint32_t ipc_e2amsg_bufnb;
+    // Store the size of the E2A MSG buffers
+    uint32_t ipc_e2amsg_bufsz;
+
+    /// E2A ACKs of A2E MSGs
+    uint8_t msga2e_cnt;
+    void *msga2e_hostid;
+
+    /// Fields for Debug MSGs handling
+    // Global array used to store the hostid and hostbuf addresses for Debug messages
+    struct ipc_hostbuf ipc_host_dbgbuf_array[IPC_DBGBUF_CNT];
+    // Index of the Debug messages buffers array to point to current buffer
+    uint8_t ipc_host_dbg_idx;
+    // Store the number of Debug messages buffers
+    uint32_t ipc_dbg_bufnb;
+    // Store the size of the Debug messages buffers
+    uint32_t ipc_dbg_bufsz;
+
+    /// Pointer to the attached object (used in callbacks and register accesses)
+    void *pthis;
+};
+
+extern const int nx_txdesc_cnt[];
+extern const int nx_txuser_cnt[];
+
+/**
+ ******************************************************************************
+ * @brief Returns the full/not full status of the queue the index of which is
+ * passed as parameter.
+ *
+ * @param[in]   env       Pointer to the IPC host environment
+ * @param[in]   queue_idx Index of the queue to be checked
+ *
+ * @return true if the queue is full, false otherwise
+ *
+ ******************************************************************************
+ */
+__INLINE bool ipc_host_queue_full(struct ipc_host_env_tag *env,
+                                  const int queue_idx)
+{
+    return (env->txdesc_free_idx[queue_idx] ==
+                      (env->txdesc_used_idx[queue_idx] + nx_txdesc_cnt[queue_idx]));
+}
+
+/**
+ ******************************************************************************
+ * @brief Initialize the IPC running on the Application CPU.
+ *
+ * This function:
+ *   - initializes the IPC software environments
+ *   - enables the interrupts in the IPC block
+ *
+ * @param[in]   env   Pointer to the IPC host environment
+ *
+ * @warning Since this function resets the IPC Shared memory, it must be called
+ * before the LMAC FW is launched because LMAC sets some init values in IPC
+ * Shared memory at boot.
+ *
+ ******************************************************************************
+ */
+void ipc_host_init(struct ipc_host_env_tag *env,
+                  struct ipc_host_cb_tag *cb,
+                  struct ipc_shared_env_tag *shared_env_ptr,
+                  void *pthis);
+
+/** @addtogroup IPC_TX
+ *  @{
+ */
+
+/**
+ ******************************************************************************
+ * @brief Retrieve a new free Tx descriptor (host side).
+ *
+ * This function returns a pointer to the next Tx descriptor available from the
+ * queue queue_idx to the host driver. The driver will have to fill it with the
+ * appropriate endianness and to send it to the
+ * emb side with ipc_host_txdesc_push().
+ *
+ * This function should only be called once until ipc_host_txdesc_push() is called.
+ *
+ * This function will return NULL if the queue is full.
+ *
+ * @param[in]   env   Pointer to the IPC host environment
+ * @param[in]   queue_idx   Queue index. The index can be inferred from the
+ *                          user priority of the incoming packet.
+ * @param[in]   user_pos    User position. If MU-MIMO is not used, this value
+ *                          shall be 0.
+ * @return                  Pointer to the next Tx descriptor free. This can
+ *                          point to the host memory or to shared memory,
+ *                          depending on IPC implementation.
+ *
+ ******************************************************************************
+ */
+volatile struct txdesc_host *ipc_host_txdesc_get(struct ipc_host_env_tag *env,
+                                                 const int queue_idx,
+                                                 const int user_pos);
+
+
+/**
+ ******************************************************************************
+ * @brief Push a filled Tx descriptor (host side).
+ *
+ * This function sets the next Tx descriptor available by the host side:
+ * - as used for the host side
+ * - as available for the emb side.
+ * The Tx descriptor must be correctly filled before calling this function.
+ *
+ * This function may trigger an IRQ to the emb CPU depending on the interrupt
+ * mitigation policy and on the push count.
+ *
+ * @param[in]   env   Pointer to the IPC host environment
+ * @param[in]   queue_idx   Queue index. Same value than ipc_host_txdesc_get()
+ * @param[in]   user_pos    User position. If MU-MIMO is not used, this value
+ *                          shall be 0.
+ * @param[in]   host_id     Parameter indicated by the IPC at TX confirmation,
+ *                          that allows the driver finding the buffer
+ *
+ ******************************************************************************
+ */
+void ipc_host_txdesc_push(struct ipc_host_env_tag *env, const int queue_idx,
+                          const int user_pos, void *host_id);
+
+
+/**
+ ******************************************************************************
+ * @brief Check if there are TX frames pending in the TX queues.
+ *
+ * @param[in]   env   Pointer to the IPC host environment
+ *
+ * @return true if there are frames pending, false otherwise.
+ *
+ ******************************************************************************
+ */
+bool ipc_host_tx_frames_pending(struct ipc_host_env_tag *env);
+
+/**
+ ******************************************************************************
+ * @brief Get and flush a packet from the IPC queue passed as parameter.
+ *
+ * @param[in]   env        Pointer to the IPC host environment
+ * @param[in]   queue_idx  Index of the queue to flush
+ * @param[in]   user_pos   User position to flush
+ *
+ * @return The flushed hostid if there is one, 0 otherwise.
+ *
+ ******************************************************************************
+ */
+void *ipc_host_tx_flush(struct ipc_host_env_tag *env, const int queue_idx,
+                        const int user_pos);
+
+/// @} IPC_TX
+
+/** @addtogroup IPC_RX
+ *  @{
+ */
+void ipc_host_patt_addr_push(struct ipc_host_env_tag *env, uint32_t addr);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated buffer descriptor for Rx packet (host side)
+ *
+ * This function should be called by the host IRQ handler to supply the
+ * embedded side with new empty buffer.
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   hostid      Packet ID used by the host (skbuff pointer on Linux)
+ * @param[in]   hostbuf     Pointer to the start of the buffer payload in the
+ *                          host memory (this may be inferred from the skbuff?)
+ *                          The length of this buffer should be predefined
+ *                          between host and emb statically (constant needed?).
+ *
+ ******************************************************************************
+ */
+int ipc_host_rxbuf_push(struct ipc_host_env_tag *env,
+#ifdef CONFIG_RWNX_FULLMAC
+                        uint32_t hostid,
+#endif
+                        uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated Descriptor
+ *
+ * This function should be called by the host IRQ handler to supply the
+ * embedded side with new empty buffer.
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   hostid      Address of packet for host
+ * @param[in]   hostbuf     Pointer to the start of the buffer payload in the
+ *                          host memory. The length of this buffer should be
+ *                          predefined between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_rxdesc_push(struct ipc_host_env_tag *env, void *hostid,
+                         uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated radar event buffer descriptor
+ *
+ * This function is called at Init time to initialize all radar event buffers.
+ * Then each time embedded send a radar event, this function is used to push
+ * back the same buffer once it has been handled.
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   hostid      Address of packet for host
+ * @param[in]   hostbuf     Pointer to the start of the buffer payload in the
+ *                          host memory. The length of this buffer should be
+ *                          predefined between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_radarbuf_push(struct ipc_host_env_tag *env, void *hostid,
+                           uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated unsupported rx vector buffer descriptor
+ *
+ * This function is called at Init time to initialize all unsupported rx vector
+ * buffers. Then each time the embedded sends a unsupported rx vector, this
+ * function is used to push a new unsupported rx vector buffer.
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   hostid      Address of packet for host
+ * @param[in]   hostbuf     Pointer to the start of the buffer payload in the
+ *                          host memory. The length of this buffer should be
+ *                          predefined between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_unsup_rx_vec_buf_push(struct ipc_host_env_tag *env, void *hostid,
+                                    uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated buffer descriptor for IPC MSGs (host side)
+ *
+ * This function is called at Init time to initialize all Emb2App messages
+ * buffers. Then each time embedded send a IPC message, this function is used
+ * to push back the same buffer once it has been handled.
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   hostid      Address of buffer for host
+ * @param[in]   hostbuf     Address of buffer for embedded
+ *                          The length of this buffer should be predefined
+ *                          between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_msgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+                         uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated buffer descriptor for Debug messages (host side)
+ *
+ * This function is called at Init time to initialize all debug messages.
+ * Then each time embedded send a debug message, this function is used to push
+ * back the same buffer once it has been handled.
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   hostid      Address of buffer for host
+ * @param[in]   hostbuf     Address of buffer for embedded
+ *                          The length of this buffer should be predefined
+ *                          between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_dbgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+                         uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push the pre-allocated logic analyzer and debug information buffer
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   infobuf     Address of buffer for embedded
+ *                          The length of this buffer should be predefined
+ *                          between host and emb statically.
+ *
+ ******************************************************************************
+ */
+void ipc_host_dbginfobuf_push(struct ipc_host_env_tag *env, uint32_t infobuf);
+
+/// @} IPC_RX
+
+
+
+/** @addtogroup IPC_MISC
+ *  @{
+ */
+
+/**
+ ******************************************************************************
+ * @brief Handle all IPC interrupts on the host side.
+ *
+ * The following interrupts should be handled:
+ * Tx confirmation, Rx buffer requests, Rx packet ready and kernel messages
+ *
+ * @param[in]   env   Pointer to the IPC host environment
+ *
+ ******************************************************************************
+ */
+void ipc_host_irq(struct ipc_host_env_tag *env, uint32_t status);
+
+/**
+ ******************************************************************************
+ * @brief Send a message to the embedded side
+ *
+ * @param[in]   env      Pointer to the IPC host environment
+ * @param[in]   msg_buf  Pointer to the message buffer
+ * @param[in]   msg_len  Length of the message to be transmitted
+ *
+ * @return      Non-null value on failure
+ *
+ ******************************************************************************
+ */
+int ipc_host_msg_push(struct ipc_host_env_tag *env, void *msg_buf, uint16_t len);
+
+/**
+ ******************************************************************************
+ * @brief Enable IPC interrupts
+ *
+ * @param[in]   env  Global ipc_host environment pointer
+ * @param[in]   value  Bitfield of the interrupts to enable
+ *
+ * @warning After calling this function, IPC interrupts can be triggered at any
+ * time. Potentially, an interrupt could happen even before returning from the
+ * function if there is a request pending from the embedded side.
+ *
+ ******************************************************************************
+ */
+void ipc_host_enable_irq(struct ipc_host_env_tag *env, uint32_t value);
+void ipc_host_disable_irq(struct ipc_host_env_tag *env, uint32_t value);
+
+uint32_t ipc_host_get_status(struct ipc_host_env_tag *env);
+uint32_t ipc_host_get_rawstatus(struct ipc_host_env_tag *env);
+
+/// @} IPC_MISC
+
+
+#endif // _IPC_HOST_H_
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/ipc_shared.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/ipc_shared.h
new file mode 100755
index 0000000..f904243
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/ipc_shared.h
@@ -0,0 +1,803 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ipc_shared.h
+ *
+ * @brief Shared data between both IPC modules.
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _IPC_SHARED_H_
+#define _IPC_SHARED_H_
+
+/*
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+#include "ipc_compat.h"
+#include "lmac_mac.h"
+
+/*
+ * DEFINES AND MACROS
+ ****************************************************************************************
+ */
+#define CO_BIT(pos) (1U<<(pos))
+
+#define IPC_TXQUEUE_CNT     NX_TXQ_CNT
+#define NX_TXDESC_CNT0      8
+#define NX_TXDESC_CNT1      64
+#define NX_TXDESC_CNT2      64
+#define NX_TXDESC_CNT3      32
+#if NX_TXQ_CNT == 5
+#define NX_TXDESC_CNT4      8
+#endif
+
+/*
+ * Number of Host buffers available for Data Rx handling (through DMA)
+ */
+#define IPC_RXBUF_CNT       128
+
+/*
+ * Number of shared descriptors available for Data RX handling
+ */
+#define IPC_RXDESC_CNT      128
+
+/*
+ * Number of Host buffers available for Radar events handling (through DMA)
+ */
+#define IPC_RADARBUF_CNT       16
+
+/*
+ * Number of Host buffers available for unsupported Rx vectors handling (through DMA)
+ */
+#define IPC_UNSUPRXVECBUF_CNT       8
+
+/*
+ *  Size of RxVector
+ */
+#define IPC_RXVEC_SIZE      16
+
+/*
+ * Number of Host buffers available for Emb->App MSGs sending (through DMA)
+ */
+#ifdef CONFIG_RWNX_FULLMAC
+#define IPC_MSGE2A_BUF_CNT       64
+#endif
+/*
+ * Number of Host buffers available for Debug Messages sending (through DMA)
+ */
+#define IPC_DBGBUF_CNT       32
+
+/*
+ * Length used in MSGs structures
+ */
+#define IPC_A2E_MSG_BUF_SIZE    127 // size in 4-byte words
+#ifdef CONFIG_RWNX_FULLMAC
+#define IPC_E2A_MSG_SIZE_BASE   256 // size in 4-byte words
+#endif
+
+#ifdef CONFIG_RWNX_TL4
+#define IPC_E2A_MSG_PARAM_SIZE  (IPC_E2A_MSG_SIZE_BASE + (IPC_E2A_MSG_SIZE_BASE / 2))
+#else
+#define IPC_E2A_MSG_PARAM_SIZE  IPC_E2A_MSG_SIZE_BASE
+#endif
+
+/*
+ * Debug messages buffers size (in bytes)
+ */
+#define IPC_DBG_PARAM_SIZE       256
+
+/*
+ * Define used for Rx hostbuf validity.
+ * This value should appear only when hostbuf was used for a Reception.
+ */
+#define RX_DMA_OVER_PATTERN 0xAAAAAA00
+
+/*
+ * Define used for MSG buffers validity.
+ * This value will be written only when a MSG buffer is used for sending from Emb to App.
+ */
+#define IPC_MSGE2A_VALID_PATTERN 0xADDEDE2A
+
+/*
+ * Define used for Debug messages buffers validity.
+ * This value will be written only when a DBG buffer is used for sending from Emb to App.
+ */
+#define IPC_DBG_VALID_PATTERN 0x000CACA0
+
+/*
+ *  Length of the receive vectors, in bytes
+ */
+#define DMA_HDR_PHYVECT_LEN    36
+
+/*
+ * Maximum number of payload addresses and lengths present in the descriptor
+ */
+#define NX_TX_PAYLOAD_MAX      6
+
+/*
+ * Message struct/ID API version
+ */
+#define MSG_API_VER  15
+
+/*
+ ****************************************************************************************
+ */
+// c.f LMAC/src/tx/tx_swdesc.h
+/// Descriptor filled by the Host
+struct hostdesc
+{
+    /// Pointer to packet payload
+    //u32_l packet_addr;
+    /// Size of the payload
+    u16_l packet_len;
+
+    u16_l flags_ext;
+
+#ifdef CONFIG_RWNX_FULLMAC
+    /// Address of the status descriptor in host memory (used for confirmation upload)
+    u32_l status_desc_addr;
+    /// Destination Address
+    struct mac_addr eth_dest_addr;
+    /// Source Address
+    struct mac_addr eth_src_addr;
+    /// Ethernet Type
+    u16_l ethertype;
+#else /* ! CONFIG_RWNX_FULLMAC */
+#ifdef CONFIG_RWNX_AGG_TX
+    ///Sequence Number for AMPDU MPDUs - for quick check if it's allowed within window
+    u16_l sn;
+#endif /* CONFIG_RWNX_AGG_TX */
+    /// Padding between the buffer control structure and the MPDU in host memory
+    u8_l padding;
+#endif /* CONFIG_RWNX_FULLMAC */
+    u8_l ac;
+    /// Packet TID (0xFF if not a QoS frame)
+    u8_l tid;
+    /// Interface Id
+    u8_l vif_idx;
+    /// Station Id (0xFF if station is unknown)
+    u8_l staid;
+#ifdef CONFIG_RWNX_MUMIMO_TX
+    /// MU-MIMO information (GroupId and User Position in the group) - The GroupId
+    /// is located on bits 0-5 and the User Position on bits 6-7. The GroupId value is set
+    /// to 63 if MU-MIMO shall not be used
+    u8_l mumimo_info;
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+#ifdef CONFIG_RWNX_FULLMAC
+    /// TX flags
+    u16_l flags;
+#endif /* CONFIG_RWNX_FULLMAC */
+};
+
+/// Descriptor filled by the UMAC
+struct umacdesc
+{
+#ifdef CONFIG_RWNX_AGG_TX
+    ///First Sequence Number of the BlockAck window
+    u16_l sn_win;
+    /// Flags from UMAC (match tx_hd.macctrlinfo2 format)
+    u32_l flags;
+    /// PHY related flags field - rate, GI type, BW type - filled by driver
+    u32_l phy_flags;
+#endif //(CONFIG_RWNX_AGG_TX)
+};
+
+struct txdesc_api
+{
+    /// Information provided by Host
+    struct hostdesc host;
+};
+
+
+struct txdesc_host
+{
+    u32_l ready;
+
+    /// API of the embedded part
+    struct txdesc_api api;
+};
+
+/// Comes from ipc_dma.h
+/// Element in the pool of TX DMA bridge descriptors.
+struct dma_desc
+{
+    /** Application subsystem address which is used as source address for DMA payload
+      * transfer*/
+    u32_l            src;
+    /** Points to the start of the embedded data buffer associated with this descriptor.
+     *  This address acts as the destination address for the DMA payload transfer*/
+    u32_l            dest;
+    /// Complete length of the buffer in memory
+    u16_l            length;
+    /// Control word for the DMA engine (e.g. for interrupt generation)
+    u16_l            ctrl;
+    /// Pointer to the next element of the chained list
+    u32_l            next;
+};
+
+// Comes from la.h
+/// Length of the configuration data of a logic analyzer
+#define LA_CONF_LEN          10
+
+/// Structure containing the configuration data of a logic analyzer
+struct la_conf_tag
+{
+    u32_l conf[LA_CONF_LEN];
+    u32_l trace_len;
+    u32_l diag_conf;
+};
+
+/// Size of a logic analyzer memory
+#define LA_MEM_LEN       (1024 * 1024)
+
+/// Type of errors
+enum
+{
+    /// Recoverable error, not requiring any action from Upper MAC
+    DBG_ERROR_RECOVERABLE = 0,
+    /// Fatal error, requiring Upper MAC to reset Lower MAC and HW and restart operation
+    DBG_ERROR_FATAL
+};
+
+/// Maximum length of the SW diag trace
+#define DBG_SW_DIAG_MAX_LEN   1024
+
+/// Maximum length of the error trace
+#define DBG_ERROR_TRACE_SIZE  256
+
+/// Number of MAC diagnostic port banks
+#define DBG_DIAGS_MAC_MAX     48
+
+/// Number of PHY diagnostic port banks
+#define DBG_DIAGS_PHY_MAX     32
+
+/// Maximum size of the RX header descriptor information in the debug dump
+#define DBG_RHD_MEM_LEN      (5 * 1024)
+
+/// Maximum size of the RX buffer descriptor information in the debug dump
+#define DBG_RBD_MEM_LEN      (5 * 1024)
+
+/// Maximum size of the TX header descriptor information in the debug dump
+#define DBG_THD_MEM_LEN      (10 * 1024)
+
+/// Structure containing the information about the PHY channel that is used
+struct phy_channel_info
+{
+    /// PHY channel information 1
+    u32_l info1;
+    /// PHY channel information 2
+    u32_l info2;
+};
+
+/// Debug information forwarded to host when an error occurs
+struct dbg_debug_info_tag
+{
+    /// Type of error (0: recoverable, 1: fatal)
+    u32_l error_type;
+    /// Pointer to the first RX Header Descriptor chained to the MAC HW
+    u32_l rhd;
+    /// Size of the RX header descriptor buffer
+    u32_l rhd_len;
+    /// Pointer to the first RX Buffer Descriptor chained to the MAC HW
+    u32_l rbd;
+    /// Size of the RX buffer descriptor buffer
+    u32_l rbd_len;
+    /// Pointer to the first TX Header Descriptors chained to the MAC HW
+    u32_l thd[NX_TXQ_CNT];
+    /// Size of the TX header descriptor buffer
+    u32_l thd_len[NX_TXQ_CNT];
+    /// MAC HW diag configuration
+    u32_l hw_diag;
+    /// Error message
+    u32_l error[DBG_ERROR_TRACE_SIZE/4];
+    /// SW diag configuration length
+    u32_l sw_diag_len;
+    /// SW diag configuration
+    u32_l sw_diag[DBG_SW_DIAG_MAX_LEN/4];
+    /// PHY channel information
+    struct phy_channel_info chan_info;
+    /// Embedded LA configuration
+    struct la_conf_tag la_conf;
+    /// MAC diagnostic port state
+    u16_l diags_mac[DBG_DIAGS_MAC_MAX];
+    /// PHY diagnostic port state
+    u16_l diags_phy[DBG_DIAGS_PHY_MAX];
+    /// MAC HW RX Header descriptor pointer
+    u32_l rhd_hw_ptr;
+    /// MAC HW RX Buffer descriptor pointer
+    u32_l rbd_hw_ptr;
+};
+
+/// Full debug dump that is forwarded to host in case of error
+struct dbg_debug_dump_tag
+{
+    /// Debug information
+    struct dbg_debug_info_tag dbg_info;
+
+    /// RX header descriptor memory
+    u32_l rhd_mem[DBG_RHD_MEM_LEN/4];
+
+    /// RX buffer descriptor memory
+    u32_l rbd_mem[DBG_RBD_MEM_LEN/4];
+
+    /// TX header descriptor memory
+    u32_l thd_mem[NX_TXQ_CNT][DBG_THD_MEM_LEN/4];
+
+    /// Logic analyzer memory
+    u32_l la_mem[LA_MEM_LEN/4];
+};
+
+
+/// Number of pulses in a radar event structure
+#define RADAR_PULSE_MAX   4
+
+/// Definition of an array of radar pulses
+struct radar_pulse_array_desc
+{
+    /// Buffer containing the radar pulses
+    u32_l pulse[RADAR_PULSE_MAX];
+    /// Index of the radar detection chain that detected those pulses
+    u32_l idx;
+    /// Number of valid pulses in the buffer
+    u32_l cnt;
+};
+
+/// Bit mapping inside a radar pulse element
+struct radar_pulse {
+    s32_l freq:6; /** Freq (resolution is 2Mhz range is [-Fadc/4 .. Fadc/4]) */
+    u32_l fom:4;  /** Figure of Merit */
+    u32_l len:6;  /** Length of the current radar pulse (resolution is 2us) */
+    u32_l rep:16; /** Time interval between the previous radar event
+                      and the current one (in us) */
+};
+
+/// Definition of a RX vector descriptor
+struct rx_vector_desc
+{
+    /// PHY channel information
+    struct phy_channel_info phy_info;
+
+    /// RX vector 1
+    u32_l rx_vect1[IPC_RXVEC_SIZE/4];
+
+    /// Used to print a valid rx vector
+    u32_l pattern;
+};
+
+///
+struct rxdesc_tag
+{
+    /// Host Buffer Address
+    u32_l host_id;
+    /// Length
+    u32_l frame_len;
+    /// Status
+    u16_l status;
+};
+
+/**
+ ****************************************************************************************
+ *  @defgroup IPC IPC
+ *  @ingroup NXMAC
+ *  @brief Inter Processor Communication module.
+ *
+ * The IPC module implements the protocol to communicate between the Host CPU
+ * and the Embedded CPU.
+ *
+ * @see http://en.wikipedia.org/wiki/Circular_buffer
+ * For more information about the ring buffer typical use and difficulties.
+ ****************************************************************************************
+ */
+
+
+/**
+ ****************************************************************************************
+ * @addtogroup IPC_TX IPC Tx path
+ *  @ingroup IPC
+ *  @brief IPC Tx path structures and functions
+ *
+ * A typical use case of the IPC Tx path API:
+ * @msc
+ * hscale = "2";
+ *
+ * a [label=Driver],
+ * b [label="IPC host"],
+ * c [label="IPC emb"],
+ * d [label=Firmware];
+ *
+ * ---   [label="Tx descriptor queue example"];
+ * a=>a  [label="Driver receives a Tx packet from OS"];
+ * a=>b  [label="ipc_host_txdesc_get()"];
+ * a<<b  [label="struct txdesc_host *"];
+ * a=>a  [label="Driver fill the descriptor"];
+ * a=>b  [label="ipc_host_txdesc_push()"];
+ * ...   [label="(several Tx desc can be pushed)"];
+ * b:>c  [label="Tx desc queue filled IRQ"];
+ * c=>>d [label="EDCA sub-scheduler callback"];
+ * c<<d  [label="Tx desc queue to pop"];
+ * c=>>d [label="UMAC Tx desc callback"];
+ * ...   [label="(several Tx desc can be popped)"];
+ * d=>d  [label="Packets are sent or discarded"];
+ * ---   [label="Tx confirm queue example"];
+ * c<=d  [label="ipc_emb_txcfm_push()"];
+ * c>>d  [label="Request accepted"];
+ * ...   [label="(several Tx cfm can be pushed)"];
+ * b<:c  [label="Tx cfm queue filled IRQ"];
+ * a<<=b [label="Driver's Tx Confirm callback"];
+ * a=>b  [label="ipc_host_txcfm_pop()"];
+ * a<<b  [label="struct ipc_txcfm"];
+ * a<=a  [label="Packets are freed by the driver"];
+ * @endmsc
+ *
+ * @{
+ ****************************************************************************************
+ */
+
+/// @} IPC_TX
+
+/**
+ ****************************************************************************************
+ *  @defgroup IPC_RX IPC Rx path
+ *  @ingroup IPC
+ *  @brief IPC Rx path functions and structures
+ *
+ * A typical use case of the IPC Rx path API:
+ * @msc
+ * hscale = "2";
+ *
+ * a [label=Firmware],
+ * b [label="IPC emb"],
+ * c [label="IPC host"],
+ * d [label=Driver];
+ *
+ * ---   [label="Rx buffer and desc queues usage example"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * ...   [label="(several Rx buffer are pushed)"];
+ * a=>a  [label=" Frame is received\n from the medium"];
+ * a<<b  [label="struct ipc_rxbuf"];
+ * a=>a  [label=" Firmware fill the buffer\n with received frame"];
+ * a<<b  [label="Push accepted"];
+ * ...   [label="(several Rx desc can be pushed)"];
+ * b:>c  [label="Rx desc queue filled IRQ"];
+ * c=>>d [label="Driver Rx packet callback"];
+ * c<=d  [label="ipc_host_rxdesc_pop()"];
+ * d=>d  [label="Rx packet is handed \nover to the OS "];
+ * ...   [label="(several Rx desc can be poped)"];
+ * ---   [label="Rx buffer request exemple"];
+ * b:>c  [label="Low Rx buffer count IRQ"];
+ * a<<b  [label="struct ipc_rxbuf"];
+ * c=>>d [label="Driver Rx buffer callback"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * ...   [label="(several Rx buffer are pushed)"];
+ * @endmsc
+ *
+ * @addtogroup IPC_RX
+ * @{
+ ****************************************************************************************
+ */
+
+/// @} IPC_RX
+
+
+
+/**
+ ****************************************************************************************
+ *  @defgroup IPC_MISC IPC Misc
+ *  @ingroup IPC
+ *  @brief IPC miscellaneous functions
+ ****************************************************************************************
+ */
+/** IPC header structure.  This structure is stored at the beginning of every IPC message.
+ * @warning This structure's size must NOT exceed 4 bytes in length.
+ */
+struct ipc_header
+{
+    /// IPC message type.
+    u16_l type;
+    /// IPC message size in number of bytes.
+    u16_l size;
+};
+
+struct ipc_msg_elt
+{
+    /// Message header (alignment forced on word size, see allocation in shared env).
+    struct ipc_header header __ALIGN4;
+};
+
+/// Message structure for MSGs from Emb to App
+struct ipc_e2a_msg
+{
+    u16_l id;                ///< Message id.
+    u16_l dummy_dest_id;
+    u16_l dummy_src_id;
+    u16_l param_len;         ///< Parameter embedded struct length.
+    u32_l pattern;           ///< Used to stamp a valid MSG buffer
+    u32_l param[IPC_E2A_MSG_PARAM_SIZE];  ///< Parameter embedded struct. Must be word-aligned.
+};
+
+/// Message structure for Debug messages from Emb to App
+struct ipc_dbg_msg
+{
+    u32_l string[IPC_DBG_PARAM_SIZE/4]; ///< Debug string
+    u32_l pattern;                    ///< Used to stamp a valid buffer
+};
+
+/// Message structure for MSGs from App to Emb.
+/// Actually a sub-structure will be used when filling the messages.
+struct ipc_a2e_msg
+{
+    u32_l dummy_word;                // used to cope with kernel message structure
+    u32_l msg[IPC_A2E_MSG_BUF_SIZE]; // body of the msg
+};
+
+struct ipc_shared_rx_buf
+{
+    /// < ptr to hostbuf client (ipc_host client) structure
+    u32_l hostid;
+    /// < ptr to real hostbuf dma address
+    u32_l dma_addr;
+};
+
+struct ipc_shared_rx_desc
+{
+    /// DMA Address
+    u32_l dma_addr;
+};
+
+/// Structure containing FW characteristics for compatibility checking
+struct compatibility_tag {
+    /// Size of IPC shared memory
+    u16_l ipc_shared_size;
+    /// Message struct/ID API version
+    u16_l msg_api;
+    /// Version of IPC shared
+    u8_l ipc_shared_version;
+    /// Number of host buffers available for Emb->App MSGs sending
+    u8_l msge2a_buf_cnt;
+    /// Number of host buffers available for Debug Messages sending
+    u8_l dbgbuf_cnt;
+    /// Number of host buffers available for Radar events handling
+    u8_l radarbuf_cnt;
+    /// Number of host buffers available for unsupported Rx vectors handling
+    u8_l unsuprxvecbuf_cnt;
+    /// Number of shared descriptors available for Data RX handling
+    u8_l rxdesc_cnt;
+    /// Number of host buffers available for Data Rx handling
+    u8_l rxbuf_cnt;
+    /// Number of descriptors in BK TX queue (power of 2, min 4, max 64)
+    u8_l bk_txq;
+    /// Number of descriptors in BE TX queue (power of 2, min 4, max 64)
+    u8_l be_txq;
+    /// Number of descriptors in VI TX queue (power of 2, min 4, max 64)
+    u8_l vi_txq;
+    /// Number of descriptors in VO TX queue (power of 2, min 4, max 64)
+    u8_l vo_txq;
+    /// Number of descriptors in BCN TX queue (power of 2, min 4, max 64)
+    u8_l bcn_txq;
+};
+
+/*
+ * TYPE and STRUCT DEFINITIONS
+ ****************************************************************************************
+ */
+
+
+// Indexes are defined in the MIB shared structure
+struct ipc_shared_env_tag
+{
+    volatile struct compatibility_tag comp_info; //FW characteristics
+
+    volatile struct ipc_a2e_msg msg_a2e_buf; // room for MSG to be sent from App to Emb
+
+    // Fields for MSGs sending from Emb to App
+    volatile struct    ipc_e2a_msg msg_e2a_buf; // room to build the MSG to be DMA Xferred
+    volatile struct    dma_desc msg_dma_desc;   // DMA descriptor for Emb->App MSGs Xfers
+    volatile u32_l  msg_e2a_hostbuf_addr [IPC_MSGE2A_BUF_CNT]; // buffers @ for DMA Xfers
+
+    // Fields for Debug MSGs sending from Emb to App
+    volatile struct    ipc_dbg_msg dbg_buf; // room to build the MSG to be DMA Xferred
+    volatile struct    dma_desc dbg_dma_desc;   // DMA descriptor for Emb->App MSGs Xfers
+    volatile u32_l  dbg_hostbuf_addr [IPC_DBGBUF_CNT]; // buffers @ for MSGs DMA Xfers
+    volatile u32_l  la_dbginfo_addr; // Host buffer address for the debug information
+    volatile u32_l  pattern_addr;
+    volatile u32_l  radarbuf_hostbuf [IPC_RADARBUF_CNT]; // buffers @ for Radar Events
+    volatile u32_l  unsuprxvecbuf_hostbuf [IPC_UNSUPRXVECBUF_CNT]; // buffers @ for unsupported Rx vectors
+    volatile struct txdesc_host txdesc0[CONFIG_USER_MAX][NX_TXDESC_CNT0];
+    volatile struct txdesc_host txdesc1[CONFIG_USER_MAX][NX_TXDESC_CNT1];
+    volatile struct txdesc_host txdesc2[CONFIG_USER_MAX][NX_TXDESC_CNT2];
+    volatile struct txdesc_host txdesc3[CONFIG_USER_MAX][NX_TXDESC_CNT3];
+    #if NX_TXQ_CNT == 5
+    volatile struct txdesc_host txdesc4[1][NX_TXDESC_CNT4];
+    #endif
+    #ifdef CONFIG_RWNX_FULLMAC
+    // RX Descriptors Array
+    volatile struct ipc_shared_rx_desc host_rxdesc[IPC_RXDESC_CNT];
+    // RX Buffers Array
+    volatile struct ipc_shared_rx_buf  host_rxbuf[IPC_RXBUF_CNT];
+    #else
+    // buffers @ for Data Rx
+    volatile u32_l host_rxbuf[IPC_RXBUF_CNT];
+    #endif /* CONFIG_RWNX_FULLMAC */
+
+    u32_l buffered[NX_REMOTE_STA_MAX][TID_MAX];
+
+    volatile uint16_t trace_pattern;
+    volatile uint32_t trace_start;
+    volatile uint32_t trace_end;
+    volatile uint32_t trace_size;
+    volatile uint32_t trace_offset;
+    volatile uint32_t trace_nb_compo;
+    volatile uint32_t trace_offset_compo;
+};
+
+extern struct ipc_shared_env_tag ipc_shared_env;
+
+
+/*
+ * TYPE and STRUCT DEFINITIONS
+ ****************************************************************************************
+ */
+
+// IRQs from app to emb
+/// Interrupts bits used for the TX descriptors of the AC queues
+#ifdef CONFIG_RWNX_MUMIMO_TX
+#ifdef CONFIG_RWNX_OLD_IPC
+#error "MU-MIMO cannot be compiled for old IPC"
+#endif
+/// Interrupts bits used
+#if CONFIG_USER_MAX > 3
+#define IPC_IRQ_A2E_USER_MSK       0xF
+#elif CONFIG_USER_MAX > 2
+#define IPC_IRQ_A2E_USER_MSK       0x7
+#else
+#define IPC_IRQ_A2E_USER_MSK       0x3
+#endif
+
+/// Offset of the interrupts for AC0
+#define IPC_IRQ_A2E_AC0_OFT        8
+/// Mask of the interrupts for AC0
+#define IPC_IRQ_A2E_AC0_MSK       (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC0_OFT)
+/// Offset of the interrupts for AC1
+#define IPC_IRQ_A2E_AC1_OFT       (IPC_IRQ_A2E_AC0_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC1
+#define IPC_IRQ_A2E_AC1_MSK       (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC1_OFT)
+/// Offset of the interrupts for AC2
+#define IPC_IRQ_A2E_AC2_OFT       (IPC_IRQ_A2E_AC1_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC2
+#define IPC_IRQ_A2E_AC2_MSK       (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC2_OFT)
+/// Offset of the interrupts for AC3
+#define IPC_IRQ_A2E_AC3_OFT       (IPC_IRQ_A2E_AC2_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC3
+#define IPC_IRQ_A2E_AC3_MSK       (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC3_OFT)
+/// Offset of the interrupts for BCN
+#define IPC_IRQ_A2E_BCN_OFT       (IPC_IRQ_A2E_AC3_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for BCN
+#define IPC_IRQ_A2E_BCN_MSK       CO_BIT(IPC_IRQ_A2E_BCN_OFT)
+
+#define IPC_IRQ_A2E_AC_TXDESC     (IPC_IRQ_A2E_AC0_MSK | IPC_IRQ_A2E_AC1_MSK | \
+                                   IPC_IRQ_A2E_AC2_MSK | IPC_IRQ_A2E_AC3_MSK)
+
+/// Interrupts bits used for the TX descriptors of the BCN queue
+#if NX_TXQ_CNT < 5
+#define IPC_IRQ_A2E_BCN_TXDESC      0
+#else
+#define IPC_IRQ_A2E_BCN_TXDESC      (0x01 << IPC_IRQ_A2E_BCN_OFT)
+#endif
+
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_A2E_TXDESC          (IPC_IRQ_A2E_AC_TXDESC | IPC_IRQ_A2E_BCN_TXDESC)
+#else
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_A2E_TXDESC          0xFF00
+#endif
+
+#define IPC_IRQ_A2E_TXDESC_FIRSTBIT (8)
+#define IPC_IRQ_A2E_RXBUF_BACK      CO_BIT(5)
+#define IPC_IRQ_A2E_RXDESC_BACK     CO_BIT(4)
+
+#define IPC_IRQ_A2E_MSG             CO_BIT(1)
+#define IPC_IRQ_A2E_DBG             CO_BIT(0)
+
+#define IPC_IRQ_A2E_ALL             (IPC_IRQ_A2E_TXDESC|IPC_IRQ_A2E_MSG|IPC_IRQ_A2E_DBG)
+
+// IRQs from emb to app
+#define IPC_IRQ_E2A_TXCFM_POS   7
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+#ifdef CONFIG_RWNX_OLD_IPC
+#error "MU-MIMO cannot be compiled for old IPC"
+#endif
+/// Interrupts bits used
+#if CONFIG_USER_MAX > 3
+#define IPC_IRQ_E2A_USER_MSK       0xF
+#elif CONFIG_USER_MAX > 2
+#define IPC_IRQ_E2A_USER_MSK       0x7
+#else
+#define IPC_IRQ_E2A_USER_MSK       0x3
+#endif
+
+/// Offset of the interrupts for AC0
+#define IPC_IRQ_E2A_AC0_OFT        IPC_IRQ_E2A_TXCFM_POS
+/// Mask of the interrupts for AC0
+#define IPC_IRQ_E2A_AC0_MSK       (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC0_OFT)
+/// Offset of the interrupts for AC1
+#define IPC_IRQ_E2A_AC1_OFT       (IPC_IRQ_E2A_AC0_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC1
+#define IPC_IRQ_E2A_AC1_MSK       (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC1_OFT)
+/// Offset of the interrupts for AC2
+#define IPC_IRQ_E2A_AC2_OFT       (IPC_IRQ_E2A_AC1_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC2
+#define IPC_IRQ_E2A_AC2_MSK       (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC2_OFT)
+/// Offset of the interrupts for AC3
+#define IPC_IRQ_E2A_AC3_OFT       (IPC_IRQ_E2A_AC2_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC3
+#define IPC_IRQ_E2A_AC3_MSK       (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC3_OFT)
+/// Offset of the interrupts for BCN
+#define IPC_IRQ_E2A_BCN_OFT       (IPC_IRQ_E2A_AC3_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for BCN
+#define IPC_IRQ_E2A_BCN_MSK       CO_BIT(IPC_IRQ_E2A_BCN_OFT)
+
+#define IPC_IRQ_E2A_AC_TXCFM     (IPC_IRQ_E2A_AC0_MSK | IPC_IRQ_E2A_AC1_MSK | \
+                                   IPC_IRQ_E2A_AC2_MSK | IPC_IRQ_E2A_AC3_MSK)
+
+/// Interrupts bits used for the TX descriptors of the BCN queue
+#if NX_TXQ_CNT < 5
+#define IPC_IRQ_E2A_BCN_TXCFM      0
+#else
+#define IPC_IRQ_E2A_BCN_TXCFM      (0x01 << IPC_IRQ_E2A_BCN_OFT)
+#endif
+
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_E2A_TXCFM          (IPC_IRQ_E2A_AC_TXCFM | IPC_IRQ_E2A_BCN_TXCFM)
+
+#else
+
+#define IPC_IRQ_E2A_TXCFM       ((1 << NX_TXQ_CNT) - 1 ) << IPC_IRQ_E2A_TXCFM_POS
+
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+#define IPC_IRQ_E2A_UNSUP_RX_VEC    CO_BIT(7)
+#define IPC_IRQ_E2A_RADAR           CO_BIT(6)
+#define IPC_IRQ_E2A_TBTT_SEC        CO_BIT(5)
+#define IPC_IRQ_E2A_TBTT_PRIM       CO_BIT(4)
+#define IPC_IRQ_E2A_RXDESC          CO_BIT(3)
+#define IPC_IRQ_E2A_MSG_ACK         CO_BIT(2)
+#define IPC_IRQ_E2A_MSG             CO_BIT(1)
+#define IPC_IRQ_E2A_DBG             CO_BIT(0)
+
+#define IPC_IRQ_E2A_ALL         ( IPC_IRQ_E2A_TXCFM         \
+                                | IPC_IRQ_E2A_RXDESC        \
+                                | IPC_IRQ_E2A_MSG_ACK       \
+                                | IPC_IRQ_E2A_MSG           \
+                                | IPC_IRQ_E2A_DBG           \
+                                | IPC_IRQ_E2A_TBTT_PRIM     \
+                                | IPC_IRQ_E2A_TBTT_SEC      \
+                                | IPC_IRQ_E2A_RADAR         \
+                                | IPC_IRQ_E2A_UNSUP_RX_VEC)
+
+// FLAGS for RX desc
+#define IPC_RX_FORWARD          CO_BIT(1)
+#define IPC_RX_INTRABSS         CO_BIT(0)
+
+
+// IPC message TYPE
+enum
+{
+    IPC_MSG_NONE = 0,
+    IPC_MSG_WRAP,
+    IPC_MSG_KMSG,
+
+    IPC_DBG_STRING,
+
+};
+
+#endif // _IPC_SHARED_H_
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/lmac_mac.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/lmac_mac.h
new file mode 100755
index 0000000..75bd3c3
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/lmac_mac.h
@@ -0,0 +1,588 @@
+/**
+ ****************************************************************************************
+ *
+ * @file lmac_mac_types.h
+ *
+ * @brief MAC related definitions.
+ *
+ * Adapted from mac_types.h to used lmac_types.h instead of standard types
+ * eg: perl -pi -e '$_ =~ s/uint(\d{1,2})_t/u$1_l/g; \
+ *                  $_ =~ s/int(\d{1,2})_t/s$1_l/g; \
+ *                  $_ =~ s/CO_BIT/BIT/g;' lmac_mac.h
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef LMAC_MAC_H_
+#define LMAC_MAC_H_
+
+#include "lmac_types.h"
+
+/// Interface types
+enum mac_vif_type
+{
+    /// ESS STA interface
+    VIF_STA,
+    /// IBSS STA interface
+    VIF_IBSS,
+    /// AP interface
+    VIF_AP,
+    /// Mesh Point interface
+    VIF_MESH_POINT,
+    /// Monitor interface
+    VIF_MONITOR,
+    /// Unknown type
+    VIF_UNKNOWN
+};
+
+/// MAC address length in bytes.
+#define MAC_ADDR_LEN 6
+
+/// MAC address structure.
+struct mac_addr
+{
+    /// Array of 16-bit words that make up the MAC address.
+    u16_l array[MAC_ADDR_LEN/2];
+};
+
+/// SSID maximum length.
+#define MAC_SSID_LEN 32
+
+/// SSID.
+struct mac_ssid
+{
+    /// Actual length of the SSID.
+    u8_l length;
+    /// Array containing the SSID name.
+    u8_l array[MAC_SSID_LEN];
+};
+
+/// BSS type
+enum mac_bss_type
+{
+    INFRASTRUCTURE_MODE = 1,
+    INDEPENDENT_BSS_MODE,
+    ANY_BSS_MODE
+};
+
+/// Channel Band
+enum mac_chan_band
+{
+    /// 2.4GHz Band
+    PHY_BAND_2G4,
+    /// 5GHz band
+    PHY_BAND_5G,
+    /// Number of bands
+    PHY_BAND_MAX,
+};
+
+/// Operating Channel Bandwidth
+enum mac_chan_bandwidth
+{
+    /// 20MHz BW
+    PHY_CHNL_BW_20,
+    /// 40MHz BW
+    PHY_CHNL_BW_40,
+    /// 80MHz BW
+    PHY_CHNL_BW_80,
+    /// 160MHz BW
+    PHY_CHNL_BW_160,
+    /// 80+80MHz BW
+    PHY_CHNL_BW_80P80,
+    /// Reserved BW
+    PHY_CHNL_BW_OTHER,
+};
+
+/// max number of channels in the 2.4 GHZ band
+#define MAC_DOMAINCHANNEL_24G_MAX 14
+
+/// max number of channels in the 5 GHZ band
+#define MAC_DOMAINCHANNEL_5G_MAX 28
+
+/// Channel Flag
+enum mac_chan_flags
+{
+    /// Cannot initiate radiation on this channel
+    CHAN_NO_IR = BIT(0),
+    /// Channel is not allowed
+    CHAN_DISABLED = BIT(1),
+    /// Radar detection required on this channel
+    CHAN_RADAR = BIT(2),
+};
+
+/// Primary Channel definition
+struct mac_chan_def
+{
+    /// Frequency of the channel (in MHz)
+    u16_l freq;
+    /// RF band (@ref mac_chan_band)
+    u8_l band;
+    /// Additional information (@ref mac_chan_flags)
+    u8_l flags;
+    /// Max transmit power allowed on this channel (dBm)
+    s8_l tx_power;
+};
+
+/// Operating Channel
+struct mac_chan_op
+{
+    /// Band (@ref mac_chan_band)
+    u8_l band;
+    /// Channel type (@ref mac_chan_bandwidth)
+    u8_l type;
+    /// Frequency for Primary 20MHz channel (in MHz)
+    u16_l prim20_freq;
+    /// Frequency center of the contiguous channel or center of Primary 80+80 (in MHz)
+    u16_l center1_freq;
+    /// Frequency center of the non-contiguous secondary 80+80 (in MHz)
+    u16_l center2_freq;
+    /// Max transmit power allowed on this channel (dBm)
+    s8_l tx_power;
+    /// Additional information (@ref mac_chan_flags)
+    u8_l flags;
+};
+
+/// Cipher suites (order is important as it is used by MACHW)
+enum mac_cipher_suite
+{
+    /// 00-0F-AC 1
+    MAC_CIPHER_WEP40 = 0,
+    /// 00-0F-AC 2
+    MAC_CIPHER_TKIP = 1,
+    /// 00-0F-AC 4
+    MAC_CIPHER_CCMP = 2,
+    /// 00-0F-AC 5
+    MAC_CIPHER_WEP104 = 3,
+    /// 00-14-72 1
+    MAC_CIPHER_WPI_SMS4 = 4,
+    /// 00-0F-AC 6  (aka AES_CMAC)
+    MAC_CIPHER_BIP_CMAC_128 = 5,
+
+    // following cipher are not supported by MACHW
+    /// 00-0F-AC 08
+    MAC_CIPHER_GCMP_128,
+    /// 00-0F-AC 09
+    MAC_CIPHER_GCMP_256,
+    /// 00-0F-AC 10
+    MAC_CIPHER_CCMP_256,
+    /// 00-0F-AC 11
+    MAC_CIPHER_BIP_GMAC_128,
+    /// 00-0F-AC 12
+    MAC_CIPHER_BIP_GMAC_256,
+    /// 00-0F-AC 13
+    MAC_CIPHER_BIP_CMAC_256,
+
+    MAC_CIPHER_INVALID = 0xFF
+};
+
+/// Authentication and Key Management suite
+enum mac_akm_suite
+{
+    /// No security
+    MAC_AKM_NONE,
+    /// Pre RSN (WEP or WPA)
+    MAC_AKM_PRE_RSN,
+    /// 00-0F-AC 1
+    MAC_AKM_8021X,
+    /// 00-0F-AC 2
+    MAC_AKM_PSK,
+    /// 00-0F-AC 3
+    MAC_AKM_FT_8021X,
+    /// 00-0F-AC 4
+    MAC_AKM_FT_PSK,
+    /// 00-0F-AC 5
+    MAC_AKM_8021X_SHA256,
+    /// 00-0F-AC 6
+    MAC_AKM_PSK_SHA256,
+    /// 00-0F-AC 7
+    MAC_AKM_TDLS,
+    /// 00-0F-AC 8
+    MAC_AKM_SAE,
+    /// 00-0F-AC 9
+    MAC_AKM_FT_OVER_SAE,
+    /// 00-0F-AC 11
+    MAC_AKM_8021X_SUITE_B,
+    /// 00-0F-AC 12
+    MAC_AKM_8021X_SUITE_B_192,
+    /// 00-0F-AC 14
+    MAC_AKM_FILS_SHA256,
+    /// 00-0F-AC 15
+    MAC_AKM_FILS_SHA384,
+    /// 00-0F-AC 16
+    MAC_AKM_FT_FILS_SHA256,
+    /// 00-0F-AC 17
+    MAC_AKM_FT_FILS_SHA384,
+    /// 00-0F-AC 18
+    MAC_AKM_OWE,
+
+    /// 00-14-72 1
+    MAC_AKM_WAPI_CERT,
+    /// 00-14-72 2
+    MAC_AKM_WAPI_PSK,
+};
+
+/// Scan result element, parsed from beacon or probe response frames.
+struct mac_scan_result
+{
+    /// Scan result is valid
+    bool valid_flag;
+    /// Network BSSID.
+    struct mac_addr bssid;
+    /// Network name.
+    struct mac_ssid ssid;
+    /// Network type (@ref mac_bss_type).
+    u16_l bsstype;
+    /// Network channel.
+    struct mac_chan_def *chan;
+    /// Network beacon period (in TU).
+    u16_l beacon_period;
+    /// Capability information
+    u16_l cap_info;
+    /// Supported AKM (bit-field of @ref mac_akm_suite)
+    u32_l akm;
+    /// Group cipher (bit-field of @ref mac_cipher_suite)
+    u16_l group_cipher;
+    /// Group cipher (bit-field of @ref mac_cipher_suite)
+    u16_l pairwise_cipher;
+    /// RSSI of the scanned BSS (in dBm)
+    s8_l rssi;
+    ///Multi-BSSID index (0 if this is the reference (i.e. transmitted) BSSID)
+    u8_l mluti_bssid_index;
+    ///Maximum BSSID indicator
+    u8_l max_bssid_indicator;
+};
+
+/// Legacy rate 802.11 definitions
+enum mac_legacy_rates
+{
+    /// DSSS/CCK 1Mbps
+    MAC_RATE_1MBPS   =   2,
+    /// DSSS/CCK 2Mbps
+    MAC_RATE_2MBPS   =   4,
+    /// DSSS/CCK 5.5Mbps
+    MAC_RATE_5_5MBPS =  11,
+    /// OFDM 6Mbps
+    MAC_RATE_6MBPS   =  12,
+    /// OFDM 9Mbps
+    MAC_RATE_9MBPS   =  18,
+    /// DSSS/CCK 11Mbps
+    MAC_RATE_11MBPS  =  22,
+    /// OFDM 12Mbps
+    MAC_RATE_12MBPS  =  24,
+    /// OFDM 18Mbps
+    MAC_RATE_18MBPS  =  36,
+    /// OFDM 24Mbps
+    MAC_RATE_24MBPS  =  48,
+    /// OFDM 36Mbps
+    MAC_RATE_36MBPS  =  72,
+    /// OFDM 48Mbps
+    MAC_RATE_48MBPS  =  96,
+    /// OFDM 54Mbps
+    MAC_RATE_54MBPS  = 108
+};
+
+/// BSS Membership Selector definitions
+enum mac_bss_membership
+{
+    /// HT PHY
+    MAC_BSS_MEMBERSHIP_HT_PHY = 127,
+    /// VHT PHY
+    MAC_BSS_MEMBERSHIP_VHT_PHY = 126,
+};
+
+/// MAC rateset maximum length
+#define MAC_RATESET_LEN 12
+
+/// Structure containing the legacy rateset of a station
+struct mac_rateset
+{
+    /// Number of legacy rates supported
+    u8_l length;
+    /// Array of legacy rates
+    u8_l array[MAC_RATESET_LEN];
+};
+
+/// MAC Security Key maximum length
+#define MAC_SEC_KEY_LEN 32  // TKIP keys 256 bits (max length) with MIC keys
+
+/// Structure defining a security key
+struct mac_sec_key
+{
+    /// Key material length
+    u8_l length;
+    /// Key material
+    u32_l array[MAC_SEC_KEY_LEN/4];
+};
+
+/// Access Category enumeration
+enum mac_ac
+{
+    /// Background
+    AC_BK = 0,
+    /// Best-effort
+    AC_BE,
+    /// Video
+    AC_VI,
+    /// Voice
+    AC_VO,
+    /// Number of access categories
+    AC_MAX
+};
+
+/// Traffic ID enumeration
+enum mac_tid
+{
+    /// TID_0. Mapped to @ref AC_BE as per 802.11 standard.
+    TID_0,
+    /// TID_1. Mapped to @ref AC_BK as per 802.11 standard.
+    TID_1,
+    /// TID_2. Mapped to @ref AC_BK as per 802.11 standard.
+    TID_2,
+    /// TID_3. Mapped to @ref AC_BE as per 802.11 standard.
+    TID_3,
+    /// TID_4. Mapped to @ref AC_VI as per 802.11 standard.
+    TID_4,
+    /// TID_5. Mapped to @ref AC_VI as per 802.11 standard.
+    TID_5,
+    /// TID_6. Mapped to @ref AC_VO as per 802.11 standard.
+    TID_6,
+    /// TID_7. Mapped to @ref AC_VO as per 802.11 standard.
+    TID_7,
+    /// Non standard Management TID used internally
+    TID_MGT,
+    /// Number of TID supported
+    TID_MAX
+};
+
+/// MCS bitfield maximum size (in bytes)
+#define MAX_MCS_LEN 16 // 16 * 8 = 128
+
+/// MAC HT capability information element
+struct mac_htcapability
+{
+    /// HT capability information
+    u16_l ht_capa_info;
+    /// A-MPDU parameters
+    u8_l a_mpdu_param;
+    /// Supported MCS
+    u8_l mcs_rate[MAX_MCS_LEN];
+    /// HT extended capability information
+    u16_l ht_extended_capa;
+    /// Beamforming capability information
+    u32_l tx_beamforming_capa;
+    /// Antenna selection capability information
+    u8_l asel_capa;
+};
+
+/// MAC VHT capability information element
+struct mac_vhtcapability
+{
+    /// VHT capability information
+    u32_l vht_capa_info;
+    /// RX MCS map
+    u16_l rx_mcs_map;
+    /// RX highest data rate
+    u16_l rx_highest;
+    /// TX MCS map
+    u16_l tx_mcs_map;
+    /// TX highest data rate
+    u16_l tx_highest;
+};
+
+/// Length (in bytes) of the MAC HE capability field
+#define MAC_HE_MAC_CAPA_LEN 6
+/// Length (in bytes) of the PHY HE capability field
+#define MAC_HE_PHY_CAPA_LEN 11
+/// Maximum length (in bytes) of the PPE threshold data
+#define MAC_HE_PPE_THRES_MAX_LEN 25
+
+/// Structure listing the per-NSS, per-BW supported MCS combinations
+struct mac_he_mcs_nss_supp
+{
+    /// per-NSS supported MCS in RX, for BW <= 80MHz
+    u16_l rx_mcs_80;
+    /// per-NSS supported MCS in TX, for BW <= 80MHz
+    u16_l tx_mcs_80;
+    /// per-NSS supported MCS in RX, for BW = 160MHz
+    u16_l rx_mcs_160;
+    /// per-NSS supported MCS in TX, for BW = 160MHz
+    u16_l tx_mcs_160;
+    /// per-NSS supported MCS in RX, for BW = 80+80MHz
+    u16_l rx_mcs_80p80;
+    /// per-NSS supported MCS in TX, for BW = 80+80MHz
+    u16_l tx_mcs_80p80;
+};
+
+/// MAC HE capability information element
+struct mac_hecapability
+{
+    /// MAC HE capabilities
+    u8_l mac_cap_info[MAC_HE_MAC_CAPA_LEN];
+    /// PHY HE capabilities
+    u8_l phy_cap_info[MAC_HE_PHY_CAPA_LEN];
+    /// Supported MCS combinations
+    struct mac_he_mcs_nss_supp mcs_supp;
+    /// PPE Thresholds data
+    u8_l ppe_thres[MAC_HE_PPE_THRES_MAX_LEN];
+};
+
+/// Station flags
+enum mac_sta_flags
+{
+    /// Bit indicating that a STA has QoS (WMM) capability
+    STA_QOS_CAPA = BIT(0),
+    /// Bit indicating that a STA has HT capability
+    STA_HT_CAPA = BIT(1),
+    /// Bit indicating that a STA has VHT capability
+    STA_VHT_CAPA = BIT(2),
+    /// Bit indicating that a STA has MFP capability
+    STA_MFP_CAPA = BIT(3),
+    /// Bit indicating that the STA included the Operation Notification IE
+    STA_OPMOD_NOTIF = BIT(4),
+    /// Bit indicating that a STA has HE capability
+    STA_HE_CAPA = BIT(5),
+};
+
+/// Connection flags
+enum mac_connection_flags
+{
+    /// Flag indicating whether the control port is controlled by host or not
+    CONTROL_PORT_HOST = BIT(0),
+    /// Flag indicating whether the control port frame shall be sent unencrypted
+    CONTROL_PORT_NO_ENC = BIT(1),
+    /// Flag indicating whether HT and VHT shall be disabled or not
+    DISABLE_HT = BIT(2),
+    /// Flag indicating whether WPA or WPA2 authentication is in use
+    WPA_WPA2_IN_USE = BIT(3),
+    /// Flag indicating whether MFP is in use
+    MFP_IN_USE = BIT(4),
+	//  Flag indicating Roam
+	REASSOCIATION = BIT(5),
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+struct ieee80211_vht_mcs_info {
+	__le16 rx_mcs_map;
+	__le16 rx_highest;
+	__le16 tx_mcs_map;
+	__le16 tx_highest;
+} __packed;
+/**
+ * struct ieee80211_vht_cap - VHT capabilities
+ *
+ * This structure is the "VHT capabilities element" as
+ * described in 802.11ac D3.0 8.4.2.160
+ * @vht_cap_info: VHT capability info
+ * @supp_mcs: VHT MCS supported rates
+ */
+struct ieee80211_vht_cap {
+	__le32 vht_cap_info;
+	struct ieee80211_vht_mcs_info supp_mcs;
+};
+#define WLAN_EID_VHT_CAPABILITY             191
+
+struct ieee80211_sta_vht_cap {
+	bool vht_supported;
+	u32 cap; /* use IEEE80211_VHT_CAP_ */
+	struct ieee80211_vht_mcs_info vht_mcs;
+};
+#define RATE_INFO_FLAGS_VHT_MCS                        1<<1;
+#endif
+
+#ifdef CONFIG_HE_FOR_OLD_KERNEL
+#define IEEE80211_HE_MAC_CAP2_ALL_ACK									0x02
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G				0x02
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G		0x04
+#define IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD					0x20
+#define IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US			0x40
+#define IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS					0x80
+#define IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS					0x01
+#define IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US						0x02
+#define IEEE80211_HE_PHY_CAP2_DOPPLER_RX								0x20
+#define IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ						0x08
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM					0x18
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1							0x00
+#define IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA				0x40
+#define IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE								0x01
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4			0x0c
+#define IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK							0x40
+#define IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK							0x80
+#define IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU						0x01
+#define IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU						0x02
+#define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB						0x04
+#define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB						0x08
+#define IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT						0x80
+#define IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO				0x40
+#define IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI			0x04
+#define IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G				0x02
+#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB		0x10
+#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB	0x20
+
+struct ieee80211_he_cap_elem {
+	u8 mac_cap_info[6];
+	u8 phy_cap_info[11];
+} __packed;
+
+struct ieee80211_he_mcs_nss_supp {
+	__le16 rx_mcs_80;
+	__le16 tx_mcs_80;
+	__le16 rx_mcs_160;
+	__le16 tx_mcs_160;
+	__le16 rx_mcs_80p80;
+	__le16 tx_mcs_80p80;
+} __packed;
+
+#define IEEE80211_HE_PPE_THRES_MAX_LEN		25
+#define RATE_INFO_FLAGS_HE_MCS                  1<<4;
+#define WLAN_EID_EXTENSION  255
+/* Element ID Extensions for Element ID 255 */
+enum ieee80211_eid_ext {
+	WLAN_EID_EXT_ASSOC_DELAY_INFO = 1,
+	WLAN_EID_EXT_FILS_REQ_PARAMS = 2,
+	WLAN_EID_EXT_FILS_KEY_CONFIRM = 3,
+	WLAN_EID_EXT_FILS_SESSION = 4,
+	WLAN_EID_EXT_FILS_HLP_CONTAINER = 5,
+	WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN = 6,
+	WLAN_EID_EXT_KEY_DELIVERY = 7,
+	WLAN_EID_EXT_FILS_WRAPPED_DATA = 8,
+	WLAN_EID_EXT_FILS_PUBLIC_KEY = 12,
+	WLAN_EID_EXT_FILS_NONCE = 13,
+	WLAN_EID_EXT_FUTURE_CHAN_GUIDANCE = 14,
+	WLAN_EID_EXT_HE_CAPABILITY = 35,
+	WLAN_EID_EXT_HE_OPERATION = 36,
+	WLAN_EID_EXT_UORA = 37,
+	WLAN_EID_EXT_HE_MU_EDCA = 38,
+	WLAN_EID_EXT_HE_SPR = 39,
+	WLAN_EID_EXT_NDP_FEEDBACK_REPORT_PARAMSET = 41,
+	WLAN_EID_EXT_BSS_COLOR_CHG_ANN = 42,
+	WLAN_EID_EXT_QUIET_TIME_PERIOD_SETUP = 43,
+	WLAN_EID_EXT_ESS_REPORT = 45,
+	WLAN_EID_EXT_OPS = 46,
+	WLAN_EID_EXT_HE_BSS_LOAD = 47,
+	WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME = 52,
+	WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION = 55,
+	WLAN_EID_EXT_NON_INHERITANCE = 56,
+	WLAN_EID_EXT_KNOWN_BSSID = 57,
+	WLAN_EID_EXT_SHORT_SSID_LIST = 58,
+	WLAN_EID_EXT_HE_6GHZ_CAPA = 59,
+	WLAN_EID_EXT_UL_MU_POWER_CAPA = 60,
+	WLAN_EID_EXT_EHT_OPERATION = 106,
+	WLAN_EID_EXT_EHT_MULTI_LINK = 107,
+	WLAN_EID_EXT_EHT_CAPABILITY = 108,
+};
+
+struct ieee80211_sta_he_cap {
+	bool has_he;
+	struct ieee80211_he_cap_elem he_cap_elem;
+	struct ieee80211_he_mcs_nss_supp he_mcs_nss_supp;
+	u8 ppe_thres[IEEE80211_HE_PPE_THRES_MAX_LEN];
+};
+
+struct ieee80211_sband_iftype_data {
+	u16 types_mask;
+	struct ieee80211_sta_he_cap he_cap;
+};
+#endif
+#endif // LMAC_MAC_H_
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/lmac_msg.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/lmac_msg.h
new file mode 100755
index 0000000..73e0fea
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/lmac_msg.h
@@ -0,0 +1,2966 @@
+/**
+ ****************************************************************************************
+ *
+ * @file lmac_msg.h
+ *
+ * @brief Main definitions for message exchanges with LMAC
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef LMAC_MSG_H_
+#define LMAC_MSG_H_
+
+/*
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+// for MAC related elements (mac_addr, mac_ssid...)
+#include "lmac_mac.h"
+
+/*
+ ****************************************************************************************
+ */
+/////////////////////////////////////////////////////////////////////////////////
+// COMMUNICATION WITH LMAC LAYER
+/////////////////////////////////////////////////////////////////////////////////
+/* Task identifiers for communication between LMAC and DRIVER */
+enum
+{
+    TASK_NONE = (u8_l) -1,
+
+    // MAC Management task.
+    TASK_MM = 0,
+    // DEBUG task
+    TASK_DBG,
+    /// SCAN task
+    TASK_SCAN,
+    /// TDLS task
+    TASK_TDLS,
+    /// SCANU task
+    TASK_SCANU,
+    /// ME task
+    TASK_ME,
+    /// SM task
+    TASK_SM,
+    /// APM task
+    TASK_APM,
+    /// BAM task
+    TASK_BAM,
+    /// MESH task
+    TASK_MESH,
+    /// RXU task
+    TASK_RXU,
+    /// RM_task
+    TASK_RM,
+#if defined CONFIG_RWNX_FULLMAC || defined CONFIG_RWNX_FHOST
+    // This is used to define the last task that is running on the EMB processor
+    TASK_LAST_EMB = TASK_RM,
+#else
+#error "Need to define SOFTMAC or FULLMAC"
+#endif
+    // nX API task
+    TASK_API,
+    TASK_MAX,
+};
+
+
+/// For MAC HW States copied from "hal_machw.h"
+enum
+{
+    /// MAC HW IDLE State.
+    HW_IDLE = 0,
+    /// MAC HW RESERVED State.
+    HW_RESERVED,
+    /// MAC HW DOZE State.
+    HW_DOZE,
+    /// MAC HW ACTIVE State.
+    HW_ACTIVE
+};
+
+/// Power Save mode setting
+enum mm_ps_mode_state
+{
+    MM_PS_MODE_OFF,
+    MM_PS_MODE_ON,
+    MM_PS_MODE_ON_DYN,
+};
+
+/// Status/error codes used in the MAC software.
+enum
+{
+    CO_OK,
+    CO_FAIL,
+    CO_EMPTY,
+    CO_FULL,
+    CO_BAD_PARAM,
+    CO_NOT_FOUND,
+    CO_NO_MORE_ELT_AVAILABLE,
+    CO_NO_ELT_IN_USE,
+    CO_BUSY,
+    CO_OP_IN_PROGRESS,
+};
+
+/// Remain on channel operation codes
+enum mm_remain_on_channel_op
+{
+    MM_ROC_OP_START = 0,
+    MM_ROC_OP_CANCEL,
+};
+
+#define DRV_TASK_ID 100
+
+/// Message Identifier. The number of messages is limited to 0xFFFF.
+/// The message ID is divided in two parts:
+/// - bits[15..10] : task index (no more than 64 tasks supported).
+/// - bits[9..0] : message index (no more that 1024 messages per task).
+typedef u16 lmac_msg_id_t;
+
+typedef u16 lmac_task_id_t;
+
+/// Build the first message ID of a task.
+#define LMAC_FIRST_MSG(task) ((lmac_msg_id_t)((task) << 10))
+
+#define MSG_T(msg) ((lmac_task_id_t)((msg) >> 10))
+#define MSG_I(msg) ((msg) & ((1<<10)-1))
+
+/// Message structure.
+struct lmac_msg
+{
+    lmac_msg_id_t     id;         ///< Message id.
+    lmac_task_id_t    dest_id;    ///< Destination kernel identifier.
+    lmac_task_id_t    src_id;     ///< Source kernel identifier.
+    u16        param_len;  ///< Parameter embedded struct length.
+    u32        param[];   ///< Parameter embedded struct. Must be word-aligned.
+};
+
+/// List of messages related to the task.
+enum mm_msg_tag
+{
+    /// RESET Request.
+    MM_RESET_REQ = LMAC_FIRST_MSG(TASK_MM),
+    /// RESET Confirmation.
+    MM_RESET_CFM,
+    /// START Request.
+    MM_START_REQ,
+    /// START Confirmation.
+    MM_START_CFM,
+    /// Read Version Request.
+    MM_VERSION_REQ,
+    /// Read Version Confirmation.
+    MM_VERSION_CFM,
+    /// ADD INTERFACE Request.
+    MM_ADD_IF_REQ,
+    /// ADD INTERFACE Confirmation.
+    MM_ADD_IF_CFM,
+    /// REMOVE INTERFACE Request.
+    MM_REMOVE_IF_REQ,
+    /// REMOVE INTERFACE Confirmation.
+    MM_REMOVE_IF_CFM,
+    /// STA ADD Request.
+    MM_STA_ADD_REQ,
+    /// STA ADD Confirm.
+    MM_STA_ADD_CFM,
+    /// STA DEL Request.
+    MM_STA_DEL_REQ,
+    /// STA DEL Confirm.
+    MM_STA_DEL_CFM,
+    /// RX FILTER CONFIGURATION Request.
+    MM_SET_FILTER_REQ,
+    /// RX FILTER CONFIGURATION Confirmation.
+    MM_SET_FILTER_CFM,
+    /// CHANNEL CONFIGURATION Request.
+    MM_SET_CHANNEL_REQ,
+    /// CHANNEL CONFIGURATION Confirmation.
+    MM_SET_CHANNEL_CFM,
+    /// DTIM PERIOD CONFIGURATION Request.
+    MM_SET_DTIM_REQ,
+    /// DTIM PERIOD CONFIGURATION Confirmation.
+    MM_SET_DTIM_CFM,
+    /// BEACON INTERVAL CONFIGURATION Request.
+    MM_SET_BEACON_INT_REQ,
+    /// BEACON INTERVAL CONFIGURATION Confirmation.
+    MM_SET_BEACON_INT_CFM,
+    /// BASIC RATES CONFIGURATION Request.
+    MM_SET_BASIC_RATES_REQ,
+    /// BASIC RATES CONFIGURATION Confirmation.
+    MM_SET_BASIC_RATES_CFM,
+    /// BSSID CONFIGURATION Request.
+    MM_SET_BSSID_REQ,
+    /// BSSID CONFIGURATION Confirmation.
+    MM_SET_BSSID_CFM,
+    /// EDCA PARAMETERS CONFIGURATION Request.
+    MM_SET_EDCA_REQ,
+    /// EDCA PARAMETERS CONFIGURATION Confirmation.
+    MM_SET_EDCA_CFM,
+    /// ABGN MODE CONFIGURATION Request.
+    MM_SET_MODE_REQ,
+    /// ABGN MODE CONFIGURATION Confirmation.
+    MM_SET_MODE_CFM,
+    /// Request setting the VIF active state (i.e associated or AP started)
+    MM_SET_VIF_STATE_REQ,
+    /// Confirmation of the @ref MM_SET_VIF_STATE_REQ message.
+    MM_SET_VIF_STATE_CFM,
+    /// SLOT TIME PARAMETERS CONFIGURATION Request.
+    MM_SET_SLOTTIME_REQ,
+    /// SLOT TIME PARAMETERS CONFIGURATION Confirmation.
+    MM_SET_SLOTTIME_CFM,
+    /// Power Mode Change Request.
+    MM_SET_IDLE_REQ,
+    /// Power Mode Change Confirm.
+    MM_SET_IDLE_CFM,
+    /// KEY ADD Request.
+    MM_KEY_ADD_REQ,
+    /// KEY ADD Confirm.
+    MM_KEY_ADD_CFM,
+    /// KEY DEL Request.
+    MM_KEY_DEL_REQ,
+    /// KEY DEL Confirm.
+    MM_KEY_DEL_CFM,
+    /// Block Ack agreement info addition
+    MM_BA_ADD_REQ,
+    /// Block Ack agreement info addition confirmation
+    MM_BA_ADD_CFM,
+    /// Block Ack agreement info deletion
+    MM_BA_DEL_REQ,
+    /// Block Ack agreement info deletion confirmation
+    MM_BA_DEL_CFM,
+    /// Indication of the primary TBTT to the upper MAC. Upon the reception of this
+    // message the upper MAC has to push the beacon(s) to the beacon transmission queue.
+    MM_PRIMARY_TBTT_IND,
+    /// Indication of the secondary TBTT to the upper MAC. Upon the reception of this
+    // message the upper MAC has to push the beacon(s) to the beacon transmission queue.
+    MM_SECONDARY_TBTT_IND,
+    /// Request for changing the TX power
+    MM_SET_POWER_REQ,
+    /// Confirmation of the TX power change
+    MM_SET_POWER_CFM,
+    /// Request to the LMAC to trigger the embedded logic analyzer and forward the debug
+    /// dump.
+    MM_DBG_TRIGGER_REQ,
+    /// Set Power Save mode
+    MM_SET_PS_MODE_REQ,
+    /// Set Power Save mode confirmation
+    MM_SET_PS_MODE_CFM,
+    /// Request to add a channel context
+    MM_CHAN_CTXT_ADD_REQ,
+    /// Confirmation of the channel context addition
+    MM_CHAN_CTXT_ADD_CFM,
+    /// Request to delete a channel context
+    MM_CHAN_CTXT_DEL_REQ,
+    /// Confirmation of the channel context deletion
+    MM_CHAN_CTXT_DEL_CFM,
+    /// Request to link a channel context to a VIF
+    MM_CHAN_CTXT_LINK_REQ,
+    /// Confirmation of the channel context link
+    MM_CHAN_CTXT_LINK_CFM,
+    /// Request to unlink a channel context from a VIF
+    MM_CHAN_CTXT_UNLINK_REQ,
+    /// Confirmation of the channel context unlink
+    MM_CHAN_CTXT_UNLINK_CFM,
+    /// Request to update a channel context
+    MM_CHAN_CTXT_UPDATE_REQ,
+    /// Confirmation of the channel context update
+    MM_CHAN_CTXT_UPDATE_CFM,
+    /// Request to schedule a channel context
+    MM_CHAN_CTXT_SCHED_REQ,
+    /// Confirmation of the channel context scheduling
+    MM_CHAN_CTXT_SCHED_CFM,
+    /// Request to change the beacon template in LMAC
+    MM_BCN_CHANGE_REQ,
+    /// Confirmation of the beacon change
+    MM_BCN_CHANGE_CFM,
+    /// Request to update the TIM in the beacon (i.e to indicate traffic bufferized at AP)
+    MM_TIM_UPDATE_REQ,
+    /// Confirmation of the TIM update
+    MM_TIM_UPDATE_CFM,
+    /// Connection loss indication
+    MM_CONNECTION_LOSS_IND,
+    /// Channel context switch indication to the upper layers
+    MM_CHANNEL_SWITCH_IND,
+    /// Channel context pre-switch indication to the upper layers
+    MM_CHANNEL_PRE_SWITCH_IND,
+    /// Request to remain on channel or cancel remain on channel
+    MM_REMAIN_ON_CHANNEL_REQ,
+    /// Confirmation of the (cancel) remain on channel request
+    MM_REMAIN_ON_CHANNEL_CFM,
+    /// Remain on channel expired indication
+    MM_REMAIN_ON_CHANNEL_EXP_IND,
+    /// Indication of a PS state change of a peer device
+    MM_PS_CHANGE_IND,
+    /// Indication that some buffered traffic should be sent to the peer device
+    MM_TRAFFIC_REQ_IND,
+    /// Request to modify the STA Power-save mode options
+    MM_SET_PS_OPTIONS_REQ,
+    /// Confirmation of the PS options setting
+    MM_SET_PS_OPTIONS_CFM,
+    /// Indication of PS state change for a P2P VIF
+    MM_P2P_VIF_PS_CHANGE_IND,
+    /// Indication that CSA counter has been updated
+    MM_CSA_COUNTER_IND,
+    /// Channel occupation report indication
+    MM_CHANNEL_SURVEY_IND,
+    /// Message containing Beamformer Information
+    MM_BFMER_ENABLE_REQ,
+    /// Request to Start/Stop/Update NOA - GO Only
+    MM_SET_P2P_NOA_REQ,
+    /// Request to Start/Stop/Update Opportunistic PS - GO Only
+    MM_SET_P2P_OPPPS_REQ,
+    /// Start/Stop/Update NOA Confirmation
+    MM_SET_P2P_NOA_CFM,
+    /// Start/Stop/Update Opportunistic PS Confirmation
+    MM_SET_P2P_OPPPS_CFM,
+    /// P2P NoA Update Indication - GO Only
+    MM_P2P_NOA_UPD_IND,
+    /// Request to set RSSI threshold and RSSI hysteresis
+    MM_CFG_RSSI_REQ,
+    /// Indication that RSSI level is below or above the threshold
+    MM_RSSI_STATUS_IND,
+    /// Indication that CSA is done
+    MM_CSA_FINISH_IND,
+    /// Indication that CSA is in prorgess (resp. done) and traffic must be stopped (resp. restarted)
+    MM_CSA_TRAFFIC_IND,
+    /// Request to update the group information of a station
+    MM_MU_GROUP_UPDATE_REQ,
+    /// Confirmation of the @ref MM_MU_GROUP_UPDATE_REQ message
+    MM_MU_GROUP_UPDATE_CFM,
+    /// Request to initialize the antenna diversity algorithm
+    MM_ANT_DIV_INIT_REQ,
+    /// Request to stop the antenna diversity algorithm
+    MM_ANT_DIV_STOP_REQ,
+    /// Request to update the antenna switch status
+    MM_ANT_DIV_UPDATE_REQ,
+    /// Request to switch the antenna connected to path_0
+    MM_SWITCH_ANTENNA_REQ,
+    /// Indication that a packet loss has occurred
+    MM_PKTLOSS_IND,
+
+    MM_SET_ARPOFFLOAD_REQ,
+    MM_SET_ARPOFFLOAD_CFM,
+    MM_SET_AGG_DISABLE_REQ,
+    MM_SET_AGG_DISABLE_CFM,
+    MM_SET_COEX_REQ,
+    MM_SET_COEX_CFM,
+    MM_SET_RF_CONFIG_REQ,
+    MM_SET_RF_CONFIG_CFM,
+    MM_SET_RF_CALIB_REQ,
+    MM_SET_RF_CALIB_CFM,
+
+    /// MU EDCA PARAMETERS Configuration Request.
+    MM_SET_MU_EDCA_REQ,
+    /// MU EDCA PARAMETERS Configuration Confirmation.
+    MM_SET_MU_EDCA_CFM,
+    /// UORA PARAMETERS Configuration Request.
+    MM_SET_UORA_REQ,
+    /// UORA PARAMETERS Configuration Confirmation.
+    MM_SET_UORA_CFM,
+    /// TXOP RTS THRESHOLD Configuration Request.
+    MM_SET_TXOP_RTS_THRES_REQ,
+    /// TXOP RTS THRESHOLD Configuration Confirmation.
+    MM_SET_TXOP_RTS_THRES_CFM,
+    /// HE BSS Color Configuration Request.
+    MM_SET_BSS_COLOR_REQ,
+    /// HE BSS Color Configuration Confirmation.
+    MM_SET_BSS_COLOR_CFM,
+
+    MM_GET_MAC_ADDR_REQ,
+    MM_GET_MAC_ADDR_CFM,
+
+	MM_GET_STA_INFO_REQ,
+	MM_GET_STA_INFO_CFM,
+
+    MM_SET_TXPWR_LVL_REQ,
+    MM_SET_TXPWR_LVL_CFM,
+
+    MM_SET_TXPWR_OFST_REQ,
+    MM_SET_TXPWR_OFST_CFM,
+
+    MM_SET_STACK_START_REQ,
+    MM_SET_STACK_START_CFM,
+
+    MM_APM_STALOSS_IND,
+
+    MM_SET_VENDOR_HWCONFIG_REQ,
+    MM_SET_VENDOR_HWCONFIG_CFM,
+
+	MM_GET_FW_VERSION_REQ,
+    MM_GET_FW_VERSION_CFM,
+
+    MM_SET_TXPWR_LVL_ADJ_REQ,
+    MM_SET_TXPWR_LVL_ADJ_CFM,
+
+	/// MAX number of messages
+	MM_MAX,
+};
+
+/// Interface types
+enum
+{
+    /// ESS STA interface
+    MM_STA,
+    /// IBSS STA interface
+    MM_IBSS,
+    /// AP interface
+    MM_AP,
+    // Mesh Point interface
+    MM_MESH_POINT,
+    // Monitor interface
+    MM_MONITOR,
+};
+
+///BA agreement types
+enum
+{
+    ///BlockAck agreement for TX
+    BA_AGMT_TX,
+    ///BlockAck agreement for RX
+    BA_AGMT_RX,
+};
+
+///BA agreement related status
+enum
+{
+    ///Correct BA agreement establishment
+    BA_AGMT_ESTABLISHED,
+    ///BA agreement already exists for STA+TID requested, cannot override it (should have been deleted first)
+    BA_AGMT_ALREADY_EXISTS,
+    ///Correct BA agreement deletion
+    BA_AGMT_DELETED,
+    ///BA agreement for the (STA, TID) doesn't exist so nothing to delete
+    BA_AGMT_DOESNT_EXIST,
+};
+
+/// Features supported by LMAC - Positions
+enum mm_features
+{
+    /// Beaconing
+    MM_FEAT_BCN_BIT         = 0,
+    /// Autonomous Beacon Transmission
+    MM_FEAT_AUTOBCN_BIT,
+    /// Scan in LMAC
+    MM_FEAT_HWSCAN_BIT,
+    /// Connection Monitoring
+    MM_FEAT_CMON_BIT,
+    /// Multi Role
+    MM_FEAT_MROLE_BIT,
+    /// Radar Detection
+    MM_FEAT_RADAR_BIT,
+    /// Power Save
+    MM_FEAT_PS_BIT,
+    /// UAPSD
+    MM_FEAT_UAPSD_BIT,
+    /// DPSM
+    MM_FEAT_DPSM_BIT,
+    /// A-MPDU
+    MM_FEAT_AMPDU_BIT,
+    /// A-MSDU
+    MM_FEAT_AMSDU_BIT,
+    /// Channel Context
+    MM_FEAT_CHNL_CTXT_BIT,
+    /// Packet reordering
+    MM_FEAT_REORD_BIT,
+    /// P2P
+    MM_FEAT_P2P_BIT,
+    /// P2P Go
+    MM_FEAT_P2P_GO_BIT,
+    /// UMAC Present
+    MM_FEAT_UMAC_BIT,
+    /// VHT support
+    MM_FEAT_VHT_BIT,
+    /// Beamformee
+    MM_FEAT_BFMEE_BIT,
+    /// Beamformer
+    MM_FEAT_BFMER_BIT,
+    /// WAPI
+    MM_FEAT_WAPI_BIT,
+    /// MFP
+    MM_FEAT_MFP_BIT,
+    /// Mu-MIMO RX support
+    MM_FEAT_MU_MIMO_RX_BIT,
+    /// Mu-MIMO TX support
+    MM_FEAT_MU_MIMO_TX_BIT,
+    /// Wireless Mesh Networking
+    MM_FEAT_MESH_BIT,
+    /// TDLS support
+    MM_FEAT_TDLS_BIT,
+    /// Antenna Diversity support
+    MM_FEAT_ANT_DIV_BIT,
+    /// UF support
+    MM_FEAT_UF_BIT,
+    /// A-MSDU maximum size (bit0)
+    MM_AMSDU_MAX_SIZE_BIT0,
+    /// A-MSDU maximum size (bit1)
+    MM_AMSDU_MAX_SIZE_BIT1,
+    /// MON_DATA support
+    MM_FEAT_MON_DATA_BIT,
+    /// HE (802.11ax) support
+    MM_FEAT_HE_BIT,
+};
+
+/// Maximum number of words in the configuration buffer
+#define PHY_CFG_BUF_SIZE     16
+
+/// Structure containing the parameters of the PHY configuration
+struct phy_cfg_tag
+{
+    /// Buffer containing the parameters specific for the PHY used
+    u32_l parameters[PHY_CFG_BUF_SIZE];
+};
+
+/// Structure containing the parameters of the Trident PHY configuration
+struct phy_trd_cfg_tag
+{
+    /// MDM type(nxm)(upper nibble) and MDM2RF path mapping(lower nibble)
+    u8_l path_mapping;
+    /// TX DC offset compensation
+    u32_l tx_dc_off_comp;
+};
+
+/// Structure containing the parameters of the Karst PHY configuration
+struct phy_karst_cfg_tag
+{
+    /// TX IQ mismatch compensation in 2.4GHz
+    u32_l tx_iq_comp_2_4G[2];
+    /// RX IQ mismatch compensation in 2.4GHz
+    u32_l rx_iq_comp_2_4G[2];
+    /// TX IQ mismatch compensation in 5GHz
+    u32_l tx_iq_comp_5G[2];
+    /// RX IQ mismatch compensation in 5GHz
+    u32_l rx_iq_comp_5G[2];
+    /// RF path used by default (0 or 1)
+    u8_l path_used;
+};
+
+/// Structure containing the parameters of the @ref MM_START_REQ message
+struct mm_start_req
+{
+    /// PHY configuration
+    struct phy_cfg_tag phy_cfg;
+    /// UAPSD timeout
+    u32_l uapsd_timeout;
+    /// Local LP clock accuracy (in ppm)
+    u16_l lp_clk_accuracy;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_CHANNEL_REQ message
+struct mm_set_channel_req
+{
+    /// Channel information
+    struct mac_chan_op chan;
+    /// Index of the RF for which the channel has to be set (0: operating (primary), 1: secondary
+    /// RF (used for additional radar detection). This parameter is reserved if no secondary RF
+    /// is available in the system
+    u8_l index;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_CHANNEL_CFM message
+struct mm_set_channel_cfm
+{
+    /// Radio index to be used in policy table
+    u8_l radio_idx;
+    /// TX power configured (in dBm)
+    s8_l power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_DTIM_REQ message
+struct mm_set_dtim_req
+{
+    /// DTIM period
+    u8_l dtim_period;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_POWER_REQ message
+struct mm_set_power_req
+{
+    /// Index of the interface for which the parameter is configured
+    u8_l inst_nbr;
+    /// TX power (in dBm)
+    s8_l power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_POWER_CFM message
+struct mm_set_power_cfm
+{
+    /// Radio index to be used in policy table
+    u8_l radio_idx;
+    /// TX power configured (in dBm)
+    s8_l power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BEACON_INT_REQ message
+struct mm_set_beacon_int_req
+{
+    /// Beacon interval
+    u16_l beacon_int;
+    /// Index of the interface for which the parameter is configured
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BASIC_RATES_REQ message
+struct mm_set_basic_rates_req
+{
+    /// Basic rate set (as expected by bssBasicRateSet field of Rates MAC HW register)
+    u32_l rates;
+    /// Index of the interface for which the parameter is configured
+    u8_l inst_nbr;
+    /// Band on which the interface will operate
+    u8_l band;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BSSID_REQ message
+struct mm_set_bssid_req
+{
+    /// BSSID to be configured in HW
+    struct mac_addr bssid;
+    /// Index of the interface for which the parameter is configured
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_FILTER_REQ message
+struct mm_set_filter_req
+{
+    /// RX filter to be put into rxCntrlReg HW register
+    u32_l filter;
+};
+
+/// Structure containing the parameters of the @ref MM_ADD_IF_REQ message.
+struct mm_add_if_req
+{
+    /// Type of the interface (AP, STA, ADHOC, ...)
+    u8_l type;
+    /// MAC ADDR of the interface to start
+    struct mac_addr addr;
+    /// P2P Interface
+    bool_l p2p;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_EDCA_REQ message
+struct mm_set_edca_req
+{
+    /// EDCA parameters of the queue (as expected by edcaACxReg HW register)
+    u32_l ac_param;
+    /// Flag indicating if UAPSD can be used on this queue
+    bool_l uapsd;
+    /// HW queue for which the parameters are configured
+    u8_l hw_queue;
+    /// Index of the interface for which the parameters are configured
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_MU_EDCA_REQ message
+struct mm_set_mu_edca_req
+{
+    /// MU EDCA parameters of the different HE queues
+    u32_l param[AC_MAX];
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UORA_REQ message
+struct mm_set_uora_req
+{
+    /// Minimum exponent of OFDMA Contention Window.
+    u8_l eocw_min;
+    /// Maximum exponent of OFDMA Contention Window.
+    u8_l eocw_max;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_TXOP_RTS_THRES_REQ message
+struct mm_set_txop_rts_thres_req
+{
+    /// TXOP RTS threshold
+    u16_l txop_dur_rts_thres;
+    /// Index of the interface for which the parameter is configured
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BSS_COLOR_REQ message
+struct mm_set_bss_color_req
+{
+    /// HE BSS color, formatted as per BSS_COLOR MAC HW register
+    u32_l bss_color;
+};
+
+struct mm_set_idle_req
+{
+    u8_l hw_idle;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_SLOTTIME_REQ message
+struct mm_set_slottime_req
+{
+    /// Slot time expressed in us
+    u8_l slottime;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_MODE_REQ message
+struct mm_set_mode_req
+{
+    /// abgnMode field of macCntrl1Reg register
+    u8_l abgnmode;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_VIF_STATE_REQ message
+struct mm_set_vif_state_req
+{
+    /// Association Id received from the AP (valid only if the VIF is of STA type)
+    u16_l aid;
+    /// Flag indicating if the VIF is active or not
+    bool_l active;
+    /// Interface index
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_ADD_IF_CFM message.
+struct mm_add_if_cfm
+{
+    /// Status of operation (different from 0 if unsuccessful)
+    u8_l status;
+    /// Interface index assigned by the LMAC
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_REMOVE_IF_REQ message.
+struct mm_remove_if_req
+{
+    /// Interface index assigned by the LMAC
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_VERSION_CFM message.
+struct mm_version_cfm
+{
+    /// Version of the LMAC FW
+    u32_l version_lmac;
+    /// Version1 of the MAC HW (as encoded in version1Reg MAC HW register)
+    u32_l version_machw_1;
+    /// Version2 of the MAC HW (as encoded in version2Reg MAC HW register)
+    u32_l version_machw_2;
+    /// Version1 of the PHY (depends on actual PHY)
+    u32_l version_phy_1;
+    /// Version2 of the PHY (depends on actual PHY)
+    u32_l version_phy_2;
+    /// Supported Features
+    u32_l features;
+    /// Maximum number of supported stations
+    u16_l max_sta_nb;
+    /// Maximum number of supported virtual interfaces
+    u8_l max_vif_nb;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_ADD_REQ message.
+struct mm_sta_add_req
+{
+    /// Maximum A-MPDU size, in bytes, for HE frames
+    u32_l ampdu_size_max_he;
+    /// Maximum A-MPDU size, in bytes, for VHT frames
+    u32_l ampdu_size_max_vht;
+    /// PAID/GID
+    u32_l paid_gid;
+    /// Maximum A-MPDU size, in bytes, for HT frames
+    u16_l ampdu_size_max_ht;
+    /// MAC address of the station to be added
+    struct mac_addr mac_addr;
+    /// A-MPDU spacing, in us
+    u8_l ampdu_spacing_min;
+    /// Interface index
+    u8_l inst_nbr;
+    /// TDLS station
+    bool_l tdls_sta;
+    /// Indicate if the station is TDLS link initiator station
+    bool_l tdls_sta_initiator;
+    /// Indicate if the TDLS Channel Switch is allowed
+    bool_l tdls_chsw_allowed;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_ADD_CFM message.
+struct mm_sta_add_cfm
+{
+    /// Status of the operation (different from 0 if unsuccessful)
+    u8_l status;
+    /// Index assigned by the LMAC to the newly added station
+    u8_l sta_idx;
+    /// MAC HW index of the newly added station
+    u8_l hw_sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_DEL_REQ message.
+struct mm_sta_del_req
+{
+    /// Index of the station to be deleted
+    u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_DEL_CFM message.
+struct mm_sta_del_cfm
+{
+    /// Status of the operation (different from 0 if unsuccessful)
+    u8_l     status;
+};
+
+/// Structure containing the parameters of the SET_POWER_MODE REQ message.
+struct mm_setpowermode_req
+{
+    u8_l mode;
+    u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the SET_POWER_MODE CFM message.
+struct mm_setpowermode_cfm
+{
+    u8_l     status;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_ADD REQ message.
+struct mm_key_add_req
+{
+    /// Key index (valid only for default keys)
+    u8_l key_idx;
+    /// STA index (valid only for pairwise or mesh group keys)
+    u8_l sta_idx;
+    /// Key material
+    struct mac_sec_key key;
+    /// Cipher suite (WEP64, WEP128, TKIP, CCMP)
+    u8_l cipher_suite;
+    /// Index of the interface for which the key is set (valid only for default keys or mesh group keys)
+    u8_l inst_nbr;
+    /// A-MSDU SPP parameter
+    u8_l spp;
+    /// Indicate if provided key is a pairwise key or not
+    bool_l pairwise;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_ADD_CFM message.
+struct mm_key_add_cfm
+{
+    /// Status of the operation (different from 0 if unsuccessful)
+    u8_l status;
+    /// HW index of the key just added
+    u8_l hw_key_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_DEL_REQ message.
+struct mm_key_del_req
+{
+    /// HW index of the key to be deleted
+    u8_l hw_key_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_ADD_REQ message.
+struct mm_ba_add_req
+{
+    ///Type of agreement (0: TX, 1: RX)
+    u8_l  type;
+    ///Index of peer station with which the agreement is made
+    u8_l  sta_idx;
+    ///TID for which the agreement is made with peer station
+    u8_l  tid;
+    ///Buffer size - number of MPDUs that can be held in its buffer per TID
+    u8_l  bufsz;
+    /// Start sequence number negotiated during BA setup - the one in first aggregated MPDU counts more
+    u16_l ssn;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_ADD_CFM message.
+struct mm_ba_add_cfm
+{
+    ///Index of peer station for which the agreement is being confirmed
+    u8_l sta_idx;
+    ///TID for which the agreement is being confirmed
+    u8_l tid;
+    /// Status of ba establishment
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_DEL_REQ message.
+struct mm_ba_del_req
+{
+    ///Type of agreement (0: TX, 1: RX)
+    u8_l type;
+    ///Index of peer station for which the agreement is being deleted
+    u8_l sta_idx;
+    ///TID for which the agreement is being deleted
+    u8_l tid;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_DEL_CFM message.
+struct mm_ba_del_cfm
+{
+    ///Index of peer station for which the agreement deletion is being confirmed
+    u8_l sta_idx;
+    ///TID for which the agreement deletion is being confirmed
+    u8_l tid;
+    /// Status of ba deletion
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message
+struct mm_chan_ctxt_add_req
+{
+    /// Operating channel
+    struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message
+struct mm_chan_ctxt_add_cfm
+{
+    /// Status of the addition
+    u8_l status;
+    /// Index of the new channel context
+    u8_l index;
+};
+
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_DEL_REQ message
+struct mm_chan_ctxt_del_req
+{
+    /// Index of the new channel context to be deleted
+    u8_l index;
+};
+
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_LINK_REQ message
+struct mm_chan_ctxt_link_req
+{
+    /// VIF index
+    u8_l vif_index;
+    /// Channel context index
+    u8_l chan_index;
+    /// Indicate if this is a channel switch (unlink current ctx first if true)
+    u8_l chan_switch;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UNLINK_REQ message
+struct mm_chan_ctxt_unlink_req
+{
+    /// VIF index
+    u8_l vif_index;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UPDATE_REQ message
+struct mm_chan_ctxt_update_req
+{
+    /// Channel context index
+    u8_l chan_index;
+    /// New channel information
+    struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_SCHED_REQ message
+struct mm_chan_ctxt_sched_req
+{
+    /// VIF index
+    u8_l vif_index;
+    /// Channel context index
+    u8_l chan_index;
+    /// Type of the scheduling request (0: normal scheduling, 1: derogatory
+    /// scheduling)
+    u8_l type;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_SWITCH_IND message
+struct mm_channel_switch_ind
+{
+    /// Index of the channel context we will switch to
+    u8_l chan_index;
+    /// Indicate if the switch has been triggered by a Remain on channel request
+    bool_l roc;
+    /// VIF on which remain on channel operation has been started (if roc == 1)
+    u8_l vif_index;
+    /// Indicate if the switch has been triggered by a TDLS Remain on channel request
+    bool_l roc_tdls;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_PRE_SWITCH_IND message
+struct mm_channel_pre_switch_ind
+{
+    /// Index of the channel context we will switch to
+    u8_l chan_index;
+};
+
+/// Structure containing the parameters of the @ref MM_CONNECTION_LOSS_IND message.
+struct mm_connection_loss_ind
+{
+    /// VIF instance number
+    u8_l inst_nbr;
+};
+
+
+/// Structure containing the parameters of the @ref MM_DBG_TRIGGER_REQ message.
+struct mm_dbg_trigger_req
+{
+    /// Error trace to be reported by the LMAC
+    char error[64];
+};
+
+/// Structure containing the parameters of the @ref MM_SET_PS_MODE_REQ message.
+struct mm_set_ps_mode_req
+{
+    /// Power Save is activated or deactivated
+    u8_l  new_state;
+};
+
+/// Structure containing the parameters of the @ref MM_BCN_CHANGE_REQ message.
+#define BCN_MAX_CSA_CPT 2
+struct mm_bcn_change_req
+{
+    /// Pointer, in host memory, to the new beacon template
+    u32_l bcn_ptr;
+    /// Length of the beacon template
+    u16_l bcn_len;
+    /// Offset of the TIM IE in the beacon
+    u16_l tim_oft;
+    /// Length of the TIM IE
+    u8_l tim_len;
+    /// Index of the VIF for which the beacon is updated
+    u8_l inst_nbr;
+    /// Offset of CSA (channel switch announcement) counters (0 means no counter)
+    u8_l csa_oft[BCN_MAX_CSA_CPT];
+};
+
+
+/// Structure containing the parameters of the @ref MM_TIM_UPDATE_REQ message.
+struct mm_tim_update_req
+{
+    /// Association ID of the STA the bit of which has to be updated (0 for BC/MC traffic)
+    u16_l aid;
+    /// Flag indicating the availability of data packets for the given STA
+    u8_l tx_avail;
+    /// Index of the VIF for which the TIM is updated
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_REQ message.
+struct mm_remain_on_channel_req
+{
+    /// Operation Code
+    u8_l op_code;
+    /// VIF Index
+    u8_l vif_index;
+    /// Band (2.4GHz or 5GHz)
+    u8_l band;
+    /// Channel type: 20,40,80,160 or 80+80 MHz
+    u8_l type;
+    /// Frequency for Primary 20MHz channel (in MHz)
+    u16_l prim20_freq;
+    /// Frequency for Center of the contiguous channel or center of Primary 80+80
+    u16_l center1_freq;
+    /// Frequency for Center of the non-contiguous secondary 80+80
+    u16_l center2_freq;
+    /// Duration (in ms)
+    u32_l duration_ms;
+    /// TX power (in dBm)
+    s8_l tx_power;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_CFM message
+struct mm_remain_on_channel_cfm
+{
+    /// Operation Code
+    u8_l op_code;
+    /// Status of the operation
+    u8_l status;
+    /// Channel Context index
+    u8_l chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_EXP_IND message
+struct mm_remain_on_channel_exp_ind
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// Channel Context index
+    u8_l chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_REQ message.
+struct mm_set_uapsd_tmr_req
+{
+    /// action: Start or Stop the timer
+    u8_l  action;
+    /// timeout value, in milliseconds
+    u32_l  timeout;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_CFM message.
+struct mm_set_uapsd_tmr_cfm
+{
+    /// Status of the operation (different from 0 if unsuccessful)
+    u8_l     status;
+};
+
+
+/// Structure containing the parameters of the @ref MM_PS_CHANGE_IND message
+struct mm_ps_change_ind
+{
+    /// Index of the peer device that is switching its PS state
+    u8_l sta_idx;
+    /// New PS state of the peer device (0: active, 1: sleeping)
+    u8_l ps_state;
+};
+
+/// Structure containing the parameters of the @ref MM_P2P_VIF_PS_CHANGE_IND message
+struct mm_p2p_vif_ps_change_ind
+{
+    /// Index of the P2P VIF that is switching its PS state
+    u8_l vif_index;
+    /// New PS state of the P2P VIF interface (0: active, 1: sleeping)
+    u8_l ps_state;
+};
+
+/// Structure containing the parameters of the @ref MM_TRAFFIC_REQ_IND message
+struct mm_traffic_req_ind
+{
+    /// Index of the peer device that needs traffic
+    u8_l sta_idx;
+    /// Number of packets that need to be sent (if 0, all buffered traffic shall be sent and
+    /// if set to @ref PS_SP_INTERRUPTED, it means that current service period has been interrupted)
+    u8_l pkt_cnt;
+    /// Flag indicating if the traffic request concerns U-APSD queues or not
+    bool_l uapsd;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_PS_OPTIONS_REQ message.
+struct mm_set_ps_options_req
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// Listen interval (0 if wake up shall be based on DTIM period)
+    u16_l listen_interval;
+    /// Flag indicating if we shall listen the BC/MC traffic or not
+    bool_l dont_listen_bc_mc;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_COUNTER_IND message
+struct mm_csa_counter_ind
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// Updated CSA counter value
+    u8_l csa_count;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_SURVEY_IND message
+struct mm_channel_survey_ind
+{
+    /// Frequency of the channel
+    u16_l freq;
+    /// Noise in dbm
+    s8_l noise_dbm;
+    /// Amount of time spent of the channel (in ms)
+    u32_l chan_time_ms;
+    /// Amount of time the primary channel was sensed busy
+    u32_l chan_time_busy_ms;
+};
+
+/// Structure containing the parameters of the @ref MM_BFMER_ENABLE_REQ message.
+struct mm_bfmer_enable_req
+{
+    /**
+     * Address of the beamforming report space allocated in host memory
+     * (Valid only if vht_su_bfmee is true)
+     */
+    u32_l host_bfr_addr;
+    /**
+     * Size of the beamforming report space allocated in host memory. This space should
+     * be twice the maximum size of the expected beamforming reports as the FW will
+     * divide it in two in order to be able to upload a new report while another one is
+     * used in transmission
+     */
+    u16_l host_bfr_size;
+    /// AID
+    u16_l aid;
+    /// Station Index
+    u8_l sta_idx;
+    /// Maximum number of spatial streams the station can receive
+    u8_l rx_nss;
+    /**
+     * Indicate if peer STA is MU Beamformee (VHT) capable
+     * (Valid only if vht_su_bfmee is true)
+     */
+    bool_l vht_mu_bfmee;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_REQ message.
+struct mm_set_p2p_noa_req
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// Allocated NOA Instance Number - Valid only if count = 0
+    u8_l noa_inst_nb;
+    /// Count
+    u8_l count;
+    /// Indicate if NoA can be paused for traffic reason
+    bool_l dyn_noa;
+    /// Duration (in us)
+    u32_l duration_us;
+    /// Interval (in us)
+    u32_l interval_us;
+    /// Start Time offset from next TBTT (in us)
+    u32_l start_offset;
+};
+
+#ifdef AICWF_ARP_OFFLOAD
+struct mm_set_arpoffload_en_req
+{
+	u32_l ipaddr;
+	u8_l enable;
+	u8_l vif_idx;
+};
+
+struct mm_set_arpoffload_en_cfm
+{
+	u8_l status;
+};
+#endif
+
+struct mm_set_agg_disable_req
+{
+    u8_l disable;
+    u8_l staidx;
+};
+
+struct mm_set_coex_req
+{
+    u8_l bt_on;
+    u8_l disable_coexnull;
+    u8_l enable_nullcts;
+    u8_l enable_periodic_timer;
+    u8_l coex_timeslot_set;
+    u32_l coex_timeslot[2];
+};
+
+struct mm_set_rf_config_req
+{
+    u8_l table_sel;
+    u8_l table_ofst;
+    u8_l table_num;
+	u8_l deft_page;
+    u32_l data[64];
+
+};
+
+struct mm_set_rf_calib_req
+{
+    u32_l cal_cfg_24g;
+    u32_l cal_cfg_5g;
+    u32_l param_alpha;
+    u32_l bt_calib_en;
+    u32_l bt_calib_param;
+    u8_l xtal_cap;
+    u8_l xtal_cap_fine;
+};
+
+struct mm_set_rf_calib_cfm
+{
+    u32_l rxgain_24g_addr;
+    u32_l rxgain_5g_addr;
+    u32_l txgain_24g_addr;
+    u32_l txgain_5g_addr;
+};
+
+struct mm_get_mac_addr_req
+{
+    u32_l get;
+};
+
+struct mm_get_mac_addr_cfm
+{
+    u8_l mac_addr[6];
+};
+
+struct mm_get_sta_info_req {
+	u8_l sta_idx;
+};
+
+struct mm_get_sta_info_cfm {
+	u32_l rate_info;
+	u32_l txfailed;
+	u8    rssi;
+};
+
+typedef struct
+{
+    u8_l enable;
+    u8_l dsss;
+    u8_l ofdmlowrate_2g4;
+    u8_l ofdm64qam_2g4;
+    u8_l ofdm256qam_2g4;
+    u8_l ofdm1024qam_2g4;
+    u8_l ofdmlowrate_5g;
+    u8_l ofdm64qam_5g;
+    u8_l ofdm256qam_5g;
+    u8_l ofdm1024qam_5g;
+} txpwr_lvl_conf_t;
+
+typedef struct
+{
+    u8_l enable;
+    s8_l pwrlvl_11b_11ag_2g4[12];
+    s8_l pwrlvl_11n_11ac_2g4[10];
+    s8_l pwrlvl_11ax_2g4[12];
+} txpwr_lvl_conf_v2_t;
+
+typedef struct
+{
+    u8_l enable;
+    s8_l pwrlvl_11b_11ag_2g4[12];
+    s8_l pwrlvl_11n_11ac_2g4[10];
+    s8_l pwrlvl_11ax_2g4[12];
+    s8_l pwrlvl_11a_5g[12];
+    s8_l pwrlvl_11n_11ac_5g[10];
+    s8_l pwrlvl_11ax_5g[12];
+} txpwr_lvl_conf_v3_t;
+
+typedef struct
+{
+    u8_l enable;
+    s8_l pwrlvl_adj_tbl_2g4[3];
+    s8_l pwrlvl_adj_tbl_5g[6];
+} txpwr_lvl_adj_conf_t;
+
+struct mm_set_txpwr_lvl_req
+{
+  union {
+    txpwr_lvl_conf_t txpwr_lvl;
+    txpwr_lvl_conf_v2_t txpwr_lvl_v2;
+	txpwr_lvl_conf_v3_t txpwr_lvl_v3;
+  };
+};
+
+struct mm_set_txpwr_lvl_adj_req
+{
+    txpwr_lvl_adj_conf_t txpwr_lvl_adj;
+};
+
+typedef struct
+{
+    u8_l loss_enable;
+    s8_l loss_value;
+} txpwr_loss_conf_t;
+
+typedef struct
+{
+    u8_l enable;
+    s8_l chan_1_4;
+    s8_l chan_5_9;
+    s8_l chan_10_13;
+    s8_l chan_36_64;
+    s8_l chan_100_120;
+    s8_l chan_122_140;
+    s8_l chan_142_165;
+} txpwr_ofst_conf_t;
+
+/*
+ * pwrofst2x_tbl_2g4[3][3]:
+ * +---------------+----------+----------+----------+
+ * | RateTyp\ChGrp |  CH_1_4  |  CH_5_9  | CH_10_13 |
+ * +---------------+----------+----------+----------+
+ * | DSSS          |  [0][0]  |  [0][1]  |  [0][2]  |
+ * +---------------+----------+----------+----------+
+ * | OFDM_HIGHRATE |  [1][0]  |  [1][1]  |  [1][2]  |
+ * +---------------+----------+----------+----------+
+ * | OFDM_LOWRATE  |  [2][0]  |  [2][1]  |  [2][2]  |
+ * +---------------+----------+----------+----------+
+ * pwrofst2x_tbl_5g[3][6]:
+ * +---------------+--------------+--------------+----------------+----------------+----------------+----------------+
+ * | RateTyp\ChGrp | CH_42(36~50) | CH_58(51~64) | CH_106(98~114) | CH_122(115~130)| CH_138(131~146)| CH_155(147~166)|
+ * +---------------+--------------+--------------+----------------+----------------+----------------+----------------+
+ * | OFDM_LOWRATE  |    [0][0]    |    [0][1]    |     [0][2]     |     [0][3]     |     [0][4]     |     [0][5]     |
+ * +---------------+--------------+--------------+----------------+----------------+----------------+----------------+
+ * | OFDM_HIGHRATE |    [1][0]    |    [1][1]    |     [1][2]     |     [1][3]     |     [1][4]     |     [1][5]     |
+ * +---------------+--------------+--------------+----------------+----------------+----------------+----------------+
+ * | OFDM_MIDRATE  |    [2][0]    |    [2][1]    |     [2][2]     |     [2][3]     |     [2][4]     |     [2][5]     |
+ * +---------------+--------------+--------------+----------------+----------------+----------------+----------------+
+ */
+
+typedef struct
+{
+    int8_t enable;
+    int8_t pwrofst2x_tbl_2g4[3][3];
+    int8_t pwrofst2x_tbl_5g[3][6];
+} txpwr_ofst2x_conf_t;
+
+
+struct mm_set_txpwr_ofst_req
+{
+    union {
+	  txpwr_ofst_conf_t txpwr_ofst;
+	  txpwr_ofst2x_conf_t txpwr_ofst2x;
+	};
+};
+
+struct mm_set_stack_start_req {
+	u8_l is_stack_start;
+	u8_l efuse_valid;
+	u8_l set_vendor_info;
+	u8_l fwtrace_redir;
+};
+
+struct mm_set_stack_start_cfm {
+	u8_l is_5g_support;
+	u8_l vendor_info;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_REQ message.
+struct mm_set_p2p_oppps_req
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// CTWindow
+    u8_l ctwindow;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_CFM message.
+struct mm_set_p2p_noa_cfm
+{
+    /// Request status
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_CFM message.
+struct mm_set_p2p_oppps_cfm
+{
+    /// Request status
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_P2P_NOA_UPD_IND message.
+struct mm_p2p_noa_upd_ind
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// NOA Instance Number
+    u8_l noa_inst_nb;
+    /// NoA Type
+    u8_l noa_type;
+    /// Count
+    u8_l count;
+    /// Duration (in us)
+    u32_l duration_us;
+    /// Interval (in us)
+    u32_l interval_us;
+    /// Start Time
+    u32_l start_time;
+};
+
+/// Structure containing the parameters of the @ref MM_CFG_RSSI_REQ message
+struct mm_cfg_rssi_req
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// RSSI threshold
+    s8_l rssi_thold;
+    /// RSSI hysteresis
+    u8_l rssi_hyst;
+};
+
+/// Structure containing the parameters of the @ref MM_RSSI_STATUS_IND message
+struct mm_rssi_status_ind
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// Status of the RSSI
+    bool_l rssi_status;
+    /// Current RSSI
+    s8_l rssi;
+};
+
+/// Structure containing the parameters of the @ref MM_PKTLOSS_IND message
+struct mm_pktloss_ind
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// Address of the STA for which there is a packet loss
+    struct mac_addr mac_addr;
+    /// Number of packets lost
+    u32 num_packets;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_FINISH_IND message
+struct mm_csa_finish_ind
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// Status of the operation
+    u8_l status;
+    /// New channel ctx index
+    u8_l chan_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_TRAFFIC_IND message
+struct mm_csa_traffic_ind
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// Is tx traffic enable or disable
+    bool_l enable;
+};
+
+/// Structure containing the parameters of the @ref MM_MU_GROUP_UPDATE_REQ message.
+/// Size allocated for the structure depends of the number of group
+struct mm_mu_group_update_req
+{
+    /// Station index
+    u8_l sta_idx;
+    /// Number of groups the STA belongs to
+    u8_l group_cnt;
+    /// Group information
+    struct
+    {
+        /// Group Id
+        u8_l group_id;
+        /// User position
+        u8_l user_pos;
+    } groups[0];
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Scan messages
+///////////////////////////////////////////////////////////////////////////////
+enum scan_msg_tag
+{
+    /// Scanning start Request.
+    SCAN_START_REQ = LMAC_FIRST_MSG(TASK_SCAN),
+    /// Scanning start Confirmation.
+    SCAN_START_CFM,
+    /// End of scanning indication.
+    SCAN_DONE_IND,
+    /// Cancel scan request
+    SCAN_CANCEL_REQ,
+    /// Cancel scan confirmation
+    SCAN_CANCEL_CFM,
+
+    /// MAX number of messages
+    SCAN_MAX,
+};
+
+/// Maximum number of SSIDs in a scan request
+#define SCAN_SSID_MAX   3
+
+/// Maximum number of channels in a scan request
+#define SCAN_CHANNEL_MAX (MAC_DOMAINCHANNEL_24G_MAX + MAC_DOMAINCHANNEL_5G_MAX)
+
+/// Maximum length of the ProbeReq IEs (SoftMAC mode)
+#define SCAN_MAX_IE_LEN 300
+
+/// Maximum number of PHY bands supported
+#define SCAN_BAND_MAX 2
+
+/// Structure containing the parameters of the @ref SCAN_START_REQ message
+struct scan_start_req
+{
+    /// List of channel to be scanned
+    struct mac_chan_def chan[SCAN_CHANNEL_MAX];
+    /// List of SSIDs to be scanned
+    struct mac_ssid ssid[SCAN_SSID_MAX];
+    /// BSSID to be scanned
+    struct mac_addr bssid;
+    /// Pointer (in host memory) to the additional IEs that need to be added to the ProbeReq
+    /// (following the SSID element)
+    u32_l add_ies;
+    /// Length of the additional IEs
+    u16_l add_ie_len;
+    /// Index of the VIF that is scanning
+    u8_l vif_idx;
+    /// Number of channels to scan
+    u8_l chan_cnt;
+    /// Number of SSIDs to scan for
+    u8_l ssid_cnt;
+    /// no CCK - For P2P frames not being sent at CCK rate in 2GHz band.
+    bool no_cck;
+};
+
+/// Structure containing the parameters of the @ref SCAN_START_CFM message
+struct scan_start_cfm
+{
+    /// Status of the request
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref SCAN_CANCEL_REQ message
+struct scan_cancel_req
+{
+};
+
+/// Structure containing the parameters of the @ref SCAN_START_CFM message
+struct scan_cancel_cfm
+{
+    /// Status of the request
+    u8_l status;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Scanu messages
+///////////////////////////////////////////////////////////////////////////////
+/// Messages that are logically related to the task.
+enum
+{
+    /// Scan request from host.
+    SCANU_START_REQ = LMAC_FIRST_MSG(TASK_SCANU),
+    /// Scanning start Confirmation.
+    SCANU_START_CFM,
+    /// Join request
+    SCANU_JOIN_REQ,
+    /// Join confirmation.
+    SCANU_JOIN_CFM,
+    /// Scan result indication.
+    SCANU_RESULT_IND,
+    /// Fast scan request from any other module.
+    SCANU_FAST_REQ,
+    /// Confirmation of fast scan request.
+    SCANU_FAST_CFM,
+
+	SCANU_VENDOR_IE_REQ,
+	SCANU_VENDOR_IE_CFM,
+	SCANU_START_CFM_ADDTIONAL,
+	SCANU_CANCEL_REQ,
+	SCANU_CANCEL_CFM,
+
+    /// MAX number of messages
+    SCANU_MAX,
+};
+
+/// Maximum length of the additional ProbeReq IEs (FullMAC mode)
+#define SCANU_MAX_IE_LEN  200
+
+/// Structure containing the parameters of the @ref SCANU_START_REQ message
+struct scanu_start_req
+{
+    /// List of channel to be scanned
+    struct mac_chan_def chan[SCAN_CHANNEL_MAX];
+    /// List of SSIDs to be scanned
+    struct mac_ssid ssid[SCAN_SSID_MAX];
+    /// BSSID to be scanned (or WILDCARD BSSID if no BSSID is searched in particular)
+    struct mac_addr bssid;
+    /// Address (in host memory) of the additional IEs that need to be added to the ProbeReq
+    /// (following the SSID element)
+    u32_l add_ies;
+    /// Length of the additional IEs
+    u16_l add_ie_len;
+    /// Index of the VIF that is scanning
+    u8_l vif_idx;
+    /// Number of channels to scan
+    u8_l chan_cnt;
+    /// Number of SSIDs to scan for
+    u8_l ssid_cnt;
+    /// no CCK - For P2P frames not being sent at CCK rate in 2GHz band.
+    bool no_cck;
+};
+
+struct scanu_vendor_ie_req
+{
+	u16_l add_ie_len;
+	u8_l vif_idx;
+	u8_l  ie[256];
+};
+
+/// Structure containing the parameters of the @ref SCANU_START_CFM message
+struct scanu_start_cfm
+{
+    /// Index of the VIF that was scanning
+    u8_l vif_idx;
+    /// Status of the request
+    u8_l status;
+    /// Number of scan results available
+    u8_l result_cnt;
+};
+
+/// Parameters of the @SCANU_RESULT_IND message
+struct scanu_result_ind
+{
+    /// Length of the frame
+    u16_l length;
+    /// Frame control field of the frame.
+    u16_l framectrl;
+    /// Center frequency on which we received the packet
+    u16_l center_freq;
+    /// PHY band
+    u8_l band;
+    /// Index of the station that sent the frame. 0xFF if unknown.
+    u8_l sta_idx;
+    /// Index of the VIF that received the frame. 0xFF if unknown.
+    u8_l inst_nbr;
+    /// RSSI of the received frame.
+    s8_l rssi;
+    /// Frame payload.
+    u32_l payload[];
+};
+
+/// Structure containing the parameters of the message.
+struct scanu_fast_req
+{
+    /// The SSID to scan in the channel.
+    struct mac_ssid ssid;
+    /// BSSID.
+    struct mac_addr bssid;
+    /// Probe delay.
+    u16_l probe_delay;
+    /// Minimum channel time.
+    u16_l minch_time;
+    /// Maximum channel time.
+    u16_l maxch_time;
+    /// The channel number to scan.
+    u16_l ch_nbr;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For ME messages
+///////////////////////////////////////////////////////////////////////////////
+/// Messages that are logically related to the task.
+enum
+{
+    /// Configuration request from host.
+    ME_CONFIG_REQ = LMAC_FIRST_MSG(TASK_ME),
+    /// Configuration confirmation.
+    ME_CONFIG_CFM,
+    /// Configuration request from host.
+    ME_CHAN_CONFIG_REQ,
+    /// Configuration confirmation.
+    ME_CHAN_CONFIG_CFM,
+    /// Set control port state for a station.
+    ME_SET_CONTROL_PORT_REQ,
+    /// Control port setting confirmation.
+    ME_SET_CONTROL_PORT_CFM,
+    /// TKIP MIC failure indication.
+    ME_TKIP_MIC_FAILURE_IND,
+    /// Add a station to the FW (AP mode)
+    ME_STA_ADD_REQ,
+    /// Confirmation of the STA addition
+    ME_STA_ADD_CFM,
+    /// Delete a station from the FW (AP mode)
+    ME_STA_DEL_REQ,
+    /// Confirmation of the STA deletion
+    ME_STA_DEL_CFM,
+    /// Indication of a TX RA/TID queue credit update
+    ME_TX_CREDITS_UPDATE_IND,
+    /// Request indicating to the FW that there is traffic buffered on host
+    ME_TRAFFIC_IND_REQ,
+    /// Confirmation that the @ref ME_TRAFFIC_IND_REQ has been executed
+    ME_TRAFFIC_IND_CFM,
+    /// Request of RC statistics to a station
+    ME_RC_STATS_REQ,
+    /// RC statistics confirmation
+    ME_RC_STATS_CFM,
+    /// RC fixed rate request
+    ME_RC_SET_RATE_REQ,
+    /// Configure monitor interface
+    ME_CONFIG_MONITOR_REQ,
+    /// Configure monitor interface response
+    ME_CONFIG_MONITOR_CFM,
+    /// Setting power Save mode request from host
+    ME_SET_PS_MODE_REQ,
+    /// Set power Save mode confirmation
+    ME_SET_PS_MODE_CFM,
+    /// Setting Low Power level request from host
+    ME_SET_LP_LEVEL_REQ,
+    /// Set Low Power level confirmation
+    ME_SET_LP_LEVEL_CFM,
+    /// MAX number of messages
+    ME_MAX,
+};
+
+/// Structure containing the parameters of the @ref ME_START_REQ message
+struct me_config_req
+{
+    /// HT Capabilities
+    struct mac_htcapability ht_cap;
+    /// VHT Capabilities
+    struct mac_vhtcapability vht_cap;
+    /// HE capabilities
+    struct mac_hecapability he_cap;
+    /// Lifetime of packets sent under a BlockAck agreement (expressed in TUs)
+    u16_l tx_lft;
+    /// Maximum supported BW
+    u8_l phy_bw_max;
+    /// Boolean indicating if HT is supported or not
+    bool_l ht_supp;
+    /// Boolean indicating if VHT is supported or not
+    bool_l vht_supp;
+    /// Boolean indicating if HE is supported or not
+    bool_l he_supp;
+    /// Boolean indicating if HE OFDMA UL is enabled or not
+    bool_l he_ul_on;
+    /// Boolean indicating if PS mode shall be enabled or not
+    bool_l ps_on;
+    /// Boolean indicating if Antenna Diversity shall be enabled or not
+    bool_l ant_div_on;
+    /// Boolean indicating if Dynamic PS mode shall be used or not
+    bool_l dpsm;
+};
+
+/// Structure containing the parameters of the @ref ME_CHAN_CONFIG_REQ message
+struct me_chan_config_req
+{
+    /// List of 2.4GHz supported channels
+    struct mac_chan_def chan2G4[MAC_DOMAINCHANNEL_24G_MAX];
+    /// List of 5GHz supported channels
+    struct mac_chan_def chan5G[MAC_DOMAINCHANNEL_5G_MAX];
+    /// Number of 2.4GHz channels in the list
+    u8_l chan2G4_cnt;
+    /// Number of 5GHz channels in the list
+    u8_l chan5G_cnt;
+};
+
+/// Structure containing the parameters of the @ref ME_SET_CONTROL_PORT_REQ message
+struct me_set_control_port_req
+{
+    /// Index of the station for which the control port is opened
+    u8_l sta_idx;
+    /// Control port state
+    bool_l control_port_open;
+};
+
+/// Structure containing the parameters of the @ref ME_TKIP_MIC_FAILURE_IND message
+struct me_tkip_mic_failure_ind
+{
+    /// Address of the sending STA
+    struct mac_addr addr;
+    /// TSC value
+    u64_l tsc;
+    /// Boolean indicating if the packet was a group or unicast one (true if group)
+    bool_l ga;
+    /// Key Id
+    u8_l keyid;
+    /// VIF index
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_ADD_REQ message
+struct me_sta_add_req
+{
+    /// MAC address of the station to be added
+    struct mac_addr mac_addr;
+    /// Supported legacy rates
+    struct mac_rateset rate_set;
+    /// HT Capabilities
+    struct mac_htcapability ht_cap;
+    /// VHT Capabilities
+    struct mac_vhtcapability vht_cap;
+    /// HE capabilities
+    struct mac_hecapability he_cap;
+    /// Flags giving additional information about the station (@ref mac_sta_flags)
+    u32_l flags;
+    /// Association ID of the station
+    u16_l aid;
+    /// Bit field indicating which queues have U-APSD enabled
+    u8_l uapsd_queues;
+    /// Maximum size, in frames, of a APSD service period
+    u8_l max_sp_len;
+    /// Operation mode information (valid if bit @ref STA_OPMOD_NOTIF is
+    /// set in the flags)
+    u8_l opmode;
+    /// Index of the VIF the station is attached to
+    u8_l vif_idx;
+    /// Whether the the station is TDLS station
+    bool_l tdls_sta;
+    /// Indicate if the station is TDLS link initiator station
+    bool_l tdls_sta_initiator;
+    /// Indicate if the TDLS Channel Switch is allowed
+    bool_l tdls_chsw_allowed;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_ADD_CFM message
+struct me_sta_add_cfm
+{
+    /// Station index
+    u8_l sta_idx;
+    /// Status of the station addition
+    u8_l status;
+    /// PM state of the station
+    u8_l pm_state;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_DEL_REQ message.
+struct me_sta_del_req
+{
+    /// Index of the station to be deleted
+    u8_l sta_idx;
+    /// Whether the the station is TDLS station
+    bool_l tdls_sta;
+};
+
+/// Structure containing the parameters of the @ref ME_TX_CREDITS_UPDATE_IND message.
+struct me_tx_credits_update_ind
+{
+    /// Index of the station for which the credits are updated
+    u8_l sta_idx;
+    /// TID for which the credits are updated
+    u8_l tid;
+    /// Offset to be applied on the credit count
+    s8_l credits;
+};
+
+/// Structure containing the parameters of the @ref ME_TRAFFIC_IND_REQ message.
+struct me_traffic_ind_req
+{
+    /// Index of the station for which UAPSD traffic is available on host
+    u8_l sta_idx;
+    /// Flag indicating the availability of UAPSD packets for the given STA
+    u8_l tx_avail;
+    /// Indicate if traffic is on uapsd-enabled queues
+    bool_l uapsd;
+};
+
+struct mm_apm_staloss_ind
+{
+    u8_l sta_idx;
+    u8_l vif_idx;
+    u8_l mac_addr[6];
+};
+
+struct mm_set_txop_req
+{
+    u16_l txop_bk;
+    u16_l txop_be;
+    u16_l txop_vi;
+    u16_l txop_vo;
+    u8_l  long_nav_en;
+    u8_l  cfe_en;
+};
+
+struct mm_get_chip_temp_req
+{
+    u32_l hwconfig_id;
+};
+
+struct mm_get_chip_temp_cfm
+{
+    /// Temp degree val
+    s8_l degree;
+};
+
+struct mm_set_vendor_hwconfig_cfm
+{
+    u32_l hwconfig_id;
+    union {
+        struct mm_get_chip_temp_cfm chip_temp_cfm;
+    };
+};
+
+
+struct mm_get_fw_version_cfm
+{
+    u8_l fw_version_len;
+    u8_l fw_version[63];
+};
+
+/// Structure containing the parameters of the @ref ME_RC_STATS_REQ message.
+struct me_rc_stats_req
+{
+    /// Index of the station for which the RC statistics are requested
+    u8_l sta_idx;
+};
+
+/// Structure containing the rate control statistics
+struct rc_rate_stats
+{
+    /// Number of attempts (per sampling interval)
+    u16_l attempts;
+    /// Number of success (per sampling interval)
+    u16_l success;
+    /// Estimated probability of success (EWMA)
+    u16_l probability;
+    /// Rate configuration of the sample
+    u16_l rate_config;
+    union
+    {
+        struct {
+            /// Number of times the sample has been skipped (per sampling interval)
+            u8_l  sample_skipped;
+            /// Whether the old probability is available
+            bool_l  old_prob_available;
+            /// Whether the rate can be used in the retry chain
+            bool_l rate_allowed;
+        };
+        struct {
+            /// RU size and UL length received in the latest HE trigger frame
+            u16_l ru_and_length;
+        };
+    };
+};
+
+/// Number of RC samples
+#define RC_MAX_N_SAMPLE 10
+/// Index of the HE statistics element in the table
+#define RC_HE_STATS_IDX RC_MAX_N_SAMPLE
+
+/// Structure containing the parameters of the @ref ME_RC_STATS_CFM message.
+struct me_rc_stats_cfm
+{
+    /// Index of the station for which the RC statistics are provided
+    u8_l sta_idx;
+    /// Number of samples used in the RC algorithm
+    u16_l no_samples;
+    /// Number of MPDUs transmitted (per sampling interval)
+    u16_l ampdu_len;
+    /// Number of AMPDUs transmitted (per sampling interval)
+    u16_l ampdu_packets;
+    /// Average number of MPDUs in each AMPDU frame (EWMA)
+    u32_l avg_ampdu_len;
+    // Current step 0 of the retry chain
+    u8_l sw_retry_step;
+    /// Trial transmission period
+    u8_l sample_wait;
+    /// Retry chain steps
+    u16_l retry_step_idx[4];
+    /// RC statistics - Max number of RC samples, plus one for the HE TB statistics
+    struct rc_rate_stats rate_stats[RC_MAX_N_SAMPLE + 1];
+    /// Throughput - Max number of RC samples, plus one for the HE TB statistics
+    u32_l tp[RC_MAX_N_SAMPLE + 1];
+};
+
+/// Structure containing the parameters of the @ref ME_RC_SET_RATE_REQ message.
+struct me_rc_set_rate_req
+{
+    /// Index of the station for which the fixed rate is set
+    u8_l sta_idx;
+    /// Rate configuration to be set
+    u16_l fixed_rate_cfg;
+};
+
+/// Structure containing the parameters of the @ref ME_CONFIG_MONITOR_REQ message.
+struct me_config_monitor_req {
+	/// Channel to configure
+	struct mac_chan_op chan;
+	/// Is channel data valid
+	bool_l chan_set;
+	/// Enable report of unsupported HT frames
+	bool_l uf;
+	/// Enable auto-reply as the mac_addr matches
+	bool_l auto_reply;
+};
+
+/// Structure containing the parameters of the @ref ME_CONFIG_MONITOR_CFM message.
+struct me_config_monitor_cfm
+{
+    /// Channel context index
+    u8_l chan_index;
+    /// Channel parameters
+    struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref ME_SET_PS_MODE_REQ message.
+struct me_set_ps_mode_req
+{
+    /// Power Save is activated or deactivated
+    u8_l  ps_state;
+};
+
+/// Structure containing the parameters of the @ref ME_SET_LP_LEVEL_REQ message.
+struct me_set_lp_level_req
+{
+    /// Low Power level
+    u8_l lp_level;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For SM messages
+///////////////////////////////////////////////////////////////////////////////
+/// Message API of the SM task
+enum sm_msg_tag
+{
+    /// Request to connect to an AP
+    SM_CONNECT_REQ = LMAC_FIRST_MSG(TASK_SM),
+    /// Confirmation of connection
+    SM_CONNECT_CFM,
+    /// Indicates that the SM associated to the AP
+    SM_CONNECT_IND,
+    /// Request to disconnect
+    SM_DISCONNECT_REQ,
+    /// Confirmation of disconnection
+    SM_DISCONNECT_CFM,
+    /// Indicates that the SM disassociated the AP
+    SM_DISCONNECT_IND,
+    /// Request to start external authentication
+    SM_EXTERNAL_AUTH_REQUIRED_IND,
+    /// Response to external authentication request
+    SM_EXTERNAL_AUTH_REQUIRED_RSP,
+
+    /// MAX number of messages
+    SM_MAX,
+};
+
+/// Structure containing the parameters of @ref SM_CONNECT_REQ message.
+struct sm_connect_req
+{
+    /// SSID to connect to
+    struct mac_ssid ssid;
+    /// BSSID to connect to (if not specified, set this field to WILDCARD BSSID)
+    struct mac_addr bssid;
+    /// Channel on which we have to connect (if not specified, set -1 in the chan.freq field)
+    struct mac_chan_def chan;
+    /// Connection flags (see @ref mac_connection_flags)
+    u32_l flags;
+    /// Control port Ethertype (in network endianness)
+    u16_l ctrl_port_ethertype;
+    /// Length of the association request IEs
+    u16_l ie_len;
+    /// Listen interval to be used for this connection
+    u16_l listen_interval;
+    /// Flag indicating if the we have to wait for the BC/MC traffic after beacon or not
+    bool_l dont_wait_bcmc;
+    /// Authentication type
+    u8_l auth_type;
+    /// UAPSD queues (bit0: VO, bit1: VI, bit2: BE, bit3: BK)
+    u8_l uapsd_queues;
+    /// VIF index
+    u8_l vif_idx;
+    /// Buffer containing the additional information elements to be put in the
+    /// association request
+    u32_l ie_buf[64];
+};
+
+/// Structure containing the parameters of the @ref SM_CONNECT_CFM message.
+struct sm_connect_cfm
+{
+    /// Status. If 0, it means that the connection procedure will be performed and that
+    /// a subsequent @ref SM_CONNECT_IND message will be forwarded once the procedure is
+    /// completed
+    u8_l status;
+};
+
+#define SM_ASSOC_IE_LEN   800
+/// Structure containing the parameters of the @ref SM_CONNECT_IND message.
+struct sm_connect_ind
+{
+    /// Status code of the connection procedure
+    u16_l status_code;
+    /// BSSID
+    struct mac_addr bssid;
+    /// Flag indicating if the indication refers to an internal roaming or from a host request
+    bool_l roamed;
+    /// Index of the VIF for which the association process is complete
+    u8_l vif_idx;
+    /// Index of the STA entry allocated for the AP
+    u8_l ap_idx;
+    /// Index of the LMAC channel context the connection is attached to
+    u8_l ch_idx;
+    /// Flag indicating if the AP is supporting QoS
+    bool_l qos;
+    /// ACM bits set in the AP WMM parameter element
+    u8_l acm;
+    /// Length of the AssocReq IEs
+    u16_l assoc_req_ie_len;
+    /// Length of the AssocRsp IEs
+    u16_l assoc_rsp_ie_len;
+    /// IE buffer
+    u32_l assoc_ie_buf[SM_ASSOC_IE_LEN/4];
+
+    u16_l aid;
+    u8_l band;
+    u16_l center_freq;
+    u8_l width;
+    u32_l center_freq1;
+    u32_l center_freq2;
+
+    /// EDCA parameters
+    u32_l ac_param[AC_MAX];
+};
+
+/// Structure containing the parameters of the @ref SM_DISCONNECT_REQ message.
+struct sm_disconnect_req
+{
+    /// Reason of the deauthentication.
+    u16_l reason_code;
+    /// Index of the VIF.
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of SM_ASSOCIATION_IND the message
+struct sm_association_ind
+{
+    // MAC ADDR of the STA
+    struct mac_addr     me_mac_addr;
+};
+
+
+/// Structure containing the parameters of the @ref SM_DISCONNECT_IND message.
+struct sm_disconnect_ind
+{
+    /// Reason of the disconnection.
+    u16_l reason_code;
+    /// Index of the VIF.
+    u8_l vif_idx;
+    /// FT over DS is ongoing
+    bool_l ft_over_ds;
+	u8_l reassoc;
+};
+
+/// Structure containing the parameters of the @ref SM_EXTERNAL_AUTH_REQUIRED_IND
+struct sm_external_auth_required_ind
+{
+    /// Index of the VIF.
+    u8_l vif_idx;
+    /// SSID to authenticate to
+    struct mac_ssid ssid;
+    /// BSSID to authenticate to
+    struct mac_addr bssid;
+    /// AKM suite of the respective authentication
+    u32_l akm;
+};
+
+/// Structure containing the parameters of the @ref SM_EXTERNAL_AUTH_REQUIRED_RSP
+struct sm_external_auth_required_rsp
+{
+    /// Index of the VIF.
+    u8_l vif_idx;
+    /// Authentication status
+    u16_l status;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For APM messages
+///////////////////////////////////////////////////////////////////////////////
+/// Message API of the APM task
+enum apm_msg_tag
+{
+    /// Request to start the AP.
+    APM_START_REQ = LMAC_FIRST_MSG(TASK_APM),
+    /// Confirmation of the AP start.
+    APM_START_CFM,
+    /// Request to stop the AP.
+    APM_STOP_REQ,
+    /// Confirmation of the AP stop.
+    APM_STOP_CFM,
+    /// Request to start CAC
+    APM_START_CAC_REQ,
+    /// Confirmation of the CAC start
+    APM_START_CAC_CFM,
+    /// Request to stop CAC
+    APM_STOP_CAC_REQ,
+    /// Confirmation of the CAC stop
+    APM_STOP_CAC_CFM,
+
+	APM_SET_BEACON_IE_REQ,
+	APM_SET_BEACON_IE_CFM,
+    /// MAX number of messages
+    APM_MAX,
+};
+
+/// Structure containing the parameters of the @ref APM_START_REQ message.
+struct apm_start_req
+{
+    /// Basic rate set
+    struct mac_rateset basic_rates;
+    /// Control channel on which we have to enable the AP
+    struct mac_chan_def chan;
+    /// Center frequency of the first segment
+    u32_l center_freq1;
+    /// Center frequency of the second segment (only in 80+80 configuration)
+    u32_l center_freq2;
+    /// Width of channel
+    u8_l ch_width;
+    /// Address, in host memory, to the beacon template
+    u32_l bcn_addr;
+    /// Length of the beacon template
+    u16_l bcn_len;
+    /// Offset of the TIM IE in the beacon
+    u16_l tim_oft;
+    /// Beacon interval
+    u16_l bcn_int;
+    /// Flags (@ref mac_connection_flags)
+    u32_l flags;
+    /// Control port Ethertype
+    u16_l ctrl_port_ethertype;
+    /// Length of the TIM IE
+    u8_l tim_len;
+    /// Index of the VIF for which the AP is started
+    u8_l vif_idx;
+};
+
+struct apm_set_bcn_ie_req
+{
+    u8_l vif_idx;
+    u16_l bcn_ie_len;
+    u8_l bcn_ie[512];
+};
+
+/// Structure containing the parameters of the @ref APM_START_CFM message.
+struct apm_start_cfm
+{
+    /// Status of the AP starting procedure
+    u8_l status;
+    /// Index of the VIF for which the AP is started
+    u8_l vif_idx;
+    /// Index of the channel context attached to the VIF
+    u8_l ch_idx;
+    /// Index of the STA used for BC/MC traffic
+    u8_l bcmc_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_STOP_REQ message.
+struct apm_stop_req
+{
+    /// Index of the VIF for which the AP has to be stopped
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_START_CAC_REQ message.
+struct apm_start_cac_req
+{
+    /// Control channel on which we have to start the CAC
+    struct mac_chan_def chan;
+    /// Center frequency of the first segment
+    u32_l center_freq1;
+    /// Center frequency of the second segment (only in 80+80 configuration)
+    u32_l center_freq2;
+    /// Width of channel
+    u8_l ch_width;
+    /// Index of the VIF for which the CAC is started
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_START_CAC_CFM message.
+struct apm_start_cac_cfm
+{
+    /// Status of the CAC starting procedure
+    u8_l status;
+    /// Index of the channel context attached to the VIF for CAC
+    u8_l ch_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_STOP_CAC_REQ message.
+struct apm_stop_cac_req
+{
+    /// Index of the VIF for which the CAC has to be stopped
+    u8_l vif_idx;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For MESH messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// Maximum length of the Mesh ID
+#define MESH_MESHID_MAX_LEN     (32)
+
+/// Message API of the MESH task
+enum mesh_msg_tag
+{
+    /// Request to start the MP
+    MESH_START_REQ = LMAC_FIRST_MSG(TASK_MESH),
+    /// Confirmation of the MP start.
+    MESH_START_CFM,
+
+    /// Request to stop the MP.
+    MESH_STOP_REQ,
+    /// Confirmation of the MP stop.
+    MESH_STOP_CFM,
+
+    // Request to update the MP
+    MESH_UPDATE_REQ,
+    /// Confirmation of the MP update
+    MESH_UPDATE_CFM,
+
+    /// Request information about a given link
+    MESH_PEER_INFO_REQ,
+    /// Response to the MESH_PEER_INFO_REQ message
+    MESH_PEER_INFO_CFM,
+
+    /// Request automatic establishment of a path with a given mesh STA
+    MESH_PATH_CREATE_REQ,
+    /// Confirmation to the MESH_PATH_CREATE_REQ message
+    MESH_PATH_CREATE_CFM,
+
+    /// Request a path update (delete path, modify next hop mesh STA)
+    MESH_PATH_UPDATE_REQ,
+    /// Confirmation to the MESH_PATH_UPDATE_REQ message
+    MESH_PATH_UPDATE_CFM,
+
+    /// Indication from Host that the indicated Mesh Interface is a proxy for an external STA
+    MESH_PROXY_ADD_REQ,
+
+    /// Indicate that a connection has been established or lost
+    MESH_PEER_UPDATE_IND,
+    /// Notification that a connection has been established or lost (when MPM handled by userspace)
+    MESH_PEER_UPDATE_NTF = MESH_PEER_UPDATE_IND,
+
+    /// Indicate that a path is now active or inactive
+    MESH_PATH_UPDATE_IND,
+    /// Indicate that proxy information have been updated
+    MESH_PROXY_UPDATE_IND,
+
+    /// MAX number of messages
+    MESH_MAX,
+};
+
+/// Structure containing the parameters of the @ref MESH_START_REQ message.
+struct mesh_start_req
+{
+    /// Basic rate set
+    struct mac_rateset basic_rates;
+    /// Control channel on which we have to enable the AP
+    struct mac_chan_def chan;
+    /// Center frequency of the first segment
+    u32_l center_freq1;
+    /// Center frequency of the second segment (only in 80+80 configuration)
+    u32_l center_freq2;
+    /// Width of channel
+    u8_l ch_width;
+    /// DTIM Period
+    u8_l dtim_period;
+    /// Beacon Interval
+    u16_l bcn_int;
+    /// Index of the VIF for which the MP is started
+    u8_l vif_index;
+    /// Length of the Mesh ID
+    u8_l mesh_id_len;
+    /// Mesh ID
+    u8_l mesh_id[MESH_MESHID_MAX_LEN];
+    /// Address of the IEs to download
+    u32_l ie_addr;
+    /// Length of the provided IEs
+    u8_l ie_len;
+    /// Indicate if Mesh Peering Management (MPM) protocol is handled in userspace
+    bool_l user_mpm;
+    /// Indicate if Mesh Point is using authentication
+    bool_l is_auth;
+    /// Indicate which authentication method is used
+    u8_l auth_id;
+};
+
+/// Structure containing the parameters of the @ref MESH_START_CFM message.
+struct mesh_start_cfm
+{
+    /// Status of the MP starting procedure
+    u8_l status;
+    /// Index of the VIF for which the MP is started
+    u8_l vif_idx;
+    /// Index of the channel context attached to the VIF
+    u8_l ch_idx;
+    /// Index of the STA used for BC/MC traffic
+    u8_l bcmc_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_STOP_REQ message.
+struct mesh_stop_req
+{
+    /// Index of the VIF for which the MP has to be stopped
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_STOP_CFM message.
+struct mesh_stop_cfm
+{
+    /// Index of the VIF for which the MP has to be stopped
+    u8_l vif_idx;
+   /// Status
+    u8_l status;
+};
+
+/// Bit fields for mesh_update_req message's flags value
+enum mesh_update_flags_bit
+{
+    /// Root Mode
+    MESH_UPDATE_FLAGS_ROOT_MODE_BIT = 0,
+    /// Gate Mode
+    MESH_UPDATE_FLAGS_GATE_MODE_BIT,
+    /// Mesh Forwarding
+    MESH_UPDATE_FLAGS_MESH_FWD_BIT,
+    /// Local Power Save Mode
+    MESH_UPDATE_FLAGS_LOCAL_PSM_BIT,
+};
+
+/// Structure containing the parameters of the @ref MESH_UPDATE_REQ message.
+struct mesh_update_req
+{
+    /// Flags, indicate fields which have been updated
+    u8_l flags;
+    /// VIF Index
+    u8_l vif_idx;
+    /// Root Mode
+    u8_l root_mode;
+    /// Gate Announcement
+    bool_l gate_announ;
+    /// Mesh Forwarding
+    bool_l mesh_forward;
+    /// Local PS Mode
+    u8_l local_ps_mode;
+};
+
+/// Structure containing the parameters of the @ref MESH_UPDATE_CFM message.
+struct mesh_update_cfm
+{
+    /// Status
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_INFO_REQ message.
+struct mesh_peer_info_req
+{
+    ///Index of the station allocated for the peer
+    u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_INFO_CFM message.
+struct mesh_peer_info_cfm
+{
+    /// Response status
+    u8_l status;
+    /// Index of the station allocated for the peer
+    u8_l sta_idx;
+    /// Local Link ID
+    u16_l local_link_id;
+    /// Peer Link ID
+    u16_l peer_link_id;
+    /// Local PS Mode
+    u8_l local_ps_mode;
+    /// Peer PS Mode
+    u8_l peer_ps_mode;
+    /// Non-peer PS Mode
+    u8_l non_peer_ps_mode;
+    /// Link State
+    u8_l link_state;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_CREATE_REQ message.
+struct mesh_path_create_req
+{
+    /// Index of the interface on which path has to be created
+    u8_l vif_idx;
+    /// Indicate if originator MAC Address is provided
+    bool_l has_orig_addr;
+    /// Path Target MAC Address
+    struct mac_addr tgt_mac_addr;
+    /// Originator MAC Address
+    struct mac_addr orig_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_CREATE_CFM message.
+struct mesh_path_create_cfm
+{
+    /// Confirmation status
+    u8_l status;
+    /// VIF Index
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_REQ message.
+struct mesh_path_update_req
+{
+    /// Indicate if path must be deleted
+    bool_l delete;
+    /// Index of the interface on which path has to be created
+    u8_l vif_idx;
+    /// Path Target MAC Address
+    struct mac_addr tgt_mac_addr;
+    /// Next Hop MAC Address
+    struct mac_addr nhop_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_CFM message.
+struct mesh_path_update_cfm
+{
+    /// Confirmation status
+    u8_l status;
+    /// VIF Index
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PROXY_ADD_REQ message.
+struct mesh_proxy_add_req
+{
+    /// VIF Index
+    u8_l vif_idx;
+    /// MAC Address of the External STA
+    struct mac_addr ext_sta_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PROXY_UPDATE_IND
+struct mesh_proxy_update_ind
+{
+    /// Indicate if proxy information has been added or deleted
+    bool_l delete;
+    /// Indicate if we are a proxy for the external STA
+    bool_l local;
+    /// VIF Index
+    u8_l vif_idx;
+    /// MAC Address of the External STA
+    struct mac_addr ext_sta_addr;
+    /// MAC Address of the proxy (only valid if local is false)
+    struct mac_addr proxy_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_UPDATE_IND message.
+struct mesh_peer_update_ind
+{
+    /// Indicate if connection has been established or lost
+    bool_l estab;
+    /// VIF Index
+    u8_l vif_idx;
+    /// STA Index
+    u8_l sta_idx;
+    /// Peer MAC Address
+    struct mac_addr peer_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_UPDATE_NTF message.
+struct mesh_peer_update_ntf
+{
+    /// VIF Index
+    u8_l vif_idx;
+    /// STA Index
+    u8_l sta_idx;
+    /// Mesh Link State
+    u8_l state;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_IND message.
+struct mesh_path_update_ind
+{
+    /// Indicate if path is deleted or not
+    bool_l delete;
+    /// Indicate if path is towards an external STA (not part of MBSS)
+    bool_l ext_sta;
+    /// VIF Index
+    u8_l vif_idx;
+    /// Path Index
+    u8_l path_idx;
+    /// Target MAC Address
+    struct mac_addr tgt_mac_addr;
+    /// External STA MAC Address (only if ext_sta is true)
+    struct mac_addr ext_sta_mac_addr;
+    /// Next Hop STA Index
+    u8_l nhop_sta_idx;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Debug messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// Messages related to Debug Task
+enum dbg_msg_tag
+{
+    /// Memory read request
+    DBG_MEM_READ_REQ = LMAC_FIRST_MSG(TASK_DBG),
+    /// Memory read confirm
+    DBG_MEM_READ_CFM,
+    /// Memory write request
+    DBG_MEM_WRITE_REQ,
+    /// Memory write confirm
+    DBG_MEM_WRITE_CFM,
+    /// Module filter request
+    DBG_SET_MOD_FILTER_REQ,
+    /// Module filter confirm
+    DBG_SET_MOD_FILTER_CFM,
+    /// Severity filter request
+    DBG_SET_SEV_FILTER_REQ,
+    /// Severity filter confirm
+    DBG_SET_SEV_FILTER_CFM,
+    /// LMAC/MAC HW fatal error indication
+    DBG_ERROR_IND,
+    /// Request to get system statistics
+    DBG_GET_SYS_STAT_REQ,
+    /// COnfirmation of system statistics
+    DBG_GET_SYS_STAT_CFM,
+    /// Memory block write request
+    DBG_MEM_BLOCK_WRITE_REQ,
+    /// Memory block write confirm
+    DBG_MEM_BLOCK_WRITE_CFM,
+    /// Start app request
+    DBG_START_APP_REQ,
+    /// Start app confirm
+    DBG_START_APP_CFM,
+    /// Start npc request
+    DBG_START_NPC_REQ,
+    /// Start npc confirm
+    DBG_START_NPC_CFM,
+    /// Memory mask write request
+    DBG_MEM_MASK_WRITE_REQ,
+    /// Memory mask write confirm
+    DBG_MEM_MASK_WRITE_CFM,
+
+    DBG_RFTEST_CMD_REQ,
+    DBG_RFTEST_CMD_CFM,
+    DBG_BINDING_REQ,
+    DBG_BINDING_CFM,
+    DBG_BINDING_IND,
+
+    DBG_CUSTOM_MSG_REQ,
+    DBG_CUSTOM_MSG_CFM,
+    DBG_CUSTOM_MSG_IND,
+
+    /// Max number of Debug messages
+    DBG_MAX,
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_READ_REQ message.
+struct dbg_mem_read_req
+{
+    u32_l memaddr;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_READ_CFM message.
+struct dbg_mem_read_cfm
+{
+    u32_l memaddr;
+    u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_REQ message.
+struct dbg_mem_write_req
+{
+    u32_l memaddr;
+    u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_CFM message.
+struct dbg_mem_write_cfm
+{
+    u32_l memaddr;
+    u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_MASK_WRITE_REQ message.
+struct dbg_mem_mask_write_req
+{
+    u32_l memaddr;
+    u32_l memmask;
+    u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_MASK_WRITE_CFM message.
+struct dbg_mem_mask_write_cfm
+{
+    u32_l memaddr;
+    u32_l memdata;
+};
+
+#ifdef CONFIG_RFTEST
+struct dbg_rftest_cmd_req
+{
+    u32_l cmd;
+    u32_l argc;
+    u8_l argv[30];
+};
+
+struct dbg_rftest_cmd_cfm
+{
+    u32_l rftest_result[16];
+};
+#endif
+
+#ifdef CONFIG_MCU_MESSAGE
+/// Structure containing the parameters of the @ref DBG_CUSTOM_MSG_REQ message.
+struct dbg_custom_msg_req
+{
+    u32_l cmd;
+    u32_l len;
+    u32_l flags;
+    u32_l buf[1];
+};
+
+/// Structure containing the parameters of the @ref DBG_CUSTOM_MSG_CFM message.
+struct dbg_custom_msg_cfm
+{
+    u32_l cmd;
+    u32_l len;
+    u32_l status;
+    u32_l buf[1];
+};
+
+typedef struct dbg_custom_msg_cfm dbg_custom_msg_ind_t;
+#endif
+
+/// Structure containing the parameters of the @ref DBG_SET_MOD_FILTER_REQ message.
+struct dbg_set_mod_filter_req
+{
+    /// Bit field indicating for each module if the traces are enabled or not
+    u32_l mod_filter;
+};
+
+/// Structure containing the parameters of the @ref DBG_SEV_MOD_FILTER_REQ message.
+struct dbg_set_sev_filter_req
+{
+    /// Bit field indicating the severity threshold for the traces
+    u32_l sev_filter;
+};
+
+/// Structure containing the parameters of the @ref DBG_GET_SYS_STAT_CFM message.
+struct dbg_get_sys_stat_cfm
+{
+    /// Time spent in CPU sleep since last reset of the system statistics
+    u32_l cpu_sleep_time;
+    /// Time spent in DOZE since last reset of the system statistics
+    u32_l doze_time;
+    /// Total time spent since last reset of the system statistics
+    u32_l stats_time;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_BLOCK_WRITE_REQ message.
+struct dbg_mem_block_write_req
+{
+    u32_l memaddr;
+    u32_l memsize;
+    u32_l memdata[512 / sizeof(u32_l)];
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_BLOCK_WRITE_CFM message.
+struct dbg_mem_block_write_cfm
+{
+    u32_l wstatus;
+};
+
+/// Structure containing the parameters of the @ref DBG_START_APP_REQ message.
+struct dbg_start_app_req
+{
+    u32_l bootaddr;
+    u32_l boottype;
+};
+
+/// Structure containing the parameters of the @ref DBG_START_APP_CFM message.
+struct dbg_start_app_cfm
+{
+    u32_l bootstatus;
+};
+
+enum {
+    HOST_START_APP_AUTO   = 1,
+    HOST_START_APP_CUSTOM = 2,
+    HOST_START_APP_REBOOT = 3,
+    HOST_START_APP_FNCALL = 4,
+    HOST_START_APP_DUMMY  = 5,
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For TDLS messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// List of messages related to the task.
+enum tdls_msg_tag
+{
+    /// TDLS channel Switch Request.
+    TDLS_CHAN_SWITCH_REQ = LMAC_FIRST_MSG(TASK_TDLS),
+    /// TDLS channel switch confirmation.
+    TDLS_CHAN_SWITCH_CFM,
+    /// TDLS channel switch indication.
+    TDLS_CHAN_SWITCH_IND,
+    /// TDLS channel switch to base channel indication.
+    TDLS_CHAN_SWITCH_BASE_IND,
+    /// TDLS cancel channel switch request.
+    TDLS_CANCEL_CHAN_SWITCH_REQ,
+    /// TDLS cancel channel switch confirmation.
+    TDLS_CANCEL_CHAN_SWITCH_CFM,
+    /// TDLS peer power save indication.
+    TDLS_PEER_PS_IND,
+    /// TDLS peer traffic indication request.
+    TDLS_PEER_TRAFFIC_IND_REQ,
+    /// TDLS peer traffic indication confirmation.
+    TDLS_PEER_TRAFFIC_IND_CFM,
+    /// MAX number of messages
+    TDLS_MAX
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_REQ message
+struct tdls_chan_switch_req
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// STA Index
+    u8_l sta_idx;
+    /// MAC address of the TDLS station
+    struct mac_addr peer_mac_addr;
+    bool_l initiator;
+    /// Band (2.4GHz or 5GHz)
+    u8_l band;
+    /// Channel type: 20,40,80,160 or 80+80 MHz
+    u8_l type;
+    /// Frequency for Primary 20MHz channel (in MHz)
+    u16_l prim20_freq;
+    /// Frequency for Center of the contiguous channel or center of Primary 80+80
+    u16_l center1_freq;
+    /// Frequency for Center of the non-contiguous secondary 80+80
+    u16_l center2_freq;
+    /// TX power (in dBm)
+    s8_l tx_power;
+    /// Operating class
+    u8_l op_class;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CANCEL_CHAN_SWITCH_REQ message
+struct tdls_cancel_chan_switch_req
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// STA Index
+    u8_l sta_idx;
+    /// MAC address of the TDLS station
+    struct mac_addr peer_mac_addr;
+};
+
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_CFM message
+struct tdls_chan_switch_cfm
+{
+    /// Status of the operation
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CANCEL_CHAN_SWITCH_CFM message
+struct tdls_cancel_chan_switch_cfm
+{
+    /// Status of the operation
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_IND message
+struct tdls_chan_switch_ind
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// Channel Context Index
+    u8_l chan_ctxt_index;
+    /// Status of the operation
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_BASE_IND message
+struct tdls_chan_switch_base_ind
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// Channel Context index
+    u8_l chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_PS_IND message
+struct tdls_peer_ps_ind
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// STA Index
+    u8_l sta_idx;
+    /// MAC ADDR of the TDLS STA
+    struct mac_addr peer_mac_addr;
+    /// Flag to indicate if the TDLS peer is going to sleep
+    bool ps_on;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_TRAFFIC_IND_REQ message
+struct tdls_peer_traffic_ind_req
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// STA Index
+    u8_l sta_idx;
+    // MAC ADDR of the TDLS STA
+    struct mac_addr peer_mac_addr;
+    /// Dialog token
+    u8_l dialog_token;
+    /// TID of the latest MPDU transmitted over the TDLS direct link to the TDLS STA
+    u8_l last_tid;
+    /// Sequence number of the latest MPDU transmitted over the TDLS direct link
+    /// to the TDLS STA
+    u16_l last_sn;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_TRAFFIC_IND_CFM message
+struct tdls_peer_traffic_ind_cfm
+{
+    /// Status of the operation
+    u8_l status;
+};
+
+
+#endif // LMAC_MSG_H_
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/lmac_types.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/lmac_types.h
new file mode 100755
index 0000000..83b1122
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/lmac_types.h
@@ -0,0 +1,62 @@
+/**
+ ****************************************************************************************
+ *
+ * @file co_types.h
+ *
+ * @brief This file replaces the need to include stdint or stdbool typical headers,
+ *        which may not be available in all toolchains, and adds new types
+ *
+ * Copyright (C) RivieraWaves 2009-2019
+ *
+ * $Rev: $
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _LMAC_INT_H_
+#define _LMAC_INT_H_
+
+
+/**
+ ****************************************************************************************
+ * @addtogroup CO_INT
+ * @ingroup COMMON
+ * @brief Common integer standard types (removes use of stdint)
+ *
+ * @{
+ ****************************************************************************************
+ */
+
+
+/*
+ * DEFINES
+ ****************************************************************************************
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+#include <linux/bits.h>
+#else
+#include <linux/bitops.h>
+#endif
+
+#ifdef CONFIG_RWNX_TL4
+typedef uint16_t u8_l;
+typedef int16_t s8_l;
+typedef uint16_t bool_l;
+#else
+typedef uint8_t u8_l;
+typedef int8_t s8_l;
+typedef bool bool_l;
+#endif
+typedef uint16_t u16_l;
+typedef int16_t s16_l;
+typedef uint32_t u32_l;
+typedef int32_t s32_l;
+typedef uint64_t u64_l;
+
+
+
+/// @} CO_INT
+#endif // _LMAC_INT_H_
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/mklink.sh b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/mklink.sh
new file mode 100755
index 0000000..f7f10c2
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/mklink.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+#
+# Generate link for shared .c files in softmac and fullmac directories
+# Note:
+# links are not mandatory and we could easily update softmac (fullmac)
+# Makefile to compile shared file directly but this would imply to recompile
+# shared code eveytime when both driver are enabled
+#
+
+set -e
+
+dir_list=
+
+if [ $1 ] && [ $1 = "clean" ]
+then
+    action="clean"
+else
+    action="create"
+fi
+
+if [ -d softmac ]
+then
+    dir_list="softmac"
+fi
+
+if [ -d fullmac ]
+then
+    dir_list="$dir_list fullmac"
+fi
+
+if [ -d fhost ]
+then
+    dir_list="$dir_list fhost"
+fi
+
+for f in $(find . -maxdepth 1 -name "*.c")
+do
+    for d in $dir_list
+    do
+	if [ $action = "clean" ]
+	then
+	    # only delete link
+	    if [ -L $d/${f#./} ]
+	    then
+		rm -f $d/${f#./}
+	    fi
+	else
+	    # only create link if file doesn't exist
+	    # (whether it is a link or not)
+	    if [ ! -e $d/${f#./} ]
+	    then
+		ln -sf .$f $d/${f#./}
+	    fi
+	fi
+    done
+done
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/mkvers.sh b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/mkvers.sh
new file mode 100755
index 0000000..df4fd8e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/mkvers.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+#
+# Outputs svn revision of directory $VERDIR into $TARGET if it changes $TARGET
+# example output omitting enclosing quotes:
+#
+# '#define RWNX_VERS_REV "svn1676M"'
+# '#define RWNX_VERS_MOD "vX.X.X.X"'
+# '#define RWNX_VERS_OTH "build: username date hour"'
+
+set -e
+
+VERDIR=$(dirname $(readlink -f $0))
+TARGET=$1
+
+DATE_FORMAT="%b %d %Y %T"
+RWNX_VERS_MOD=$(grep RWNX_VERS_NUM $VERDIR/Makefile | cut -f2 -d=)
+
+tmpout=$TARGET.tmp
+cd $VERDIR
+
+if (svn info . >& /dev/null)
+then
+    svnrev=svn$(svnversion -c | sed 's/.*://')
+elif (git status -uno >& /dev/null)
+then
+    # If git-svn find the corresponding svn revision
+    if (git svn info >& /dev/null)
+    then
+	idx=0
+	while [ ! "$svnrev" ]
+	do
+	    # loop on all commit to find the first one that match a svn revision
+	    svnrev=$(git svn find-rev HEAD~$idx)
+	    if [ $? -ne 0 ]
+	    then
+		break;
+	    elif [ ! "$svnrev" ]
+	    then
+		idx=$((idx + 1))
+	    elif [ $idx -gt 0 ] || (git status -uno --porcelain | grep -q '^ M')
+	    then
+		# If this is not the HEAD, or working copy is not clean then we're
+		# not at a commited svn revision so add 'M'
+		svnrev=$svnrev"M"
+	    fi
+	done
+    fi
+
+    # append git info (sha1 and branch name)
+    git_sha1=$(git rev-parse --short HEAD)
+    if (git status -uno --porcelain | grep -q '^ M')
+    then
+	git_sha1+="M"
+    fi
+    git_branch=$(git symbolic-ref --short HEAD 2>/dev/null || echo "detached")
+    if [ "$svnrev" ]
+    then
+	svnrev="svn$svnrev ($git_sha1/$git_branch)"
+    else
+	svnrev="$git_sha1 ($git_branch)"
+    fi
+else
+    svnrev="Unknown Revision"
+fi
+
+date=$(LC_TIME=C date +"$DATE_FORMAT")
+
+RWNX_VERS_REV="$svnrev"
+#      "lmac vX.X.X.X - build:"
+banner="rwnx v$RWNX_VERS_MOD - build: $(whoami) $date - $RWNX_VERS_REV"
+
+define() { echo "#define $1 \"$2\""; }
+{
+	define "RWNX_VERS_REV"    "$RWNX_VERS_REV"
+	define "RWNX_VERS_MOD"    "$RWNX_VERS_MOD"
+	define "RWNX_VERS_BANNER" "$banner"
+} > $tmpout
+
+
+
+
+cmp -s $TARGET $tmpout && rm -f $tmpout || mv $tmpout $TARGET
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/reg_access.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/reg_access.h
new file mode 100755
index 0000000..75c896f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/reg_access.h
@@ -0,0 +1,159 @@
+/**
+ ******************************************************************************
+ *
+ * @file reg_access.h
+ *
+ * @brief Definitions and macros for MAC HW and platform register accesses
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef REG_ACCESS_H_
+#define REG_ACCESS_H_
+
+/*****************************************************************************
+ * Addresses within RWNX_ADDR_CPU
+ *****************************************************************************/
+#define RAM_LMAC_FW_ADDR               0x00150000
+
+#define ROM_FMAC_FW_ADDR               0x00010000
+#define ROM_FMAC_PATCH_ADDR            0x00180000
+#define ROM_FMAC_PATCH_ADDR_U01        0x00181000
+#define RAM_FMAC_FW_ADDR               0x00120000
+
+
+/*****************************************************************************
+ * Addresses within RWNX_ADDR_SYSTEM
+ *****************************************************************************/
+/* Shard RAM */
+#define SHARED_RAM_START_ADDR          0x00000000
+
+/* IPC registers */
+#define IPC_REG_BASE_ADDR              0x00800000
+
+/* System Controller Registers */
+#define SYSCTRL_SIGNATURE_ADDR         0x00900000
+// old diag register name
+#define SYSCTRL_DIAG_CONF_ADDR         0x00900068
+#define SYSCTRL_PHYDIAG_CONF_ADDR      0x00900074
+#define SYSCTRL_RIUDIAG_CONF_ADDR      0x00900078
+// new diag register name
+#define SYSCTRL_DIAG_CONF0             0x00900064
+#define SYSCTRL_DIAG_CONF1             0x00900068
+#define SYSCTRL_DIAG_CONF2             0x00900074
+#define SYSCTRL_DIAG_CONF3             0x00900078
+#define SYSCTRL_MISC_CNTL_ADDR         0x009000E0
+#define   BOOTROM_ENABLE               BIT(4)
+#define   FPGA_B_RESET                 BIT(1)
+#define   SOFT_RESET                   BIT(0)
+
+/* MAC platform */
+#define NXMAC_VERSION_1_ADDR           0x00B00004
+#define   NXMAC_MU_MIMO_TX_BIT         BIT(19)
+#define   NXMAC_BFMER_BIT              BIT(18)
+#define   NXMAC_BFMEE_BIT              BIT(17)
+#define   NXMAC_MAC_80211MH_FORMAT_BIT BIT(16)
+#define   NXMAC_COEX_BIT               BIT(14)
+#define   NXMAC_WAPI_BIT               BIT(13)
+#define   NXMAC_TPC_BIT                BIT(12)
+#define   NXMAC_VHT_BIT                BIT(11)
+#define   NXMAC_HT_BIT                 BIT(10)
+#define   NXMAC_RCE_BIT                BIT(8)
+#define   NXMAC_CCMP_BIT               BIT(7)
+#define   NXMAC_TKIP_BIT               BIT(6)
+#define   NXMAC_WEP_BIT                BIT(5)
+#define   NXMAC_SECURITY_BIT           BIT(4)
+#define   NXMAC_SME_BIT                BIT(3)
+#define   NXMAC_HCCA_BIT               BIT(2)
+#define   NXMAC_EDCA_BIT               BIT(1)
+#define   NXMAC_QOS_BIT                BIT(0)
+
+#define NXMAC_RX_CNTRL_ADDR                     0x00B00060
+#define   NXMAC_EN_DUPLICATE_DETECTION_BIT      BIT(31)
+#define   NXMAC_ACCEPT_UNKNOWN_BIT              BIT(30)
+#define   NXMAC_ACCEPT_OTHER_DATA_FRAMES_BIT    BIT(29)
+#define   NXMAC_ACCEPT_QO_S_NULL_BIT            BIT(28)
+#define   NXMAC_ACCEPT_QCFWO_DATA_BIT           BIT(27)
+#define   NXMAC_ACCEPT_Q_DATA_BIT               BIT(26)
+#define   NXMAC_ACCEPT_CFWO_DATA_BIT            BIT(25)
+#define   NXMAC_ACCEPT_DATA_BIT                 BIT(24)
+#define   NXMAC_ACCEPT_OTHER_CNTRL_FRAMES_BIT   BIT(23)
+#define   NXMAC_ACCEPT_CF_END_BIT               BIT(22)
+#define   NXMAC_ACCEPT_ACK_BIT                  BIT(21)
+#define   NXMAC_ACCEPT_CTS_BIT                  BIT(20)
+#define   NXMAC_ACCEPT_RTS_BIT                  BIT(19)
+#define   NXMAC_ACCEPT_PS_POLL_BIT              BIT(18)
+#define   NXMAC_ACCEPT_BA_BIT                   BIT(17)
+#define   NXMAC_ACCEPT_BAR_BIT                  BIT(16)
+#define   NXMAC_ACCEPT_OTHER_MGMT_FRAMES_BIT    BIT(15)
+#define   NXMAC_ACCEPT_BFMEE_FRAMES_BIT         BIT(14)
+#define   NXMAC_ACCEPT_ALL_BEACON_BIT           BIT(13)
+#define   NXMAC_ACCEPT_NOT_EXPECTED_BA_BIT      BIT(12)
+#define   NXMAC_ACCEPT_DECRYPT_ERROR_FRAMES_BIT BIT(11)
+#define   NXMAC_ACCEPT_BEACON_BIT               BIT(10)
+#define   NXMAC_ACCEPT_PROBE_RESP_BIT           BIT(9)
+#define   NXMAC_ACCEPT_PROBE_REQ_BIT            BIT(8)
+#define   NXMAC_ACCEPT_MY_UNICAST_BIT           BIT(7)
+#define   NXMAC_ACCEPT_UNICAST_BIT              BIT(6)
+#define   NXMAC_ACCEPT_ERROR_FRAMES_BIT         BIT(5)
+#define   NXMAC_ACCEPT_OTHER_BSSID_BIT          BIT(4)
+#define   NXMAC_ACCEPT_BROADCAST_BIT            BIT(3)
+#define   NXMAC_ACCEPT_MULTICAST_BIT            BIT(2)
+#define   NXMAC_DONT_DECRYPT_BIT                BIT(1)
+#define   NXMAC_EXC_UNENCRYPTED_BIT             BIT(0)
+
+#define NXMAC_DEBUG_PORT_SEL_ADDR      0x00B00510
+#define NXMAC_SW_SET_PROFILING_ADDR    0x00B08564
+#define NXMAC_SW_CLEAR_PROFILING_ADDR  0x00B08568
+
+/* Modem Status */
+#define MDM_HDMCONFIG_ADDR             0x00C00000
+
+/* Clock gating configuration */
+#define MDM_MEMCLKCTRL0_ADDR           0x00C00848
+#define MDM_CLKGATEFCTRL0_ADDR         0x00C00874
+#define CRM_CLKGATEFCTRL0_ADDR         0x00940010
+
+/* AGC (trident) */
+#define AGC_RWNXAGCCNTL_ADDR           0x00C02060
+
+/* LDPC RAM*/
+#define PHY_LDPC_RAM_ADDR              0x00C09000
+
+/* FCU (elma )*/
+#define FCU_RWNXFCAGCCNTL_ADDR         0x00C09034
+
+/* AGC RAM */
+#define PHY_AGC_UCODE_ADDR             0x00C0A000
+
+/* RIU */
+#define RIU_RWNXVERSION_ADDR           0x00C0B000
+#define RIU_RWNXDYNAMICCONFIG_ADDR     0x00C0B008
+#define RIU_AGCMEMBISTSTAT_ADDR        0x00C0B238
+#define RIU_AGCMEMSIGNATURESTAT_ADDR   0x00C0B23C
+#define RIU_RWNXAGCCNTL_ADDR           0x00C0B390
+
+/* FCU RAM */
+#define PHY_FCU_UCODE_ADDR             0x00C0E000
+
+/* RF ITF */
+#define FPGAB_MPIF_SEL_ADDR            0x00C10030
+#define RF_V6_DIAGPORT_CONF1_ADDR      0x00C10010
+#define RF_v6_PHYDIAG_CONF1_ADDR       0x00C10018
+
+#define RF_V7_DIAGPORT_CONF1_ADDR      0x00F10010
+#define RF_v7_PHYDIAG_CONF1_ADDR       0x00F10018
+
+/*****************************************************************************
+ * Macros for generated register files
+ *****************************************************************************/
+/* Macros for IPC registers access (used in reg_ipc_app.h) */
+#define REG_IPC_APP_RD(env, INDEX)                                      \
+    (*(volatile u32*)((u8*)env + IPC_REG_BASE_ADDR + 4*(INDEX)))
+
+#define REG_IPC_APP_WR(env, INDEX, value)                               \
+    (*(volatile u32*)((u8*)env + IPC_REG_BASE_ADDR + 4*(INDEX)) = value)
+
+#endif /* REG_ACCESS_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/reg_ipc_app.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/reg_ipc_app.h
new file mode 100755
index 0000000..2a3b545
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/reg_ipc_app.h
@@ -0,0 +1,299 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_ipc_app.h
+ *
+ * @brief IPC module register definitions
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _REG_IPC_APP_H_
+#define _REG_IPC_APP_H_
+
+#ifndef __KERNEL__
+#include <stdint.h>
+#include "arch.h"
+#else
+#include "ipc_compat.h"
+#endif
+#include "reg_access.h"
+
+#define REG_IPC_APP_DECODING_MASK 0x0000007F
+
+/**
+ * @brief APP2EMB_TRIGGER register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   -----------
+ *  31:00      APP2EMB_TRIGGER   0x0
+ * </pre>
+ */
+#define IPC_APP2EMB_TRIGGER_ADDR   0x12000000
+#define IPC_APP2EMB_TRIGGER_OFFSET 0x00000000
+#define IPC_APP2EMB_TRIGGER_INDEX  0x00000000
+#define IPC_APP2EMB_TRIGGER_RESET  0x00000000
+
+__INLINE u32 ipc_app2emb_trigger_get(void *env)
+{
+    return REG_IPC_APP_RD(env, IPC_APP2EMB_TRIGGER_INDEX);
+}
+
+__INLINE void ipc_app2emb_trigger_set(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_APP2EMB_TRIGGER_INDEX, value);
+}
+
+// field definitions
+#define IPC_APP2EMB_TRIGGER_MASK   ((u32)0xFFFFFFFF)
+#define IPC_APP2EMB_TRIGGER_LSB    0
+#define IPC_APP2EMB_TRIGGER_WIDTH  ((u32)0x00000020)
+
+#define IPC_APP2EMB_TRIGGER_RST    0x0
+
+__INLINE u32 ipc_app2emb_trigger_getf(void *env)
+{
+    u32 localVal = REG_IPC_APP_RD(env, IPC_APP2EMB_TRIGGER_INDEX);
+    ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+    return (localVal >> 0);
+}
+
+__INLINE void ipc_app2emb_trigger_setf(void *env, u32 app2embtrigger)
+{
+    ASSERT_ERR((((u32)app2embtrigger << 0) & ~((u32)0xFFFFFFFF)) == 0);
+    REG_IPC_APP_WR(env, IPC_APP2EMB_TRIGGER_INDEX, (u32)app2embtrigger << 0);
+}
+
+/**
+ * @brief EMB2APP_RAWSTATUS register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   -----------
+ *  31:00    EMB2APP_RAWSTATUS   0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_RAWSTATUS_ADDR   0x12000004
+#define IPC_EMB2APP_RAWSTATUS_OFFSET 0x00000004
+#define IPC_EMB2APP_RAWSTATUS_INDEX  0x00000001
+#define IPC_EMB2APP_RAWSTATUS_RESET  0x00000000
+
+__INLINE u32 ipc_emb2app_rawstatus_get(void *env)
+{
+    return REG_IPC_APP_RD(env, IPC_EMB2APP_RAWSTATUS_INDEX);
+}
+
+__INLINE void ipc_emb2app_rawstatus_set(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_EMB2APP_RAWSTATUS_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_RAWSTATUS_MASK   ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_RAWSTATUS_LSB    0
+#define IPC_EMB2APP_RAWSTATUS_WIDTH  ((u32)0x00000020)
+
+#define IPC_EMB2APP_RAWSTATUS_RST    0x0
+
+__INLINE u32 ipc_emb2app_rawstatus_getf(void *env)
+{
+    u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_RAWSTATUS_INDEX);
+    ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+    return (localVal >> 0);
+}
+
+/**
+ * @brief EMB2APP_ACK register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   -----------
+ *  31:00          EMB2APP_ACK   0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_ACK_ADDR   0x12000008
+#define IPC_EMB2APP_ACK_OFFSET 0x00000008
+#define IPC_EMB2APP_ACK_INDEX  0x00000002
+#define IPC_EMB2APP_ACK_RESET  0x00000000
+
+__INLINE u32 ipc_emb2app_ack_get(void *env)
+{
+    return REG_IPC_APP_RD(env, IPC_EMB2APP_ACK_INDEX);
+}
+
+__INLINE void ipc_emb2app_ack_clear(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_EMB2APP_ACK_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_ACK_MASK   ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_ACK_LSB    0
+#define IPC_EMB2APP_ACK_WIDTH  ((u32)0x00000020)
+
+#define IPC_EMB2APP_ACK_RST    0x0
+
+__INLINE u32 ipc_emb2app_ack_getf(void *env)
+{
+    u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_ACK_INDEX);
+    ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+    return (localVal >> 0);
+}
+
+__INLINE void ipc_emb2app_ack_clearf(void *env, u32 emb2appack)
+{
+    ASSERT_ERR((((u32)emb2appack << 0) & ~((u32)0xFFFFFFFF)) == 0);
+    REG_IPC_APP_WR(env, IPC_EMB2APP_ACK_INDEX, (u32)emb2appack << 0);
+}
+
+/**
+ * @brief EMB2APP_UNMASK_SET register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   -----------
+ *  31:00       EMB2APP_UNMASK   0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_UNMASK_SET_ADDR   0x1200000C
+#define IPC_EMB2APP_UNMASK_SET_OFFSET 0x0000000C
+#define IPC_EMB2APP_UNMASK_SET_INDEX  0x00000003
+#define IPC_EMB2APP_UNMASK_SET_RESET  0x00000000
+
+__INLINE u32 ipc_emb2app_unmask_get(void *env)
+{
+    return REG_IPC_APP_RD(env, IPC_EMB2APP_UNMASK_SET_INDEX);
+}
+
+__INLINE void ipc_emb2app_unmask_set(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_SET_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_UNMASK_MASK   ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_UNMASK_LSB    0
+#define IPC_EMB2APP_UNMASK_WIDTH  ((u32)0x00000020)
+
+#define IPC_EMB2APP_UNMASK_RST    0x0
+
+__INLINE u32 ipc_emb2app_unmask_getf(void *env)
+{
+    u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_UNMASK_SET_INDEX);
+    ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+    return (localVal >> 0);
+}
+
+__INLINE void ipc_emb2app_unmask_setf(void *env, u32 emb2appunmask)
+{
+    ASSERT_ERR((((u32)emb2appunmask << 0) & ~((u32)0xFFFFFFFF)) == 0);
+    REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_SET_INDEX, (u32)emb2appunmask << 0);
+}
+
+/**
+ * @brief EMB2APP_UNMASK_CLEAR register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   -----------
+ *  31:00       EMB2APP_UNMASK   0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_UNMASK_CLEAR_ADDR   0x12000010
+#define IPC_EMB2APP_UNMASK_CLEAR_OFFSET 0x00000010
+#define IPC_EMB2APP_UNMASK_CLEAR_INDEX  0x00000004
+#define IPC_EMB2APP_UNMASK_CLEAR_RESET  0x00000000
+
+__INLINE void ipc_emb2app_unmask_clear(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_CLEAR_INDEX, value);
+}
+
+// fields defined in symmetrical set/clear register
+__INLINE void ipc_emb2app_unmask_clearf(void *env, u32 emb2appunmask)
+{
+    ASSERT_ERR((((u32)emb2appunmask << 0) & ~((u32)0xFFFFFFFF)) == 0);
+    REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_CLEAR_INDEX, (u32)emb2appunmask << 0);
+}
+
+/**
+ * @brief EMB2APP_STATUS register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   -----------
+ *  31:00       EMB2APP_STATUS   0x0
+ * </pre>
+ */
+#ifdef CONFIG_RWNX_OLD_IPC
+#define IPC_EMB2APP_STATUS_ADDR   0x12000014
+#define IPC_EMB2APP_STATUS_OFFSET 0x00000014
+#define IPC_EMB2APP_STATUS_INDEX  0x00000005
+#else
+#define IPC_EMB2APP_STATUS_ADDR   0x1200001C
+#define IPC_EMB2APP_STATUS_OFFSET 0x0000001C
+#define IPC_EMB2APP_STATUS_INDEX  0x00000007
+#endif
+#define IPC_EMB2APP_STATUS_RESET  0x00000000
+
+__INLINE u32 ipc_emb2app_status_get(void *env)
+{
+    return REG_IPC_APP_RD(env, IPC_EMB2APP_STATUS_INDEX);
+}
+
+__INLINE void ipc_emb2app_status_set(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_EMB2APP_STATUS_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_STATUS_MASK   ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_STATUS_LSB    0
+#define IPC_EMB2APP_STATUS_WIDTH  ((u32)0x00000020)
+
+#define IPC_EMB2APP_STATUS_RST    0x0
+
+__INLINE u32 ipc_emb2app_status_getf(void *env)
+{
+    u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_STATUS_INDEX);
+    ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+    return (localVal >> 0);
+}
+
+/**
+ * @brief APP_SIGNATURE register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   ----------
+ *  31:00        APP_SIGNATURE   0x0
+ * </pre>
+ */
+#define IPC_APP_SIGNATURE_ADDR   0x12000040
+#define IPC_APP_SIGNATURE_OFFSET 0x00000040
+#define IPC_APP_SIGNATURE_INDEX  0x00000010
+#define IPC_APP_SIGNATURE_RESET  0x00000000
+
+__INLINE u32 ipc_app_signature_get(void *env)
+{
+      return REG_IPC_APP_RD(env, IPC_APP_SIGNATURE_INDEX);
+}
+
+__INLINE void ipc_app_signature_set(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_APP_SIGNATURE_INDEX, value);
+}
+
+// field definitions
+#define IPC_APP_SIGNATURE_MASK   ((u32)0xFFFFFFFF)
+#define IPC_APP_SIGNATURE_LSB    0
+#define IPC_APP_SIGNATURE_WIDTH  ((u32)0x00000020)
+
+#define IPC_APP_SIGNATURE_RST    0x0
+
+__INLINE u32 ipc_app_signature_getf(void *env)
+{
+    u32 localVal = REG_IPC_APP_RD(env, IPC_APP_SIGNATURE_INDEX);
+    ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+    return (localVal >> 0);
+}
+
+
+#endif // _REG_IPC_APP_H_
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/regdb.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/regdb.c
new file mode 100755
index 0000000..3c89ea9
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/regdb.c
@@ -0,0 +1,2873 @@
+#include <linux/nl80211.h>
+#include <net/cfg80211.h>
+#include <linux/version.h>
+
+//#include "regdb.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)
+#define REG_RULE_EXT(start, end, bw, gain, eirp, dfs_cac, reg_flags) \
+{							\
+	.freq_range.start_freq_khz = MHZ_TO_KHZ(start),	\
+	.freq_range.end_freq_khz = MHZ_TO_KHZ(end),	\
+	.freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw),	\
+	.power_rule.max_antenna_gain = DBI_TO_MBI(gain),\
+	.power_rule.max_eirp = DBM_TO_MBM(eirp),	\
+	.flags = reg_flags,				\
+}
+#define NL80211_RRF_AUTO_BW 0
+#endif
+
+static const struct ieee80211_regdomain regdom_00 = {
+	.n_reg_rules = 2,
+	.alpha2 = "00",
+	.reg_rules = {
+	// 1...14
+		REG_RULE(2390 - 10, 2510 + 10, 40, 0, 20, 0),
+	// 36...165
+		REG_RULE(5150 - 10, 5970 + 10, 80, 0, 20, 0),
+	}
+};
+
+static const struct ieee80211_regdomain regdom_AD = {
+	.alpha2 = "AD",
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5490, 5710, 80, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_AE = {
+	.alpha2 = "AE",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		//REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_AF = {
+	.alpha2 = "AF",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_AI = {
+	.alpha2 = "AI",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_AL = {
+	.alpha2 = "AL",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_AM = {
+	.alpha2 = "AM",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 18, 0, 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 18, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_AN = {
+	.alpha2 = "AN",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_AR = {
+	.alpha2 = "AR",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		//REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+		//	NL80211_RRF_AUTO_BW | 0),
+		//REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+		//	NL80211_RRF_DFS | 
+		//	NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5270, 5330, 40, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		//REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+		//	NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5815, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_AS = {
+	.alpha2 = "AS",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_AT = {
+	.alpha2 = "AT",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_AU = {
+	.alpha2 = "AU",
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_AW = {
+	.alpha2 = "AW",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_AZ = {
+	.alpha2 = "AZ",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 18, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 18, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+	},
+	.n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_BA = {
+	.alpha2 = "BA",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BB = {
+	.alpha2 = "BB",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_BD = {
+	.alpha2 = "BD",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_BE = {
+	.alpha2 = "BE",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BF = {
+	.alpha2 = "BF",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BG = {
+	.alpha2 = "BG",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BH = {
+	.alpha2 = "BH",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_BL = {
+	.alpha2 = "BL",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_BM = {
+	.alpha2 = "BM",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BN = {
+	.alpha2 = "BN",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_BO = {
+	.alpha2 = "BO",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 30, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_BR = {
+	.alpha2 = "BR",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BS = {
+	.alpha2 = "BS",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BT = {
+	.alpha2 = "BT",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_BY = {
+	.alpha2 = "BY",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_BZ = {
+	.alpha2 = "BZ",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_CA = {
+	.alpha2 = "CA",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CF = {
+	.alpha2 = "CF",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 40, 0, 17, 0, 0),
+		REG_RULE_EXT(5250, 5330, 40, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5490, 5730, 40, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 40, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CH = {
+	.alpha2 = "CH",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CI = {
+	.alpha2 = "CI",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CL = {
+	.alpha2 = "CL",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_CN = {
+	.alpha2 = "CN",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+		REG_RULE_EXT(57240, 59400, 2160, 0, 28, 0, 0),
+		REG_RULE_EXT(59400, 63720, 2160, 0, 44, 0, 0),
+		REG_RULE_EXT(63720, 65880, 2160, 0, 28, 0, 0),
+	},
+	.n_reg_rules = 7
+};
+
+static const struct ieee80211_regdomain regdom_CO = {
+	.alpha2 = "CO",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CR = {
+	.alpha2 = "CR",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5490, 5730, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CX = {
+	.alpha2 = "CX",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CY = {
+	.alpha2 = "CY",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CZ = {
+	.alpha2 = "CZ",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, 
+			NL80211_RRF_NO_OUTDOOR | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, 
+			NL80211_RRF_NO_OUTDOOR | 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_DE = {
+	.alpha2 = "DE",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5150, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_NO_OUTDOOR | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, 
+			NL80211_RRF_NO_OUTDOOR | 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5470, 5695, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		/*REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),*/
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_DK = {
+	.alpha2 = "DK",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_DM = {
+	.alpha2 = "DM",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_DO = {
+	.alpha2 = "DO",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_DZ = {
+	.alpha2 = "DZ",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5670, 160, 0, 23, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_EC = {
+	.alpha2 = "EC",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5490, 5730, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_EE = {
+	.alpha2 = "EE",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_EG = {
+	.alpha2 = "EG",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_ES = {
+	.alpha2 = "ES",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, 
+			NL80211_RRF_NO_OUTDOOR | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, 
+			NL80211_RRF_NO_OUTDOOR | 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_ET = {
+	.alpha2 = "ET",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_FI = {
+	.alpha2 = "FI",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_FM = {
+	.alpha2 = "FM",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_FR = {
+	.alpha2 = "FR",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5695, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_GB = {
+	.alpha2 = "GB",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_GD = {
+	.alpha2 = "GD",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_GE = {
+	.alpha2 = "GE",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 18, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 18, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_GF = {
+	.alpha2 = "GF",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_GH = {
+	.alpha2 = "GH",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_GL = {
+	.alpha2 = "GL",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5490, 5710, 80, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_GP = {
+	.alpha2 = "GP",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_GR = {
+	.alpha2 = "GR",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_GT = {
+	.alpha2 = "GT",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_GU = {
+	.alpha2 = "GU",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5490, 5730, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_GY = {
+	.alpha2 = "GY",
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_HK = {
+	.alpha2 = "HK",
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_HN = {
+	.alpha2 = "HN",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_HR = {
+	.alpha2 = "HR",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_HT = {
+	.alpha2 = "HT",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_HU = {
+	.alpha2 = "HU",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_ID = {
+	.alpha2 = "ID",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5735, 5815, 80, 0, 23, 0, 0),
+	},
+	.n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_IE = {
+	.alpha2 = "IE",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_IL = {
+	.alpha2 = "IL",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, 
+			NL80211_RRF_NO_OUTDOOR | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5350, 80, 0, 23, 0, 
+			NL80211_RRF_NO_OUTDOOR | 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+	},
+	.n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_IN = {
+	.alpha2 = "IN",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_IR = {
+	.alpha2 = "IR",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_IS = {
+	.alpha2 = "IS",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_IT = {
+	.alpha2 = "IT",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_JM = {
+	.alpha2 = "JM",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_JO = {
+	.alpha2 = "JO",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 23, 0, 0),
+	},
+	.n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_JP = {
+	.alpha2 = "JP",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(2474, 2494, 20, 0, 20, 0, 
+			NL80211_RRF_NO_OFDM | 0),
+		REG_RULE_EXT(4910, 4990, 40, 0, 23, 0, 0),
+		REG_RULE_EXT(5030, 5090, 40, 0, 23, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 23, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 7
+};
+
+static const struct ieee80211_regdomain regdom_KE = {
+	.alpha2 = "KE",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, 0),
+		REG_RULE_EXT(5490, 5570, 80, 0, 30, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5775, 40, 0, 23, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_KH = {
+	.alpha2 = "KH",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_KN = {
+	.alpha2 = "KN",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 30, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5815, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_KP = {
+	.alpha2 = "KP",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5630, 80, 0, 30, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5815, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_KR = {
+	.alpha2 = "KR",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 30, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_KW = {
+	.alpha2 = "KW",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+	},
+	.n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_KY = {
+	.alpha2 = "KY",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_KZ = {
+	.alpha2 = "KZ",
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+	},
+	.n_reg_rules = 1
+};
+
+static const struct ieee80211_regdomain regdom_LB = {
+	.alpha2 = "LB",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_LC = {
+	.alpha2 = "LC",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 30, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5815, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_LI = {
+	.alpha2 = "LI",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_LK = {
+	.alpha2 = "LK",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5490, 5730, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_LS = {
+	.alpha2 = "LS",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_LT = {
+	.alpha2 = "LT",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_LU = {
+	.alpha2 = "LU",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_LV = {
+	.alpha2 = "LV",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MA = {
+	.alpha2 = "MA",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+	},
+	.n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_MC = {
+	.alpha2 = "MC",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MD = {
+	.alpha2 = "MD",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_ME = {
+	.alpha2 = "ME",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MF = {
+	.alpha2 = "MF",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MH = {
+	.alpha2 = "MH",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MK = {
+	.alpha2 = "MK",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MN = {
+	.alpha2 = "MN",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MO = {
+	.alpha2 = "MO",
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 40, 0, 23, 0, 0),
+		REG_RULE_EXT(5250, 5330, 40, 0, 23, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 40, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MP = {
+	.alpha2 = "MP",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MQ = {
+	.alpha2 = "MQ",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MR = {
+	.alpha2 = "MR",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MT = {
+	.alpha2 = "MT",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MU = {
+	.alpha2 = "MU",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MW = {
+	.alpha2 = "MW",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MX = {
+	.alpha2 = "MX",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MY = {
+	.alpha2 = "MY",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_NI = {
+	.alpha2 = "NI",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_NL = {
+	.alpha2 = "NL",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_NO_OUTDOOR | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_NO_OUTDOOR | 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_NO = {
+	.alpha2 = "NO",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5470, 5795, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5815, 5850, 35, 0, 33, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(17100, 17300, 200, 0, 20, 0, 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 7
+};
+
+static const struct ieee80211_regdomain regdom_NP = {
+	.alpha2 = "NP",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_NZ = {
+	.alpha2 = "NZ",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_OM = {
+	.alpha2 = "OM",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_PA = {
+	.alpha2 = "PA",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_PE = {
+	.alpha2 = "PE",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PF = {
+	.alpha2 = "PF",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_PG = {
+	.alpha2 = "PG",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PH = {
+	.alpha2 = "PH",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PK = {
+	.alpha2 = "PK",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_PL = {
+	.alpha2 = "PL",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PM = {
+	.alpha2 = "PM",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_PR = {
+	.alpha2 = "PR",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PT = {
+	.alpha2 = "PT",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PW = {
+	.alpha2 = "PW",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PY = {
+	.alpha2 = "PY",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_QA = {
+	.alpha2 = "QA",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_RE = {
+	.alpha2 = "RE",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_RO = {
+	.alpha2 = "RO",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_RS = {
+	.alpha2 = "RS",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5150, 5350, 40, 0, 23, 0, 
+			NL80211_RRF_NO_OUTDOOR | 0),
+		REG_RULE_EXT(5470, 5725, 20, 0, 30, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_RU = {
+	.alpha2 = "RU",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5650, 5730, 80, 0, 30, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_RW = {
+	.alpha2 = "RW",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_SA = {
+	.alpha2 = "SA",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_SE = {
+	.alpha2 = "SE",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_SG = {
+	.alpha2 = "SG",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_SI = {
+	.alpha2 = "SI",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_SK = {
+	.alpha2 = "SK",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_SN = {
+	.alpha2 = "SN",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_SR = {
+	.alpha2 = "SR",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_SV = {
+	.alpha2 = "SV",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_SY = {
+	.alpha2 = "SY",
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+	},
+	.n_reg_rules = 1
+};
+
+static const struct ieee80211_regdomain regdom_TC = {
+	.alpha2 = "TC",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_TD = {
+	.alpha2 = "TD",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_TG = {
+	.alpha2 = "TG",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5250, 5330, 40, 0, 20, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5490, 5710, 40, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_TH = {
+	.alpha2 = "TH",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_TN = {
+	.alpha2 = "TN",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+	},
+	.n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_TR = {
+	.alpha2 = "TR",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_TT = {
+	.alpha2 = "TT",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_TW = {
+	.alpha2 = "TW",
+	.dfs_region = NL80211_DFS_JP,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5270, 5330, 40, 0, 17, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5490, 5590, 80, 0, 30, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5650, 5710, 40, 0, 30, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_UA = {
+	.alpha2 = "UA",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 
+			NL80211_RRF_NO_OUTDOOR | 0),
+		REG_RULE_EXT(5150, 5350, 40, 0, 20, 0, 
+			NL80211_RRF_NO_OUTDOOR | 0),
+		REG_RULE_EXT(5490, 5670, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0),
+		REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_UG = {
+	.alpha2 = "UG",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_US = {
+	.alpha2 = "US",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		// 1...13
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		// 36 40 44 48 
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		// 52 56 60 64 
+		REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		// 100 104 108 112 116 120 124 
+		REG_RULE_EXT(5490, 5600, 80, 0, 24, 0,
+				NL80211_RRF_DFS | 0),
+		// 128 132 136 140
+		REG_RULE_EXT(5650, 5710, 40, 0, 24, 0,
+				NL80211_RRF_DFS | 0),
+		// 149 153 157 161 165
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+		REG_RULE_EXT(57240, 63720, 2160, 0, 40, 0, 0),
+	},
+	.n_reg_rules = 7
+};
+
+static const struct ieee80211_regdomain regdom_UY = {
+	.alpha2 = "UY",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_UZ = {
+	.alpha2 = "UZ",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+	},
+	.n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_VC = {
+	.alpha2 = "VC",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_VE = {
+	.alpha2 = "VE",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_VI = {
+	.alpha2 = "VI",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_VN = {
+	.alpha2 = "VN",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5490, 5730, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_VU = {
+	.alpha2 = "VU",
+	.dfs_region = NL80211_DFS_FCC,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, 
+			NL80211_RRF_DFS | 0),
+		REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+	},
+	.n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_WF = {
+	.alpha2 = "WF",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_YE = {
+	.alpha2 = "YE",
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+	},
+	.n_reg_rules = 1
+};
+
+static const struct ieee80211_regdomain regdom_YT = {
+	.alpha2 = "YT",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_ZA = {
+	.alpha2 = "ZA",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5695, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+		/*REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),*/
+	},
+	.n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_ZW = {
+	.alpha2 = "ZW",
+	.dfs_region = NL80211_DFS_ETSI,
+	.reg_rules = {
+		REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+		REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, 
+			NL80211_RRF_DFS | 
+			NL80211_RRF_AUTO_BW | 0),
+		REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, 
+			NL80211_RRF_DFS | 0),
+	},
+	.n_reg_rules = 4
+};
+
+const struct ieee80211_regdomain *reg_regdb1[] = {
+	&regdom_00,
+	&regdom_AD,
+	&regdom_AE,
+	&regdom_AF,
+	&regdom_AI,
+	&regdom_AL,
+	&regdom_AM,
+	&regdom_AN,
+	&regdom_AR,
+	&regdom_AS,
+	&regdom_AT,
+	&regdom_AU,
+	&regdom_AW,
+	&regdom_AZ,
+	&regdom_BA,
+	&regdom_BB,
+	&regdom_BD,
+	&regdom_BE,
+	&regdom_BF,
+	&regdom_BG,
+	&regdom_BH,
+	&regdom_BL,
+	&regdom_BM,
+	&regdom_BN,
+	&regdom_BO,
+	&regdom_BR,
+	&regdom_BS,
+	&regdom_BT,
+	&regdom_BY,
+	&regdom_BZ,
+	&regdom_CA,
+	&regdom_CF,
+	&regdom_CH,
+	&regdom_CI,
+	&regdom_CL,
+	&regdom_CN,
+	&regdom_CO,
+	&regdom_CR,
+	&regdom_CX,
+	&regdom_CY,
+	&regdom_CZ,
+	&regdom_DE,
+	&regdom_DK,
+	&regdom_DM,
+	&regdom_DO,
+	&regdom_DZ,
+	&regdom_EC,
+	&regdom_EE,
+	&regdom_EG,
+	&regdom_ES,
+	&regdom_ET,
+	&regdom_FI,
+	&regdom_FM,
+	&regdom_FR,
+	&regdom_GB,
+	&regdom_GD,
+	&regdom_GE,
+	&regdom_GF,
+	&regdom_GH,
+	&regdom_GL,
+	&regdom_GP,
+	&regdom_GR,
+	&regdom_GT,
+	&regdom_GU,
+	&regdom_GY,
+	&regdom_HK,
+	&regdom_HN,
+	&regdom_HR,
+	&regdom_HT,
+	&regdom_HU,
+	&regdom_ID,
+	&regdom_IE,
+	&regdom_IL,
+	&regdom_IN,
+	&regdom_IR,
+	&regdom_IS,
+	&regdom_IT,
+	&regdom_JM,
+	&regdom_JO,
+	&regdom_JP,
+	&regdom_KE,
+	&regdom_KH,
+	&regdom_KN,
+	&regdom_KP,
+	&regdom_KR,
+	&regdom_KW,
+	&regdom_KY,
+	&regdom_KZ,
+	&regdom_LB,
+	&regdom_LC,
+	&regdom_LI,
+	&regdom_LK,
+	&regdom_LS,
+	&regdom_LT,
+	&regdom_LU,
+	&regdom_LV,
+	&regdom_MA,
+	&regdom_MC,
+	&regdom_MD,
+	&regdom_ME,
+	&regdom_MF,
+	&regdom_MH,
+	&regdom_MK,
+	&regdom_MN,
+	&regdom_MO,
+	&regdom_MP,
+	&regdom_MQ,
+	&regdom_MR,
+	&regdom_MT,
+	&regdom_MU,
+	&regdom_MW,
+	&regdom_MX,
+	&regdom_MY,
+	&regdom_NI,
+	&regdom_NL,
+	&regdom_NO,
+	&regdom_NP,
+	&regdom_NZ,
+	&regdom_OM,
+	&regdom_PA,
+	&regdom_PE,
+	&regdom_PF,
+	&regdom_PG,
+	&regdom_PH,
+	&regdom_PK,
+	&regdom_PL,
+	&regdom_PM,
+	&regdom_PR,
+	&regdom_PT,
+	&regdom_PW,
+	&regdom_PY,
+	&regdom_QA,
+	&regdom_RE,
+	&regdom_RO,
+	&regdom_RS,
+	&regdom_RU,
+	&regdom_RW,
+	&regdom_SA,
+	&regdom_SE,
+	&regdom_SG,
+	&regdom_SI,
+	&regdom_SK,
+	&regdom_SN,
+	&regdom_SR,
+	&regdom_SV,
+	&regdom_SY,
+	&regdom_TC,
+	&regdom_TD,
+	&regdom_TG,
+	&regdom_TH,
+	&regdom_TN,
+	&regdom_TR,
+	&regdom_TT,
+	&regdom_TW,
+	&regdom_UA,
+	&regdom_UG,
+	&regdom_US,
+	&regdom_UY,
+	&regdom_UZ,
+	&regdom_VC,
+	&regdom_VE,
+	&regdom_VI,
+	&regdom_VN,
+	&regdom_VU,
+	&regdom_WF,
+	&regdom_YE,
+	&regdom_YT,
+	&regdom_ZA,
+	&regdom_ZW,
+};
+
+int reg_regdb_size1 = ARRAY_SIZE(reg_regdb1);
+
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_bfmer.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_bfmer.c
new file mode 100755
index 0000000..4be5ec0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_bfmer.c
@@ -0,0 +1,105 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_bfmer.c
+ *
+ * @brief VHT Beamformer function definitions
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ******************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+
+#include <linux/slab.h>
+#include "rwnx_bfmer.h"
+
+/**
+ * FUNCTION DEFINITIONS
+ ******************************************************************************
+ */
+
+int rwnx_bfmer_report_add(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta,
+                          unsigned int length)
+{
+    gfp_t flags;
+    struct rwnx_bfmer_report *bfm_report ;
+
+    if (in_softirq())
+        flags = GFP_ATOMIC;
+    else
+        flags = GFP_KERNEL;
+
+    /* Allocate a structure that will contain the beamforming report */
+    bfm_report = kmalloc(sizeof(*bfm_report) + length, flags);
+
+
+    /* Check report allocation */
+    if (!bfm_report) {
+        /* Do not use beamforming */
+        return -1;
+    }
+
+    /* Store report length */
+    bfm_report->length = length;
+
+    /*
+     * Need to provide a Virtual Address to the MAC so that it can
+     * upload the received Beamforming Report in driver memory
+     */
+    bfm_report->dma_addr = dma_map_single(rwnx_hw->dev, &bfm_report->report[0],
+                                          length, DMA_FROM_DEVICE);
+
+    /* Check DMA mapping result */
+    if (dma_mapping_error(rwnx_hw->dev, bfm_report->dma_addr)) {
+        /* Free allocated report */
+        kfree(bfm_report);
+        /* And leave */
+        return -1;
+    }
+
+    /* Store report structure */
+    rwnx_sta->bfm_report = bfm_report;
+
+    return 0;
+}
+
+void rwnx_bfmer_report_del(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta)
+{
+    /* Verify if a report has been allocated */
+    if (rwnx_sta->bfm_report) {
+        struct rwnx_bfmer_report *bfm_report = rwnx_sta->bfm_report;
+
+        /* Unmap DMA region */
+        dma_unmap_single(rwnx_hw->dev, bfm_report->dma_addr,
+                         bfm_report->length, DMA_BIDIRECTIONAL);
+
+        /* Free allocated report structure and clean the pointer */
+        kfree(bfm_report);
+        rwnx_sta->bfm_report = NULL;
+    }
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+u8 rwnx_bfmer_get_rx_nss(const struct ieee80211_vht_cap *vht_capa)
+{
+    int i;
+    u8 rx_nss = 0;
+    u16 rx_mcs_map = le16_to_cpu(vht_capa->supp_mcs.rx_mcs_map);
+
+    for (i = 7; i >= 0; i--) {
+        u8 mcs = (rx_mcs_map >> (2 * i)) & 3;
+
+        if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+            rx_nss = i + 1;
+            break;
+        }
+    }
+
+    return rx_nss;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_bfmer.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_bfmer.h
new file mode 100755
index 0000000..9a313bf
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_bfmer.h
@@ -0,0 +1,100 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_bfmer.h
+ *
+ * @brief VHT Beamformer function declarations
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_BFMER_H_
+#define _RWNX_BFMER_H_
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+
+#include "rwnx_defs.h"
+
+/**
+ * DEFINES
+ ******************************************************************************
+ */
+
+/// Maximal supported report length (in bytes)
+#define RWNX_BFMER_REPORT_MAX_LEN     2048
+
+/// Size of the allocated report space (twice the maximum report length)
+#define RWNX_BFMER_REPORT_SPACE_SIZE  (RWNX_BFMER_REPORT_MAX_LEN * 2)
+
+/**
+ * TYPE DEFINITIONS
+ ******************************************************************************
+ */
+
+/*
+ * Structure used to store a beamforming report.
+ */
+struct rwnx_bfmer_report {
+    dma_addr_t dma_addr;    /* Virtual address provided to MAC for
+                               DMA transfer of the Beamforming Report */
+    unsigned int length;    /* Report Length */
+    u8 report[1];           /* Report to be used for VHT TX Beamforming */
+};
+
+/**
+ * FUNCTION DECLARATIONS
+ ******************************************************************************
+ */
+
+/**
+ ******************************************************************************
+ * @brief Allocate memory aiming to contains the Beamforming Report received
+ * from a Beamformee capable capable.
+ * The providing length shall be large enough to contain the VHT Compressed
+ * Beaforming Report and the MU Exclusive part.
+ * It also perform a DMA Mapping providing an address to be provided to the HW
+ * responsible for the DMA transfer of the report.
+ * If successful a struct rwnx_bfmer_report object is allocated, it's address
+ * is stored in rwnx_sta->bfm_report.
+ *
+ * @param[in] rwnx_hw   PHY Information
+ * @param[in] rwnx_sta  Peer STA Information
+ * @param[in] length    Memory size to be allocated
+ *
+ * @return 0 if operation is successful, else -1.
+ ******************************************************************************
+ */
+int rwnx_bfmer_report_add(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta,
+                          unsigned int length);
+
+/**
+ ******************************************************************************
+ * @brief Free a previously allocated memory intended to be used for
+ * Beamforming Reports.
+ *
+ * @param[in] rwnx_hw   PHY Information
+ * @param[in] rwnx_sta  Peer STA Information
+ *
+ ******************************************************************************
+ */
+void rwnx_bfmer_report_del(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta);
+
+#ifdef CONFIG_RWNX_FULLMAC
+/**
+ ******************************************************************************
+ * @brief Parse a Rx VHT-MCS map in order to deduce the maximum number of
+ * Spatial Streams supported by a beamformee.
+ *
+ * @param[in] vht_capa  Received VHT Capability field.
+ *
+ ******************************************************************************
+ */
+u8 rwnx_bfmer_get_rx_nss(const struct ieee80211_vht_cap *vht_capa);
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#endif /* _RWNX_BFMER_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_cfgfile.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_cfgfile.c
new file mode 100755
index 0000000..defd292
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_cfgfile.c
@@ -0,0 +1,293 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_configparse.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+#include <linux/firmware.h>
+#include <linux/if_ether.h>
+
+#include "rwnx_defs.h"
+#include "rwnx_cfgfile.h"
+#include "rwnx_platform.h"
+/**
+ *
+ */
+
+void cutwords(char *page,int start,int end, u8** dst){
+    int i;
+    int pos = 0;
+    for(i=start; i<=end; i++){
+        (*dst)[pos++]=page[i];
+    }
+    (*dst)[pos] = '\0';
+    return;
+}
+
+static u8 *rwnx_find_tag(const u8 *file_data, unsigned int file_size,
+                                 const char *tag_name, unsigned int tag_len, u8 **comp)
+{
+    unsigned int curr, line_start = 0, line_size, str_start=0;
+    u8* cutwd;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Walk through all the lines of the configuration file */
+    while (line_start < file_size) {
+        /* Search the end of the current line (or the end of the file) */
+        for (curr = line_start; curr < file_size; curr++)
+            if (file_data[curr] == '\n')
+                break;
+
+        /* Compute the line size */
+        line_size = curr - line_start;// -1;
+
+        if (!strncmp(&file_data[str_start], tag_name, strlen(tag_name))) {
+            cutwords(file_data,(line_start + strlen(tag_name)),
+            (line_start + strlen(tag_name))+16, comp);
+            cutwd = *comp;
+            return cutwd;
+        }
+
+        if (!strncmp(&file_data[str_start + 26], tag_name, strlen(tag_name))) {
+            cutwords(file_data,(line_start + strlen(tag_name) +26),
+                (line_start + strlen(tag_name))+16+26, comp);
+            cutwd = *comp;
+            return cutwd;
+        }
+
+        if (!strncmp(&file_data[str_start + 52], tag_name, strlen(tag_name))) {
+            cutwords(file_data,(line_start + strlen(tag_name) +52), 
+                (line_start + strlen(tag_name))+16+52, comp);
+            cutwd = *comp;
+            return cutwd;
+        }
+
+        #if 0
+        /* Check if this line contains the expected tag */
+        if ((line_size == (strlen(tag_name) + tag_len)) &&
+            (!strncmp(&file_data[line_start], tag_name, strlen(tag_name))))
+            return (&file_data[line_start + strlen(tag_name)]);
+        #endif
+
+        /* Move to next line */
+        line_start = curr + 1;
+    }
+
+    /* Tag not found */
+    return NULL;
+}
+
+/**
+ * Parse the Config file used at init time
+ */
+int rwnx_parse_configfile(struct rwnx_hw *rwnx_hw, const char *filename,
+                          struct rwnx_conf_file *config, const char* mac_tag)
+{
+    const struct firmware *config_fw;
+    u8 dflt_mac[ETH_ALEN] = {0x88, 0x00, 0x20, 0x22, 0x08, 0x15};
+    int ret, size;
+    u8 *tag_ptr;
+    u32 *dst=NULL;
+    u8* compt = kmalloc(sizeof(u8)*18, GFP_KERNEL);
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    get_random_bytes(&dflt_mac[4], 2);
+
+    #if 0
+    if ((ret = request_firmware(&config_fw, filename, rwnx_hw->dev))) {
+        printk(KERN_CRIT "%s: Failed to get %s (%d)\n", __func__, filename, ret);
+        return ret;
+    }
+    #else
+    size = rwnx_load_firmware(&dst, filename, rwnx_hw->dev);
+    if (size <= 0) {
+        printk("wrong size of firmware file\n");
+        kfree(dst);
+        dst = NULL;
+        return -1;
+    }
+    #endif
+
+    /* Get MAC Address */
+    #if 0
+    tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+                            "MAC_ADDR=", strlen("00:00:00:00:00:00"));
+    #else
+    tag_ptr = rwnx_find_tag((char*)dst, size,
+                            mac_tag, strlen("00:00:00:00:00:00"), &compt);
+    #endif
+    if (tag_ptr != NULL) {
+        u8 *addr = config->mac_addr;
+        if (sscanf(tag_ptr,
+                   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+                   addr + 0, addr + 1, addr + 2,
+                   addr + 3, addr + 4, addr + 5) != ETH_ALEN)
+            memcpy(config->mac_addr, dflt_mac, ETH_ALEN);
+    } else
+        memcpy(config->mac_addr, dflt_mac, ETH_ALEN);
+
+    RWNX_DBG("MAC Address is:\n%pM\n", config->mac_addr);
+
+    kfree(compt);
+    /* Release the configuration file */
+    //release_firmware(config_fw);
+
+    return 0;
+}
+
+#if 0
+/**
+ * Parse the Config file used at init time
+ */
+int rwnx_parse_phy_configfile(struct rwnx_hw *rwnx_hw, const char *filename,
+                              struct rwnx_phy_conf_file *config, int path)
+{
+    const struct firmware *config_fw;
+    int ret;
+    const u8 *tag_ptr;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if ((ret = request_firmware(&config_fw, filename, rwnx_hw->dev))) {
+        printk(KERN_CRIT "%s: Failed to get %s (%d)\n", __func__, filename, ret);
+        return ret;
+    }
+
+    /* Get Trident path mapping */
+    tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+                            "TRD_PATH_MAPPING=", strlen("00"));
+    if (tag_ptr != NULL) {
+        u8 val;
+        if (sscanf(tag_ptr, "%hhx", &val) == 1)
+            config->trd.path_mapping = val;
+        else
+            config->trd.path_mapping = path;
+    } else
+        config->trd.path_mapping = path;
+
+    RWNX_DBG("Trident path mapping is: %d\n", config->trd.path_mapping);
+
+    /* Get DC offset compensation */
+    tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+                            "TX_DC_OFF_COMP=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->trd.tx_dc_off_comp) != 1)
+            config->trd.tx_dc_off_comp = 0;
+    } else
+        config->trd.tx_dc_off_comp = 0;
+
+    RWNX_DBG("TX DC offset compensation is: %08X\n", config->trd.tx_dc_off_comp);
+
+    /* Get Karst TX IQ compensation value for path0 on 2.4GHz */
+    tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_TX_IQ_COMP_2_4G_PATH_0=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_2_4G[0]) != 1)
+            config->karst.tx_iq_comp_2_4G[0] = 0x01000000;
+    } else
+        config->karst.tx_iq_comp_2_4G[0] = 0x01000000;
+
+    RWNX_DBG("Karst TX IQ compensation for path 0 on 2.4GHz is: %08X\n", config->karst.tx_iq_comp_2_4G[0]);
+
+    /* Get Karst TX IQ compensation value for path1 on 2.4GHz */
+    tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_TX_IQ_COMP_2_4G_PATH_1=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_2_4G[1]) != 1)
+            config->karst.tx_iq_comp_2_4G[1] = 0x01000000;
+    } else
+        config->karst.tx_iq_comp_2_4G[1] = 0x01000000;
+
+    RWNX_DBG("Karst TX IQ compensation for path 1 on 2.4GHz is: %08X\n", config->karst.tx_iq_comp_2_4G[1]);
+
+    /* Get Karst RX IQ compensation value for path0 on 2.4GHz */
+    tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_RX_IQ_COMP_2_4G_PATH_0=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_2_4G[0]) != 1)
+            config->karst.rx_iq_comp_2_4G[0] = 0x01000000;
+    } else
+        config->karst.rx_iq_comp_2_4G[0] = 0x01000000;
+
+    RWNX_DBG("Karst RX IQ compensation for path 0 on 2.4GHz is: %08X\n", config->karst.rx_iq_comp_2_4G[0]);
+
+    /* Get Karst RX IQ compensation value for path1 on 2.4GHz */
+    tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_RX_IQ_COMP_2_4G_PATH_1=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_2_4G[1]) != 1)
+            config->karst.rx_iq_comp_2_4G[1] = 0x01000000;
+    } else
+        config->karst.rx_iq_comp_2_4G[1] = 0x01000000;
+
+    RWNX_DBG("Karst RX IQ compensation for path 1 on 2.4GHz is: %08X\n", config->karst.rx_iq_comp_2_4G[1]);
+
+    /* Get Karst TX IQ compensation value for path0 on 5GHz */
+    tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_TX_IQ_COMP_5G_PATH_0=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_5G[0]) != 1)
+            config->karst.tx_iq_comp_5G[0] = 0x01000000;
+    } else
+        config->karst.tx_iq_comp_5G[0] = 0x01000000;
+
+    RWNX_DBG("Karst TX IQ compensation for path 0 on 5GHz is: %08X\n", config->karst.tx_iq_comp_5G[0]);
+
+    /* Get Karst TX IQ compensation value for path1 on 5GHz */
+    tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_TX_IQ_COMP_5G_PATH_1=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_5G[1]) != 1)
+            config->karst.tx_iq_comp_5G[1] = 0x01000000;
+    } else
+        config->karst.tx_iq_comp_5G[1] = 0x01000000;
+
+    RWNX_DBG("Karst TX IQ compensation for path 1 on 5GHz is: %08X\n", config->karst.tx_iq_comp_5G[1]);
+
+    /* Get Karst RX IQ compensation value for path0 on 5GHz */
+    tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_RX_IQ_COMP_5G_PATH_0=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_5G[0]) != 1)
+            config->karst.rx_iq_comp_5G[0] = 0x01000000;
+    } else
+        config->karst.rx_iq_comp_5G[0] = 0x01000000;
+
+    RWNX_DBG("Karst RX IQ compensation for path 0 on 5GHz is: %08X\n", config->karst.rx_iq_comp_5G[0]);
+
+    /* Get Karst RX IQ compensation value for path1 on 5GHz */
+    tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_RX_IQ_COMP_5G_PATH_1=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_5G[1]) != 1)
+            config->karst.rx_iq_comp_5G[1] = 0x01000000;
+    } else
+        config->karst.rx_iq_comp_5G[1] = 0x01000000;
+
+    RWNX_DBG("Karst RX IQ compensation for path 1 on 5GHz is: %08X\n", config->karst.rx_iq_comp_5G[1]);
+
+    /* Get Karst default path */
+    tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_DEFAULT_PATH=", strlen("00"));
+    if (tag_ptr != NULL) {
+        u8 val;
+        if (sscanf(tag_ptr, "%hhx", &val) == 1)
+            config->karst.path_used = val;
+        else
+            config->karst.path_used = path;
+    } else
+        config->karst.path_used = path;
+
+    RWNX_DBG("Karst default path is: %d\n", config->karst.path_used);
+
+    /* Release the configuration file */
+    release_firmware(config_fw);
+
+    return 0;
+}
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_cfgfile.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_cfgfile.h
new file mode 100755
index 0000000..797adce
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_cfgfile.h
@@ -0,0 +1,35 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_cfgfile.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_CFGFILE_H_
+#define _RWNX_CFGFILE_H_
+
+/*
+ * Structure used to retrieve information from the Config file used at Initialization time
+ */
+struct rwnx_conf_file {
+    u8 mac_addr[ETH_ALEN];
+};
+
+/*
+ * Structure used to retrieve information from the PHY Config file used at Initialization time
+ */
+struct rwnx_phy_conf_file {
+    struct phy_trd_cfg_tag trd;
+    struct phy_karst_cfg_tag karst;
+};
+
+int rwnx_parse_configfile(struct rwnx_hw *rwnx_hw, const char *filename,
+                          struct rwnx_conf_file *config, const char* mac_tag);
+
+int rwnx_parse_phy_configfile(struct rwnx_hw *rwnx_hw, const char *filename,
+                              struct rwnx_phy_conf_file *config, int path);
+
+#endif /* _RWNX_CFGFILE_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_cmds.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_cmds.c
new file mode 100755
index 0000000..e1fb977
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_cmds.c
@@ -0,0 +1,724 @@
+/**
+ ******************************************************************************
+ *
+ * rwnx_cmds.c
+ *
+ * Handles queueing (push to IPC, ack/cfm from IPC) of commands issued to
+ * LMAC FW
+ *
+ * Copyright (C) RivieraWaves 2014-2019
+ *
+ ******************************************************************************
+ */
+
+#include <linux/list.h>
+#include <linux/kthread.h>
+#include "rwnx_cmds.h"
+#include "rwnx_defs.h"
+#include "rwnx_strs.h"
+#include "rwnx_txq.h"
+//#define CREATE_TRACE_POINTS
+#include "rwnx_events.h"
+#include "aicwf_txrxif.h"
+#ifdef AICWF_SDIO_SUPPORT
+#include "aicwf_sdio.h"
+#else
+#include "aicwf_usb.h"
+#endif
+/**
+ *
+ */
+extern int aicwf_sdio_writeb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val);
+
+static void cmd_dump(const struct rwnx_cmd *cmd)
+{
+    printk(KERN_CRIT "tkn[%d]  flags:%04x  result:%3d  cmd:%4d-%-24s - reqcfm(%4d-%-s)\n",
+           cmd->tkn, cmd->flags, cmd->result, cmd->id, RWNX_ID2STR(cmd->id),
+           cmd->reqid, cmd->reqid != (lmac_msg_id_t)-1 ? RWNX_ID2STR(cmd->reqid) : "none");
+}
+
+/**
+ *
+ */
+static void cmd_complete(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
+{
+    //RWNX_DBG(RWNX_FN_ENTRY_STR);
+    lockdep_assert_held(&cmd_mgr->lock);
+
+    list_del(&cmd->list);
+    cmd_mgr->queue_sz--;
+
+    cmd->flags |= RWNX_CMD_FLAG_DONE;
+    if (cmd->flags & RWNX_CMD_FLAG_NONBLOCK) {
+        kfree(cmd);
+    } else {
+        if (RWNX_CMD_WAIT_COMPLETE(cmd->flags)) {
+            cmd->result = 0;
+            complete(&cmd->complete);
+        }
+    }
+}
+
+int cmd_mgr_queue_force_defer(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
+{
+    bool defer_push = false;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifdef CREATE_TRACE_POINTS
+    trace_msg_send(cmd->id);
+#endif
+    spin_lock_bh(&cmd_mgr->lock);
+
+    if (cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) {
+        printk(KERN_CRIT"cmd queue crashed\n");
+        cmd->result = -EPIPE;
+        spin_unlock_bh(&cmd_mgr->lock);
+        return -EPIPE;
+    }
+
+    #ifndef CONFIG_RWNX_FHOST
+    if (!list_empty(&cmd_mgr->cmds)) {
+        if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) {
+            printk(KERN_CRIT"Too many cmds (%d) already queued\n",
+                   cmd_mgr->max_queue_sz);
+            cmd->result = -ENOMEM;
+            spin_unlock_bh(&cmd_mgr->lock);
+            #ifdef CMD_WORKQUEUE
+            WAKE_CMD_WORK(cmd_mgr);
+            #else
+            //tasklet_schedule(&cmd_mgr->cmd_tasklet);
+            complete(&cmd_mgr->cmdproc_trgg);
+            #endif
+            return -ENOMEM;
+        }
+    }
+    #endif
+
+    cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH;
+    defer_push = true;
+
+    if (cmd->flags & RWNX_CMD_FLAG_REQ_CFM)
+        cmd->flags |= RWNX_CMD_FLAG_WAIT_CFM;
+
+    cmd->tkn    = cmd_mgr->next_tkn++;
+    cmd->result = -EINTR;
+
+    if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK))
+        init_completion(&cmd->complete);
+
+    list_add_tail(&cmd->list, &cmd_mgr->cmds);
+    cmd_mgr->queue_sz++;
+    spin_unlock_bh(&cmd_mgr->lock);
+    #ifdef CMD_WORKQUEUE
+    WAKE_CMD_WORK(cmd_mgr);
+    #else
+    //tasklet_schedule(&cmd_mgr->cmd_tasklet);
+    complete(&cmd_mgr->cmdproc_trgg);
+    #endif
+    return 0;
+}
+#if 1
+static void aic8800_start_system_reset_flow(struct aic_sdio_dev *aic)
+{
+    int ret = 0;
+    printk(KERN_ERR "wlan error reset flow.\n");
+    char *event_string = "DHDISDOWN=1";
+    char *envp[] = { event_string, NULL };
+    printk(KERN_ERR "send event.\n");	
+	ret=kobject_uevent_env(&aic->dev->kobj, KOBJ_CHANGE,envp);
+	if(!ret)
+	   printk(KERN_ERR "wlan error event send.\n");
+}
+#endif
+
+static int cmd_mgr_queue(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
+{
+#ifdef AICWF_SDIO_SUPPORT
+    int ret;
+    struct aic_sdio_dev *sdiodev = container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
+#endif
+#ifdef AICWF_USB_SUPPORT
+    struct aic_usb_dev *usbdev = container_of(cmd_mgr, struct aic_usb_dev, cmd_mgr);
+#endif
+    bool defer_push = false;
+
+    //RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifdef CREATE_TRACE_POINTS
+    trace_msg_send(cmd->id);
+#endif
+    spin_lock_bh(&cmd_mgr->lock);
+
+    if (cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) {
+        printk(KERN_CRIT"cmd queue crashed\n");
+        cmd->result = -EPIPE;
+        spin_unlock_bh(&cmd_mgr->lock);
+        return -EPIPE;
+    }
+
+    #ifndef CONFIG_RWNX_FHOST
+    if (!list_empty(&cmd_mgr->cmds)) {
+        struct rwnx_cmd *last;
+
+        if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) {
+            printk(KERN_CRIT"Too many cmds (%d) already queued\n",
+                   cmd_mgr->max_queue_sz);
+            cmd->result = -ENOMEM;
+            spin_unlock_bh(&cmd_mgr->lock);
+            #ifdef CMD_WORKQUEUE
+            WAKE_CMD_WORK(cmd_mgr);
+            #else
+            //tasklet_schedule(&cmd_mgr->cmd_tasklet);
+            complete(&cmd_mgr->cmdproc_trgg);
+            #endif
+            return -ENOMEM;
+        }
+        last = list_entry(cmd_mgr->cmds.prev, struct rwnx_cmd, list);
+        if (last->flags & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_PUSH | RWNX_CMD_FLAG_WAIT_CFM)) {
+#if 0 // queue even NONBLOCK command.
+            if (cmd->flags & RWNX_CMD_FLAG_NONBLOCK) {
+                printk(KERN_CRIT"cmd queue busy\n");
+                cmd->result = -EBUSY;
+                spin_unlock_bh(&cmd_mgr->lock);
+                return -EBUSY;
+            }
+#endif
+            cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH;
+            defer_push = true;
+        }
+    }
+    #endif
+
+#if 0
+    cmd->flags |= RWNX_CMD_FLAG_WAIT_ACK;
+#endif
+    if (cmd->flags & RWNX_CMD_FLAG_REQ_CFM)
+        cmd->flags |= RWNX_CMD_FLAG_WAIT_CFM;
+
+    cmd->tkn    = cmd_mgr->next_tkn++;
+    cmd->result = -EINTR;
+
+    if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK))
+        init_completion(&cmd->complete);
+
+    list_add_tail(&cmd->list, &cmd_mgr->cmds);
+    cmd_mgr->queue_sz++;
+
+	if(cmd->a2e_msg->id == ME_TRAFFIC_IND_REQ
+	#ifdef AICWF_ARP_OFFLOAD
+		|| cmd->a2e_msg->id == MM_SET_ARPOFFLOAD_REQ
+	#endif
+	) {
+		defer_push = true;
+		cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH;
+		//printk("defer push: tkn=%d\r\n", cmd->tkn);
+	}
+
+    //spin_unlock_bh(&cmd_mgr->lock);
+    if (!defer_push) {
+	spin_unlock_bh(&cmd_mgr->lock);
+        printk("queue:id=%x, param_len=%u\n",cmd->a2e_msg->id, cmd->a2e_msg->param_len);
+        #ifdef AICWF_SDIO_SUPPORT
+        aicwf_set_cmd_tx((void *)(sdiodev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+        #else
+        aicwf_set_cmd_tx((void *)(usbdev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+        #endif
+        //rwnx_ipc_msg_push(rwnx_hw, cmd, RWNX_CMD_A2EMSG_LEN(cmd->a2e_msg));
+
+        kfree(cmd->a2e_msg);
+    } else {
+	if(cmd_mgr->queue_sz <= 1){
+		spin_unlock_bh(&cmd_mgr->lock);
+        #ifdef CMD_WORKQUEUE
+		WAKE_CMD_WORK(cmd_mgr);
+        #else
+        //tasklet_schedule(&cmd_mgr->cmd_tasklet);
+        complete(&cmd_mgr->cmdproc_trgg);
+        #endif
+	} else
+		spin_unlock_bh(&cmd_mgr->lock);
+		return 0;
+	}
+
+    if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK)) {
+        #ifdef CONFIG_RWNX_FHOST
+        if (wait_for_completion_killable(&cmd->complete)) {
+            cmd->result = -EINTR;
+            spin_lock_bh(&cmd_mgr->lock);
+            cmd_complete(cmd_mgr, cmd);
+            spin_unlock_bh(&cmd_mgr->lock);
+            /* TODO: kill the cmd at fw level */
+        }
+        #else
+        unsigned long tout = msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz);
+        if (!wait_for_completion_timeout(&cmd->complete, tout)) {
+            printk(KERN_CRIT"cmd timed-out\n");
+            #ifdef AICWF_SDIO_SUPPORT
+            ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, 2);
+            if (ret < 0) {
+                sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.wakeup_reg);
+            }
+#if 1
+	spin_lock_bh(&sdiodev->rwnx_hw->tx_lock);
+       int i;
+       for(i=0; i<NX_NB_TXQ;i++){
+               if(skb_queue_len(&sdiodev->rwnx_hw->txq[i].sk_list)!= 0)
+                       //printk("i=%d, len=%d\n",skb_queue_len(&sdiodev->rwnx_hw->txq[i].sk_list));
+			rwnx_txq_flush(sdiodev->rwnx_hw, &sdiodev->rwnx_hw->txq[i]);
+               }
+	spin_unlock_bh(&sdiodev->rwnx_hw->tx_lock);
+#endif
+			spin_lock_bh(&sdiodev->tx_priv->txqlock);
+			aicwf_txframe_queue_flush(&sdiodev->tx_priv->txq);
+			atomic_set(&sdiodev->tx_priv->tx_pktcnt, 0);
+			spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+
+            aic8800_start_system_reset_flow(sdiodev);
+        #endif
+
+            cmd_dump(cmd);
+            spin_lock_bh(&cmd_mgr->lock);
+            cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED;
+            if (!(cmd->flags & RWNX_CMD_FLAG_DONE)) {
+                cmd->result = -ETIMEDOUT;
+                cmd_complete(cmd_mgr, cmd);
+            }
+            spin_unlock_bh(&cmd_mgr->lock);
+        }
+		else{
+			kfree(cmd);
+            if(!list_empty(&cmd_mgr->cmds))
+                #ifdef CMD_WORKQUEUE
+                WAKE_CMD_WORK(cmd_mgr);
+                #else
+                //tasklet_schedule(&cmd_mgr->cmd_tasklet);
+                complete(&cmd_mgr->cmdproc_trgg);
+                #endif
+		}
+        #endif
+    } else {
+        cmd->result = 0;
+    }
+
+    return 0;
+}
+
+/**
+ *
+ */
+static int cmd_mgr_llind(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
+{
+    struct rwnx_cmd *cur, *acked = NULL, *next = NULL;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    spin_lock_bh(&cmd_mgr->lock);
+    list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+        if (!acked) {
+            if (cur->tkn == cmd->tkn) {
+                if (WARN_ON_ONCE(cur != cmd)) {
+                    cmd_dump(cmd);
+                }
+                acked = cur;
+                continue;
+            }
+        }
+        if (cur->flags & RWNX_CMD_FLAG_WAIT_PUSH) {
+                next = cur;
+                break;
+        }
+    }
+    if (!acked) {
+        printk(KERN_CRIT "Error: acked cmd not found\n");
+    } else {
+        cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK;
+        if (RWNX_CMD_WAIT_COMPLETE(cmd->flags))
+            cmd_complete(cmd_mgr, cmd);
+    }
+
+    if (next) {
+	#if 0 //there is no ack
+        struct rwnx_hw *rwnx_hw = container_of(cmd_mgr, struct rwnx_hw, cmd_mgr);
+        next->flags &= ~RWNX_CMD_FLAG_WAIT_PUSH;
+        rwnx_ipc_msg_push(rwnx_hw, next, RWNX_CMD_A2EMSG_LEN(next->a2e_msg));
+        kfree(next->a2e_msg);
+	#endif
+    }
+    spin_unlock(&cmd_mgr->lock);
+
+    return 0;
+}
+
+#ifdef CMD_WORKQUEUE
+void cmd_mgr_task_process(struct work_struct *work)
+{
+    struct rwnx_cmd_mgr *cmd_mgr = container_of(work, struct rwnx_cmd_mgr, cmdWork);
+    struct rwnx_cmd *cur = NULL, *next = NULL;
+    unsigned long tout;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    while(1) {
+        next = NULL;
+        spin_lock_bh(&cmd_mgr->lock);
+
+        list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+            if (cur->flags & RWNX_CMD_FLAG_WAIT_PUSH) { //just judge the first
+                    next = cur;
+            }
+            break;
+        }
+        spin_unlock_bh(&cmd_mgr->lock);
+
+        if(next == NULL)
+            break;
+
+        if (next) {
+ 	    #ifdef AICWF_SDIO_SUPPORT
+            struct aic_sdio_dev *sdiodev = container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
+	    #endif
+	    #ifdef AICWF_USB_SUPPORT
+    	    struct aic_usb_dev *usbdev = container_of(cmd_mgr, struct aic_usb_dev, cmd_mgr);
+	    #endif
+            next->flags &= ~RWNX_CMD_FLAG_WAIT_PUSH;
+
+            printk("cmd_process, cmd->id=%d, tkn=%d, sz=%d\r\n",next->reqid, next->tkn, cmd_mgr->queue_sz);
+            //rwnx_ipc_msg_push(rwnx_hw, next, RWNX_CMD_A2EMSG_LEN(next->a2e_msg));
+#ifdef AICWF_SDIO_SUPPORT
+            aicwf_set_cmd_tx((void *)(sdiodev), next->a2e_msg, sizeof(struct lmac_msg) + next->a2e_msg->param_len);
+#else
+            aicwf_set_cmd_tx((void *)(usbdev), next->a2e_msg, sizeof(struct lmac_msg) + next->a2e_msg->param_len);
+#endif
+            kfree(next->a2e_msg);
+
+            tout = msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz);
+            if (!wait_for_completion_timeout(&next->complete, tout)) {
+                printk(KERN_CRIT"cmd timed-out\n");
+                cmd_dump(next);
+                spin_lock_bh(&cmd_mgr->lock);
+                cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED;
+                if (!(next->flags & RWNX_CMD_FLAG_DONE)) {
+                    next->result = -ETIMEDOUT;
+                    cmd_complete(cmd_mgr, next);
+                }
+                spin_unlock_bh(&cmd_mgr->lock);
+            } else
+		kfree(next);
+        }
+    }
+
+}
+#endif
+
+#ifndef CMD_WORKQUEUE
+void cmd_mgr_process(struct rwnx_cmd_mgr *cmd_mgr)
+{
+    struct rwnx_cmd *cur = NULL, *next = NULL;
+    unsigned long tout;
+#ifdef AICWF_SDIO_SUPPORT
+    int ret;
+    struct aic_sdio_dev *sdiodev = container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
+    if(sdiodev->bus_if->state == BUS_DOWN_ST) {
+        printk("cmd process: bus is down\n");
+        return ;
+    }
+#endif
+#ifdef AICWF_USB_SUPPORT
+        struct aic_usb_dev *usbdev = container_of(cmd_mgr, struct aic_usb_dev, cmd_mgr);
+#endif
+
+    //RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    while(1) {
+        next = NULL;
+        spin_lock_bh(&cmd_mgr->lock);
+
+        list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+            if (cur->flags & RWNX_CMD_FLAG_WAIT_PUSH) { //just judge the first
+                next = cur;
+            }
+            break;
+        }
+        spin_unlock_bh(&cmd_mgr->lock);
+
+        if(next == NULL)
+            break;
+
+        if (next) {
+            next->flags &= ~RWNX_CMD_FLAG_WAIT_PUSH;
+
+            printk("cmd_mgr_task_process, cmd->id=%d, tkn=%d\r\n",next->reqid, next->tkn);
+            //rwnx_ipc_msg_push(rwnx_hw, next, RWNX_CMD_A2EMSG_LEN(next->a2e_msg));
+#ifdef AICWF_SDIO_SUPPORT
+            aicwf_set_cmd_tx((void *)(sdiodev), next->a2e_msg, sizeof(struct lmac_msg) + next->a2e_msg->param_len);
+#else
+            aicwf_set_cmd_tx((void *)(usbdev), next->a2e_msg, sizeof(struct lmac_msg) + next->a2e_msg->param_len);
+#endif
+            kfree(next->a2e_msg);
+
+            tout = msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz);
+            if (!wait_for_completion_timeout(&next->complete, tout)) {
+                printk(KERN_CRIT"cmd timed-out\n");
+#ifdef AICWF_SDIO_SUPPORT
+                ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, 2);
+                if (ret < 0) {
+                    sdio_err("reg:%d write failed1!\n", sdiodev->sdio_reg.wakeup_reg);
+                }
+#if 1
+					spin_lock_bh(&sdiodev->rwnx_hw->tx_lock);
+					   int i;
+					   for(i=0; i<NX_NB_TXQ;i++){
+							   if(skb_queue_len(&sdiodev->rwnx_hw->txq[i].sk_list)!= 0)
+									   //printk("i=%d, len=%d\n",skb_queue_len(&sdiodev->rwnx_hw->txq[i].sk_list));
+							rwnx_txq_flush(sdiodev->rwnx_hw, &sdiodev->rwnx_hw->txq[i]);
+							   }
+					spin_unlock_bh(&sdiodev->rwnx_hw->tx_lock);
+#endif
+
+
+				spin_lock_bh(&sdiodev->tx_priv->txqlock);
+				aicwf_txframe_queue_flush(&sdiodev->tx_priv->txq);
+				atomic_set(&sdiodev->tx_priv->tx_pktcnt, 0);
+				spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+
+                aic8800_start_system_reset_flow(sdiodev);
+#endif
+                cmd_dump(next);
+                spin_lock_bh(&cmd_mgr->lock);
+                cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED;
+                if (!(next->flags & RWNX_CMD_FLAG_DONE)) {
+                    next->result = -ETIMEDOUT;
+                    cmd_complete(cmd_mgr, next);
+                }
+                spin_unlock_bh(&cmd_mgr->lock);
+            } else
+				kfree(next);	
+        }
+    }
+}
+
+static inline void aic_thread_wait_stop(void)
+{
+        set_current_state(TASK_INTERRUPTIBLE);
+        while (!kthread_should_stop()) {
+                schedule();
+                set_current_state(TASK_INTERRUPTIBLE);
+        }
+        __set_current_state(TASK_RUNNING);
+}
+
+void cmd_mgr_process_thread(void *data)
+{
+    struct rwnx_cmd_mgr *cmd_mgr = (struct rwnx_cmd_mgr *)data;
+    struct sched_param param = { .sched_priority = 1 };
+    param.sched_priority = 37;
+    sched_setscheduler(current, SCHED_FIFO, &param);
+
+    while (1) {
+        if (!wait_for_completion_interruptible(&cmd_mgr->cmdproc_trgg)) {
+	    struct aic_sdio_dev *sdiodev = container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
+	    if(sdiodev->bus_if->state == BUS_DOWN_ST) {
+	        printk("cmd process: bus is down\n");
+	        break;
+    	    }
+            cmd_mgr_process(cmd_mgr);
+        }
+    }
+    aic_thread_wait_stop();
+    return 0;
+}
+
+#endif
+
+static int cmd_mgr_run_callback(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd,
+                                struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb)
+{
+    int res;
+
+    if (! cb){
+        return 0;
+    }
+    //RWNX_DBG(RWNX_FN_ENTRY_STR);
+    spin_lock_bh(&rwnx_hw->cb_lock);
+    res = cb(rwnx_hw, cmd, msg);
+    spin_unlock_bh(&rwnx_hw->cb_lock);
+
+    return res;
+}
+
+/**
+ *
+
+ */
+static int cmd_mgr_msgind(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd_e2amsg *msg,
+                          msg_cb_fct cb)
+{
+#ifdef AICWF_SDIO_SUPPORT
+    struct aic_sdio_dev *sdiodev = container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
+    struct rwnx_hw *rwnx_hw = sdiodev->rwnx_hw;
+#endif
+#ifdef AICWF_USB_SUPPORT
+    struct aic_usb_dev *usbdev = container_of(cmd_mgr, struct aic_usb_dev, cmd_mgr);
+    struct rwnx_hw *rwnx_hw = usbdev->rwnx_hw;
+#endif
+    struct rwnx_cmd *cmd, *pos;
+    bool found = false;
+
+   // RWNX_DBG(RWNX_FN_ENTRY_STR);
+   printk("msgind: %d, sz=%d\n", msg->id, cmd_mgr->queue_sz);
+#ifdef CREATE_TRACE_POINTS
+    trace_msg_recv(msg->id);
+#endif
+    spin_lock_bh(&cmd_mgr->lock);
+    list_for_each_entry_safe(cmd, pos, &cmd_mgr->cmds, list) {
+        if (cmd->reqid == msg->id &&
+            (cmd->flags & RWNX_CMD_FLAG_WAIT_CFM)) {
+
+            if (!cmd_mgr_run_callback(rwnx_hw, cmd, msg, cb)) {
+                found = true;
+                cmd->flags &= ~RWNX_CMD_FLAG_WAIT_CFM;
+
+                if (WARN((msg->param_len > RWNX_CMD_E2AMSG_LEN_MAX),
+                         "Unexpect E2A msg len %d > %d\n", msg->param_len,
+                         RWNX_CMD_E2AMSG_LEN_MAX)) {
+                    msg->param_len = RWNX_CMD_E2AMSG_LEN_MAX;
+                }
+
+                if (cmd->e2a_msg && msg->param_len)
+                    memcpy(cmd->e2a_msg, &msg->param, msg->param_len);
+
+                if (RWNX_CMD_WAIT_COMPLETE(cmd->flags))
+                    cmd_complete(cmd_mgr, cmd);
+
+                break;
+            }
+        }
+    }
+    spin_unlock_bh(&cmd_mgr->lock);
+
+    if (!found)
+        cmd_mgr_run_callback(rwnx_hw, NULL, msg, cb);
+
+    return 0;
+}
+
+/**
+ *
+ */
+static void cmd_mgr_print(struct rwnx_cmd_mgr *cmd_mgr)
+{
+    struct rwnx_cmd *cur;
+
+    spin_lock_bh(&cmd_mgr->lock);
+    RWNX_DBG("q_sz/max: %2d / %2d - next tkn: %d\n",
+             cmd_mgr->queue_sz, cmd_mgr->max_queue_sz,
+             cmd_mgr->next_tkn);
+    list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+        cmd_dump(cur);
+    }
+    spin_unlock_bh(&cmd_mgr->lock);
+}
+
+static void cmd_mgr_drain(struct rwnx_cmd_mgr *cmd_mgr)
+{
+    struct rwnx_cmd *cur, *nxt;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    spin_lock_bh(&cmd_mgr->lock);
+    list_for_each_entry_safe(cur, nxt, &cmd_mgr->cmds, list) {
+        list_del(&cur->list);
+        cmd_mgr->queue_sz--;
+        if (!(cur->flags & RWNX_CMD_FLAG_NONBLOCK))
+            complete(&cur->complete);
+    }
+    spin_unlock_bh(&cmd_mgr->lock);
+}
+
+void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr)
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    INIT_LIST_HEAD(&cmd_mgr->cmds);
+	cmd_mgr->state = RWNX_CMD_MGR_STATE_INITED;
+    spin_lock_init(&cmd_mgr->lock);
+    cmd_mgr->max_queue_sz = RWNX_CMD_MAX_QUEUED;
+    cmd_mgr->queue  = &cmd_mgr_queue;
+    cmd_mgr->print  = &cmd_mgr_print;
+    cmd_mgr->drain  = &cmd_mgr_drain;
+    cmd_mgr->llind  = &cmd_mgr_llind;
+    cmd_mgr->msgind = &cmd_mgr_msgind;
+
+    #ifdef CMD_WORKQUEUE
+    INIT_WORK(&cmd_mgr->cmdWork, cmd_mgr_task_process);
+    cmd_mgr->cmd_wq = create_singlethread_workqueue("cmd_wq");
+    if (!cmd_mgr->cmd_wq) {
+        txrx_err("insufficient memory to create cmd workqueue.\n");
+        return;
+    }
+    #else
+    init_completion(&cmd_mgr->cmdproc_trgg);
+    cmd_mgr->cmd_thread = kthread_run(cmd_mgr_process_thread, (void *)cmd_mgr, "aicwf_cmd_thread");
+    //tasklet_init(&cmd_mgr->cmd_tasklet,
+    //     (void(*)(unsigned long))cmd_mgr_process_tasklet,
+    //     (unsigned long)cmd_mgr);
+    #endif
+}
+
+void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr)
+{
+    cmd_mgr->print(cmd_mgr);
+    cmd_mgr->drain(cmd_mgr);
+    cmd_mgr->print(cmd_mgr);
+	#ifdef CMD_WORKQUEUE
+    flush_workqueue(cmd_mgr->cmd_wq);
+    destroy_workqueue(cmd_mgr->cmd_wq);
+	#endif
+    memset(cmd_mgr, 0, sizeof(*cmd_mgr));
+}
+
+
+void aicwf_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len)
+{
+    u8 *buffer = NULL;
+    u16 index = 0;
+#ifdef AICWF_SDIO_SUPPORT
+	struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *)dev;
+    struct aicwf_bus *bus = sdiodev->bus_if;
+#else
+	struct aic_usb_dev *usbdev = (struct aic_usb_dev *)dev;
+	struct aicwf_bus *bus = NULL;
+    if (!usbdev->state) {
+        printk("down msg \n");
+        return;
+    }
+	bus = usbdev->bus_if;
+#endif
+    buffer = bus->cmd_buf;
+
+    memset(buffer, 0, CMD_BUF_MAX);
+    buffer[0] = (len+4) & 0x00ff;
+    buffer[1] = ((len+4) >> 8) &0x0f;
+    buffer[2] = 0x11;
+    if (sdiodev->chipid == PRODUCT_ID_AIC8801 || sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
+        sdiodev->chipid == PRODUCT_ID_AIC8800DW)
+        buffer[3] = 0x0;
+    else if (sdiodev->chipid == PRODUCT_ID_AIC8800D80)
+	    buffer[3] = crc8_ponl_107(&buffer[0], 3); // crc8
+    index += 4;
+    //there is a dummy word
+    index += 4;
+
+	//make sure little endian
+    put_u16(&buffer[index], msg->id);
+    index += 2;
+    put_u16(&buffer[index], msg->dest_id);
+    index += 2;
+    put_u16(&buffer[index], msg->src_id);
+    index += 2;
+    put_u16(&buffer[index], msg->param_len);
+    index += 2;
+    memcpy(&buffer[index], (u8 *)msg->param, msg->param_len);
+
+    aicwf_bus_txmsg(bus, buffer, len + 8);
+}
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_cmds.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_cmds.h
new file mode 100755
index 0000000..5d30b2f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_cmds.h
@@ -0,0 +1,128 @@
+/**
+ ******************************************************************************
+ *
+ * rwnx_cmds.h
+ *
+ * Copyright (C) RivieraWaves 2014-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_CMDS_H_
+#define _RWNX_CMDS_H_
+
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include "lmac_msg.h"
+
+#ifdef CONFIG_RWNX_SDM
+#define RWNX_80211_CMD_TIMEOUT_MS    (20 * 300)
+#elif defined(CONFIG_RWNX_FHOST)
+#define RWNX_80211_CMD_TIMEOUT_MS    (10000)
+#else
+#ifdef AICWF_USB_SUPPORT
+#define RWNX_80211_CMD_TIMEOUT_MS    5000//300
+#else
+#define RWNX_80211_CMD_TIMEOUT_MS    5000//500//300
+#endif
+#endif
+
+#define RWNX_CMD_FLAG_NONBLOCK      BIT(0)
+#define RWNX_CMD_FLAG_REQ_CFM       BIT(1)
+#define RWNX_CMD_FLAG_WAIT_PUSH     BIT(2)
+#define RWNX_CMD_FLAG_WAIT_ACK      BIT(3)
+#define RWNX_CMD_FLAG_WAIT_CFM      BIT(4)
+#define RWNX_CMD_FLAG_DONE          BIT(5)
+/* ATM IPC design makes it possible to get the CFM before the ACK,
+ * otherwise this could have simply been a state enum */
+#define RWNX_CMD_WAIT_COMPLETE(flags) \
+    (!(flags & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_CFM)))
+
+#define RWNX_CMD_MAX_QUEUED         16//8
+
+#ifdef CONFIG_RWNX_FHOST
+#include "ipc_fhost.h"
+#define rwnx_cmd_e2amsg ipc_fhost_msg
+#define rwnx_cmd_a2emsg ipc_fhost_msg
+#define RWNX_CMD_A2EMSG_LEN(m) (m->param_len)
+#define RWNX_CMD_E2AMSG_LEN_MAX IPC_FHOST_MSG_BUF_SIZE
+struct rwnx_term_stream;
+
+#else /* !CONFIG_RWNX_FHOST*/
+#include "ipc_shared.h"
+#define rwnx_cmd_e2amsg ipc_e2a_msg
+#define rwnx_cmd_a2emsg lmac_msg
+#define RWNX_CMD_A2EMSG_LEN(m) (sizeof(struct lmac_msg) + m->param_len)
+#define RWNX_CMD_E2AMSG_LEN_MAX (IPC_E2A_MSG_PARAM_SIZE * 4)
+
+#endif /* CONFIG_RWNX_FHOST*/
+
+struct rwnx_hw;
+struct rwnx_cmd;
+typedef int (*msg_cb_fct)(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd,
+                          struct rwnx_cmd_e2amsg *msg);
+static inline void put_u16(u8 *buf, u16 data)
+{
+    buf[0] = (u8)(data&0x00ff);
+    buf[1] = (u8)((data >> 8)&0x00ff);
+}
+
+enum rwnx_cmd_mgr_state {
+    RWNX_CMD_MGR_STATE_DEINIT,
+    RWNX_CMD_MGR_STATE_INITED,
+    RWNX_CMD_MGR_STATE_CRASHED,
+};
+
+struct rwnx_cmd {
+    struct list_head list;
+    lmac_msg_id_t id;
+    lmac_msg_id_t reqid;
+    struct rwnx_cmd_a2emsg *a2e_msg;
+    char *e2a_msg;
+    u32 tkn;
+    u16 flags;
+
+    struct completion complete;
+    u32 result;
+    #ifdef CONFIG_RWNX_FHOST
+    struct rwnx_term_stream *stream;
+    #endif
+};
+
+struct rwnx_cmd_mgr {
+    enum rwnx_cmd_mgr_state state;
+    spinlock_t lock;
+    u32 next_tkn;
+    u32 queue_sz;
+    u32 max_queue_sz;
+
+    struct list_head cmds;
+
+    int  (*queue)(struct rwnx_cmd_mgr *, struct rwnx_cmd *);
+    int  (*llind)(struct rwnx_cmd_mgr *, struct rwnx_cmd *);
+    int  (*msgind)(struct rwnx_cmd_mgr *, struct rwnx_cmd_e2amsg *, msg_cb_fct);
+    void (*print)(struct rwnx_cmd_mgr *);
+    void (*drain)(struct rwnx_cmd_mgr *);
+
+#ifdef CMD_WORKQUEUE
+    struct work_struct cmdWork;
+    struct workqueue_struct *cmd_wq;
+#else
+    //struct tasklet_struct cmd_tasklet;
+    struct completion cmdproc_trgg;
+    struct task_struct *cmd_thread;
+#endif
+};
+#ifdef CMD_WORKQUEUE
+#define WAKE_CMD_WORK(cmd_mgr) \
+    do { \
+        queue_work((cmd_mgr)->cmd_wq, &cmd_mgr->cmdWork); \
+    } while (0)
+#endif
+void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr);
+void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr);
+int cmd_mgr_queue_force_defer(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd);
+void aicwf_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len);
+
+#endif /* _RWNX_CMDS_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_compat.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_compat.h
new file mode 100755
index 0000000..0c86952
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_compat.h
@@ -0,0 +1,406 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_compat.h
+ *
+ * Ensure driver compilation for linux 3.16 to 3.19
+ *
+ * To avoid too many #if LINUX_VERSION_CODE if the code, when prototype change
+ * between different kernel version:
+ * - For external function, define a macro whose name is the function name with
+ *   _compat suffix and prototype (actually the number of parameter) of the
+ *   latest version. Then latest version this macro simply call the function
+ *   and for older kernel version it call the function adapting the api.
+ * - For internal function (e.g. cfg80211_ops) do the same but the macro name
+ *   doesn't need to have the _compat suffix when the function is not used
+ *   directly by the driver
+ *
+ * Copyright (C) RivieraWaves 2018
+ *
+ ******************************************************************************
+ */
+#ifndef _RWNX_COMPAT_H_
+#define _RWNX_COMPAT_H_
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+#error "Minimum kernel version supported is 3.1.0"
+#endif
+
+/* Generic */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)
+#define __bf_shf(x) (__builtin_ffsll(x) - 1)
+#define FIELD_PREP(_mask, _val) \
+    (((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask))
+#else
+#include <linux/bitfield.h>
+#endif
+
+/* CFG80211 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK IEEE80211_HE_MAC_CAP3_MAX_A_AMPDU_LEN_EXP_MASK
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+#define IEEE80211_RADIOTAP_HE 23
+#define IEEE80211_RADIOTAP_HE_MU 24
+
+struct ieee80211_radiotap_he {
+	__le16 data1, data2, data3, data4, data5, data6;
+};
+
+enum ieee80211_radiotap_he_bits {
+	IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MASK		= 3,
+	IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU		= 0,
+	IEEE80211_RADIOTAP_HE_DATA1_FORMAT_EXT_SU	= 1,
+	IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU		= 2,
+	IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG		= 3,
+
+	IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN	= 0x0004,
+	IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN	= 0x0008,
+	IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN		= 0x0010,
+	IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN	= 0x0020,
+	IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN	= 0x0040,
+	IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN	= 0x0080,
+	IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN	= 0x0100,
+	IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN		= 0x0200,
+	IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN	= 0x0400,
+	IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN	= 0x0800,
+	IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN	= 0x1000,
+	IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN	= 0x2000,
+	IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN	= 0x4000,
+	IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN	= 0x8000,
+
+	IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_KNOWN	= 0x0001,
+	IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN		= 0x0002,
+	IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN	= 0x0004,
+	IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN	= 0x0008,
+	IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN		= 0x0010,
+	IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN	= 0x0020,
+	IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN		= 0x0040,
+	IEEE80211_RADIOTAP_HE_DATA2_MIDAMBLE_KNOWN	= 0x0080,
+	IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET		= 0x3f00,
+	IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET_KNOWN	= 0x4000,
+	IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC	= 0x8000,
+
+	IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR		= 0x003f,
+	IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE		= 0x0040,
+	IEEE80211_RADIOTAP_HE_DATA3_UL_DL		= 0x0080,
+	IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS		= 0x0f00,
+	IEEE80211_RADIOTAP_HE_DATA3_DATA_DCM		= 0x1000,
+	IEEE80211_RADIOTAP_HE_DATA3_CODING		= 0x2000,
+	IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG	= 0x4000,
+	IEEE80211_RADIOTAP_HE_DATA3_STBC		= 0x8000,
+
+	IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE	= 0x000f,
+	IEEE80211_RADIOTAP_HE_DATA4_MU_STA_ID		= 0x7ff0,
+	IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1	= 0x000f,
+	IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2	= 0x00f0,
+	IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3	= 0x0f00,
+	IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4	= 0xf000,
+
+	IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC	= 0x000f,
+		IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ	= 0,
+		IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ	= 1,
+		IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ	= 2,
+		IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ	= 3,
+		IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_26T	= 4,
+		IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_52T	= 5,
+		IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_106T	= 6,
+		IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_242T	= 7,
+		IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_484T	= 8,
+		IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_996T	= 9,
+		IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_2x996T	= 10,
+
+	IEEE80211_RADIOTAP_HE_DATA5_GI			= 0x0030,
+		IEEE80211_RADIOTAP_HE_DATA5_GI_0_8			= 0,
+		IEEE80211_RADIOTAP_HE_DATA5_GI_1_6			= 1,
+		IEEE80211_RADIOTAP_HE_DATA5_GI_3_2			= 2,
+
+	IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE		= 0x00c0,
+		IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN		= 0,
+		IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X			= 1,
+		IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X			= 2,
+		IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X			= 3,
+	IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS	= 0x0700,
+	IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD		= 0x3000,
+	IEEE80211_RADIOTAP_HE_DATA5_TXBF		= 0x4000,
+	IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG		= 0x8000,
+
+	IEEE80211_RADIOTAP_HE_DATA6_NSTS		= 0x000f,
+	IEEE80211_RADIOTAP_HE_DATA6_DOPPLER		= 0x0010,
+	IEEE80211_RADIOTAP_HE_DATA6_TXOP		= 0x7f00,
+	IEEE80211_RADIOTAP_HE_DATA6_MIDAMBLE_PDCTY	= 0x8000,
+};
+
+struct ieee80211_radiotap_he_mu {
+	__le16 flags1, flags2;
+	u8 ru_ch1[4];
+	u8 ru_ch2[4];
+};
+
+enum ieee80211_radiotap_he_mu_bits {
+	IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS		= 0x000f,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN		= 0x0010,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM		= 0x0020,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN		= 0x0040,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN	= 0x0080,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN		= 0x0100,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN		= 0x0200,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN	= 0x1000,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU		= 0x2000,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN	= 0x4000,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN	= 0x8000,
+
+	IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW	= 0x0003,
+		IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_20MHZ	= 0x0000,
+		IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_40MHZ	= 0x0001,
+		IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_80MHZ	= 0x0002,
+		IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_160MHZ	= 0x0003,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN	= 0x0004,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP		= 0x0008,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS	= 0x00f0,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW	= 0x0300,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN= 0x0400,
+	IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU		= 0x0800,
+};
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+
+
+
+#define CCFS0(vht) vht->center_freq_seg1_idx
+#define CCFS1(vht) vht->center_freq_seg2_idx
+
+#else
+#define CCFS0(vht) vht->center_freq_seg0_idx
+#define CCFS1(vht) vht->center_freq_seg1_idx
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+#define cfg80211_cqm_rssi_notify(dev, event, level, gfp) \
+    cfg80211_cqm_rssi_notify(dev, event, gfp)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)
+#define ieee80211_amsdu_to_8023s(skb, list, addr, iftype, extra_headroom, check_da, check_sa) \
+    ieee80211_amsdu_to_8023s(skb, list, addr, iftype, extra_headroom, false)
+#endif
+
+#if LINUX_VERSION_CODE  < KERNEL_VERSION(4, 7, 0)
+#define NUM_NL80211_BANDS IEEE80211_NUM_BANDS
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+#define cfg80211_disconnected(dev, reason, ie, len, local, gfp) \
+    cfg80211_disconnected(dev, reason, ie, len, gfp)
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)) && !(defined CONFIG_VENDOR_RWNX)
+#define ieee80211_chandef_to_operating_class(chan_def, op_class) 0
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+#define SURVEY_INFO_TIME          SURVEY_INFO_CHANNEL_TIME
+#define SURVEY_INFO_TIME_BUSY     SURVEY_INFO_CHANNEL_TIME_BUSY
+#define SURVEY_INFO_TIME_EXT_BUSY SURVEY_INFO_CHANNEL_TIME_EXT_BUSY
+#define SURVEY_INFO_TIME_RX       SURVEY_INFO_CHANNEL_TIME_RX
+#define SURVEY_INFO_TIME_TX       SURVEY_INFO_CHANNEL_TIME_TX
+
+#define SURVEY_TIME(s) s->channel_time
+#define SURVEY_TIME_BUSY(s) s->channel_time_busy
+#else
+#define SURVEY_TIME(s) s->time
+#define SURVEY_TIME_BUSY(s) s->time_busy
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
+#define cfg80211_ch_switch_started_notify(dev, chandef, count)
+
+#define WLAN_BSS_COEX_INFORMATION_REQUEST	BIT(0)
+#define WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING	BIT(2)
+#define WLAN_EXT_CAPA4_TDLS_BUFFER_STA		BIT(4)
+#define WLAN_EXT_CAPA4_TDLS_PEER_PSM		BIT(5)
+#define WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH		BIT(6)
+#define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED	BIT(7)
+#define NL80211_FEATURE_TDLS_CHANNEL_SWITCH     0
+
+#define STA_TDLS_INITIATOR(sta) 0
+
+#define REGULATORY_IGNORE_STALE_KICKOFF 0
+#else
+#define STA_TDLS_INITIATOR(sta) sta->tdls_initiator
+#endif
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))
+#define cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags)             \
+    cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags, GFP_ATOMIC)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
+#define cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags)             \
+    cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, GFP_ATOMIC)
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
+
+#include <linux/types.h>
+
+struct ieee80211_wmm_ac_param {
+	u8 aci_aifsn; /* AIFSN, ACM, ACI */
+	u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+	__le16 txop_limit;
+} __packed;
+
+struct ieee80211_wmm_param_ie {
+	u8 element_id; /* Element ID: 221 (0xdd); */
+	u8 len; /* Length: 24 */
+	/* required fields for WMM version 1 */
+	u8 oui[3]; /* 00:50:f2 */
+	u8 oui_type; /* 2 */
+	u8 oui_subtype; /* 1 */
+	u8 version; /* 1 for WMM version 1.0 */
+	u8 qos_info; /* AP/STA specific QoS info */
+	u8 reserved; /* 0 */
+	/* AC_BE, AC_BK, AC_VI, AC_VO */
+	struct ieee80211_wmm_ac_param ac[4];
+} __packed;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+enum {
+    IEEE80211_HE_MCS_SUPPORT_0_7    = 0,
+    IEEE80211_HE_MCS_SUPPORT_0_9    = 1,
+    IEEE80211_HE_MCS_SUPPORT_0_11   = 2,
+    IEEE80211_HE_MCS_NOT_SUPPORTED  = 3,
+};
+#endif
+
+/* MAC80211 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
+#define rwnx_ops_mgd_prepare_tx(hw, vif, duration) \
+    rwnx_ops_mgd_prepare_tx(hw, vif)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+
+#define RX_ENC_HT(s) s->flag |= RX_FLAG_HT
+#define RX_ENC_HT_GF(s) s->flag |= (RX_FLAG_HT | RX_FLAG_HT_GF)
+#define RX_ENC_VHT(s) s->flag |= RX_FLAG_HT
+#define RX_ENC_HE(s) s->flag |= RX_FLAG_HT
+#define RX_ENC_FLAG_SHORT_GI(s) s->flag |= RX_FLAG_SHORT_GI
+#define RX_ENC_FLAG_SHORT_PRE(s) s->flag |= RX_FLAG_SHORTPRE
+#define RX_ENC_FLAG_LDPC(s) s->flag |= RX_FLAG_LDPC
+#define RX_BW_40MHZ(s) s->flag |= RX_FLAG_40MHZ
+#define RX_BW_80MHZ(s) s->vht_flag |= RX_VHT_FLAG_80MHZ
+#define RX_BW_160MHZ(s) s->vht_flag |= RX_VHT_FLAG_160MHZ
+#define RX_NSS(s) s->vht_nss
+
+#else
+#define RX_ENC_HT(s) s->encoding = RX_ENC_HT
+#define RX_ENC_HT_GF(s) { s->encoding = RX_ENC_HT;      \
+        s->enc_flags |= RX_ENC_FLAG_HT_GF; }
+#define RX_ENC_VHT(s) s->encoding = RX_ENC_VHT
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+#define RX_ENC_HE(s) s->encoding = RX_ENC_VHT
+#else
+#define RX_ENC_HE(s) s->encoding = RX_ENC_HE
+#endif
+#define RX_ENC_FLAG_SHORT_GI(s) s->enc_flags |= RX_ENC_FLAG_SHORT_GI
+#define RX_ENC_FLAG_SHORT_PRE(s) s->enc_flags |= RX_ENC_FLAG_SHORTPRE
+#define RX_ENC_FLAG_LDPC(s) s->enc_flags |= RX_ENC_FLAG_LDPC
+#define RX_BW_40MHZ(s) s->bw = RATE_INFO_BW_40
+#define RX_BW_80MHZ(s) s->bw = RATE_INFO_BW_80
+#define RX_BW_160MHZ(s) s->bw = RATE_INFO_BW_160
+#define RX_NSS(s) s->nss
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+#define ieee80211_cqm_rssi_notify(vif, event, level, gfp) \
+    ieee80211_cqm_rssi_notify(vif, event, gfp)
+#endif
+
+#ifndef CONFIG_VENDOR_RWNX_AMSDUS_TX
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0))
+#define rwnx_ops_ampdu_action(hw, vif, params) \
+    rwnx_ops_ampdu_action(hw, vif, enum ieee80211_ampdu_mlme_action action, \
+                          struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size)
+#elif  (LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0))
+#define rwnx_ops_ampdu_action(hw, vif, params) \
+    rwnx_ops_ampdu_action(hw, vif, enum ieee80211_ampdu_mlme_action action, \
+                          struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size, \
+                          bool amsdu)
+#endif
+#endif /* CONFIG_VENDOR_RWNX_AMSDUS_TX */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+#define IEEE80211_HW_SUPPORT_FAST_XMIT 0
+#define ieee80211_hw_check(hw, feat) (hw->flags & IEEE80211_HW_##feat)
+#define ieee80211_hw_set(hw, feat) {hw->flags |= IEEE80211_HW_##feat;}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
+#define rwnx_ops_sw_scan_start(hw, vif, mac_addr) \
+    rwnx_ops_sw_scan_start(hw)
+#define rwnx_ops_sw_scan_complete(hw, vif) \
+    rwnx_ops_sw_scan_complete(hw)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+#define rwnx_ops_hw_scan(hw, vif, hw_req) \
+    rwnx_ops_hw_scan(hw, vif, struct cfg80211_scan_request *req)
+#endif
+
+/* NET */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+#define rwnx_select_queue(dev, skb, sb_dev) \
+    rwnx_select_queue(dev, skb)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+#define rwnx_select_queue(dev, skb, sb_dev) \
+    rwnx_select_queue(dev, skb, void *accel_priv, select_queue_fallback_t fallback)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)
+#define rwnx_select_queue(dev, skb, sb_dev) \
+    rwnx_select_queue(dev, skb, sb_dev, select_queue_fallback_t fallback)
+#else
+#define rwnx_select_queue(dev, skb, sb_dev) \
+    rwnx_select_queue(dev, skb, sb_dev)
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) && !(defined CONFIG_VENDOR_RWNX)
+#define sk_pacing_shift_update(sk, shift)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+#define alloc_netdev_mqs(size, name, assign, setup, txqs, rxqs) \
+    alloc_netdev_mqs(size, name, setup, txqs, rxqs)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+#define NET_NAME_UNKNOWN 0
+#endif
+
+/* TRACE */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+#define trace_print_symbols_seq ftrace_print_symbols_seq
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+#define trace_seq_buffer_ptr(p) p->buffer + p->len
+#endif
+
+/* TIME */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
+#define time64_to_tm(t, o, tm) time_to_tm((time_t)t, o, tm)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
+#define ktime_get_real_seconds get_seconds
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+typedef __s64 time64_t;
+#endif
+
+#endif /* _RWNX_COMPAT_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_debugfs.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_debugfs.c
new file mode 100755
index 0000000..70bc683
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_debugfs.c
@@ -0,0 +1,2135 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_debugfs.c
+ *
+ * @brief Definition of debugfs entries
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/debugfs.h>
+#include <linux/string.h>
+#include <linux/sort.h>
+#include <linux/vmalloc.h>
+
+#include "rwnx_debugfs.h"
+#include "rwnx_msg_tx.h"
+#include "rwnx_radar.h"
+#include "rwnx_tx.h"
+
+#ifdef CONFIG_DEBUG_FS_AIC
+#ifdef CONFIG_RWNX_FULLMAC
+static ssize_t rwnx_dbgfs_stats_read(struct file *file,
+									 char __user *user_buf,
+									 size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char *buf;
+	int ret;
+	int i, skipped;
+	ssize_t read;
+	int bufsz = (NX_TXQ_CNT) * 20 + (ARRAY_SIZE(priv->stats.amsdus_rx) + 1) * 40
+		+ (ARRAY_SIZE(priv->stats.ampdus_tx) * 30);
+
+	if (*ppos)
+		return 0;
+
+	buf = kmalloc(bufsz, GFP_ATOMIC);
+	if (buf == NULL)
+		return 0;
+
+	ret = scnprintf(buf, bufsz, "TXQs CFM balances ");
+	for (i = 0; i < NX_TXQ_CNT; i++)
+		ret += scnprintf(&buf[ret], bufsz - ret,
+						 "  [%1d]:%3d", i,
+						 priv->stats.cfm_balance[i]);
+
+	ret += scnprintf(&buf[ret], bufsz - ret, "\n");
+
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+	ret += scnprintf(&buf[ret], bufsz - ret,
+					 "\nAMSDU[len]       done         failed   received\n");
+	for (i = skipped = 0; i < NX_TX_PAYLOAD_MAX; i++) {
+		if (priv->stats.amsdus[i].done) {
+			per = DIV_ROUND_UP((priv->stats.amsdus[i].failed) *
+							   100, priv->stats.amsdus[i].done);
+		} else if (priv->stats.amsdus_rx[i]) {
+			per = 0;
+		} else {
+			per = 0;
+			skipped = 1;
+			continue;
+		}
+		if (skipped) {
+			ret += scnprintf(&buf[ret], bufsz - ret, "   ...\n");
+			skipped = 0;
+		}
+
+		ret += scnprintf(&buf[ret], bufsz - ret,
+						 "   [%2d]    %10d %8d(%3d%%) %10d\n",  i ? i + 1 : i,
+						 priv->stats.amsdus[i].done,
+						 priv->stats.amsdus[i].failed, per,
+						 priv->stats.amsdus_rx[i]);
+	}
+
+	for (; i < ARRAY_SIZE(priv->stats.amsdus_rx); i++) {
+		if (!priv->stats.amsdus_rx[i]) {
+			skipped = 1;
+			continue;
+		}
+		if (skipped) {
+			ret += scnprintf(&buf[ret], bufsz - ret, "   ...\n");
+			skipped = 0;
+		}
+
+		ret += scnprintf(&buf[ret], bufsz - ret,
+						 "   [%2d]                              %10d\n",
+						 i + 1, priv->stats.amsdus_rx[i]);
+	}
+#else
+	ret += scnprintf(&buf[ret], bufsz - ret,
+					 "\nAMSDU[len]   received\n");
+	for (i = skipped = 0; i < ARRAY_SIZE(priv->stats.amsdus_rx); i++) {
+		if (!priv->stats.amsdus_rx[i]) {
+			skipped = 1;
+			continue;
+		}
+		if (skipped) {
+			ret += scnprintf(&buf[ret], bufsz - ret,
+							 "   ...\n");
+			skipped = 0;
+		}
+
+		ret += scnprintf(&buf[ret], bufsz - ret,
+						 "   [%2d]    %10d\n",
+						 i + 1, priv->stats.amsdus_rx[i]);
+	}
+
+#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
+
+	ret += scnprintf(&buf[ret], bufsz - ret,
+					 "\nAMPDU[len]     done  received\n");
+	for (i = skipped = 0; i < ARRAY_SIZE(priv->stats.ampdus_tx); i++) {
+		if (!priv->stats.ampdus_tx[i] && !priv->stats.ampdus_rx[i]) {
+			skipped = 1;
+			continue;
+		}
+		if (skipped) {
+			ret += scnprintf(&buf[ret], bufsz - ret,
+							 "    ...\n");
+			skipped = 0;
+		}
+
+		ret += scnprintf(&buf[ret], bufsz - ret,
+						 "   [%2d]   %9d %9d\n", i ? i + 1 : i,
+						 priv->stats.ampdus_tx[i], priv->stats.ampdus_rx[i]);
+	}
+
+	ret += scnprintf(&buf[ret], bufsz - ret,
+					 "#mpdu missed        %9d\n",
+					 priv->stats.ampdus_rx_miss);
+	read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+	kfree(buf);
+
+	return read;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+static ssize_t rwnx_dbgfs_stats_write(struct file *file,
+									  const char __user *user_buf,
+									  size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+
+	/* Prevent from interrupt preemption as these statistics are updated under
+	 * interrupt */
+	spin_lock_bh(&priv->tx_lock);
+
+	memset(&priv->stats, 0, sizeof(priv->stats));
+
+	spin_unlock_bh(&priv->tx_lock);
+
+	return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(stats);
+
+#define TXQ_STA_PREF "tid|"
+#define TXQ_STA_PREF_FMT "%3d|"
+
+#ifdef CONFIG_RWNX_FULLMAC
+#define TXQ_VIF_PREF "type|"
+#define TXQ_VIF_PREF_FMT "%4s|"
+#else
+#define TXQ_VIF_PREF "AC|"
+#define TXQ_VIF_PREF_FMT "%2s|"
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#define TXQ_HDR "idx| status|credit|ready|retry"
+#define TXQ_HDR_FMT "%3d|%s%s%s%s%s%s%s|%6d|%5d|%5d"
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+#ifdef CONFIG_RWNX_FULLMAC
+#define TXQ_HDR_SUFF "|amsdu"
+#define TXQ_HDR_SUFF_FMT "|%5d"
+#else
+#define TXQ_HDR_SUFF "|amsdu-ht|amdsu-vht"
+#define TXQ_HDR_SUFF_FMT "|%8d|%9d"
+#endif /* CONFIG_RWNX_FULLMAC */
+#else
+#define TXQ_HDR_SUFF ""
+#define TXQ_HDR_SUF_FMT ""
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+
+#define TXQ_HDR_MAX_LEN (sizeof(TXQ_STA_PREF) + sizeof(TXQ_HDR) + sizeof(TXQ_HDR_SUFF) + 1)
+
+#ifdef CONFIG_RWNX_FULLMAC
+#define PS_HDR  "Legacy PS: ready=%d, sp=%d / UAPSD: ready=%d, sp=%d"
+#define PS_HDR_LEGACY "Legacy PS: ready=%d, sp=%d"
+#define PS_HDR_UAPSD  "UAPSD: ready=%d, sp=%d"
+#define PS_HDR_MAX_LEN  sizeof("Legacy PS: ready=xxx, sp=xxx / UAPSD: ready=xxx, sp=xxx\n")
+#else
+#define PS_HDR ""
+#define PS_HDR_MAX_LEN 0
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#define STA_HDR "** STA %d (%pM)\n"
+#define STA_HDR_MAX_LEN (sizeof("- STA xx (xx:xx:xx:xx:xx:xx)\n") + PS_HDR_MAX_LEN)
+
+#ifdef CONFIG_RWNX_FULLMAC
+#define VIF_HDR "* VIF [%d] %s\n"
+#define VIF_HDR_MAX_LEN (sizeof(VIF_HDR) + IFNAMSIZ)
+#else
+#define VIF_HDR "* VIF [%d]\n"
+#define VIF_HDR_MAX_LEN sizeof(VIF_HDR)
+#endif
+
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+
+#ifdef CONFIG_RWNX_FULLMAC
+#define VIF_SEP "---------------------------------------\n"
+#else
+#define VIF_SEP "----------------------------------------------------\n"
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#else /* ! CONFIG_RWNX_AMSDUS_TX */
+#define VIF_SEP "---------------------------------\n"
+#endif /* CONFIG_RWNX_AMSDUS_TX*/
+
+#define VIF_SEP_LEN sizeof(VIF_SEP)
+
+#define CAPTION "status: L=in hwq list, F=stop full, P=stop sta PS, V=stop vif PS, C=stop channel, S=stop CSA, M=stop MU"
+#define CAPTION_LEN sizeof(CAPTION)
+
+#define STA_TXQ 0
+#define VIF_TXQ 1
+
+static int rwnx_dbgfs_txq(char *buf, size_t size, struct rwnx_txq *txq, int type, int tid, char *name)
+{
+	int res, idx = 0;
+
+	if (type == STA_TXQ) {
+		res = scnprintf(&buf[idx], size, TXQ_STA_PREF_FMT, tid);
+		idx += res;
+		size -= res;
+	} else {
+		res = scnprintf(&buf[idx], size, TXQ_VIF_PREF_FMT, name);
+		idx += res;
+		size -= res;
+	}
+
+	res = scnprintf(&buf[idx], size, TXQ_HDR_FMT, txq->idx,
+					(txq->status & RWNX_TXQ_IN_HWQ_LIST) ? "L" : " ",
+					(txq->status & RWNX_TXQ_STOP_FULL) ? "F" : " ",
+					(txq->status & RWNX_TXQ_STOP_STA_PS) ? "P" : " ",
+					(txq->status & RWNX_TXQ_STOP_VIF_PS) ? "V" : " ",
+					(txq->status & RWNX_TXQ_STOP_CHAN) ? "C" : " ",
+					(txq->status & RWNX_TXQ_STOP_CSA) ? "S" : " ",
+					(txq->status & RWNX_TXQ_STOP_MU_POS) ? "M" : " ",
+					txq->credits, skb_queue_len(&txq->sk_list),
+					txq->nb_retry);
+	idx += res;
+	size -= res;
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+	if (type == STA_TXQ) {
+		res = scnprintf(&buf[idx], size, TXQ_HDR_SUFF_FMT,
+#ifdef CONFIG_RWNX_FULLMAC
+						txq->amsdu_len
+#else
+						txq->amsdu_ht_len_cap, txq->amsdu_vht_len_cap
+#endif /* CONFIG_RWNX_FULLMAC */
+						);
+		idx += res;
+		size -= res;
+	}
+#endif
+
+	res = scnprintf(&buf[idx], size, "\n");
+	idx += res;
+	size -= res;
+
+	return idx;
+}
+
+static int rwnx_dbgfs_txq_sta(char *buf, size_t size, struct rwnx_sta *rwnx_sta,
+							  struct rwnx_hw *rwnx_hw)
+{
+	int tid, res, idx = 0;
+	struct rwnx_txq *txq;
+
+	res = scnprintf(&buf[idx], size, "\n" STA_HDR,
+			rwnx_sta->sta_idx,
+#ifdef CONFIG_RWNX_FULLMAC
+	rwnx_sta->mac_addr
+#endif /* CONFIG_RWNX_FULLMAC */
+			);
+	idx += res;
+	size -= res;
+
+#ifdef CONFIG_RWNX_FULLMAC
+	if (rwnx_sta->ps.active) {
+		if (rwnx_sta->uapsd_tids &&
+			(rwnx_sta->uapsd_tids == ((1 << NX_NB_TXQ_PER_STA) - 1)))
+			res = scnprintf(&buf[idx], size, PS_HDR_UAPSD "\n",
+							rwnx_sta->ps.pkt_ready[UAPSD_ID],
+							rwnx_sta->ps.sp_cnt[UAPSD_ID]);
+		else if (rwnx_sta->uapsd_tids)
+			res = scnprintf(&buf[idx], size, PS_HDR "\n",
+							rwnx_sta->ps.pkt_ready[LEGACY_PS_ID],
+							rwnx_sta->ps.sp_cnt[LEGACY_PS_ID],
+							rwnx_sta->ps.pkt_ready[UAPSD_ID],
+							rwnx_sta->ps.sp_cnt[UAPSD_ID]);
+		else
+			res = scnprintf(&buf[idx], size, PS_HDR_LEGACY "\n",
+							rwnx_sta->ps.pkt_ready[LEGACY_PS_ID],
+							rwnx_sta->ps.sp_cnt[LEGACY_PS_ID]);
+		idx += res;
+		size -= res;
+	} else {
+		res = scnprintf(&buf[idx], size, "\n");
+		idx += res;
+		size -= res;
+	}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+
+	res = scnprintf(&buf[idx], size, TXQ_STA_PREF TXQ_HDR TXQ_HDR_SUFF "\n");
+	idx += res;
+	size -= res;
+
+
+	foreach_sta_txq(rwnx_sta, txq, tid, rwnx_hw) {
+		res = rwnx_dbgfs_txq(&buf[idx], size, txq, STA_TXQ, tid, NULL);
+		idx += res;
+		size -= res;
+	}
+
+	return idx;
+}
+
+static int rwnx_dbgfs_txq_vif(char *buf, size_t size, struct rwnx_vif *rwnx_vif,
+							  struct rwnx_hw *rwnx_hw)
+{
+	int res, idx = 0;
+	struct rwnx_txq *txq;
+	struct rwnx_sta *rwnx_sta;
+
+#ifdef CONFIG_RWNX_FULLMAC
+	res = scnprintf(&buf[idx], size, VIF_HDR, rwnx_vif->vif_index, rwnx_vif->ndev->name);
+	idx += res;
+	size -= res;
+	if (!rwnx_vif->up || rwnx_vif->ndev == NULL)
+		return idx;
+
+#else
+	int ac;
+	char ac_name[2] = {'0', '\0'};
+
+	res = scnprintf(&buf[idx], size, VIF_HDR, rwnx_vif->vif_index);
+	idx += res;
+	size -= res;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#ifdef CONFIG_RWNX_FULLMAC
+	if (RWNX_VIF_TYPE(rwnx_vif) ==  NL80211_IFTYPE_AP ||
+		RWNX_VIF_TYPE(rwnx_vif) ==  NL80211_IFTYPE_P2P_GO ||
+		RWNX_VIF_TYPE(rwnx_vif) ==  NL80211_IFTYPE_MESH_POINT) {
+		res = scnprintf(&buf[idx], size, TXQ_VIF_PREF TXQ_HDR "\n");
+		idx += res;
+		size -= res;
+		txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+		res = rwnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, "UNK");
+		idx += res;
+		size -= res;
+		txq = rwnx_txq_vif_get(rwnx_vif, NX_BCMC_TXQ_TYPE);
+		res = rwnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, "BCMC");
+		idx += res;
+		size -= res;
+		rwnx_sta = &rwnx_hw->sta_table[rwnx_vif->ap.bcmc_index];
+		if (rwnx_sta->ps.active) {
+			res = scnprintf(&buf[idx], size, PS_HDR_LEGACY "\n",
+							rwnx_sta->ps.sp_cnt[LEGACY_PS_ID],
+							rwnx_sta->ps.sp_cnt[LEGACY_PS_ID]);
+			idx += res;
+			size -= res;
+		} else {
+			res = scnprintf(&buf[idx], size, "\n");
+			idx += res;
+			size -= res;
+		}
+
+		list_for_each_entry(rwnx_sta, &rwnx_vif->ap.sta_list, list) {
+			res = rwnx_dbgfs_txq_sta(&buf[idx], size, rwnx_sta, rwnx_hw);
+			idx += res;
+			size -= res;
+		}
+	} else if (RWNX_VIF_TYPE(rwnx_vif) ==  NL80211_IFTYPE_STATION ||
+			   RWNX_VIF_TYPE(rwnx_vif) ==  NL80211_IFTYPE_P2P_CLIENT) {
+		if (rwnx_vif->sta.ap) {
+			res = rwnx_dbgfs_txq_sta(&buf[idx], size, rwnx_vif->sta.ap, rwnx_hw);
+			idx += res;
+			size -= res;
+		}
+	}
+
+#else
+	res = scnprintf(&buf[idx], size, TXQ_VIF_PREF TXQ_HDR "\n");
+	idx += res;
+	size -= res;
+
+	foreach_vif_txq(rwnx_vif, txq, ac) {
+		ac_name[0]++;
+		res = rwnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, ac_name);
+		idx += res;
+		size -= res;
+	}
+
+	list_for_each_entry(rwnx_sta, &rwnx_vif->stations, list) {
+		res = rwnx_dbgfs_txq_sta(&buf[idx], size, rwnx_sta, rwnx_hw);
+		idx += res;
+		size -= res;
+	}
+#endif /* CONFIG_RWNX_FULLMAC */
+	return idx;
+}
+
+static ssize_t rwnx_dbgfs_txq_read(struct file *file,
+								   char __user *user_buf,
+								   size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *rwnx_hw = file->private_data;
+	struct rwnx_vif *vif;
+	char *buf;
+	int idx, res;
+	ssize_t read;
+	size_t bufsz = ((NX_VIRT_DEV_MAX * (VIF_HDR_MAX_LEN + 2 * VIF_SEP_LEN)) +
+					(NX_REMOTE_STA_MAX * STA_HDR_MAX_LEN) +
+					((NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX + NX_NB_TXQ) *
+					 TXQ_HDR_MAX_LEN) + CAPTION_LEN);
+
+	/* everything is read in one go */
+	if (*ppos)
+		return 0;
+
+	bufsz = min_t(size_t, bufsz, count);
+	buf = kmalloc(bufsz, GFP_ATOMIC);
+	if (buf == NULL)
+		return 0;
+
+	bufsz--;
+	idx = 0;
+
+	res = scnprintf(&buf[idx], bufsz, CAPTION);
+	idx += res;
+	bufsz -= res;
+
+	//spin_lock_bh(&rwnx_hw->tx_lock);
+	list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+		res = scnprintf(&buf[idx], bufsz, "\n"VIF_SEP);
+		idx += res;
+		bufsz -= res;
+		res = rwnx_dbgfs_txq_vif(&buf[idx], bufsz, vif, rwnx_hw);
+		idx += res;
+		bufsz -= res;
+		res = scnprintf(&buf[idx], bufsz, VIF_SEP);
+		idx += res;
+		bufsz -= res;
+	}
+	//spin_unlock_bh(&rwnx_hw->tx_lock);
+
+	read = simple_read_from_buffer(user_buf, count, ppos, buf, idx);
+	kfree(buf);
+
+	return read;
+}
+DEBUGFS_READ_FILE_OPS(txq);
+
+static ssize_t rwnx_dbgfs_acsinfo_read(struct file *file,
+										   char __user *user_buf,
+										   size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+#ifdef CONFIG_RWNX_FULLMAC
+	struct wiphy *wiphy = priv->wiphy;
+#endif //CONFIG_RWNX_FULLMAC
+	int survey_cnt = 0;
+	int len = 0;
+	int band, chan_cnt;
+	int band_max = NL80211_BAND_5GHZ;
+	char *buf = (char *)vmalloc((SCAN_CHANNEL_MAX + 1) * 43);
+	ssize_t size;
+
+	if (!buf)
+		return 0;
+
+	if (priv->band_5g_support)
+		band_max = NL80211_BAND_5GHZ + 1;
+
+	mutex_lock(&priv->dbgdump_elem.mutex);
+
+	len += scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+					 "FREQ    TIME(ms)    BUSY(ms)    NOISE(dBm)\n");
+
+	for (band = NL80211_BAND_2GHZ; band < band_max; band++) {
+		for (chan_cnt = 0; chan_cnt < wiphy->bands[band]->n_channels; chan_cnt++) {
+			struct rwnx_survey_info *p_survey_info = &priv->survey[survey_cnt];
+			struct ieee80211_channel *p_chan = &wiphy->bands[band]->channels[chan_cnt];
+
+			if (p_survey_info->filled) {
+				len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - len - 1, count),
+								 "%d    %03d         %03d         %d\n",
+								 p_chan->center_freq,
+								 p_survey_info->chan_time_ms,
+								 p_survey_info->chan_time_busy_ms,
+								 p_survey_info->noise_dbm);
+			} else {
+				len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) -len -1, count),
+								 "%d    NOT AVAILABLE\n",
+								 p_chan->center_freq);
+			}
+
+			survey_cnt++;
+		}
+	}
+
+	mutex_unlock(&priv->dbgdump_elem.mutex);
+
+	size = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	vfree(buf);
+
+	return size;
+}
+
+DEBUGFS_READ_FILE_OPS(acsinfo);
+
+static ssize_t rwnx_dbgfs_fw_dbg_read(struct file *file,
+										   char __user *user_buf,
+										   size_t count, loff_t *ppos)
+{
+	char help[] = "usage: [MOD:<ALL|KE|DBG|IPC|DMA|MM|TX|RX|PHY>]* "
+		"[DBG:<NONE|CRT|ERR|WRN|INF|VRB>]\n";
+
+	return simple_read_from_buffer(user_buf, count, ppos, help, sizeof(help));
+}
+
+
+static ssize_t rwnx_dbgfs_fw_dbg_write(struct file *file,
+											const char __user *user_buf,
+											size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[32];
+	int idx = 0;
+	u32 mod = 0;
+	size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+	buf[len] = '\0';
+
+#define RWNX_MOD_TOKEN(str, val)                                        \
+	do {								\
+		if (strncmp(&buf[idx], str, sizeof(str) - 1) == 0) {               \
+			idx += sizeof(str) - 1;                                         \
+			mod |= val;                                                     \
+			continue;                                                       \
+		}									\
+	} while (0)
+
+#define RWNX_DBG_TOKEN(str, val)                                \
+	do {							\
+		if (strncmp(&buf[idx], str, sizeof(str) - 1) == 0) {        \
+			idx += sizeof(str) - 1;                                 \
+			dbg = val;                                              \
+			goto dbg_done;                                          \
+		}								\
+	} while (0)
+
+	while ((idx + 4) < len) {
+		if (strncmp(&buf[idx], "MOD:", 4) == 0) {
+			idx += 4;
+			RWNX_MOD_TOKEN("ALL", 0xffffffff);
+			RWNX_MOD_TOKEN("KE",  BIT(0));
+			RWNX_MOD_TOKEN("DBG", BIT(1));
+			RWNX_MOD_TOKEN("IPC", BIT(2));
+			RWNX_MOD_TOKEN("DMA", BIT(3));
+			RWNX_MOD_TOKEN("MM",  BIT(4));
+			RWNX_MOD_TOKEN("TX",  BIT(5));
+			RWNX_MOD_TOKEN("RX",  BIT(6));
+			RWNX_MOD_TOKEN("PHY", BIT(7));
+			idx++;
+		} else if (strncmp(&buf[idx], "DBG:", 4) == 0) {
+			u32 dbg = 0;
+			idx += 4;
+			RWNX_DBG_TOKEN("NONE", 0);
+			RWNX_DBG_TOKEN("CRT",  1);
+			RWNX_DBG_TOKEN("ERR",  2);
+			RWNX_DBG_TOKEN("WRN",  3);
+			RWNX_DBG_TOKEN("INF",  4);
+			RWNX_DBG_TOKEN("VRB",  5);
+			idx++;
+			continue;
+		  dbg_done:
+			rwnx_send_dbg_set_sev_filter_req(priv, dbg);
+		} else {
+			idx++;
+		}
+	}
+
+	if (mod) {
+		rwnx_send_dbg_set_mod_filter_req(priv, mod);
+	}
+
+	return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg);
+
+static ssize_t rwnx_dbgfs_sys_stats_read(struct file *file,
+										 char __user *user_buf,
+										 size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[3*64];
+	int len = 0;
+	ssize_t read;
+	int error = 0;
+	struct dbg_get_sys_stat_cfm cfm;
+	u32 sleep_int, sleep_frac, doze_int, doze_frac;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Get the information from the FW */
+	error = rwnx_send_dbg_get_sys_stat_req(priv, &cfm);
+	if (error)
+		return error;
+
+	if (cfm.stats_time == 0)
+		return 0;
+
+	sleep_int = ((cfm.cpu_sleep_time * 100) / cfm.stats_time);
+	sleep_frac = (((cfm.cpu_sleep_time * 100) % cfm.stats_time) * 10) / cfm.stats_time;
+	doze_int = ((cfm.doze_time * 100) / cfm.stats_time);
+	doze_frac = (((cfm.doze_time * 100) % cfm.stats_time) * 10) / cfm.stats_time;
+
+	len += scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+					 "\nSystem statistics:\n");
+	len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - 1, count),
+					 "  CPU sleep [%%]: %d.%d\n", sleep_int, sleep_frac);
+	len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - 1, count),
+					 "  Doze      [%%]: %d.%d\n", doze_int, doze_frac);
+
+	read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+	return read;
+}
+
+DEBUGFS_READ_FILE_OPS(sys_stats);
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+static ssize_t rwnx_dbgfs_mu_group_read(struct file *file,
+										char __user *user_buf,
+										size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *rwnx_hw = file->private_data;
+	struct rwnx_mu_info *mu = &rwnx_hw->mu;
+	struct rwnx_mu_group *group;
+	size_t bufsz = NX_MU_GROUP_MAX * sizeof("xx = (xx - xx - xx - xx)\n") + 50;
+	char *buf;
+	int j, res, idx = 0;
+
+	if (*ppos)
+		return 0;
+
+	buf = kmalloc(bufsz, GFP_ATOMIC);
+	if (buf == NULL)
+		return 0;
+
+	res = scnprintf(&buf[idx], bufsz, "MU Group list (%d groups, %d users max)\n",
+					NX_MU_GROUP_MAX, CONFIG_USER_MAX);
+	idx += res;
+	bufsz -= res;
+
+	list_for_each_entry(group, &mu->active_groups, list) {
+		if (group->user_cnt) {
+			res = scnprintf(&buf[idx], bufsz, "%2d = (", group->group_id);
+			idx += res;
+			bufsz -= res;
+			for (j = 0; j < (CONFIG_USER_MAX - 1) ; j++) {
+				if (group->users[j])
+					res = scnprintf(&buf[idx], bufsz, "%2d - ",
+									group->users[j]->sta_idx);
+				else
+					res = scnprintf(&buf[idx], bufsz, ".. - ");
+
+				idx += res;
+				bufsz -= res;
+			}
+
+			if (group->users[j])
+				res = scnprintf(&buf[idx], bufsz, "%2d)\n",
+								group->users[j]->sta_idx);
+			else
+				res = scnprintf(&buf[idx], bufsz, "..)\n");
+
+			idx += res;
+			bufsz -= res;
+		}
+	}
+
+	res = simple_read_from_buffer(user_buf, count, ppos, buf, idx);
+	kfree(buf);
+
+	return res;
+}
+
+DEBUGFS_READ_FILE_OPS(mu_group);
+#endif
+
+#ifdef CONFIG_RWNX_P2P_DEBUGFS
+static ssize_t rwnx_dbgfs_oppps_write(struct file *file,
+									  const char __user *user_buf,
+									  size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *rw_hw = file->private_data;
+	struct rwnx_vif *rw_vif;
+	char buf[32];
+	size_t len = min_t(size_t, count, sizeof(buf) - 1);
+	int ctw;
+
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+	buf[len] = '\0';
+
+	/* Read the written CT Window (provided in ms) value */
+	if (sscanf(buf, "ctw=%d", &ctw) > 0) {
+		/* Check if at least one VIF is configured as P2P GO */
+		list_for_each_entry(rw_vif, &rw_hw->vifs, list) {
+#ifdef CONFIG_RWNX_FULLMAC
+			if (RWNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_P2P_GO) {
+#endif /* CONFIG_RWNX_FULLMAC */
+				struct mm_set_p2p_oppps_cfm cfm;
+
+				/* Forward request to the embedded and wait for confirmation */
+				rwnx_send_p2p_oppps_req(rw_hw, rw_vif, (u8)ctw, &cfm);
+
+				break;
+			}
+		}
+	}
+
+	return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(oppps);
+
+static ssize_t rwnx_dbgfs_noa_write(struct file *file,
+									const char __user *user_buf,
+									size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *rw_hw = file->private_data;
+	struct rwnx_vif *rw_vif;
+	char buf[64];
+	size_t len = min_t(size_t, count, sizeof(buf) - 1);
+	int noa_count, interval, duration, dyn_noa;
+
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+	buf[len] = '\0';
+
+	/* Read the written NOA information */
+	if (sscanf(buf, "count=%d interval=%d duration=%d dyn=%d",
+		&noa_count, &interval, &duration, &dyn_noa) > 0) {
+	/* Check if at least one VIF is configured as P2P GO */
+	list_for_each_entry(rw_vif, &rw_hw->vifs, list) {
+#ifdef CONFIG_RWNX_FULLMAC
+	if (RWNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_P2P_GO) {
+#endif /* CONFIG_RWNX_FULLMAC */
+		struct mm_set_p2p_noa_cfm cfm;
+
+				/* Forward request to the embedded and wait for confirmation */
+				rwnx_send_p2p_noa_req(rw_hw, rw_vif, noa_count, interval,
+									  duration, (dyn_noa > 0),  &cfm);
+
+				break;
+			}
+		}
+	}
+
+	return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(noa);
+#endif /* CONFIG_RWNX_P2P_DEBUGFS */
+
+static char fw_log_buffer[FW_LOG_SIZE];
+
+static ssize_t rwnx_dbgfs_fw_log_read(struct file *file,
+											  char __user *user_buf,
+											  size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	size_t not_cpy;
+	size_t nb_cpy;
+	char *log = fw_log_buffer;
+
+	printk("%s, %d, %p, %p\n", __func__, priv->debugfs.fw_log.buf.size, priv->debugfs.fw_log.buf.start, priv->debugfs.fw_log.buf.dataend);
+	//spin_lock_bh(&priv->debugfs.fw_log.lock);
+
+	if ((priv->debugfs.fw_log.buf.start + priv->debugfs.fw_log.buf.size) >= priv->debugfs.fw_log.buf.dataend) {
+		memcpy(log, priv->debugfs.fw_log.buf.start, priv->debugfs.fw_log.buf.dataend - priv->debugfs.fw_log.buf.start);
+		not_cpy = copy_to_user(user_buf, log, priv->debugfs.fw_log.buf.dataend - priv->debugfs.fw_log.buf.start);
+		nb_cpy = priv->debugfs.fw_log.buf.dataend - priv->debugfs.fw_log.buf.start - not_cpy;
+		priv->debugfs.fw_log.buf.start = priv->debugfs.fw_log.buf.data;
+	} else {
+		memcpy(log, priv->debugfs.fw_log.buf.start, priv->debugfs.fw_log.buf.size);
+		not_cpy = copy_to_user(user_buf, log, priv->debugfs.fw_log.buf.size);
+		nb_cpy = priv->debugfs.fw_log.buf.size - not_cpy;
+		priv->debugfs.fw_log.buf.start = priv->debugfs.fw_log.buf.start + priv->debugfs.fw_log.buf.size - not_cpy;
+	}
+
+	priv->debugfs.fw_log.buf.size -= nb_cpy;
+	//spin_unlock_bh(&priv->debugfs.fw_log.lock);
+
+	printk("nb_cpy=%lu, not_cpy=%lu, start=%p, end=%p\n", (long unsigned int)nb_cpy, (long unsigned int)not_cpy, priv->debugfs.fw_log.buf.start, priv->debugfs.fw_log.buf.end);
+	return nb_cpy;
+}
+
+static ssize_t rwnx_dbgfs_fw_log_write(struct file *file,
+											   const char __user *user_buf,
+											   size_t count, loff_t *ppos)
+{
+	//struct rwnx_hw *priv = file->private_data;
+
+	printk("%s\n", __func__);
+	return count;
+}
+DEBUGFS_READ_WRITE_FILE_OPS(fw_log);
+
+#ifdef CONFIG_RWNX_RADAR
+static ssize_t rwnx_dbgfs_pulses_read(struct file *file,
+									  char __user *user_buf,
+									  size_t count, loff_t *ppos,
+									  int rd_idx)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char *buf;
+	int len = 0;
+	int bufsz;
+	int i;
+	int index;
+	struct rwnx_radar_pulses *p = &priv->radar.pulses[rd_idx];
+	ssize_t read;
+
+	if (*ppos != 0)
+		return 0;
+
+	/* Prevent from interrupt preemption */
+	spin_lock_bh(&priv->radar.lock);
+	bufsz = p->count * 34 + 51;
+	bufsz += rwnx_radar_dump_pattern_detector(NULL, 0, &priv->radar, rd_idx);
+	buf = kmalloc(bufsz, GFP_ATOMIC);
+	if (buf == NULL) {
+		spin_unlock_bh(&priv->radar.lock);
+		return 0;
+	}
+
+	if (p->count) {
+		len += scnprintf(&buf[len], bufsz - len,
+						 " PRI     WIDTH     FOM     FREQ\n");
+		index = p->index;
+		for (i = 0; i < p->count; i++) {
+			struct radar_pulse *pulse;
+
+			if (index > 0)
+				index--;
+			else
+				index = RWNX_RADAR_PULSE_MAX - 1;
+
+			pulse = (struct radar_pulse *) &p->buffer[index];
+
+			len += scnprintf(&buf[len], bufsz - len,
+							 "%05dus  %03dus     %2d%%    %+3dMHz\n", pulse->rep,
+							 2 * pulse->len, 6 * pulse->fom, 2*pulse->freq);
+		}
+	}
+
+	len += rwnx_radar_dump_pattern_detector(&buf[len], bufsz - len,
+											&priv->radar, rd_idx);
+
+	spin_unlock_bh(&priv->radar.lock);
+
+	read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+	kfree(buf);
+
+	return read;
+}
+
+static ssize_t rwnx_dbgfs_pulses_prim_read(struct file *file,
+										   char __user *user_buf,
+										   size_t count, loff_t *ppos)
+{
+	return rwnx_dbgfs_pulses_read(file, user_buf, count, ppos, 0);
+}
+
+DEBUGFS_READ_FILE_OPS(pulses_prim);
+
+static ssize_t rwnx_dbgfs_pulses_sec_read(struct file *file,
+										  char __user *user_buf,
+										  size_t count, loff_t *ppos)
+{
+	return rwnx_dbgfs_pulses_read(file, user_buf, count, ppos, 1);
+}
+
+DEBUGFS_READ_FILE_OPS(pulses_sec);
+
+static ssize_t rwnx_dbgfs_detected_read(struct file *file,
+										char __user *user_buf,
+										size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char *buf;
+	int bufsz, len = 0;
+	ssize_t read;
+
+	if (*ppos != 0)
+		return 0;
+
+	bufsz = 5; // RIU:\n
+	bufsz += rwnx_radar_dump_radar_detected(NULL, 0, &priv->radar,
+											RWNX_RADAR_RIU);
+
+	if (priv->phy.cnt > 1) {
+		bufsz += 5; // FCU:\n
+		bufsz += rwnx_radar_dump_radar_detected(NULL, 0, &priv->radar,
+												RWNX_RADAR_FCU);
+	}
+
+	buf = kmalloc(bufsz, GFP_KERNEL);
+	if (buf == NULL) {
+		return 0;
+	}
+
+	len = scnprintf(&buf[len], bufsz, "RIU:\n");
+	len += rwnx_radar_dump_radar_detected(&buf[len], bufsz - len, &priv->radar,
+											RWNX_RADAR_RIU);
+
+	if (priv->phy.cnt > 1) {
+		len += scnprintf(&buf[len], bufsz - len, "FCU:\n");
+		len += rwnx_radar_dump_radar_detected(&buf[len], bufsz - len,
+											  &priv->radar, RWNX_RADAR_FCU);
+	}
+
+	read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+	kfree(buf);
+
+	return read;
+}
+
+DEBUGFS_READ_FILE_OPS(detected);
+
+static ssize_t rwnx_dbgfs_enable_read(struct file *file,
+									char __user *user_buf,
+									size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[32];
+	int ret;
+	ssize_t read;
+
+	ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+					"RIU=%d FCU=%d\n", priv->radar.dpd[RWNX_RADAR_RIU]->enabled,
+					priv->radar.dpd[RWNX_RADAR_FCU]->enabled);
+
+	read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+	return read;
+}
+
+static ssize_t rwnx_dbgfs_enable_write(struct file *file,
+									 const char __user *user_buf,
+									 size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[32];
+	int val;
+	size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+
+	if (sscanf(buf, "RIU=%d", &val) > 0)
+		rwnx_radar_detection_enable(&priv->radar, val, RWNX_RADAR_RIU);
+
+	if (sscanf(buf, "FCU=%d", &val) > 0)
+		rwnx_radar_detection_enable(&priv->radar, val, RWNX_RADAR_FCU);
+
+	return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(enable);
+
+static ssize_t rwnx_dbgfs_band_read(struct file *file,
+									char __user *user_buf,
+									size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[32];
+	int ret;
+	ssize_t read;
+
+	ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+					"BAND=%d\n", priv->phy.sec_chan.band);
+
+	read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+	return read;
+}
+
+static ssize_t rwnx_dbgfs_band_write(struct file *file,
+									 const char __user *user_buf,
+									 size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[32];
+	int val;
+	size_t len = min_t(size_t, count, sizeof(buf) - 1);
+	int band_max = NL80211_BAND_5GHZ;
+
+	if (priv->band_5g_support)
+		band_max = NL80211_BAND_5GHZ + 1;
+
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+
+	if ((sscanf(buf, "%d", &val) > 0) && (val >= 0) && (val < band_max))
+		priv->phy.sec_chan.band = val;
+
+	return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(band);
+
+static ssize_t rwnx_dbgfs_type_read(struct file *file,
+									char __user *user_buf,
+									size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[32];
+	int ret;
+	ssize_t read;
+
+	ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+					"TYPE=%d\n", priv->phy.sec_chan.type);
+
+	read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+	return read;
+}
+
+static ssize_t rwnx_dbgfs_type_write(struct file *file,
+									 const char __user *user_buf,
+									 size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[32];
+	int val;
+	size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+
+	if ((sscanf(buf, "%d", &val) > 0) && (val >= PHY_CHNL_BW_20) &&
+		(val <= PHY_CHNL_BW_80P80))
+		priv->phy.sec_chan.type = val;
+
+	return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(type);
+
+static ssize_t rwnx_dbgfs_prim20_read(struct file *file,
+									  char __user *user_buf,
+									  size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[32];
+	int ret;
+	ssize_t read;
+
+	ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+					"PRIM20=%dMHz\n", priv->phy.sec_chan.prim20_freq);
+
+	read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+	return read;
+}
+
+static ssize_t rwnx_dbgfs_prim20_write(struct file *file,
+									   const char __user *user_buf,
+									   size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[32];
+	int val;
+	size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+
+	if (sscanf(buf, "%d", &val) > 0)
+		priv->phy.sec_chan.prim20_freq = val;
+
+	return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(prim20);
+
+static ssize_t rwnx_dbgfs_center1_read(struct file *file,
+									   char __user *user_buf,
+									   size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[32];
+	int ret;
+	ssize_t read;
+
+	ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+					"CENTER1=%dMHz\n", priv->phy.sec_chan.center_freq1);
+
+	read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+	return read;
+}
+
+static ssize_t rwnx_dbgfs_center1_write(struct file *file,
+										const char __user *user_buf,
+										size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[32];
+	int val;
+	size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+
+	if (sscanf(buf, "%d", &val) > 0)
+		priv->phy.sec_chan.center_freq1 = val;
+
+	return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(center1);
+
+static ssize_t rwnx_dbgfs_center2_read(struct file *file,
+									   char __user *user_buf,
+									   size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[32];
+	int ret;
+	ssize_t read;
+
+	ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+					"CENTER2=%dMHz\n", priv->phy.sec_chan.center_freq2);
+
+	read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+	return read;
+}
+
+static ssize_t rwnx_dbgfs_center2_write(struct file *file,
+										const char __user *user_buf,
+										size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+	char buf[32];
+	int val;
+	size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+
+	if (sscanf(buf, "%d", &val) > 0)
+		priv->phy.sec_chan.center_freq2 = val;
+
+	return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(center2);
+
+
+static ssize_t rwnx_dbgfs_set_read(struct file *file,
+								   char __user *user_buf,
+								   size_t count, loff_t *ppos)
+{
+	return 0;
+}
+
+static ssize_t rwnx_dbgfs_set_write(struct file *file,
+									const char __user *user_buf,
+									size_t count, loff_t *ppos)
+{
+	struct rwnx_hw *priv = file->private_data;
+
+	rwnx_send_set_channel(priv, 1, NULL);
+	rwnx_radar_detection_enable(&priv->radar, RWNX_RADAR_DETECT_ENABLE,
+								RWNX_RADAR_FCU);
+
+	return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(set);
+#endif /* CONFIG_RWNX_RADAR */
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+#define LINE_MAX_SZ 150
+
+struct st {
+	char line[LINE_MAX_SZ + 1];
+	unsigned int r_idx;
+};
+
+static int compare_idx(const void *st1, const void *st2)
+{
+	int index1 = ((struct st *)st1)->r_idx;
+	int index2 = ((struct st *)st2)->r_idx;
+
+	if (index1 > index2)
+		return 1;
+	if (index1 < index2)
+		return -1;
+
+	return 0;
+}
+
+static const int ru_size[] = {
+	26,
+	52,
+	106,
+	242,
+	484,
+	996
+};
+
+static int print_rate(char *buf, int size, int format, int nss, int mcs, int bw,
+					  int sgi, int pre, int *r_idx)
+{
+	int res = 0;
+	int bitrates_cck[4] = { 10, 20, 55, 110 };
+	int bitrates_ofdm[8] = { 6, 9, 12, 18, 24, 36, 48, 54};
+	char he_gi[3][4] = {"0.8", "1.6", "3.2"};
+
+	if (format < FORMATMOD_HT_MF) {
+		if (mcs < 4) {
+			if (r_idx) {
+				*r_idx = (mcs * 2) + pre;
+				res = scnprintf(buf, size - res, "%3d ", *r_idx);
+			}
+			res += scnprintf(&buf[res], size - res, "L-CCK/%cP      %2u.%1uM    ",
+							 pre > 0 ? 'L' : 'S',
+							 bitrates_cck[mcs] / 10,
+							 bitrates_cck[mcs] % 10);
+		} else {
+			mcs -= 4;
+			if (r_idx) {
+				*r_idx = N_CCK + mcs;
+				res = scnprintf(buf, size - res, "%3d ", *r_idx);
+			}
+			res += scnprintf(&buf[res], size - res, "L-OFDM        %2u.0M    ",
+							 bitrates_ofdm[mcs]);
+		}
+	} else if (format < FORMATMOD_VHT) {
+		if (r_idx) {
+			*r_idx = N_CCK + N_OFDM + nss * 32 + mcs * 4 + bw * 2 + sgi;
+			res = scnprintf(buf, size - res, "%3d ", *r_idx);
+		}
+		mcs += nss * 8;
+		res += scnprintf(&buf[res], size - res, "HT%d/%cGI       MCS%-2d   ",
+						 20 * (1 << bw), sgi ? 'S' : 'L', mcs);
+	} else if (format == FORMATMOD_VHT) {
+		if (r_idx) {
+			*r_idx = N_CCK + N_OFDM + N_HT + nss * 80 + mcs * 8 + bw * 2 + sgi;
+			res = scnprintf(buf, size - res, "%3d ", *r_idx);
+		}
+		res += scnprintf(&buf[res], size - res, "VHT%d/%cGI%*cMCS%d/%1d  ",
+						 20 * (1 << bw), sgi ? 'S' : 'L', bw > 2 ? 5 : 6, ' ',
+						 mcs, nss + 1);
+	} else if (format == FORMATMOD_HE_SU) {
+		if (r_idx) {
+			*r_idx = N_CCK + N_OFDM + N_HT + N_VHT + nss * 144 + mcs * 12 + bw * 3 + sgi;
+			res = scnprintf(buf, size - res, "%3d ", *r_idx);
+		}
+		res += scnprintf(&buf[res], size - res, "HE%d/GI%s%*cMCS%d/%1d%*c",
+						 20 * (1 << bw), he_gi[sgi], bw > 2 ? 4 : 5, ' ',
+						 mcs, nss + 1, mcs > 9 ? 1 : 2, ' ');
+	} else {
+		if (r_idx) {
+			*r_idx = N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU + nss * 216 + mcs * 18 + bw * 3 + sgi;
+			res = scnprintf(buf, size - res, "%3d ", *r_idx);
+		}
+		res += scnprintf(&buf[res], size - res, "HEMU-%d/GI%s%*cMCS%d/%1d%*c",
+						 ru_size[bw], he_gi[sgi], bw > 1 ? 1 : 2, ' ',
+						 mcs, nss + 1, mcs > 9 ? 1 : 2, ' ');
+
+	}
+
+	return res;
+}
+
+static int print_rate_from_cfg(char *buf, int size, u32 rate_config, int *r_idx, int ru_size)
+{
+	union rwnx_rate_ctrl_info *r_cfg = (union rwnx_rate_ctrl_info *)&rate_config;
+	union rwnx_mcs_index *mcs_index = (union rwnx_mcs_index *)&rate_config;
+	unsigned int ft, pre, gi, bw, nss, mcs, len;
+
+	ft = r_cfg->formatModTx;
+	pre = r_cfg->giAndPreTypeTx >> 1;
+	gi = r_cfg->giAndPreTypeTx;
+	bw = r_cfg->bwTx;
+	if (ft == FORMATMOD_HE_MU) {
+		mcs = mcs_index->he.mcs;
+		nss = mcs_index->he.nss;
+		bw = ru_size;
+	} else if (ft == FORMATMOD_HE_SU) {
+		mcs = mcs_index->he.mcs;
+		nss = mcs_index->he.nss;
+	} else if (ft == FORMATMOD_VHT) {
+		mcs = mcs_index->vht.mcs;
+		nss = mcs_index->vht.nss;
+	} else if (ft >= FORMATMOD_HT_MF) {
+		mcs = mcs_index->ht.mcs;
+		nss = mcs_index->ht.nss;
+	} else {
+		mcs = mcs_index->legacy;
+		nss = 0;
+	}
+
+	len = print_rate(buf, size, ft, nss, mcs, bw, gi, pre, r_idx);
+	return len;
+}
+
+static void idx_to_rate_cfg(int idx, union rwnx_rate_ctrl_info *r_cfg, int *ru_size)
+{
+	r_cfg->value = 0;
+	if (idx < N_CCK) {
+		r_cfg->formatModTx = FORMATMOD_NON_HT;
+		r_cfg->giAndPreTypeTx = (idx & 1) << 1;
+		r_cfg->mcsIndexTx = idx / 2;
+	} else if (idx < (N_CCK + N_OFDM)) {
+		r_cfg->formatModTx = FORMATMOD_NON_HT;
+		r_cfg->mcsIndexTx =  idx - N_CCK + 4;
+	} else if (idx < (N_CCK + N_OFDM + N_HT)) {
+		union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+		idx -= (N_CCK + N_OFDM);
+		r_cfg->formatModTx = FORMATMOD_HT_MF;
+		r->ht.nss = idx / (8*2*2);
+		r->ht.mcs = (idx % (8*2*2)) / (2*2);
+		r_cfg->bwTx = ((idx % (8*2*2)) % (2*2)) / 2;
+		r_cfg->giAndPreTypeTx = idx & 1;
+	} else if (idx < (N_CCK + N_OFDM + N_HT + N_VHT)) {
+		union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+		idx -= (N_CCK + N_OFDM + N_HT);
+		r_cfg->formatModTx = FORMATMOD_VHT;
+		r->vht.nss = idx / (10*4*2);
+		r->vht.mcs = (idx % (10*4*2)) / (4*2);
+		r_cfg->bwTx = ((idx % (10*4*2)) % (4*2)) / 2;
+		r_cfg->giAndPreTypeTx = idx & 1;
+	} else if (idx < (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU)) {
+		union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+		idx -= (N_CCK + N_OFDM + N_HT + N_VHT);
+		r_cfg->formatModTx = FORMATMOD_HE_SU;
+		r->vht.nss = idx / (12*4*3);
+		r->vht.mcs = (idx % (12*4*3)) / (4*3);
+		r_cfg->bwTx = ((idx % (12*4*3)) % (4*3)) / 3;
+		r_cfg->giAndPreTypeTx = idx % 3;
+	} else {
+		union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+		BUG_ON(ru_size == NULL);
+
+		idx -= (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU);
+		r_cfg->formatModTx = FORMATMOD_HE_MU;
+		r->vht.nss = idx / (12*6*3);
+		r->vht.mcs = (idx % (12*6*3)) / (6*3);
+		*ru_size = ((idx % (12*6*3)) % (6*3)) / 3;
+		r_cfg->giAndPreTypeTx = idx % 3;
+		r_cfg->bwTx = 0;
+	}
+}
+
+
+static void idx_to_rate_cfg1(unsigned int formatmod,
+	unsigned int mcs,unsigned int nss,
+	unsigned int bwTx,unsigned int gi,
+	 union rwnx_rate_ctrl_info *r_cfg, int *ru_size)
+{
+    r_cfg->value = 0;
+
+    switch(formatmod){
+		case FORMATMOD_NON_HT:
+		{
+			r_cfg->formatModTx = formatmod;
+			r_cfg->giAndPreTypeTx = 1;
+			r_cfg->mcsIndexTx = mcs;
+            break;
+		}
+		case FORMATMOD_NON_HT_DUP_OFDM:
+		{
+			r_cfg->formatModTx = formatmod;
+			r_cfg->giAndPreTypeTx = gi;
+			r_cfg->mcsIndexTx = mcs;
+            break;
+		}
+        case FORMATMOD_HT_MF:
+		{
+			union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+			r_cfg->formatModTx = formatmod;
+            r->ht.nss = nss;
+            r->ht.mcs = mcs;
+            r_cfg->bwTx = bwTx;
+            r_cfg->giAndPreTypeTx = gi;
+            break;
+        }
+        case FORMATMOD_VHT:
+        case FORMATMOD_HE_SU:
+        {
+			union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+			r_cfg->formatModTx = formatmod;
+            r->vht.nss = nss;
+            r->vht.mcs = mcs;
+            r_cfg->bwTx = bwTx;
+            r_cfg->giAndPreTypeTx = gi;
+            break;
+        }
+        case FORMATMOD_HE_MU:
+        {
+			union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+			r_cfg->formatModTx = formatmod;
+            r->he.nss = nss;
+            r->he.mcs = mcs;
+            r_cfg->bwTx = 0;
+            r_cfg->giAndPreTypeTx = gi;
+            break;
+        }
+        default:
+            printk("Don't have the formatmod");
+    }
+}
+
+static ssize_t rwnx_dbgfs_rc_stats_read(struct file *file,
+										char __user *user_buf,
+										size_t count, loff_t *ppos)
+{
+	struct rwnx_sta *sta = NULL;
+	struct rwnx_hw *priv = file->private_data;
+	char *buf;
+	int bufsz, len = 0;
+	ssize_t read;
+	int i = 0;
+	int error = 0;
+	struct me_rc_stats_cfm me_rc_stats_cfm;
+	unsigned int no_samples;
+	struct st *st;
+	u8 mac[6];
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* everything should fit in one call */
+	if (*ppos)
+		return 0;
+
+	/* Get the station index from MAC address */
+	sscanf(file->f_path.dentry->d_parent->d_iname, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+			&mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
+	if (mac == NULL)
+		return 0;
+	sta = rwnx_get_sta(priv, mac);
+	if (sta == NULL)
+		return 0;
+
+	/* Forward the information to the LMAC */
+	error = rwnx_send_me_rc_stats(priv, sta->sta_idx, &me_rc_stats_cfm);
+	if (error)
+		return error;
+
+	no_samples = me_rc_stats_cfm.no_samples;
+	if (no_samples == 0)
+		return 0;
+
+	bufsz = no_samples * LINE_MAX_SZ + 500;
+
+	buf = kmalloc(bufsz + 1, GFP_ATOMIC);
+	if (buf == NULL)
+		return 0;
+
+	st = kmalloc(sizeof(struct st) * no_samples, GFP_ATOMIC);
+	if (st == NULL) {
+		kfree(buf);
+		return 0;
+	}
+
+	for (i = 0; i < no_samples; i++) {
+		unsigned int tp, eprob;
+		len = print_rate_from_cfg(st[i].line, LINE_MAX_SZ,
+								  me_rc_stats_cfm.rate_stats[i].rate_config,
+								  &st[i].r_idx, 0);
+
+		if (me_rc_stats_cfm.sw_retry_step != 0) {
+			len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len,  "%c",
+					me_rc_stats_cfm.retry_step_idx[me_rc_stats_cfm.sw_retry_step] == i ? '*' : ' ');
+		} else {
+			len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, " ");
+		}
+		len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
+				me_rc_stats_cfm.retry_step_idx[0] == i ? 'T' : ' ');
+		len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
+				me_rc_stats_cfm.retry_step_idx[1] == i ? 't' : ' ');
+		len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c ",
+				me_rc_stats_cfm.retry_step_idx[2] == i ? 'P' : ' ');
+
+		tp = me_rc_stats_cfm.tp[i] / 10;
+		len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, " %4u.%1u",
+						 tp / 10, tp % 10);
+
+		eprob = ((me_rc_stats_cfm.rate_stats[i].probability * 1000) >> 16) + 1;
+		len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len,
+						 "  %4u.%1u %5u(%6u)  %6u",
+						 eprob / 10, eprob % 10,
+						 me_rc_stats_cfm.rate_stats[i].success,
+						 me_rc_stats_cfm.rate_stats[i].attempts,
+						 me_rc_stats_cfm.rate_stats[i].sample_skipped);
+	}
+	len = scnprintf(buf, bufsz,
+					 "\nTX rate info for %02X:%02X:%02X:%02X:%02X:%02X:\n",
+					 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+	len += scnprintf(&buf[len], bufsz - len,
+			" #  type           rate             tpt   eprob    ok(   tot)   skipped\n");
+
+	// add sorted statistics to the buffer
+	sort(st, no_samples, sizeof(st[0]), compare_idx, NULL);
+	for (i = 0; i < no_samples; i++) {
+		len += scnprintf(&buf[len], bufsz - len, "%s\n", st[i].line);
+	}
+
+	// display HE TB statistics if any
+	if (me_rc_stats_cfm.rate_stats[RC_HE_STATS_IDX].rate_config != 0) {
+		unsigned int tp, eprob;
+		struct rc_rate_stats *rate_stats = &me_rc_stats_cfm.rate_stats[RC_HE_STATS_IDX];
+		int ru_index = rate_stats->ru_and_length & 0x07;
+		int ul_length = rate_stats->ru_and_length >> 3;
+
+		len += scnprintf(&buf[len], bufsz - len,
+						 "\nHE TB rate info:\n");
+
+		len += scnprintf(&buf[len], bufsz - len,
+				"    type           rate             tpt   eprob    ok(   tot)   ul_length\n    ");
+		len += print_rate_from_cfg(&buf[len], bufsz - len, rate_stats->rate_config,
+								   NULL, ru_index);
+
+		tp = me_rc_stats_cfm.tp[RC_HE_STATS_IDX] / 10;
+		len += scnprintf(&buf[len], bufsz - len, "      %4u.%1u",
+						 tp / 10, tp % 10);
+
+		eprob = ((rate_stats->probability * 1000) >> 16) + 1;
+		len += scnprintf(&buf[len], bufsz - len,
+						 "  %4u.%1u %5u(%6u)  %6u\n",
+						 eprob / 10, eprob % 10,
+						 rate_stats->success,
+						 rate_stats->attempts,
+						 ul_length);
+	}
+
+	len += scnprintf(&buf[len], bufsz - len, "\n MPDUs AMPDUs AvLen trialP");
+	len += scnprintf(&buf[len], bufsz - len, "\n%6u %6u %3d.%1d %6u\n",
+					 me_rc_stats_cfm.ampdu_len,
+					 me_rc_stats_cfm.ampdu_packets,
+					 me_rc_stats_cfm.avg_ampdu_len >> 16,
+					 ((me_rc_stats_cfm.avg_ampdu_len * 10) >> 16) % 10,
+					 me_rc_stats_cfm.sample_wait);
+
+	read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+	kfree(buf);
+	kfree(st);
+
+	return read;
+}
+
+DEBUGFS_READ_FILE_OPS(rc_stats);
+
+static ssize_t rwnx_dbgfs_rc_fixed_rate_idx_write(struct file *file,
+												  const char __user *user_buf,
+												  size_t count, loff_t *ppos)
+{
+	struct rwnx_sta *sta = NULL;
+	struct rwnx_hw *priv = file->private_data;
+	u8 mac[6];
+	char buf[20];
+	//int fixed_rate_idx = -1;
+	union rwnx_rate_ctrl_info rate_config;
+	int error = 0;
+	size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Get the station index from MAC address */
+	sscanf(file->f_path.dentry->d_parent->d_iname, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+			&mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
+	if (mac == NULL)
+		return 0;
+	sta = rwnx_get_sta(priv, mac);
+	if (sta == NULL)
+		return 0;
+
+	/* Get the content of the file */
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+	buf[len] = '\0';
+#if 0
+	sscanf(buf, "%i\n", &fixed_rate_idx);
+
+	/* Convert rate index into rate configuration */
+	if ((fixed_rate_idx < 0) || (fixed_rate_idx >= (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU))) {
+		// disable fixed rate
+		rate_config.value = (u32)-1;
+	} else {
+		idx_to_rate_cfg(fixed_rate_idx, &rate_config, NULL);
+	}
+#else
+
+
+	scanf(buf, "%u %u %u %u %u",&formatmod, &mcs, &nss, &bwTx, &gi);
+        printk("%u %u %u %u %u\n",formatmod, mcs, nss, bwTx, gi);
+
+    if((formatmod > 6) || (mcs > 11) || (nss > 8) || (bwTx > 6) || (gi > 3)){
+        printk("error parameter");
+        // disable fixed rate
+        rate_config.value = (u32)-1;
+    }
+    else
+    {
+        //idx_to_rate_cfg(fixed_rate_idx, &rate_config, NULL);
+        idx_to_rate_cfg1(formatmod, mcs, nss, bwTx, gi, &rate_config, NULL);
+    }
+#endif
+
+        printk("formatModTx=%u mcsIndexTx=%u bwTx=%u giAndPreTypeTx=%u\n",r_cfg->formatModTx,r_cfg->mcsIndexTx,r_cfg->bwTx,r_cfg->giAndPreTypeTx);
+// Forward the request to the LMAC
+	error = rwnx_send_me_rc_set_rate(priv, sta->sta_idx, (u16)rate_config.value);
+	if (error != 0) {
+		return error;
+	}
+
+	priv->debugfs.rc_config[sta->sta_idx] = (int)rate_config.value;
+	return len;
+}
+
+DEBUGFS_WRITE_FILE_OPS(rc_fixed_rate_idx);
+
+static ssize_t rwnx_dbgfs_last_rx_read(struct file *file,
+									   char __user *user_buf,
+									   size_t count, loff_t *ppos)
+{
+	struct rwnx_sta *sta = NULL;
+	struct rwnx_hw *priv = file->private_data;
+	struct rwnx_rx_rate_stats *rate_stats;
+	char *buf;
+	int bufsz, i, len = 0;
+	ssize_t read;
+	unsigned int fmt, pre, bw, nss, mcs, gi;
+	u8 mac[6];
+	struct rx_vector_1 *last_rx;
+	char hist[] = "##################################################";
+	int hist_len = sizeof(hist) - 1;
+	u8 nrx;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* everything should fit in one call */
+	if (*ppos)
+		return 0;
+
+	/* Get the station index from MAC address */
+	sscanf(file->f_path.dentry->d_parent->d_iname, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+			&mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
+	if (mac == NULL)
+		return 0;
+	sta = rwnx_get_sta(priv, mac);
+	if (sta == NULL)
+		return 0;
+
+	rate_stats = &sta->stats.rx_rate;
+	bufsz = (rate_stats->rate_cnt * (50 + hist_len) + 200);
+	buf = kmalloc(bufsz + 1, GFP_ATOMIC);
+	if (buf == NULL)
+		return 0;
+
+	// Get number of RX paths
+	nrx = (priv->version_cfm.version_phy_1 & MDM_NRX_MASK) >> MDM_NRX_LSB;
+
+	len += scnprintf(buf, bufsz,
+					 "\nRX rate info for %02X:%02X:%02X:%02X:%02X:%02X:\n",
+					 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+	// Display Statistics
+	for (i = 0; i < rate_stats->size; i++) {
+		if (rate_stats->table[i]) {
+			union rwnx_rate_ctrl_info rate_config;
+			int percent = (rate_stats->table[i] * 1000) / rate_stats->cpt;
+			int p;
+			int ru_size;
+
+			idx_to_rate_cfg(i, &rate_config, &ru_size);
+			len += print_rate_from_cfg(&buf[len], bufsz - len,
+									   rate_config.value, NULL, ru_size);
+			p = (percent * hist_len) / 1000;
+			len += scnprintf(&buf[len], bufsz - len, ": %6d(%3d.%1d%%)%.*s\n",
+							 rate_stats->table[i],
+							 percent / 10, percent % 10, p, hist);
+		}
+	}
+
+	// Display detailed info of the last received rate
+	last_rx = &sta->stats.last_rx.rx_vect1;
+
+	len += scnprintf(&buf[len], bufsz - len, "\nLast received rate\n"
+					 "  type         rate    LDPC STBC BEAMFM DCM DOPPLER %s\n",
+					 (nrx > 1) ? "rssi1(dBm) rssi2(dBm)" : "rssi(dBm)");
+
+	fmt = last_rx->format_mod;
+	bw = last_rx->ch_bw;
+	pre = last_rx->pre_type;
+	if (fmt >= FORMATMOD_HE_SU) {
+		mcs = last_rx->he.mcs;
+		nss = last_rx->he.nss;
+		gi = last_rx->he.gi_type;
+		if (fmt == FORMATMOD_HE_MU)
+			bw = last_rx->he.ru_size;
+	} else if (fmt == FORMATMOD_VHT) {
+		mcs = last_rx->vht.mcs;
+		nss = last_rx->vht.nss;
+		gi = last_rx->vht.short_gi;
+	} else if (fmt >= FORMATMOD_HT_MF) {
+		mcs = last_rx->ht.mcs % 8;
+		nss = last_rx->ht.mcs / 8;;
+		gi = last_rx->ht.short_gi;
+	} else {
+		BUG_ON((mcs = legrates_lut[last_rx->leg_rate]) == -1);
+		nss = 0;
+		gi = 0;
+	}
+
+	len += print_rate(&buf[len], bufsz - len, fmt, nss, mcs, bw, gi, pre, NULL);
+
+	/* flags for HT/VHT/HE */
+	if (fmt >= FORMATMOD_HE_SU) {
+		len += scnprintf(&buf[len], bufsz - len, "  %c    %c     %c    %c     %c",
+						 last_rx->he.fec ? 'L' : ' ',
+						 last_rx->he.stbc ? 'S' : ' ',
+						 last_rx->he.beamformed ? 'B' : ' ',
+						 last_rx->he.dcm ? 'D' : ' ',
+						 last_rx->he.doppler ? 'D' : ' ');
+	} else if (fmt == FORMATMOD_VHT) {
+		len += scnprintf(&buf[len], bufsz - len, "  %c    %c     %c           ",
+						 last_rx->vht.fec ? 'L' : ' ',
+						 last_rx->vht.stbc ? 'S' : ' ',
+						 last_rx->vht.beamformed ? 'B' : ' ');
+	} else if (fmt >= FORMATMOD_HT_MF) {
+		len += scnprintf(&buf[len], bufsz - len, "  %c    %c                  ",
+						 last_rx->ht.fec ? 'L' : ' ',
+						 last_rx->ht.stbc ? 'S' : ' ');
+	} else {
+		len += scnprintf(&buf[len], bufsz - len, "                         ");
+	}
+	if (nrx > 1) {
+		len += scnprintf(&buf[len], bufsz - len, "       %-4d       %d\n",
+						 last_rx->rssi1, last_rx->rssi1);
+	} else {
+		len += scnprintf(&buf[len], bufsz - len, "      %d\n", last_rx->rssi1);
+	}
+
+	read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+	kfree(buf);
+	return read;
+}
+
+static ssize_t rwnx_dbgfs_last_rx_write(struct file *file,
+										const char __user *user_buf,
+										size_t count, loff_t *ppos)
+{
+	struct rwnx_sta *sta = NULL;
+	struct rwnx_hw *priv = file->private_data;
+	u8 mac[6];
+
+	/* Get the station index from MAC address */
+	sscanf(file->f_path.dentry->d_parent->d_iname, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+		   &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
+	if (mac == NULL)
+		return 0;
+	sta = rwnx_get_sta(priv, mac);
+	if (sta == NULL)
+		return 0;
+
+	/* Prevent from interrupt preemption as these statistics are updated under
+	 * interrupt */
+	spin_lock_bh(&priv->tx_lock);
+	memset(sta->stats.rx_rate.table, 0,
+		   sta->stats.rx_rate.size * sizeof(sta->stats.rx_rate.table[0]));
+	sta->stats.rx_rate.cpt = 0;
+	sta->stats.rx_rate.rate_cnt = 0;
+	spin_unlock_bh(&priv->tx_lock);
+
+	return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(last_rx);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#ifdef CONFIG_RWNX_FULLMAC
+static void rwnx_rc_stat_work(struct work_struct *ws)
+{
+	struct rwnx_debugfs *rwnx_debugfs = container_of(ws, struct rwnx_debugfs,
+													 rc_stat_work);
+	struct rwnx_hw *rwnx_hw = container_of(rwnx_debugfs, struct rwnx_hw,
+										   debugfs);
+	struct rwnx_sta *sta;
+	uint8_t ridx, sta_idx;
+
+	ridx = rwnx_debugfs->rc_read;
+	sta_idx = rwnx_debugfs->rc_sta[ridx];
+	if (sta_idx > (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) {
+		WARN(1, "Invalid sta index %d", sta_idx);
+		return;
+	}
+
+	rwnx_debugfs->rc_sta[ridx] = 0xFF;
+	ridx = (ridx + 1) % ARRAY_SIZE(rwnx_debugfs->rc_sta);
+	rwnx_debugfs->rc_read = ridx;
+	sta = &rwnx_hw->sta_table[sta_idx];
+	if (!sta) {
+		WARN(1, "Invalid sta %d", sta_idx);
+		return;
+	}
+
+	if (rwnx_debugfs->dir_sta[sta_idx] == NULL) {
+		/* register the sta */
+		struct dentry *dir_rc = rwnx_debugfs->dir_rc;
+		struct dentry *dir_sta;
+		struct dentry *file;
+		char sta_name[18];
+		struct rwnx_rx_rate_stats *rate_stats = &sta->stats.rx_rate;
+		int nb_rx_rate = N_CCK + N_OFDM;
+		struct rwnx_rc_config_save *rc_cfg, *next;
+
+		if (sta->sta_idx >= NX_REMOTE_STA_MAX) {
+			scnprintf(sta_name, sizeof(sta_name), "bc_mc");
+		} else {
+			scnprintf(sta_name, sizeof(sta_name), "%pM", sta->mac_addr);
+		}
+
+		dir_sta = debugfs_create_dir(sta_name, dir_rc);
+		if (!dir_sta)
+			goto error;
+
+		rwnx_debugfs->dir_sta[sta->sta_idx] = dir_sta;
+
+		file = debugfs_create_file("stats", S_IRUSR, dir_sta, rwnx_hw,
+								   &rwnx_dbgfs_rc_stats_ops);
+		if (IS_ERR_OR_NULL(file))
+			goto error_after_dir;
+
+		file = debugfs_create_file("fixed_rate_idx", S_IWUSR, dir_sta, rwnx_hw,
+								   &rwnx_dbgfs_rc_fixed_rate_idx_ops);
+		if (IS_ERR_OR_NULL(file))
+			goto error_after_dir;
+
+		file = debugfs_create_file("rx_rate", S_IRUSR | S_IWUSR, dir_sta, rwnx_hw,
+								   &rwnx_dbgfs_last_rx_ops);
+		if (IS_ERR_OR_NULL(file))
+			goto error_after_dir;
+
+		if (rwnx_hw->mod_params->ht_on)
+			nb_rx_rate += N_HT;
+
+		if (rwnx_hw->mod_params->vht_on)
+			nb_rx_rate += N_VHT;
+
+		if (rwnx_hw->mod_params->he_on)
+			nb_rx_rate += N_HE_SU + N_HE_MU;
+
+		rate_stats->table = kzalloc(nb_rx_rate * sizeof(rate_stats->table[0]),
+									GFP_KERNEL);
+		if (!rate_stats->table)
+			goto error_after_dir;
+
+		rate_stats->size = nb_rx_rate;
+		rate_stats->cpt = 0;
+		rate_stats->rate_cnt = 0;
+
+		/* By default enable rate contoller */
+		rwnx_debugfs->rc_config[sta_idx] = -1;
+
+		/* Unless we already fix the rate for this station */
+		list_for_each_entry_safe(rc_cfg, next, &rwnx_debugfs->rc_config_save, list) {
+			if (jiffies_to_msecs(jiffies - rc_cfg->timestamp) > RC_CONFIG_DUR) {
+				list_del(&rc_cfg->list);
+				kfree(rc_cfg);
+			} else if (!memcmp(rc_cfg->mac_addr, sta->mac_addr, ETH_ALEN)) {
+				rwnx_debugfs->rc_config[sta_idx] = rc_cfg->rate;
+				list_del(&rc_cfg->list);
+				kfree(rc_cfg);
+				break;
+			}
+		}
+
+		if ((rwnx_debugfs->rc_config[sta_idx] >= 0) &&
+			rwnx_send_me_rc_set_rate(rwnx_hw, sta_idx,
+									 (u16)rwnx_debugfs->rc_config[sta_idx]))
+			rwnx_debugfs->rc_config[sta_idx] = -1;
+
+	} else {
+		/* unregister the sta */
+		if (sta->stats.rx_rate.table) {
+			kfree(sta->stats.rx_rate.table);
+			sta->stats.rx_rate.table = NULL;
+		}
+		sta->stats.rx_rate.size = 0;
+		sta->stats.rx_rate.cpt  = 0;
+		sta->stats.rx_rate.rate_cnt = 0;
+
+		/* If fix rate was set for this station, save the configuration in case
+		   we reconnect to this station within RC_CONFIG_DUR msec */
+		if (rwnx_debugfs->rc_config[sta_idx] >= 0) {
+			struct rwnx_rc_config_save *rc_cfg;
+			rc_cfg = kmalloc(sizeof(*rc_cfg), GFP_KERNEL);
+			if (rc_cfg) {
+				rc_cfg->rate = rwnx_debugfs->rc_config[sta_idx];
+				rc_cfg->timestamp = jiffies;
+				memcpy(rc_cfg->mac_addr, sta->mac_addr, ETH_ALEN);
+				list_add_tail(&rc_cfg->list, &rwnx_debugfs->rc_config_save);
+			}
+		}
+
+		debugfs_remove_recursive(rwnx_debugfs->dir_sta[sta_idx]);
+		rwnx_debugfs->dir_sta[sta->sta_idx] = NULL;
+	}
+
+	return;
+
+error_after_dir:
+	debugfs_remove_recursive(rwnx_debugfs->dir_sta[sta_idx]);
+	rwnx_debugfs->dir_sta[sta->sta_idx] = NULL;
+error:
+	dev_err(rwnx_hw->dev,
+			"Error while (un)registering debug entry for sta %d\n", sta_idx);
+}
+
+void _rwnx_dbgfs_rc_stat_write(struct rwnx_debugfs *rwnx_debugfs, uint8_t sta_idx)
+{
+	uint8_t widx = rwnx_debugfs->rc_write;
+	if (rwnx_debugfs->rc_sta[widx] != 0XFF) {
+		WARN(1, "Overlap in debugfs rc_sta table\n");
+	}
+
+	if (rwnx_debugfs->unregistering)
+		return;
+
+	rwnx_debugfs->rc_sta[widx] = sta_idx;
+	widx = (widx + 1) % ARRAY_SIZE(rwnx_debugfs->rc_sta);
+	rwnx_debugfs->rc_write = widx;
+
+	schedule_work(&rwnx_debugfs->rc_stat_work);
+}
+
+void rwnx_dbgfs_register_rc_stat(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta)
+{
+	_rwnx_dbgfs_rc_stat_write(&rwnx_hw->debugfs, sta->sta_idx);
+}
+
+void rwnx_dbgfs_unregister_rc_stat(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta)
+{
+	_rwnx_dbgfs_rc_stat_write(&rwnx_hw->debugfs, sta->sta_idx);
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+int rwnx_dbgfs_register(struct rwnx_hw *rwnx_hw, const char *name)
+{
+#ifdef CONFIG_RWNX_FULLMAC
+	struct dentry *phyd = rwnx_hw->wiphy->debugfsdir;
+	struct dentry *dir_rc;
+#endif /* CONFIG_RWNX_FULLMAC */
+	struct rwnx_debugfs *rwnx_debugfs = &rwnx_hw->debugfs;
+	struct dentry *dir_drv, *dir_diags;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	dir_drv = debugfs_create_dir(name, phyd);
+	if (!dir_drv)
+		return -ENOMEM;
+
+	rwnx_debugfs->dir = dir_drv;
+	rwnx_debugfs->unregistering = false;
+
+	dir_diags = debugfs_create_dir("diags", dir_drv);
+	if (!dir_diags)
+		goto err;
+
+#ifdef CONFIG_RWNX_FULLMAC
+	dir_rc = debugfs_create_dir("rc", dir_drv);
+	if (!dir_rc)
+		goto err;
+	rwnx_debugfs->dir_rc = dir_rc;
+	INIT_WORK(&rwnx_debugfs->rc_stat_work, rwnx_rc_stat_work);
+	INIT_LIST_HEAD(&rwnx_debugfs->rc_config_save);
+	rwnx_debugfs->rc_write = rwnx_debugfs->rc_read = 0;
+	memset(rwnx_debugfs->rc_sta, 0xFF, sizeof(rwnx_debugfs->rc_sta));
+#endif
+
+	DEBUGFS_ADD_U32(tcp_pacing_shift, dir_drv, &rwnx_hw->tcp_pacing_shift,
+			S_IWUSR | S_IRUSR);
+	DEBUGFS_ADD_FILE(stats, dir_drv, S_IWUSR | S_IRUSR);
+	DEBUGFS_ADD_FILE(sys_stats, dir_drv,  S_IRUSR);
+	DEBUGFS_ADD_FILE(txq, dir_drv, S_IRUSR);
+	DEBUGFS_ADD_FILE(acsinfo, dir_drv, S_IRUSR);
+#ifdef CONFIG_RWNX_MUMIMO_TX
+	DEBUGFS_ADD_FILE(mu_group, dir_drv, S_IRUSR);
+#endif
+
+#ifdef CONFIG_RWNX_P2P_DEBUGFS
+	{
+		/* Create a p2p directory */
+		struct dentry *dir_p2p;
+		dir_p2p = debugfs_create_dir("p2p", dir_drv);
+		if (!dir_p2p)
+			goto err;
+
+		/* Add file allowing to control Opportunistic PS */
+		DEBUGFS_ADD_FILE(oppps, dir_p2p, S_IRUSR);
+		/* Add file allowing to control Notice of Absence */
+		DEBUGFS_ADD_FILE(noa, dir_p2p, S_IRUSR);
+	}
+#endif /* CONFIG_RWNX_P2P_DEBUGFS */
+
+	if (rwnx_hw->fwlog_en) {
+		rwnx_fw_log_init(&rwnx_hw->debugfs.fw_log);
+		DEBUGFS_ADD_FILE(fw_log, dir_drv, S_IWUSR | S_IRUSR);
+	}
+#ifdef CONFIG_RWNX_RADAR
+	{
+		struct dentry *dir_radar, *dir_sec;
+		dir_radar = debugfs_create_dir("radar", dir_drv);
+		if (!dir_radar)
+			goto err;
+
+		DEBUGFS_ADD_FILE(pulses_prim, dir_radar, S_IRUSR);
+		DEBUGFS_ADD_FILE(detected,    dir_radar, S_IRUSR);
+		DEBUGFS_ADD_FILE(enable,      dir_radar, S_IRUSR);
+
+		if (rwnx_hw->phy.cnt == 2) {
+			DEBUGFS_ADD_FILE(pulses_sec, dir_radar, S_IRUSR);
+
+			dir_sec = debugfs_create_dir("sec", dir_radar);
+			if (!dir_sec)
+				goto err;
+
+			DEBUGFS_ADD_FILE(band,    dir_sec, S_IWUSR | S_IRUSR);
+			DEBUGFS_ADD_FILE(type,    dir_sec, S_IWUSR | S_IRUSR);
+			DEBUGFS_ADD_FILE(prim20,  dir_sec, S_IWUSR | S_IRUSR);
+			DEBUGFS_ADD_FILE(center1, dir_sec, S_IWUSR | S_IRUSR);
+			DEBUGFS_ADD_FILE(center2, dir_sec, S_IWUSR | S_IRUSR);
+			DEBUGFS_ADD_FILE(set,     dir_sec, S_IWUSR | S_IRUSR);
+		}
+	}
+#endif /* CONFIG_RWNX_RADAR */
+	return 0;
+
+err:
+	rwnx_dbgfs_unregister(rwnx_hw);
+	return -ENOMEM;
+}
+
+void rwnx_dbgfs_unregister(struct rwnx_hw *rwnx_hw)
+{
+	struct rwnx_debugfs *rwnx_debugfs = &rwnx_hw->debugfs;
+#ifdef CONFIG_RWNX_FULLMAC
+	struct rwnx_rc_config_save *cfg, *next;
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+	list_for_each_entry_safe(cfg, next, &rwnx_debugfs->rc_config_save, list) {
+		list_del(&cfg->list);
+		kfree(cfg);
+	}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+	if (rwnx_hw->fwlog_en)
+		rwnx_fw_log_deinit(&rwnx_hw->debugfs.fw_log);
+
+	if (!rwnx_hw->debugfs.dir)
+		return;
+
+	rwnx_debugfs->unregistering = true;
+#ifdef CONFIG_RWNX_FULLMAC
+	flush_work(&rwnx_debugfs->rc_stat_work);
+#endif
+	debugfs_remove_recursive(rwnx_hw->debugfs.dir);
+	rwnx_hw->debugfs.dir = NULL;
+}
+
+#endif /* CONFIG_DEBUG_FS_AIC */
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_debugfs.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_debugfs.h
new file mode 100755
index 0000000..7f5c7c9
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_debugfs.h
@@ -0,0 +1,202 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_debugfs.h
+ *
+ * @brief Miscellaneous utility function definitions
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+
+#ifndef _RWNX_DEBUGFS_H_
+#define _RWNX_DEBUGFS_H_
+#include <linux/version.h>
+
+#include <linux/workqueue.h>
+#include <linux/if_ether.h>
+#include "rwnx_fw_trace.h"
+
+struct rwnx_hw;
+struct rwnx_sta;
+
+/* some macros taken from iwlwifi */
+/* TODO: replace with generic read and fill read buffer in open to avoid double
+ * reads */
+#define DEBUGFS_ADD_FILE(name, parent, mode) do {               \
+	if (!debugfs_create_file(#name, mode, parent, rwnx_hw,      \
+				&rwnx_dbgfs_##name##_ops))                      \
+	goto err;                                                   \
+} while (0)
+
+#define DEBUGFS_ADD_BOOL(name, parent, ptr) do {                \
+	struct dentry *__tmp;                                       \
+	__tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR,       \
+			parent, ptr);                                       \
+	if (IS_ERR(__tmp) || !__tmp)                                \
+	goto err;                                                   \
+} while (0)
+
+#define DEBUGFS_ADD_X64(name, parent, ptr) do {                 \
+	struct dentry *__tmp;                                       \
+	__tmp = debugfs_create_x64(#name, S_IWUSR | S_IRUSR,        \
+			parent, ptr);                                       \
+	if (IS_ERR(__tmp) || !__tmp)                                \
+	goto err;                                                   \
+} while (0)
+
+#define DEBUGFS_ADD_U64(name, parent, ptr, mode) do {           \
+	struct dentry *__tmp;                                       \
+	__tmp = debugfs_create_u64(#name, mode,                     \
+			parent, ptr);                                       \
+	if (IS_ERR(__tmp) || !__tmp)                                \
+	goto err;                                                   \
+} while (0)
+
+#define DEBUGFS_ADD_X32(name, parent, ptr) do {                 \
+	struct dentry *__tmp;                                       \
+	__tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR,        \
+			parent, ptr);                                       \
+	if (IS_ERR(__tmp) || !__tmp)                                \
+	goto err;                                                   \
+} while (0)
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
+#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do {           \
+        debugfs_create_u32(#name, mode,                                 \
+            parent, ptr);                                       \
+} while (0)
+#else
+#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do {                   \
+                struct dentry *__tmp;                                                                           \
+                __tmp = debugfs_create_u32(#name, mode,                                         \
+                                parent, ptr);                                                                           \
+                if (IS_ERR(__tmp) || !__tmp)                                                            \
+                goto err;                                                                                                       \
+        } while (0)
+#endif
+
+
+
+/* file operation */
+#define DEBUGFS_READ_FUNC(name)                                         \
+	static ssize_t rwnx_dbgfs_##name##_read(struct file *file,          \
+											char __user *user_buf,      \
+											size_t count, loff_t *ppos);
+
+#define DEBUGFS_WRITE_FUNC(name)                                         \
+	static ssize_t rwnx_dbgfs_##name##_write(struct file *file,          \
+											 const char __user *user_buf,\
+											 size_t count, loff_t *ppos);
+
+#define DEBUGFS_OPEN_FUNC(name)                              \
+	static int rwnx_dbgfs_##name##_open(struct inode *inode, \
+										struct file *file);
+
+#define DEBUGFS_RELEASE_FUNC(name)                              \
+	static int rwnx_dbgfs_##name##_release(struct inode *inode, \
+										   struct file *file);
+
+#define DEBUGFS_READ_FILE_OPS(name)                             \
+	DEBUGFS_READ_FUNC(name);                                    \
+static const struct file_operations rwnx_dbgfs_##name##_ops = { \
+	.read   = rwnx_dbgfs_##name##_read,                         \
+	.open   = simple_open,                                      \
+	.llseek = generic_file_llseek,                              \
+};
+
+#define DEBUGFS_WRITE_FILE_OPS(name)                            \
+	DEBUGFS_WRITE_FUNC(name);                                   \
+static const struct file_operations rwnx_dbgfs_##name##_ops = { \
+	.write  = rwnx_dbgfs_##name##_write,                        \
+	.open   = simple_open,                                      \
+	.llseek = generic_file_llseek,                              \
+};
+
+#define DEBUGFS_READ_WRITE_FILE_OPS(name)                       \
+	DEBUGFS_READ_FUNC(name);                                    \
+	DEBUGFS_WRITE_FUNC(name);                                   \
+static const struct file_operations rwnx_dbgfs_##name##_ops = { \
+	.write  = rwnx_dbgfs_##name##_write,                        \
+	.read   = rwnx_dbgfs_##name##_read,                         \
+	.open   = simple_open,                                      \
+	.llseek = generic_file_llseek,                              \
+};
+
+#define DEBUGFS_READ_WRITE_OPEN_RELEASE_FILE_OPS(name)              \
+	DEBUGFS_READ_FUNC(name);                                        \
+	DEBUGFS_WRITE_FUNC(name);                                       \
+	DEBUGFS_OPEN_FUNC(name);                                        \
+	DEBUGFS_RELEASE_FUNC(name);                                     \
+static const struct file_operations rwnx_dbgfs_##name##_ops = {     \
+	.write   = rwnx_dbgfs_##name##_write,                           \
+	.read    = rwnx_dbgfs_##name##_read,                            \
+	.open    = rwnx_dbgfs_##name##_open,                            \
+	.release = rwnx_dbgfs_##name##_release,                         \
+	.llseek  = generic_file_llseek,                                 \
+};
+
+
+#ifdef CONFIG_RWNX_DEBUGFS
+
+struct rwnx_debugfs {
+	unsigned long long rateidx;
+	struct dentry *dir;
+	bool trace_prst;
+
+	char helper_cmd[64];
+	//struct work_struct helper_work;
+	bool helper_scheduled;
+	spinlock_t umh_lock;
+	bool unregistering;
+
+#ifndef CONFIG_RWNX_FHOST
+	struct rwnx_fw_log fw_log;
+#endif /* CONFIG_RWNX_FHOST */
+
+#ifdef CONFIG_RWNX_FULLMAC
+	struct work_struct rc_stat_work;
+	uint8_t rc_sta[NX_REMOTE_STA_MAX];
+	uint8_t rc_write;
+	uint8_t rc_read;
+	struct dentry *dir_rc;
+	struct dentry *dir_sta[NX_REMOTE_STA_MAX];
+	int rc_config[NX_REMOTE_STA_MAX];
+	struct list_head rc_config_save;
+#endif
+};
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+// Max duration in msecs to save rate config for a sta after disconnection
+#define RC_CONFIG_DUR 600000
+
+struct rwnx_rc_config_save {
+	struct list_head list;
+	unsigned long timestamp;
+	int rate;
+	u8 mac_addr[ETH_ALEN];
+};
+#endif
+
+int rwnx_dbgfs_register(struct rwnx_hw *rwnx_hw, const char *name);
+void rwnx_dbgfs_unregister(struct rwnx_hw *rwnx_hw);
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_dbgfs_register_rc_stat(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta);
+void rwnx_dbgfs_unregister_rc_stat(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta);
+#endif
+#else
+struct rwnx_debugfs {
+};
+static inline int rwnx_dbgfs_register(struct rwnx_hw *rwnx_hw, const char *name) { return 0; }
+static inline void rwnx_dbgfs_unregister(struct rwnx_hw *rwnx_hw) {}
+#ifdef CONFIG_RWNX_FULLMAC
+static inline void rwnx_dbgfs_register_rc_stat(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta)  {}
+static inline void rwnx_dbgfs_unregister_rc_stat(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta)  {}
+#endif
+#endif /* CONFIG_RWNX_DEBUGFS */
+
+
+#endif /* _RWNX_DEBUGFS_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_defs.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_defs.h
new file mode 100755
index 0000000..b580f0c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_defs.h
@@ -0,0 +1,767 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_defs.h
+ *
+ * @brief Main driver structure declarations for fullmac driver
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_DEFS_H_
+#define _RWNX_DEFS_H_
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/skbuff.h>
+#include <net/cfg80211.h>
+#include <linux/slab.h>
+
+#include "rwnx_mod_params.h"
+#include "rwnx_debugfs.h"
+#include "rwnx_tx.h"
+#include "rwnx_rx.h"
+#include "rwnx_radar.h"
+#include "rwnx_utils.h"
+#include "rwnx_mu_group.h"
+#include "rwnx_platform.h"
+#include "rwnx_cmds.h"
+#ifdef CONFIG_FILTER_TCP_ACK
+#include "aicwf_tcp_ack.h"
+#endif
+
+#ifdef AICWF_SDIO_SUPPORT
+#include "aicwf_sdio.h"
+#include "sdio_host.h"
+#endif
+
+#ifdef AICWF_USB_SUPPORT
+#include "usb_host.h"
+#endif
+
+#define WPI_HDR_LEN    18
+#define WPI_PN_LEN     16
+#define WPI_PN_OFST     2
+#define WPI_MIC_LEN    16
+#define WPI_KEY_LEN    32
+#define WPI_SUBKEY_LEN 16 // WPI key is actually two 16bytes key
+
+#define LEGACY_PS_ID   0
+#define UAPSD_ID       1
+
+#define PS_SP_INTERRUPTED  255
+#define MAC_ADDR_LEN 6
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+enum nl80211_ac {
+	NL80211_AC_VO,
+	NL80211_AC_VI,
+	NL80211_AC_BE,
+	NL80211_AC_BK,
+	NL80211_NUM_ACS
+};
+
+#define GENL_HDRLEN	NLMSG_ALIGN(sizeof(struct genlmsghdr))
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+struct ieee80211_vht_operation {
+	u8 vht_op_info_chwidth;
+	u8 vht_op_info_chan_center_freq_seg1_idx;
+	u8 vht_op_info_chan_center_freq_seg2_idx;
+	__le16 vht_basic_mcs_set;
+} __packed;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
+#define NL80211_IFTYPE_P2P_DEVICE 10
+
+
+#define IEEE80211_RADIOTAP_AMPDU_STATUS 20
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+#define IEEE80211_RADIOTAP_VHT					21
+#define IEEE80211_RADIOTAP_VHT_KNOWN_GI				0x0004
+#define IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH			0x0040
+
+#define IEEE80211_RADIOTAP_VHT_FLAG_STBC			0x01
+#define IEEE80211_RADIOTAP_VHT_FLAG_SGI				0x04
+
+#define	NL80211_FEATURE_CELL_BASE_REG_HINTS		 1 << 3
+#define	NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL	 1 << 4
+#define	NL80211_FEATURE_SAE				 1 << 5
+#define	NL80211_FEATURE_LOW_PRIORITY_SCAN		 1 << 6
+#define	NL80211_FEATURE_SCAN_FLUSH			 1 << 7
+#define	NL80211_FEATURE_AP_SCAN				 1 << 8
+#define	NL80211_FEATURE_VIF_TXPOWER			 1 << 9
+#define	NL80211_FEATURE_NEED_OBSS_SCAN			 1 << 10
+#define	NL80211_FEATURE_P2P_GO_CTWIN			 1 << 11
+#define	NL80211_FEATURE_P2P_GO_OPPPS			 1 << 12
+
+
+/* 802.11ac VHT Capabilities */
+#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895			0x00000000
+#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991			0x00000001
+#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454			0x00000002
+#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ		0x00000004
+#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ	0x00000008
+#define IEEE80211_VHT_CAP_RXLDPC				0x00000010
+#define IEEE80211_VHT_CAP_SHORT_GI_80				0x00000020
+#define IEEE80211_VHT_CAP_SHORT_GI_160				0x00000040
+#define IEEE80211_VHT_CAP_TXSTBC				0x00000080
+#define IEEE80211_VHT_CAP_RXSTBC_1				0x00000100
+#define IEEE80211_VHT_CAP_RXSTBC_2				0x00000200
+#define IEEE80211_VHT_CAP_RXSTBC_3				0x00000300
+#define IEEE80211_VHT_CAP_RXSTBC_4				0x00000400
+#define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE			0x00000800
+#define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE			0x00001000
+#define IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX		0x00006000
+#define IEEE80211_VHT_CAP_SOUNDING_DIMENTION_MAX		0x00030000
+#define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE			0x00080000
+#define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE			0x00100000
+#define IEEE80211_VHT_CAP_VHT_TXOP_PS				0x00200000
+#define IEEE80211_VHT_CAP_HTC_VHT				0x00400000
+#define IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT	23
+#define IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK	\
+		(7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT)
+#define IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB	0x08000000
+#define IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB	0x0c000000
+#define IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN			0x10000000
+#define IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN			0x20000000
+
+
+enum ieee80211_vht_mcs_support {
+	IEEE80211_VHT_MCS_SUPPORT_0_7	= 0,
+	IEEE80211_VHT_MCS_SUPPORT_0_8	= 1,
+	IEEE80211_VHT_MCS_SUPPORT_0_9	= 2,
+	IEEE80211_VHT_MCS_NOT_SUPPORTED	= 3,
+};
+
+enum nl80211_chan_width {
+	NL80211_CHAN_WIDTH_20_NOHT,
+	NL80211_CHAN_WIDTH_20,
+	NL80211_CHAN_WIDTH_40,
+	NL80211_CHAN_WIDTH_80,
+	NL80211_CHAN_WIDTH_80P80,
+	NL80211_CHAN_WIDTH_160,
+};
+
+struct cfg80211_chan_def {
+	struct ieee80211_channel *chan;
+	enum nl80211_chan_width width;
+	u32 center_freq1;
+	u32 center_freq2;
+};
+/*
+inline void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
+			     struct ieee80211_channel *chan,
+			     enum nl80211_channel_type chan_type)
+{
+	if (WARN_ON(!chan))
+		return;
+	chandef->chan = chan;
+	chandef->center_freq2 = 0;
+	switch (chan_type) {
+	case NL80211_CHAN_NO_HT:
+		chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+		chandef->center_freq1 = chan->center_freq;
+		break;
+	case NL80211_CHAN_HT20:
+		chandef->width = NL80211_CHAN_WIDTH_20;
+		chandef->center_freq1 = chan->center_freq;
+		break;
+	case NL80211_CHAN_HT40PLUS:
+		chandef->width = NL80211_CHAN_WIDTH_40;
+		chandef->center_freq1 = chan->center_freq + 10;
+		break;
+	case NL80211_CHAN_HT40MINUS:
+		chandef->width = NL80211_CHAN_WIDTH_40;
+		chandef->center_freq1 = chan->center_freq - 10;
+		break;
+	default:
+		WARN_ON(1);
+	}
+}*/
+//EXPORT_SYMBOL(cfg80211_chandef_create);
+
+enum nl80211_mesh_power_mode {
+	NL80211_MESH_POWER_UNKNOWN,
+	NL80211_MESH_POWER_ACTIVE,
+	NL80211_MESH_POWER_LIGHT_SLEEP,
+	NL80211_MESH_POWER_DEEP_SLEEP,
+	__NL80211_MESH_POWER_AFTER_LAST,
+	NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1
+};
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
+#define NL80211_MESHCONF_POWER_MODE 26
+
+/*
+ * TDLS capabililites to be enabled in the 5th byte of the
+ * @WLAN_EID_EXT_CAPABILITY information element
+ */
+#define WLAN_EXT_CAPA5_TDLS_ENABLED	BIT(5)
+#define WLAN_EXT_CAPA5_TDLS_PROHIBITED	BIT(6)
+
+#define WLAN_EXT_CAPA8_OPMODE_NOTIF	BIT(6)
+
+/* TDLS specific payload type in the LLC/SNAP header */
+#define WLAN_TDLS_SNAP_RFTYPE	0x2
+
+#endif
+
+
+
+/**
+ * struct rwnx_bcn - Information of the beacon in used (AP mode)
+ *
+ * @head: head portion of beacon (before TIM IE)
+ * @tail: tail portion of beacon (after TIM IE)
+ * @ies: extra IEs (not used ?)
+ * @head_len: length of head data
+ * @tail_len: length of tail data
+ * @ies_len: length of extra IEs data
+ * @tim_len: length of TIM IE
+ * @len: Total beacon len (head + tim + tail + extra)
+ * @dtim: dtim period
+ */
+struct rwnx_bcn {
+    u8 *head;
+    u8 *tail;
+    u8 *ies;
+    size_t head_len;
+    size_t tail_len;
+    size_t ies_len;
+    size_t tim_len;
+    size_t len;
+    u8 dtim;
+};
+
+/**
+ * struct rwnx_key - Key information
+ *
+ * @hw_idx: Idx of the key from hardware point of view
+ */
+struct rwnx_key {
+    u8 hw_idx;
+};
+
+/**
+ * Structure containing information about a Mesh Path
+ */
+struct rwnx_mesh_path {
+    struct list_head list;          /* For rwnx_vif.mesh_paths */
+    u8 path_idx;                    /* Path Index */
+    struct mac_addr tgt_mac_addr;   /* Target MAC Address */
+    struct rwnx_sta *p_nhop_sta;    /* Pointer to the Next Hop STA */
+};
+
+struct rwnx_mesh_proxy {
+    struct list_head list;          /* For rwnx_vif.mesh_proxy */
+    struct mac_addr ext_sta_addr;   /* Address of the External STA */
+    struct mac_addr proxy_addr;     /* Proxy MAC Address */
+    bool local;                     /* Indicate if interface is a proxy for the device */
+};
+
+/**
+ * struct rwnx_csa - Information for CSA (Channel Switch Announcement)
+ *
+ * @vif: Pointer to the vif doing the CSA
+ * @bcn: Beacon to use after CSA
+ * @elem: IPC buffer to send the new beacon to the fw
+ * @chandef: defines the channel to use after the switch
+ * @count: Current csa counter
+ * @status: Status of the CSA at fw level
+ * @ch_idx: Index of the new channel context
+ * @work: work scheduled at the end of CSA
+ */
+struct rwnx_csa {
+    struct rwnx_vif *vif;
+    struct rwnx_bcn bcn;
+    struct rwnx_ipc_elem_var elem;
+    struct cfg80211_chan_def chandef;
+    int count;
+    int status;
+    int ch_idx;
+    struct work_struct work;
+};
+
+struct apm_probe_sta {
+	u8 sta_mac_addr[6];
+	u8 vif_idx;
+	u64 probe_id;
+	struct work_struct apmprobestaWork;
+	struct workqueue_struct *apmprobesta_wq;
+};
+
+/// Possible States of the TDLS link.
+enum tdls_status_tag {
+        /// TDLS link is not active (no TDLS peer connected)
+        TDLS_LINK_IDLE,
+        /// TDLS Setup Request transmitted
+        TDLS_SETUP_REQ_TX,
+        /// TDLS Setup Response transmitted
+        TDLS_SETUP_RSP_TX,
+        /// TDLS link is active (TDLS peer connected)
+        TDLS_LINK_ACTIVE,
+        /// TDLS Max Number of states.
+        TDLS_STATE_MAX
+};
+
+/*
+ * Structure used to save information relative to the TDLS peer.
+ * This is also linked within the rwnx_hw vifs list.
+ *
+ */
+struct rwnx_tdls {
+    bool active;                /* Indicate if TDLS link is active */
+    bool initiator;             /* Indicate if TDLS peer is the TDLS initiator */
+    bool chsw_en;               /* Indicate if channel switch is enabled */
+    u8 last_tid;                /* TID of the latest MPDU transmitted over the
+                                   TDLS direct link to the TDLS STA */
+    u16 last_sn;                /* Sequence number of the latest MPDU transmitted
+                                   over the TDLS direct link to the TDLS STA */
+    bool ps_on;                 /* Indicate if the power save is enabled on the
+                                   TDLS STA */
+    bool chsw_allowed;          /* Indicate if TDLS channel switch is allowed */
+};
+
+
+/**
+ * enum rwnx_ap_flags - AP flags
+ *
+ * @RWNX_AP_ISOLATE Isolate clients (i.e. Don't brige packets transmitted by
+ *                                   one client for another one)
+ */
+enum rwnx_ap_flags {
+    RWNX_AP_ISOLATE = BIT(0),
+};
+
+/*
+ * Structure used to save information relative to the managed interfaces.
+ * This is also linked within the rwnx_hw vifs list.
+ *
+ */
+struct rwnx_vif {
+    u8 address[ETH_ALEN] __aligned(sizeof(u16));
+    struct list_head list;
+    struct rwnx_hw *rwnx_hw;
+    struct wireless_dev wdev;
+    struct net_device *ndev;
+    struct net_device_stats net_stats;
+    struct rwnx_key key[6];
+    u8 drv_vif_index;           /* Identifier of the VIF in driver */
+    u8 vif_index;               /* Identifier of the station in FW */
+    u8 ch_index;                /* Channel context identifier */
+    bool up;                    /* Indicate if associated netdev is up
+                                   (i.e. Interface is created at fw level) */
+    bool use_4addr;             /* Should we use 4addresses mode */
+    bool is_resending;          /* Indicate if a frame is being resent on this interface */
+    bool user_mpm;              /* In case of Mesh Point VIF, indicate if MPM is handled by userspace */
+    bool roc_tdls;              /* Indicate if the ROC has been called by a
+                                   TDLS station */
+    u8 tdls_status;             /* Status of the TDLS link */
+    bool tdls_chsw_prohibited;  /* Indicate if TDLS Channel Switch is prohibited */
+    bool wep_enabled;           /* 1 if WEP is enabled */
+    bool wep_auth_err;          /* 1 if auth status code is not supported auth alg when WEP enabled */
+    enum nl80211_auth_type last_auth_type; /* Authentication type (algorithm) sent in the last connection
+                                              when WEP enabled */
+    union
+    {
+        struct
+        {
+            struct rwnx_sta *ap; /* Pointer to the peer STA entry allocated for
+                                    the AP */
+            struct rwnx_sta *tdls_sta; /* Pointer to the TDLS station */
+            bool external_auth;  /* Indicate if external authentication is in progress */
+			u32 group_cipher_type;
+			u32 paired_cipher_type;
+        } sta;
+        struct
+        {
+            u16 flags;                 /* see rwnx_ap_flags */
+            struct list_head sta_list; /* List of STA connected to the AP */
+            struct rwnx_bcn bcn;       /* beacon */
+            u8 bcmc_index;             /* Index of the BCMC sta to use */
+            #if (defined CONFIG_HE_FOR_OLD_KERNEL) || (defined CONFIG_VHT_FOR_OLD_KERNEL)
+			u8 aic_index;
+            #endif
+            struct rwnx_csa *csa;
+
+            struct list_head mpath_list; /* List of Mesh Paths used on this interface */
+            struct list_head proxy_list; /* List of Proxies Information used on this interface */
+            bool create_path;            /* Indicate if we are waiting for a MESH_CREATE_PATH_CFM
+                                            message */
+            int generation;              /* Increased each time the list of Mesh Paths is updated */
+            enum nl80211_mesh_power_mode mesh_pm; /* mesh power save mode currently set in firmware */
+            enum nl80211_mesh_power_mode next_mesh_pm; /* mesh power save mode for next peer */
+        } ap;
+        struct
+        {
+            struct rwnx_vif *master;   /* pointer on master interface */
+            struct rwnx_sta *sta_4a;
+        } ap_vlan;
+    };
+
+	u8_l key_has_add;
+	u8_l is_p2p_vif;
+	struct apm_probe_sta sta_probe;
+};
+
+#define RWNX_VIF_TYPE(rwnx_vif) (rwnx_vif->wdev.iftype)
+
+/**
+ * Structure used to store information relative to PS mode.
+ *
+ * @active: True when the sta is in PS mode.
+ *          If false, other values should be ignored
+ * @pkt_ready: Number of packets buffered for the sta in drv's txq
+ *             (1 counter for Legacy PS and 1 for U-APSD)
+ * @sp_cnt: Number of packets that remain to be pushed in the service period.
+ *          0 means that no service period is in progress
+ *          (1 counter for Legacy PS and 1 for U-APSD)
+ */
+struct rwnx_sta_ps {
+    bool active;
+    u16 pkt_ready[2];
+    u16 sp_cnt[2];
+};
+
+/**
+ * struct rwnx_rx_rate_stats - Store statistics for RX rates
+ *
+ * @table: Table indicating how many frame has been receive which each
+ * rate index. Rate index is the same as the one used by RC algo for TX
+ * @size: Size of the table array
+ * @cpt: number of frames received
+ */
+struct rwnx_rx_rate_stats {
+    int *table;
+    int size;
+    int cpt;
+    int rate_cnt;
+};
+
+/**
+ * struct rwnx_sta_stats - Structure Used to store statistics specific to a STA
+ *
+ * @last_rx: Hardware vector of the last received frame
+ * @rx_rate: Statistics of the received rates
+ */
+struct rwnx_sta_stats {
+//#ifdef CONFIG_RWNX_DEBUGFS
+    struct hw_vect last_rx;
+    struct rwnx_rx_rate_stats rx_rate;
+//#endif
+};
+
+#if (defined CONFIG_HE_FOR_OLD_KERNEL) || (defined CONFIG_VHT_FOR_OLD_KERNEL)
+struct aic_sta {
+    u8 sta_idx;             /* Identifier of the station */
+	bool he;                /* Flag indicating if the station supports HE */
+    bool vht;               /* Flag indicating if the station supports VHT */
+};
+#endif
+
+/*
+ * Structure used to save information relative to the managed stations.
+ */
+struct rwnx_sta {
+    struct list_head list;
+    u16 aid;                /* association ID */
+    u8 sta_idx;             /* Identifier of the station */
+    u8 vif_idx;             /* Identifier of the VIF (fw id) the station
+                               belongs to */
+    u8 vlan_idx;            /* Identifier of the VLAN VIF (fw id) the station
+                               belongs to (= vif_idx if no vlan in used) */
+    enum nl80211_band band; /* Band */
+    enum nl80211_chan_width width; /* Channel width */
+    u16 center_freq;        /* Center frequency */
+    u32 center_freq1;       /* Center frequency 1 */
+    u32 center_freq2;       /* Center frequency 2 */
+    u8 ch_idx;              /* Identifier of the channel
+                               context the station belongs to */
+    bool qos;               /* Flag indicating if the station
+                               supports QoS */
+    u8 acm;                 /* Bitfield indicating which queues
+                               have AC mandatory */
+    u16 uapsd_tids;         /* Bitfield indicating which tids are subject to
+                               UAPSD */
+    u8 mac_addr[ETH_ALEN];  /* MAC address of the station */
+    struct rwnx_key key;
+    bool valid;             /* Flag indicating if the entry is valid */
+    struct rwnx_sta_ps ps;  /* Information when STA is in PS (AP only) */
+#ifdef CONFIG_RWNX_BFMER
+    struct rwnx_bfmer_report *bfm_report;     /* Beamforming report to be used for
+                                                 VHT TX Beamforming */
+#ifdef CONFIG_RWNX_MUMIMO_TX
+    struct rwnx_sta_group_info group_info; /* MU grouping information for the STA */
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+#endif /* CONFIG_RWNX_BFMER */
+
+    bool ht;               /* Flag indicating if the station
+                               supports HT */
+    bool vht;               /* Flag indicating if the station
+                               supports VHT */
+    u32 ac_param[AC_MAX];  /* EDCA parameters */
+    struct rwnx_tdls tdls; /* TDLS station information */
+    struct rwnx_sta_stats stats;
+    enum nl80211_mesh_power_mode mesh_pm; /*  link-specific mesh power save mode */
+};
+
+static inline const u8 *rwnx_sta_addr(struct rwnx_sta *rwnx_sta) {
+    return rwnx_sta->mac_addr;
+}
+
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+struct rwnx_amsdu_stats {
+    int done;
+    int failed;
+};
+#endif
+
+struct rwnx_stats {
+    int cfm_balance[NX_TXQ_CNT];
+    unsigned long last_rx, last_tx; /* jiffies */
+    int ampdus_tx[IEEE80211_MAX_AMPDU_BUF];
+    int ampdus_rx[IEEE80211_MAX_AMPDU_BUF];
+    int ampdus_rx_map[4];
+    int ampdus_rx_miss;
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+    struct rwnx_amsdu_stats amsdus[NX_TX_PAYLOAD_MAX];
+#endif
+    int amsdus_rx[64];
+};
+
+struct rwnx_sec_phy_chan {
+    u16 prim20_freq;
+    u16 center_freq1;
+    u16 center_freq2;
+    enum nl80211_band band;
+    u8 type;
+};
+
+/* Structure that will contains all RoC information received from cfg80211 */
+struct rwnx_roc_elem {
+    struct wireless_dev *wdev;
+    struct ieee80211_channel *chan;
+    unsigned int duration;
+    /* Used to avoid call of CFG80211 callback upon expiration of RoC */
+    bool mgmt_roc;
+    /* Indicate if we have switch on the RoC channel */
+    bool on_chan;
+};
+
+/* Structure containing channel survey information received from MAC */
+struct rwnx_survey_info {
+    // Filled
+    u32 filled;
+    // Amount of time in ms the radio spent on the channel
+    u32 chan_time_ms;
+    // Amount of time the primary channel was sensed busy
+    u32 chan_time_busy_ms;
+    // Noise in dbm
+    s8 noise_dbm;
+};
+
+#define RWNX_CH_NOT_SET 0xFF
+#define RWNX_INVALID_VIF 0xFF
+#define RWNX_INVALID_STA 0xFF
+
+/* Structure containing channel context information */
+struct rwnx_chanctx {
+    struct cfg80211_chan_def chan_def; /* channel description */
+    u8 count;                          /* number of vif using this ctxt */
+};
+
+/**
+ * rwnx_phy_info - Phy information
+ *
+ * @phy_cnt: Number of phy interface
+ * @cfg: Configuration send to firmware
+ * @sec_chan: Channel configuration of the second phy interface (if phy_cnt > 1)
+ * @limit_bw: Set to true to limit BW on requested channel. Only set to use
+ * VHT with old radio that don't support 80MHz (deprecated)
+ */
+struct rwnx_phy_info {
+    u8 cnt;
+    struct phy_cfg_tag cfg;
+    struct rwnx_sec_phy_chan sec_chan;
+    bool limit_bw;
+};
+
+
+struct defrag_ctrl_info {
+    struct list_head list;
+    u8 sta_idx;
+    u8 tid;
+    u16 sn;
+    u8 next_fn;
+    u16 frm_len;
+    struct sk_buff *skb;
+    struct timer_list defrag_timer;
+    struct rwnx_hw *rwnx_hw;
+};
+
+struct amsdu_subframe_hdr {
+    u8 da[6];
+    u8 sa[6];
+    u16 sublen;
+};
+
+struct rwnx_hw {
+    struct rwnx_mod_params *mod_params;
+    struct device *dev;
+#ifdef AICWF_SDIO_SUPPORT
+    struct aic_sdio_dev *sdiodev;
+#endif
+#ifdef AICWF_USB_SUPPORT
+    struct aic_usb_dev *usbdev;
+#endif
+    struct wiphy *wiphy;
+    struct list_head vifs;
+    struct rwnx_vif *vif_table[NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX]; /* indexed with fw id */
+    struct rwnx_sta sta_table[NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX];
+    #if (defined CONFIG_HE_FOR_OLD_KERNEL) || (defined CONFIG_VHT_FOR_OLD_KERNEL)
+	struct aic_sta aic_table[NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX];
+    #endif
+    struct rwnx_survey_info survey[SCAN_CHANNEL_MAX];
+    struct cfg80211_scan_request *scan_request;
+    struct rwnx_chanctx chanctx_table[NX_CHAN_CTXT_CNT];
+    u8 cur_chanctx;
+
+    u8 monitor_vif; /* FW id of the monitor interface, RWNX_INVALID_VIF if no monitor vif at fw level */
+
+#ifdef CONFIG_FILTER_TCP_ACK
+	/* tcp ack management */
+	struct tcp_ack_manage ack_m;
+#endif
+
+    /* RoC Management */
+    struct rwnx_roc_elem *roc_elem;             /* Information provided by cfg80211 in its remain on channel request */
+    u32 roc_cookie_cnt;                         /* Counter used to identify RoC request sent by cfg80211 */
+
+    struct rwnx_cmd_mgr *cmd_mgr;
+
+    unsigned long drv_flags;
+    struct rwnx_plat *plat;
+
+    spinlock_t tx_lock;
+    spinlock_t cb_lock;
+    struct mutex mutex;                         /* per-device perimeter lock */
+
+    struct tasklet_struct task;
+    struct mm_version_cfm version_cfm;          /* Lower layers versions - obtained via MM_VERSION_REQ */
+
+    u32 tcp_pacing_shift;
+
+    /* IPC */
+    struct ipc_host_env_tag *ipc_env;
+#ifdef AICWF_SDIO_SUPPORT
+    struct sdio_host_env_tag sdio_env;
+#endif
+#ifdef AICWF_USB_SUPPORT
+    struct usb_host_env_tag usb_env;
+#endif
+
+    struct rwnx_ipc_elem_pool e2amsgs_pool;
+    struct rwnx_ipc_elem_pool dbgmsgs_pool;
+    struct rwnx_ipc_elem_pool e2aradars_pool;
+    struct rwnx_ipc_elem_var pattern_elem;
+    struct rwnx_ipc_dbgdump_elem dbgdump_elem;
+    struct rwnx_ipc_elem_pool e2arxdesc_pool;
+    struct rwnx_ipc_skb_elem *e2aunsuprxvec_elems;
+    struct rwnx_ipc_rxbuf_elems rxbuf_elems;
+    struct rwnx_ipc_elem_var scan_ie;
+
+    struct kmem_cache      *sw_txhdr_cache;
+
+    struct rwnx_debugfs     debugfs;
+    struct rwnx_stats       stats;
+
+    struct rwnx_txq txq[NX_NB_TXQ];
+    struct rwnx_hwq hwq[NX_TXQ_CNT];
+
+    u8 avail_idx_map;
+    u8 vif_started;
+    bool adding_sta;
+    struct rwnx_phy_info phy;
+
+    struct rwnx_radar radar;
+
+    /* extended capabilities supported */
+    u8 ext_capa[8];
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+    struct rwnx_mu_info mu;
+#endif
+    u8 is_p2p_alive;
+    u8 is_p2p_connected;
+    struct timer_list p2p_alive_timer;
+    struct rwnx_vif *p2p_dev_vif;
+    atomic_t p2p_alive_timer_count;
+	bool band_5g_support;
+	u8_l vendor_info;
+	bool fwlog_en;
+	struct list_head defrag_list;
+	spinlock_t defrag_lock;
+
+    struct work_struct apmStalossWork;
+    struct workqueue_struct *apmStaloss_wq;
+    u8 apm_vif_idx;
+    u8 sta_mac_addr[6];
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+    struct mac_chan_op ap_chan;
+    struct ieee80211_channel set_chan;
+#endif
+#ifdef CONFIG_VHT_FOR_OLD_KERNEL
+		struct ieee80211_sta_vht_cap vht_cap_2G;
+		struct ieee80211_sta_vht_cap vht_cap_5G;
+#endif
+};
+
+
+u8 *rwnx_build_bcn(struct rwnx_bcn *bcn, struct cfg80211_beacon_data *new);
+
+void rwnx_chanctx_link(struct rwnx_vif *vif, u8 idx,
+                        struct cfg80211_chan_def *chandef);
+void rwnx_chanctx_unlink(struct rwnx_vif *vif);
+int  rwnx_chanctx_valid(struct rwnx_hw *rwnx_hw, u8 idx);
+
+static inline bool is_multicast_sta(int sta_idx)
+{
+    return (sta_idx >= NX_REMOTE_STA_MAX);
+}
+struct rwnx_sta *rwnx_get_sta(struct rwnx_hw *rwnx_hw, const u8 *mac_addr);
+
+static inline uint8_t master_vif_idx(struct rwnx_vif *vif)
+{
+    if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN)) {
+        return vif->ap_vlan.master->vif_index;
+    } else {
+        return vif->vif_index;
+    }
+}
+
+void rwnx_external_auth_enable(struct rwnx_vif *vif);
+void rwnx_external_auth_disable(struct rwnx_vif *vif);
+
+#ifdef LESS_SKB
+struct aicwf_rx_buff_list {
+    struct list_head rxbuff_list;
+    int rxbuff_list_len;
+};
+
+extern struct aicwf_rx_buff_list aic_rx_buff_list;
+extern struct rx_buff *aicwf_prealloc_rxbuff_alloc(spinlock_t *lock);
+extern void aicwf_prealloc_rxbuff_free(struct rx_buff *rxbuff, spinlock_t *lock);
+#endif
+
+#endif /* _RWNX_DEFS_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_dini.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_dini.c
new file mode 100755
index 0000000..03e1ced
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_dini.c
@@ -0,0 +1,294 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_dini.c - Add support for dini platform
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#include "rwnx_dini.h"
+#include "rwnx_defs.h"
+#include "rwnx_irqs.h"
+#include "reg_access.h"
+
+/* Config FPGA is accessed via bar0 */
+#define CFPGA_DMA0_CTRL_REG             0x02C
+#define CFPGA_DMA1_CTRL_REG             0x04C
+#define CFPGA_DMA2_CTRL_REG             0x06C
+#define CFPGA_UINTR_SRC_REG             0x0E8
+#define CFPGA_UINTR_MASK_REG            0x0EC
+#define CFPGA_BAR4_HIADDR_REG           0x100
+#define CFPGA_BAR4_LOADDR_REG           0x104
+#define CFPGA_BAR4_LOADDR_MASK_REG      0x110
+#define CFPGA_BAR_TOUT                  0x120
+
+#define CFPGA_DMA_CTRL_ENABLE           0x00001400
+#define CFPGA_DMA_CTRL_DISABLE          0x00001000
+#define CFPGA_DMA_CTRL_CLEAR            0x00001800
+#define CFPGA_DMA_CTRL_REREAD_TIME_MASK (BIT(10) - 1)
+
+#define CFPGA_BAR4_LOADDR_MASK_MAX      0xFF000000
+
+#define CFPGA_PCIEX_IT                  0x00000001
+#define CFPGA_ALL_ITS                   0x0000000F
+
+/* Programmable BAR4 Window start address */
+#define CPU_RAM_WINDOW_HIGH      0x00000000
+#define CPU_RAM_WINDOW_LOW       0x00000000
+#define AHB_BRIDGE_WINDOW_HIGH   0x00000000
+#define AHB_BRIDGE_WINDOW_LOW    0x60000000
+
+struct rwnx_dini
+{
+    u8 *pci_bar0_vaddr;
+    u8 *pci_bar4_vaddr;
+};
+
+static const u32 mv_cfg_fpga_dma_ctrl_regs[] = {
+    CFPGA_DMA0_CTRL_REG,
+    CFPGA_DMA1_CTRL_REG,
+    CFPGA_DMA2_CTRL_REG
+};
+
+/* This also clears running transactions */
+static void dini_dma_on(struct rwnx_dini *rwnx_dini)
+{
+    int i;
+    u32 reread_time;
+    volatile void *reg;
+
+    for (i = 0; i < ARRAY_SIZE(mv_cfg_fpga_dma_ctrl_regs); i++) {
+        reg = rwnx_dini->pci_bar0_vaddr + mv_cfg_fpga_dma_ctrl_regs[i];
+        reread_time = readl(reg) & CFPGA_DMA_CTRL_REREAD_TIME_MASK;
+
+        writel(CFPGA_DMA_CTRL_CLEAR  | reread_time, reg);
+        writel(CFPGA_DMA_CTRL_ENABLE | reread_time, reg);
+    }
+}
+
+/* This also clears running transactions */
+static void dini_dma_off(struct rwnx_dini *rwnx_dini)
+{
+    int i;
+    u32 reread_time;
+    volatile void *reg;
+
+    for (i = 0; i < ARRAY_SIZE(mv_cfg_fpga_dma_ctrl_regs); i++) {
+        reg = rwnx_dini->pci_bar0_vaddr + mv_cfg_fpga_dma_ctrl_regs[i];
+        reread_time = readl(reg) & CFPGA_DMA_CTRL_REREAD_TIME_MASK;
+
+        writel(CFPGA_DMA_CTRL_DISABLE | reread_time, reg);
+        writel(CFPGA_DMA_CTRL_CLEAR   | reread_time, reg);
+    }
+}
+
+
+/* Configure address range for BAR4.
+ * By default BAR4_LOADDR_MASK value is 0xFF000000, then there is no need to
+ * change it because the addresses we need to access are covered by this mask
+ */
+static void dini_set_bar4_win(u32 low, u32 high, struct rwnx_dini *rwnx_dini)
+{
+    writel(low, rwnx_dini->pci_bar0_vaddr + CFPGA_BAR4_LOADDR_REG);
+    writel(high, rwnx_dini->pci_bar0_vaddr + CFPGA_BAR4_HIADDR_REG);
+    writel(CFPGA_BAR4_LOADDR_MASK_MAX,
+           rwnx_dini->pci_bar0_vaddr + CFPGA_BAR4_LOADDR_MASK_REG);
+}
+
+
+/**
+ * Enable User Interrupts of CFPGA that trigger PCIe IRQs on PCIE_10
+ * and request the corresponding IRQ line
+ */
+int rwnx_cfpga_irq_enable(struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+    struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+    unsigned int cfpga_uintr_mask;
+    volatile void *reg;
+    int ret;
+
+    /* sched_setscheduler on ONESHOT threaded irq handler for BCNs ? */
+    if ((ret = request_irq(rwnx_hw->plat->pci_dev->irq, rwnx_irq_hdlr, 0,
+                           "rwnx", rwnx_hw)))
+            return ret;
+
+    reg = rwnx_dini->pci_bar0_vaddr + CFPGA_UINTR_MASK_REG;
+    cfpga_uintr_mask = readl(reg);
+    writel(cfpga_uintr_mask | CFPGA_PCIEX_IT, reg);
+
+    return ret;
+}
+
+/**
+ * Disable User Interrupts of CFPGA that trigger PCIe IRQs on PCIE_10
+ * and free the corresponding IRQ line
+ */
+int rwnx_cfpga_irq_disable(struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+    struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+    unsigned int cfpga_uintr_mask;
+    volatile void *reg;
+
+    reg = rwnx_dini->pci_bar0_vaddr + CFPGA_UINTR_MASK_REG;
+    cfpga_uintr_mask = readl(reg);
+    writel(cfpga_uintr_mask & ~CFPGA_PCIEX_IT, reg);
+
+    free_irq(rwnx_hw->plat->pci_dev->irq, rwnx_hw);
+
+    return 0;
+}
+
+static int rwnx_dini_platform_enable(struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+    struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+
+#ifdef CONFIG_RWNX_SDM
+    writel(0x0000FFFF, rwnx_dini->pci_bar0_vaddr + CFPGA_BAR_TOUT);
+#endif
+
+    dini_dma_on(rwnx_dini);
+    return rwnx_cfpga_irq_enable(rwnx_hw);
+}
+
+static int rwnx_dini_platform_disable(struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+    struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+    int ret;
+
+    ret = rwnx_cfpga_irq_disable(rwnx_hw);
+    dini_dma_off(rwnx_dini);
+    return ret;
+}
+
+static void rwnx_dini_platform_deinit(struct rwnx_plat *rwnx_plat)
+{
+    struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+
+    pci_disable_device(rwnx_plat->pci_dev);
+    iounmap(rwnx_dini->pci_bar0_vaddr);
+    iounmap(rwnx_dini->pci_bar4_vaddr);
+    pci_release_regions(rwnx_plat->pci_dev);
+
+    kfree(rwnx_plat);
+}
+
+static u8* rwnx_dini_get_address(struct rwnx_plat *rwnx_plat, int addr_name,
+                                 unsigned int offset)
+{
+    struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+
+    if (WARN(addr_name >= RWNX_ADDR_MAX, "Invalid address %d", addr_name))
+        return NULL;
+
+    if (addr_name == RWNX_ADDR_CPU)
+        dini_set_bar4_win(CPU_RAM_WINDOW_LOW, CPU_RAM_WINDOW_HIGH, rwnx_dini);
+    else
+        dini_set_bar4_win(AHB_BRIDGE_WINDOW_LOW, AHB_BRIDGE_WINDOW_HIGH, rwnx_dini);
+
+    return rwnx_dini->pci_bar4_vaddr + offset;
+}
+
+static void rwnx_dini_ack_irq(struct rwnx_plat *rwnx_plat)
+{
+    struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+
+    writel(CFPGA_ALL_ITS, rwnx_dini->pci_bar0_vaddr + CFPGA_UINTR_SRC_REG);
+}
+
+static const u32 rwnx_dini_config_reg[] = {
+    NXMAC_DEBUG_PORT_SEL_ADDR,
+    SYSCTRL_DIAG_CONF_ADDR,
+    RF_V6_DIAGPORT_CONF1_ADDR,
+    RF_v6_PHYDIAG_CONF1_ADDR,
+};
+
+static int rwnx_dini_get_config_reg(struct rwnx_plat *rwnx_plat, const u32 **list)
+{
+    if (!list)
+        return 0;
+
+    *list = rwnx_dini_config_reg;
+    return ARRAY_SIZE(rwnx_dini_config_reg);
+}
+
+/**
+ * rwnx_dini_platform_init - Initialize the DINI platform
+ *
+ * @pci_dev PCI device
+ * @rwnx_plat Pointer on struct rwnx_stat * to be populated
+ *
+ * @return 0 on success, < 0 otherwise
+ *
+ * Allocate and initialize a rwnx_plat structure for the dini platform.
+ */
+int rwnx_dini_platform_init(struct pci_dev *pci_dev, struct rwnx_plat **rwnx_plat)
+{
+    struct rwnx_dini *rwnx_dini;
+    u16 pci_cmd;
+    int ret = 0;
+
+    *rwnx_plat = kzalloc(sizeof(struct rwnx_plat) + sizeof(struct rwnx_dini),
+                        GFP_KERNEL);
+    if (!*rwnx_plat)
+        return -ENOMEM;
+
+    rwnx_dini = (struct rwnx_dini *)(*rwnx_plat)->priv;
+
+    /* Hotplug fixups */
+    pci_read_config_word(pci_dev, PCI_COMMAND, &pci_cmd);
+    pci_cmd |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
+    pci_write_config_word(pci_dev, PCI_COMMAND, pci_cmd);
+    pci_write_config_byte(pci_dev, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES >> 2);
+
+    if ((ret = pci_enable_device(pci_dev))) {
+        dev_err(&(pci_dev->dev), "pci_enable_device failed\n");
+        goto out_enable;
+    }
+
+    pci_set_master(pci_dev);
+
+    if ((ret = pci_request_regions(pci_dev, KBUILD_MODNAME))) {
+        dev_err(&(pci_dev->dev), "pci_request_regions failed\n");
+        goto out_request;
+    }
+
+    if (!(rwnx_dini->pci_bar0_vaddr = (u8 *)pci_ioremap_bar(pci_dev, 0))) {
+        dev_err(&(pci_dev->dev), "pci_ioremap_bar(%d) failed\n", 0);
+        ret = -ENOMEM;
+        goto out_bar0;
+    }
+    if (!(rwnx_dini->pci_bar4_vaddr = (u8 *)pci_ioremap_bar(pci_dev, 4))) {
+        dev_err(&(pci_dev->dev), "pci_ioremap_bar(%d) failed\n", 4);
+        ret = -ENOMEM;
+        goto out_bar4;
+    }
+
+    (*rwnx_plat)->enable = rwnx_dini_platform_enable;
+    (*rwnx_plat)->disable = rwnx_dini_platform_disable;
+    (*rwnx_plat)->deinit = rwnx_dini_platform_deinit;
+    (*rwnx_plat)->get_address = rwnx_dini_get_address;
+    (*rwnx_plat)->ack_irq = rwnx_dini_ack_irq;
+    (*rwnx_plat)->get_config_reg = rwnx_dini_get_config_reg;
+
+#ifdef CONFIG_RWNX_SDM
+    writel(0x0000FFFF, rwnx_dini->pci_bar0_vaddr + CFPGA_BAR_TOUT);
+#endif
+
+    return 0;
+
+  out_bar4:
+    iounmap(rwnx_dini->pci_bar0_vaddr);
+  out_bar0:
+    pci_release_regions(pci_dev);
+  out_request:
+    pci_disable_device(pci_dev);
+  out_enable:
+    kfree(*rwnx_plat);
+    return ret;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_dini.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_dini.h
new file mode 100755
index 0000000..2d8cf35
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_dini.h
@@ -0,0 +1,20 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_dini.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_DINI_H_
+#define _RWNX_DINI_H_
+
+#include <linux/pci.h>
+#include "rwnx_platform.h"
+
+int rwnx_dini_platform_init(struct pci_dev *pci_dev,
+                            struct rwnx_plat **rwnx_plat);
+
+#endif /* _RWNX_DINI_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_events.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_events.h
new file mode 100755
index 0000000..a105666
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_events.h
@@ -0,0 +1,1255 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_events.h
+ *
+ * @brief Trace events definition
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM rwnx
+
+#if !defined(_RWNX_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _RWNX_EVENTS_H
+
+#include <linux/tracepoint.h>
+#ifndef CONFIG_RWNX_FHOST
+#include "rwnx_tx.h"
+#endif
+#include "rwnx_compat.h"
+
+/*****************************************************************************
+ * TRACE function for MGMT TX (FULLMAC)
+ ****************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+#include "linux/ieee80211.h"
+#if defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS)
+#include <linux/trace_seq.h>
+
+/* P2P Public Action Frames Definitions (see WiFi P2P Technical Specification, section 4.2.8) */
+/* IEEE 802.11 Public Action Usage Category - Define P2P public action frames */
+#define MGMT_ACTION_PUBLIC_CAT              (0x04)
+/* Offset of OUI Subtype field in P2P Action Frame format */
+#define MGMT_ACTION_OUI_SUBTYPE_OFFSET      (6)
+/* P2P Public Action Frame Types */
+enum p2p_action_type {
+    P2P_ACTION_GO_NEG_REQ   = 0,    /* GO Negociation Request */
+    P2P_ACTION_GO_NEG_RSP,          /* GO Negociation Response */
+    P2P_ACTION_GO_NEG_CFM,          /* GO Negociation Confirmation */
+    P2P_ACTION_INVIT_REQ,           /* P2P Invitation Request */
+    P2P_ACTION_INVIT_RSP,           /* P2P Invitation Response */
+    P2P_ACTION_DEV_DISC_REQ,        /* Device Discoverability Request */
+    P2P_ACTION_DEV_DISC_RSP,        /* Device Discoverability Response */
+    P2P_ACTION_PROV_DISC_REQ,       /* Provision Discovery Request */
+    P2P_ACTION_PROV_DISC_RSP,       /* Provision Discovery Response */
+};
+
+const char *ftrace_print_mgmt_info(struct trace_seq *p, u16 frame_control, u8 cat, u8 type, u8 p2p) {
+    const char *ret = trace_seq_buffer_ptr(p);
+
+    switch (frame_control & IEEE80211_FCTL_STYPE) {
+        case (IEEE80211_STYPE_ASSOC_REQ): trace_seq_printf(p, "Association Request"); break;
+        case (IEEE80211_STYPE_ASSOC_RESP): trace_seq_printf(p, "Association Response"); break;
+        case (IEEE80211_STYPE_REASSOC_REQ): trace_seq_printf(p, "Reassociation Request"); break;
+        case (IEEE80211_STYPE_REASSOC_RESP): trace_seq_printf(p, "Reassociation Response"); break;
+        case (IEEE80211_STYPE_PROBE_REQ): trace_seq_printf(p, "Probe Request"); break;
+        case (IEEE80211_STYPE_PROBE_RESP): trace_seq_printf(p, "Probe Response"); break;
+        case (IEEE80211_STYPE_BEACON): trace_seq_printf(p, "Beacon"); break;
+        case (IEEE80211_STYPE_ATIM): trace_seq_printf(p, "ATIM"); break;
+        case (IEEE80211_STYPE_DISASSOC): trace_seq_printf(p, "Disassociation"); break;
+        case (IEEE80211_STYPE_AUTH): trace_seq_printf(p, "Authentication"); break;
+        case (IEEE80211_STYPE_DEAUTH): trace_seq_printf(p, "Deauthentication"); break;
+        case (IEEE80211_STYPE_ACTION):
+            trace_seq_printf(p, "Action");
+            if (cat == MGMT_ACTION_PUBLIC_CAT && type == 0x9)
+                switch (p2p) {
+                    case (P2P_ACTION_GO_NEG_REQ): trace_seq_printf(p, ": GO Negociation Request"); break;
+                    case (P2P_ACTION_GO_NEG_RSP): trace_seq_printf(p, ": GO Negociation Response"); break;
+                    case (P2P_ACTION_GO_NEG_CFM): trace_seq_printf(p, ": GO Negociation Confirmation"); break;
+                    case (P2P_ACTION_INVIT_REQ): trace_seq_printf(p, ": P2P Invitation Request"); break;
+                    case (P2P_ACTION_INVIT_RSP): trace_seq_printf(p, ": P2P Invitation Response"); break;
+                    case (P2P_ACTION_DEV_DISC_REQ): trace_seq_printf(p, ": Device Discoverability Request"); break;
+                    case (P2P_ACTION_DEV_DISC_RSP): trace_seq_printf(p, ": Device Discoverability Response"); break;
+                    case (P2P_ACTION_PROV_DISC_REQ): trace_seq_printf(p, ": Provision Discovery Request"); break;
+                    case (P2P_ACTION_PROV_DISC_RSP): trace_seq_printf(p, ": Provision Discovery Response"); break;
+                    default: trace_seq_printf(p, "Unknown p2p %d", p2p); break;
+                }
+            else {
+                switch (cat) {
+                    case 0: trace_seq_printf(p, ":Spectrum %d", type); break;
+                    case 1: trace_seq_printf(p, ":QOS %d", type); break;
+                    case 2: trace_seq_printf(p, ":DLS %d", type); break;
+                    case 3: trace_seq_printf(p, ":BA %d", type); break;
+                    case 4: trace_seq_printf(p, ":Public %d", type); break;
+                    case 5: trace_seq_printf(p, ":Radio Measure %d", type); break;
+                    case 6: trace_seq_printf(p, ":Fast BSS %d", type); break;
+                    case 7: trace_seq_printf(p, ":HT Action %d", type); break;
+                    case 8: trace_seq_printf(p, ":SA Query %d", type); break;
+                    case 9: trace_seq_printf(p, ":Protected Public %d", type); break;
+                    case 10: trace_seq_printf(p, ":WNM %d", type); break;
+                    case 11: trace_seq_printf(p, ":Unprotected WNM %d", type); break;
+                    case 12: trace_seq_printf(p, ":TDLS %d", type); break;
+                    case 13: trace_seq_printf(p, ":Mesh %d", type); break;
+                    case 14: trace_seq_printf(p, ":MultiHop %d", type); break;
+                    case 15: trace_seq_printf(p, ":Self Protected %d", type); break;
+                    case 126: trace_seq_printf(p, ":Vendor protected"); break;
+                    case 127: trace_seq_printf(p, ":Vendor"); break;
+                    default: trace_seq_printf(p, ":Unknown category %d", cat); break;
+                }
+            }
+            break;
+        default: trace_seq_printf(p, "Unknown subtype %d", frame_control & IEEE80211_FCTL_STYPE); break;
+    }
+
+    trace_seq_putc(p, 0);
+
+    return ret;
+}
+#endif /* defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS) */
+
+#undef __print_mgmt_info
+#define __print_mgmt_info(frame_control, cat, type, p2p) ftrace_print_mgmt_info(p, frame_control, cat, type, p2p)
+
+TRACE_EVENT(
+    roc,
+    TP_PROTO(u8 vif_idx, u16 freq, unsigned int duration),
+    TP_ARGS(vif_idx, freq, duration),
+    TP_STRUCT__entry(
+        __field(u8, vif_idx)
+        __field(u16, freq)
+        __field(unsigned int, duration)
+                     ),
+    TP_fast_assign(
+        __entry->vif_idx = vif_idx;
+        __entry->freq = freq;
+        __entry->duration = duration;
+                   ),
+    TP_printk("f=%d vif=%d dur=%d",
+            __entry->freq, __entry->vif_idx, __entry->duration)
+);
+
+TRACE_EVENT(
+    cancel_roc,
+    TP_PROTO(u8 vif_idx),
+    TP_ARGS(vif_idx),
+    TP_STRUCT__entry(
+        __field(u8, vif_idx)
+                     ),
+    TP_fast_assign(
+        __entry->vif_idx = vif_idx;
+                   ),
+    TP_printk("vif=%d", __entry->vif_idx)
+);
+
+TRACE_EVENT(
+    roc_exp,
+    TP_PROTO(u8 vif_idx),
+    TP_ARGS(vif_idx),
+    TP_STRUCT__entry(
+        __field(u8, vif_idx)
+                     ),
+    TP_fast_assign(
+        __entry->vif_idx = vif_idx;
+                   ),
+    TP_printk("vif=%d", __entry->vif_idx)
+);
+
+TRACE_EVENT(
+    switch_roc,
+    TP_PROTO(u8 vif_idx),
+    TP_ARGS(vif_idx),
+    TP_STRUCT__entry(
+        __field(u8, vif_idx)
+                     ),
+    TP_fast_assign(
+        __entry->vif_idx = vif_idx;
+                   ),
+    TP_printk("vif=%d", __entry->vif_idx)
+);
+
+DECLARE_EVENT_CLASS(
+    mgmt_template,
+    TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
+    TP_ARGS(freq, vif_idx, sta_idx, mgmt),
+    TP_STRUCT__entry(
+        __field(u16, freq)
+        __field(u8, vif_idx)
+        __field(u8, sta_idx)
+        __field(u16, frame_control)
+        __field(u8, action_cat)
+        __field(u8, action_type)
+        __field(u8, action_p2p)
+                     ),
+    TP_fast_assign(
+        __entry->freq = freq;
+        __entry->vif_idx = vif_idx;
+        __entry->sta_idx = sta_idx;
+        __entry->frame_control = mgmt->frame_control;
+        __entry->action_cat = mgmt->u.action.category;
+        __entry->action_type = mgmt->u.action.u.wme_action.action_code;
+        __entry->action_p2p = *((u8 *)&mgmt->u.action.category
+                                 + MGMT_ACTION_OUI_SUBTYPE_OFFSET);
+                   ),
+    TP_printk("f=%d vif=%d sta=%d -> %s",
+            __entry->freq, __entry->vif_idx, __entry->sta_idx,
+              __print_mgmt_info(__entry->frame_control, __entry->action_cat,
+                                __entry->action_type, __entry->action_p2p))
+);
+
+DEFINE_EVENT(mgmt_template, mgmt_tx,
+             TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
+             TP_ARGS(freq, vif_idx, sta_idx, mgmt));
+
+DEFINE_EVENT(mgmt_template, mgmt_rx,
+             TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
+             TP_ARGS(freq, vif_idx, sta_idx, mgmt));
+
+TRACE_EVENT(
+    mgmt_cfm,
+    TP_PROTO(u8 vif_idx, u8 sta_idx, bool acked),
+    TP_ARGS(vif_idx, sta_idx, acked),
+    TP_STRUCT__entry(
+        __field(u8, vif_idx)
+        __field(u8, sta_idx)
+        __field(bool, acked)
+                     ),
+    TP_fast_assign(
+        __entry->vif_idx = vif_idx;
+        __entry->sta_idx = sta_idx;
+        __entry->acked = acked;
+                   ),
+    TP_printk("vif=%d sta=%d ack=%d",
+            __entry->vif_idx, __entry->sta_idx, __entry->acked)
+);
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/*****************************************************************************
+ * TRACE function for TXQ
+ ****************************************************************************/
+#ifndef CONFIG_RWNX_FHOST
+#if defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS)
+
+#include <linux/trace_seq.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+#include <linux/trace_events.h>
+#else
+#include <linux/ftrace_event.h>
+#endif
+
+const char *
+ftrace_print_txq(struct trace_seq *p, int txq_idx) {
+    const char *ret = trace_seq_buffer_ptr(p);
+
+    if (txq_idx == TXQ_INACTIVE) {
+        trace_seq_printf(p, "[INACTIVE]");
+    } else if (txq_idx < NX_FIRST_VIF_TXQ_IDX) {
+        trace_seq_printf(p, "[STA %d/%d]",
+                         txq_idx / NX_NB_TXQ_PER_STA,
+                         txq_idx % NX_NB_TXQ_PER_STA);
+#ifdef CONFIG_RWNX_FULLMAC
+    } else if (txq_idx < NX_FIRST_UNK_TXQ_IDX) {
+        trace_seq_printf(p, "[BC/MC %d]",
+                         txq_idx - NX_FIRST_BCMC_TXQ_IDX);
+    } else if (txq_idx < NX_OFF_CHAN_TXQ_IDX) {
+        trace_seq_printf(p, "[UNKNOWN %d]",
+                         txq_idx - NX_FIRST_UNK_TXQ_IDX);
+    } else if (txq_idx == NX_OFF_CHAN_TXQ_IDX) {
+        trace_seq_printf(p, "[OFFCHAN]");
+#else
+    } else if (txq_idx < NX_NB_TXQ) {
+        txq_idx -= NX_FIRST_VIF_TXQ_IDX;
+        trace_seq_printf(p, "[VIF %d/%d]",
+                         txq_idx / NX_NB_TXQ_PER_VIF,
+                         txq_idx % NX_NB_TXQ_PER_VIF);
+#endif
+    } else {
+        trace_seq_printf(p, "[ERROR %d]", txq_idx);
+    }
+
+    trace_seq_putc(p, 0);
+
+    return ret;
+}
+
+const char *
+ftrace_print_sta(struct trace_seq *p, int sta_idx) {
+    const char *ret = trace_seq_buffer_ptr(p);
+
+    if (sta_idx < NX_REMOTE_STA_MAX) {
+        trace_seq_printf(p, "[STA %d]", sta_idx);
+    } else {
+        trace_seq_printf(p, "[BC/MC %d]", sta_idx - NX_REMOTE_STA_MAX);
+    }
+
+    trace_seq_putc(p, 0);
+
+    return ret;
+}
+
+const char *
+ftrace_print_hwq(struct trace_seq *p, int hwq_idx) {
+
+    static const struct trace_print_flags symbols[] =
+        {{RWNX_HWQ_BK, "BK"},
+         {RWNX_HWQ_BE, "BE"},
+         {RWNX_HWQ_VI, "VI"},
+         {RWNX_HWQ_VO, "VO"},
+#ifdef CONFIG_RWNX_FULLMAC
+         {RWNX_HWQ_BCMC, "BCMC"},
+#else
+         {RWNX_HWQ_BCN, "BCN"},
+#endif
+         { -1, NULL }};
+    return trace_print_symbols_seq(p, hwq_idx, symbols);
+}
+
+const char *
+ftrace_print_hwq_cred(struct trace_seq *p, u8 *cred) {
+    const char *ret = trace_seq_buffer_ptr(p);
+
+#if CONFIG_USER_MAX == 1
+    trace_seq_printf(p, "%d", cred[0]);
+#else
+    int i;
+
+    for (i = 0; i < CONFIG_USER_MAX - 1; i++)
+        trace_seq_printf(p, "%d-", cred[i]);
+    trace_seq_printf(p, "%d", cred[i]);
+#endif
+
+    trace_seq_putc(p, 0);
+    return ret;
+}
+
+const char *
+ftrace_print_mu_info(struct trace_seq *p, u8 mu_info) {
+    const char *ret = trace_seq_buffer_ptr(p);
+
+    if (mu_info)
+        trace_seq_printf(p, "MU: %d-%d", (mu_info & 0x3f), (mu_info >> 6));
+
+    trace_seq_putc(p, 0);
+    return ret;
+}
+
+const char *
+ftrace_print_mu_group(struct trace_seq *p, int nb_user, u8 *users) {
+    const char *ret = trace_seq_buffer_ptr(p);
+    int i;
+
+    if (users[0] != 0xff)
+        trace_seq_printf(p, "(%d", users[0]);
+    else
+        trace_seq_printf(p, "(-");
+    for (i = 1; i < CONFIG_USER_MAX ; i++) {
+        if (users[i] != 0xff)
+            trace_seq_printf(p, ",%d", users[i]);
+        else
+            trace_seq_printf(p, ",-");
+    }
+
+    trace_seq_printf(p, ")");
+    trace_seq_putc(p, 0);
+    return ret;
+}
+
+const char *
+ftrace_print_amsdu(struct trace_seq *p, u16 nb_pkt) {
+    const char *ret = trace_seq_buffer_ptr(p);
+
+    if (nb_pkt > 1)
+        trace_seq_printf(p, "(AMSDU %d)", nb_pkt);
+
+    trace_seq_putc(p, 0);
+    return ret;
+}
+#endif /* defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS) */
+
+#undef __print_txq
+#define __print_txq(txq_idx) ftrace_print_txq(p, txq_idx)
+
+#undef __print_sta
+#define __print_sta(sta_idx) ftrace_print_sta(p, sta_idx)
+
+#undef __print_hwq
+#define __print_hwq(hwq) ftrace_print_hwq(p, hwq)
+
+#undef __print_hwq_cred
+#define __print_hwq_cred(cred) ftrace_print_hwq_cred(p, cred)
+
+#undef __print_mu_info
+#define __print_mu_info(mu_info) ftrace_print_mu_info(p, mu_info)
+
+#undef __print_mu_group
+#define __print_mu_group(nb, users) ftrace_print_mu_group(p, nb, users)
+
+#undef __print_amsdu
+#define __print_amsdu(nb_pkt) ftrace_print_amsdu(p, nb_pkt)
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+TRACE_EVENT(
+    txq_select,
+    TP_PROTO(int txq_idx, u16 pkt_ready_up, struct sk_buff *skb),
+    TP_ARGS(txq_idx, pkt_ready_up, skb),
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+        __field(u16, pkt_ready)
+        __field(struct sk_buff *, skb)
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq_idx;
+        __entry->pkt_ready = pkt_ready_up;
+        __entry->skb = skb;
+                   ),
+    TP_printk("%s pkt_ready_up=%d skb=%p", __print_txq(__entry->txq_idx),
+              __entry->pkt_ready, __entry->skb)
+);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+DECLARE_EVENT_CLASS(
+    hwq_template,
+    TP_PROTO(u8 hwq_idx),
+    TP_ARGS(hwq_idx),
+    TP_STRUCT__entry(
+        __field(u8, hwq_idx)
+                     ),
+    TP_fast_assign(
+        __entry->hwq_idx = hwq_idx;
+                   ),
+    TP_printk("%s", __print_hwq(__entry->hwq_idx))
+);
+
+DEFINE_EVENT(hwq_template, hwq_flowctrl_stop,
+             TP_PROTO(u8 hwq_idx),
+             TP_ARGS(hwq_idx));
+
+DEFINE_EVENT(hwq_template, hwq_flowctrl_start,
+             TP_PROTO(u8 hwq_idx),
+             TP_ARGS(hwq_idx));
+
+
+DECLARE_EVENT_CLASS(
+    txq_template,
+    TP_PROTO(struct rwnx_txq *txq),
+    TP_ARGS(txq),
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+                   ),
+    TP_printk("%s", __print_txq(__entry->txq_idx))
+);
+
+DEFINE_EVENT(txq_template, txq_add_to_hw,
+             TP_PROTO(struct rwnx_txq *txq),
+             TP_ARGS(txq));
+
+DEFINE_EVENT(txq_template, txq_del_from_hw,
+             TP_PROTO(struct rwnx_txq *txq),
+             TP_ARGS(txq));
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+DEFINE_EVENT(txq_template, txq_flowctrl_stop,
+             TP_PROTO(struct rwnx_txq *txq),
+             TP_ARGS(txq));
+
+DEFINE_EVENT(txq_template, txq_flowctrl_restart,
+             TP_PROTO(struct rwnx_txq *txq),
+             TP_ARGS(txq));
+
+#endif  /* CONFIG_RWNX_FULLMAC */
+
+TRACE_EVENT(
+    process_txq,
+    TP_PROTO(struct rwnx_txq *txq),
+    TP_ARGS(txq),
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+        __field(u16, len)
+        __field(u16, len_retry)
+        __field(s8, credit)
+        #ifdef CONFIG_RWNX_FULLMAC
+        __field(u16, limit)
+        #endif /* CONFIG_RWNX_FULLMAC*/
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+        __entry->len = skb_queue_len(&txq->sk_list);
+        #ifdef CONFIG_MAC80211_TXQ
+        __entry->len += txq->nb_ready_mac80211;
+        #endif
+        __entry->len_retry = txq->nb_retry;
+        __entry->credit = txq->credits;
+        #ifdef CONFIG_RWNX_FULLMAC
+        __entry->limit = txq->push_limit;
+        #endif /* CONFIG_RWNX_FULLMAC*/
+                   ),
+
+    #ifdef CONFIG_RWNX_FULLMAC
+    TP_printk("%s txq_credits=%d, len=%d, retry_len=%d, push_limit=%d",
+              __print_txq(__entry->txq_idx), __entry->credit,
+              __entry->len, __entry->len_retry, __entry->limit)
+    #else
+    TP_printk("%s txq_credits=%d, len=%d, retry_len=%d",
+              __print_txq(__entry->txq_idx), __entry->credit,
+              __entry->len, __entry->len_retry)
+    #endif /* CONFIG_RWNX_FULLMAC*/
+);
+
+DECLARE_EVENT_CLASS(
+    txq_reason_template,
+    TP_PROTO(struct rwnx_txq *txq, u16 reason),
+    TP_ARGS(txq, reason),
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+        __field(u16, reason)
+        __field(u16, status)
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+        __entry->reason = reason;
+        __entry->status = txq->status;
+                   ),
+    TP_printk("%s reason=%s status=%s",
+              __print_txq(__entry->txq_idx),
+              __print_symbolic(__entry->reason,
+                               {RWNX_TXQ_STOP_FULL, "FULL"},
+                               {RWNX_TXQ_STOP_CSA, "CSA"},
+                               {RWNX_TXQ_STOP_STA_PS, "PS"},
+                               {RWNX_TXQ_STOP_VIF_PS, "VPS"},
+                               {RWNX_TXQ_STOP_CHAN, "CHAN"},
+                               {RWNX_TXQ_STOP_MU_POS, "MU"}),
+              __print_flags(__entry->status, "|",
+                            {RWNX_TXQ_IN_HWQ_LIST, "IN LIST"},
+                            {RWNX_TXQ_STOP_FULL, "FULL"},
+                            {RWNX_TXQ_STOP_CSA, "CSA"},
+                            {RWNX_TXQ_STOP_STA_PS, "PS"},
+                            {RWNX_TXQ_STOP_VIF_PS, "VPS"},
+                            {RWNX_TXQ_STOP_CHAN, "CHAN"},
+                            {RWNX_TXQ_STOP_MU_POS, "MU"},
+                            {RWNX_TXQ_NDEV_FLOW_CTRL, "FLW_CTRL"}))
+);
+
+DEFINE_EVENT(txq_reason_template, txq_start,
+             TP_PROTO(struct rwnx_txq *txq, u16 reason),
+             TP_ARGS(txq, reason));
+
+DEFINE_EVENT(txq_reason_template, txq_stop,
+             TP_PROTO(struct rwnx_txq *txq, u16 reason),
+             TP_ARGS(txq, reason));
+
+
+TRACE_EVENT(
+    push_desc,
+    TP_PROTO(struct sk_buff *skb, struct rwnx_sw_txhdr *sw_txhdr, int push_flags),
+
+    TP_ARGS(skb, sw_txhdr, push_flags),
+
+    TP_STRUCT__entry(
+        __field(struct sk_buff *, skb)
+        __field(unsigned int, len)
+        __field(u16, tx_queue)
+        __field(u8, hw_queue)
+        __field(u8, push_flag)
+        __field(u32, flag)
+        __field(s8, txq_cred)
+        __field(u8, hwq_cred)
+        __field(u16, pkt_cnt)
+        __field(u8, mu_info)
+                     ),
+    TP_fast_assign(
+        __entry->skb = skb;
+        __entry->tx_queue = sw_txhdr->txq->idx;
+        __entry->push_flag = push_flags;
+        __entry->hw_queue = sw_txhdr->txq->hwq->id;
+        __entry->txq_cred = sw_txhdr->txq->credits;
+        //__entry->hwq_cred = sw_txhdr->txq->hwq->credits[RWNX_TXQ_POS_ID(sw_txhdr->txq)];
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+    #ifdef CONFIG_SDIO_AGGR
+        __entry->pkt_cnt =  sw_txhdr->desc.host.packet_cnt;
+    #endif
+#endif
+#ifdef CONFIG_RWNX_FULLMAC
+    #ifdef CONFIG_SDIO_AGGR
+        __entry->flag = sw_txhdr->desc.host.flags;
+    #endif
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+#ifdef CONFIG_RWNX_AMSDUS_TX
+        if (sw_txhdr->amsdu.len)
+            __entry->len = sw_txhdr->amsdu.len;
+        else
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+        #ifdef CONFIG_SDIO_AGGR
+            __entry->len = sw_txhdr->desc.host.packet_len[0];
+        #endif
+#else
+        #ifdef CONFIG_SDIO_AGGR
+        __entry->len = sw_txhdr->desc.host.packet_len;
+        #endif
+#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
+
+#else /* !CONFIG_RWNX_FULLMAC */
+    #ifdef CONFIG_SDIO_AGGR
+        __entry->flag = sw_txhdr->desc.umac.flags;
+    #endif
+        __entry->len = sw_txhdr->frame_len;
+        __entry->sn = sw_txhdr->sn;
+#endif /* CONFIG_RWNX_FULLMAC */
+#ifdef CONFIG_RWNX_MUMIMO_TX
+    #ifdef CONFIG_SDIO_AGGR
+        __entry->mu_info = sw_txhdr->desc.host.mumimo_info;
+    #endif
+#else
+        __entry->mu_info = 0;
+#endif
+                   ),
+
+#ifdef CONFIG_RWNX_FULLMAC
+    TP_printk("%s skb=%p (len=%d) hw_queue=%s cred_txq=%d cred_hwq=%d %s flag=%s %s%s%s",
+              __print_txq(__entry->tx_queue), __entry->skb, __entry->len,
+              __print_hwq(__entry->hw_queue),
+              __entry->txq_cred, __entry->hwq_cred,
+              __print_mu_info(__entry->mu_info),
+              __print_flags(__entry->flag, "|",
+                            {TXU_CNTRL_RETRY, "RETRY"},
+                            {TXU_CNTRL_MORE_DATA, "MOREDATA"},
+                            {TXU_CNTRL_MGMT, "MGMT"},
+                            {TXU_CNTRL_MGMT_NO_CCK, "NO_CCK"},
+                            {TXU_CNTRL_MGMT_ROBUST, "ROBUST"},
+                            {TXU_CNTRL_AMSDU, "AMSDU"},
+                            {TXU_CNTRL_USE_4ADDR, "4ADDR"},
+                            {TXU_CNTRL_EOSP, "EOSP"},
+                            {TXU_CNTRL_MESH_FWD, "MESH_FWD"},
+                            {TXU_CNTRL_TDLS, "TDLS"}),
+              (__entry->push_flag & RWNX_PUSH_IMMEDIATE) ? "(IMMEDIATE)" : "",
+              (!(__entry->flag & TXU_CNTRL_RETRY) &&
+               (__entry->push_flag & RWNX_PUSH_RETRY)) ? "(SW_RETRY)" : "",
+              __print_amsdu(__entry->pkt_cnt))
+#else
+    TP_printk("%s skb=%p (len=%d) hw_queue=%s cred_txq=%d cred_hwq=%d %s flag=%x (%s) sn=%d %s",
+              __print_txq(__entry->tx_queue), __entry->skb, __entry->len,
+              __print_hwq(__entry->hw_queue), __entry->txq_cred, __entry->hwq_cred,
+              __print_mu_info(__entry->mu_info),
+              __entry->flag,
+              __print_flags(__entry->push_flag, "|",
+                            {RWNX_PUSH_RETRY, "RETRY"},
+                            {RWNX_PUSH_IMMEDIATE, "IMMEDIATE"}),
+              __entry->sn, __print_amsdu(__entry->pkt_cnt))
+#endif /* CONFIG_RWNX_FULLMAC */
+);
+
+
+TRACE_EVENT(
+    txq_queue_skb,
+    TP_PROTO(struct sk_buff *skb, struct rwnx_txq *txq, bool retry),
+    TP_ARGS(skb, txq, retry),
+    TP_STRUCT__entry(
+        __field(struct sk_buff *, skb)
+        __field(u16, txq_idx)
+        __field(s8, credit)
+        __field(u16, q_len)
+        __field(u16, q_len_retry)
+        __field(bool, retry)
+                     ),
+    TP_fast_assign(
+        __entry->skb = skb;
+        __entry->txq_idx = txq->idx;
+        __entry->credit = txq->credits;
+        __entry->q_len = skb_queue_len(&txq->sk_list);
+        __entry->q_len_retry = txq->nb_retry;
+        __entry->retry = retry;
+                   ),
+
+    TP_printk("%s skb=%p retry=%d txq_credits=%d queue_len=%d (retry = %d)",
+              __print_txq(__entry->txq_idx), __entry->skb, __entry->retry,
+              __entry->credit, __entry->q_len, __entry->q_len_retry)
+);
+
+#ifdef CONFIG_MAC80211_TXQ
+TRACE_EVENT(
+    txq_wake,
+    TP_PROTO(struct rwnx_txq *txq),
+    TP_ARGS(txq),
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+        __field(u16, q_len)
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+        __entry->q_len = txq->nb_ready_mac80211;
+                   ),
+
+    TP_printk("%s mac80211_queue_len=%d", __print_txq(__entry->txq_idx), __entry->q_len)
+);
+
+TRACE_EVENT(
+    txq_drop,
+    TP_PROTO(struct rwnx_txq *txq, unsigned long nb_drop),
+    TP_ARGS(txq, nb_drop),
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+        __field(u16, nb_drop)
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+        __entry->nb_drop = nb_drop;
+                   ),
+
+    TP_printk("%s %u pkt have been dropped by codel in mac80211 txq",
+              __print_txq(__entry->txq_idx), __entry->nb_drop)
+);
+
+#endif
+
+
+DECLARE_EVENT_CLASS(
+    idx_template,
+    TP_PROTO(u16 idx),
+    TP_ARGS(idx),
+    TP_STRUCT__entry(
+        __field(u16, idx)
+                     ),
+    TP_fast_assign(
+        __entry->idx = idx;
+                   ),
+    TP_printk("idx=%d", __entry->idx)
+);
+
+
+DEFINE_EVENT(idx_template, txq_vif_start,
+             TP_PROTO(u16 idx),
+             TP_ARGS(idx));
+
+DEFINE_EVENT(idx_template, txq_vif_stop,
+             TP_PROTO(u16 idx),
+             TP_ARGS(idx));
+
+TRACE_EVENT(
+    process_hw_queue,
+    TP_PROTO(struct rwnx_hwq *hwq),
+    TP_ARGS(hwq),
+    TP_STRUCT__entry(
+        __field(u16, hwq)
+        __array(u8, credits, CONFIG_USER_MAX)
+                     ),
+	TP_fast_assign(
+        int i;
+        __entry->hwq = hwq->id;
+        //for (i=0; i < CONFIG_USER_MAX; i ++)
+        //    __entry->credits[i] = hwq->credits[i];
+                   ),
+    TP_printk("hw_queue=%s hw_credits=%s",
+              __print_hwq(__entry->hwq), __print_hwq_cred(__entry->credits))
+);
+
+DECLARE_EVENT_CLASS(
+    sta_idx_template,
+    TP_PROTO(u16 idx),
+    TP_ARGS(idx),
+    TP_STRUCT__entry(
+        __field(u16, idx)
+                     ),
+    TP_fast_assign(
+        __entry->idx = idx;
+                   ),
+    TP_printk("%s", __print_sta(__entry->idx))
+);
+
+DEFINE_EVENT(sta_idx_template, txq_sta_start,
+             TP_PROTO(u16 idx),
+             TP_ARGS(idx));
+
+DEFINE_EVENT(sta_idx_template, txq_sta_stop,
+             TP_PROTO(u16 idx),
+             TP_ARGS(idx));
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+DEFINE_EVENT(sta_idx_template, ps_disable,
+             TP_PROTO(u16 idx),
+             TP_ARGS(idx));
+
+#endif  /* CONFIG_RWNX_FULLMAC */
+
+TRACE_EVENT(
+    skb_confirm,
+    TP_PROTO(struct sk_buff *skb, struct rwnx_txq *txq, struct rwnx_hwq *hwq,
+#ifdef CONFIG_RWNX_FULLMAC
+             struct tx_cfm_tag *cfm
+#else
+             u8 cfm
+#endif
+             ),
+
+    TP_ARGS(skb, txq, hwq, cfm),
+
+    TP_STRUCT__entry(
+        __field(struct sk_buff *, skb)
+        __field(u16, txq_idx)
+        __field(u8, hw_queue)
+        __array(u8, hw_credit, CONFIG_USER_MAX)
+        __field(s8, sw_credit)
+        __field(s8, sw_credit_up)
+#ifdef CONFIG_RWNX_FULLMAC
+        __field(u8, ampdu_size)
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+        __field(u16, amsdu)
+#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
+        __field(u16, sn)
+#endif /* CONFIG_RWNX_FULLMAC*/
+                     ),
+
+    TP_fast_assign(
+        int i;
+        __entry->skb = skb;
+        __entry->txq_idx = txq->idx;
+        __entry->hw_queue = hwq->id;
+        //for (i = 0 ; i < CONFIG_USER_MAX ; i++)
+        //    __entry->hw_credit[i] = hwq->credits[i];
+        __entry->sw_credit = txq->credits;
+#if defined CONFIG_RWNX_FULLMAC
+        __entry->sw_credit_up = cfm->credits;
+        __entry->ampdu_size = cfm->ampdu_size;
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+        __entry->amsdu = cfm->amsdu_size;
+        __entry->sn = cfm->sn;
+#endif
+#else
+        __entry->sw_credit_up = cfm
+#endif /* CONFIG_RWNX_FULLMAC */
+                   ),
+
+    TP_printk("%s skb=%p hw_queue=%s, hw_credits=%s, txq_credits=%d (+%d)"
+#ifdef CONFIG_RWNX_FULLMAC
+              " sn=%u ampdu=%d"
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+              " amsdu=%u"
+#endif
+#endif
+              , __print_txq(__entry->txq_idx), __entry->skb,
+              __print_hwq(__entry->hw_queue),
+              __print_hwq_cred(__entry->hw_credit),
+               __entry->sw_credit, __entry->sw_credit_up
+#ifdef CONFIG_RWNX_FULLMAC
+              , __entry->sn, __entry->ampdu_size
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+              , __entry->amsdu
+#endif
+#endif
+              )
+);
+
+TRACE_EVENT(
+    credit_update,
+    TP_PROTO(struct rwnx_txq *txq, s8_l cred_up),
+
+    TP_ARGS(txq, cred_up),
+
+    TP_STRUCT__entry(
+        __field(struct sk_buff *, skb)
+        __field(u16, txq_idx)
+        __field(s8, sw_credit)
+        __field(s8, sw_credit_up)
+                     ),
+
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+        __entry->sw_credit = txq->credits;
+        __entry->sw_credit_up = cred_up;
+                   ),
+
+    TP_printk("%s txq_credits=%d (%+d)", __print_txq(__entry->txq_idx),
+              __entry->sw_credit, __entry->sw_credit_up)
+)
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+DECLARE_EVENT_CLASS(
+    ps_template,
+    TP_PROTO(struct rwnx_sta *sta),
+    TP_ARGS(sta),
+    TP_STRUCT__entry(
+        __field(u16, idx)
+        __field(u16, ready_ps)
+        __field(u16, sp_ps)
+        __field(u16, ready_uapsd)
+        __field(u16, sp_uapsd)
+                     ),
+    TP_fast_assign(
+        __entry->idx  = sta->sta_idx;
+        __entry->ready_ps = sta->ps.pkt_ready[LEGACY_PS_ID];
+        __entry->sp_ps = sta->ps.sp_cnt[LEGACY_PS_ID];
+        __entry->ready_uapsd = sta->ps.pkt_ready[UAPSD_ID];
+        __entry->sp_uapsd = sta->ps.sp_cnt[UAPSD_ID];
+                   ),
+
+    TP_printk("%s [PS] ready=%d sp=%d [UAPSD] ready=%d sp=%d",
+              __print_sta(__entry->idx), __entry->ready_ps, __entry->sp_ps,
+              __entry->ready_uapsd, __entry->sp_uapsd)
+);
+
+DEFINE_EVENT(ps_template, ps_queue,
+             TP_PROTO(struct rwnx_sta *sta),
+             TP_ARGS(sta));
+
+DEFINE_EVENT(ps_template, ps_push,
+             TP_PROTO(struct rwnx_sta *sta),
+             TP_ARGS(sta));
+
+DEFINE_EVENT(ps_template, ps_enable,
+             TP_PROTO(struct rwnx_sta *sta),
+             TP_ARGS(sta));
+
+TRACE_EVENT(
+    ps_traffic_update,
+    TP_PROTO(u16 sta_idx, u8 traffic, bool uapsd),
+
+    TP_ARGS(sta_idx, traffic, uapsd),
+
+    TP_STRUCT__entry(
+        __field(u16, sta_idx)
+        __field(u8, traffic)
+        __field(bool, uapsd)
+                     ),
+
+    TP_fast_assign(
+        __entry->sta_idx = sta_idx;
+        __entry->traffic = traffic;
+        __entry->uapsd = uapsd;
+                   ),
+
+    TP_printk("%s %s%s traffic available ", __print_sta(__entry->sta_idx),
+              __entry->traffic ? "" : "no more ",
+              __entry->uapsd ? "U-APSD" : "legacy PS")
+);
+
+TRACE_EVENT(
+    ps_traffic_req,
+    TP_PROTO(struct rwnx_sta *sta, u16 pkt_req, u8 ps_id),
+    TP_ARGS(sta, pkt_req, ps_id),
+    TP_STRUCT__entry(
+        __field(u16, idx)
+        __field(u16, pkt_req)
+        __field(u8, ps_id)
+        __field(u16, ready)
+        __field(u16, sp)
+                     ),
+    TP_fast_assign(
+        __entry->idx  = sta->sta_idx;
+        __entry->pkt_req  = pkt_req;
+        __entry->ps_id  = ps_id;
+        __entry->ready = sta->ps.pkt_ready[ps_id];
+        __entry->sp = sta->ps.sp_cnt[ps_id];
+                   ),
+
+    TP_printk("%s %s traffic request %d pkt (ready=%d, sp=%d)",
+              __print_sta(__entry->idx),
+              __entry->ps_id == UAPSD_ID ? "U-APSD" : "legacy PS" ,
+              __entry->pkt_req, __entry->ready, __entry->sp)
+);
+
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+TRACE_EVENT(
+    amsdu_subframe,
+    TP_PROTO(struct rwnx_sw_txhdr *sw_txhdr),
+    TP_ARGS(sw_txhdr),
+    TP_STRUCT__entry(
+        __field(struct sk_buff *, skb)
+        __field(u16, txq_idx)
+        __field(u8, nb)
+        __field(u32, len)
+                     ),
+    TP_fast_assign(
+        __entry->skb = sw_txhdr->skb;
+        __entry->nb = sw_txhdr->amsdu.nb;
+        __entry->len = sw_txhdr->amsdu.len;
+        __entry->txq_idx = sw_txhdr->txq->idx;
+                   ),
+
+    TP_printk("%s skb=%p %s nb_subframe=%d, len=%u",
+              __print_txq(__entry->txq_idx), __entry->skb,
+              (__entry->nb == 2) ? "Start new AMSDU" : "Add subframe",
+              __entry->nb, __entry->len)
+);
+#endif
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+TRACE_EVENT(
+    mu_group_update,
+    TP_PROTO(struct rwnx_mu_group *group),
+    TP_ARGS(group),
+    TP_STRUCT__entry(
+        __field(u8, nb_user)
+        __field(u8, group_id)
+        __array(u8, users, CONFIG_USER_MAX)
+                     ),
+    TP_fast_assign(
+        int i;
+        __entry->nb_user = group->user_cnt;
+        for (i = 0; i < CONFIG_USER_MAX ; i++) {
+            if (group->users[i]) {
+                __entry->users[i] = group->users[i]->sta_idx;
+            } else {
+                __entry->users[i] = 0xff;
+            }
+        }
+
+        __entry->group_id = group->group_id;
+                   ),
+
+    TP_printk("Group-id = %d, Users = %s",
+              __entry->group_id,
+              __print_mu_group(__entry->nb_user, __entry->users))
+);
+
+TRACE_EVENT(
+    mu_group_delete,
+    TP_PROTO(int group_id),
+    TP_ARGS(group_id),
+    TP_STRUCT__entry(
+        __field(u8, group_id)
+                     ),
+    TP_fast_assign(
+        __entry->group_id = group_id;
+                   ),
+
+    TP_printk("Group-id = %d", __entry->group_id)
+);
+
+TRACE_EVENT(
+    mu_group_selection,
+    TP_PROTO(struct rwnx_sta *sta, int group_id),
+    TP_ARGS(sta, group_id),
+    TP_STRUCT__entry(
+        __field(u8, sta_idx)
+        __field(u8, group_id)
+                     ),
+    TP_fast_assign(
+        __entry->sta_idx = sta->sta_idx;
+        __entry->group_id = group_id;
+                   ),
+
+    TP_printk("[Sta %d] Group-id = %d", __entry->sta_idx, __entry->group_id)
+);
+
+TRACE_EVENT(
+    txq_select_mu_group,
+    TP_PROTO(struct rwnx_txq *txq, int group_id, int pos),
+
+    TP_ARGS(txq, group_id, pos),
+
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+        __field(u8, group_id)
+        __field(u8, pos)
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+        __entry->group_id = group_id;
+        __entry->pos = pos;
+                   ),
+
+    TP_printk("%s: group=%d pos=%d", __print_txq(__entry->txq_idx),
+              __entry->group_id, __entry->pos)
+);
+
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+#endif /* ! CONFIG_RWNX_FHOST */
+
+/*****************************************************************************
+ * TRACE functions for MESH
+ ****************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+DECLARE_EVENT_CLASS(
+    mesh_path_template,
+    TP_PROTO(struct rwnx_mesh_path *mesh_path),
+    TP_ARGS(mesh_path),
+    TP_STRUCT__entry(
+        __field(u8, idx)
+        __field(u8, next_hop_sta)
+        __array(u8, tgt_mac, ETH_ALEN)
+                     ),
+
+    TP_fast_assign(
+        __entry->idx = mesh_path->path_idx;
+        memcpy(__entry->tgt_mac, &mesh_path->tgt_mac_addr, ETH_ALEN);
+        if (mesh_path->p_nhop_sta)
+            __entry->next_hop_sta = mesh_path->p_nhop_sta->sta_idx;
+        else
+            __entry->next_hop_sta = 0xff;
+                   ),
+
+    TP_printk("Mpath(%d): target=%pM next_hop=STA-%d",
+              __entry->idx, __entry->tgt_mac, __entry->next_hop_sta)
+);
+
+DEFINE_EVENT(mesh_path_template, mesh_create_path,
+             TP_PROTO(struct rwnx_mesh_path *mesh_path),
+             TP_ARGS(mesh_path));
+
+DEFINE_EVENT(mesh_path_template, mesh_delete_path,
+             TP_PROTO(struct rwnx_mesh_path *mesh_path),
+             TP_ARGS(mesh_path));
+
+DEFINE_EVENT(mesh_path_template, mesh_update_path,
+             TP_PROTO(struct rwnx_mesh_path *mesh_path),
+             TP_ARGS(mesh_path));
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/*****************************************************************************
+ * TRACE functions for RADAR
+ ****************************************************************************/
+#ifdef CONFIG_RWNX_RADAR
+TRACE_EVENT(
+    radar_pulse,
+    TP_PROTO(u8 chain, struct radar_pulse *pulse),
+    TP_ARGS(chain, pulse),
+    TP_STRUCT__entry(
+        __field(u8, chain)
+        __field(s16, freq)
+        __field(u16, pri)
+        __field(u8, len)
+        __field(u8, fom)
+                     ),
+    TP_fast_assign(
+        __entry->freq = pulse->freq * 2;
+        __entry->len = pulse->len * 2;
+        __entry->fom = pulse->fom * 6;
+        __entry->pri = pulse->rep;
+        __entry->chain = chain;
+                   ),
+
+    TP_printk("%s: PRI=%.5d LEN=%.3d FOM=%.2d%% freq=%dMHz ",
+              __print_symbolic(__entry->chain,
+                               {RWNX_RADAR_RIU, "RIU"},
+                               {RWNX_RADAR_FCU, "FCU"}),
+              __entry->pri, __entry->len, __entry->fom, __entry->freq)
+            );
+
+TRACE_EVENT(
+    radar_detected,
+    TP_PROTO(u8 chain, u8 region, s16 freq, u8 type, u16 pri),
+    TP_ARGS(chain, region, freq, type, pri),
+    TP_STRUCT__entry(
+        __field(u8, chain)
+        __field(u8, region)
+        __field(s16, freq)
+        __field(u8, type)
+        __field(u16, pri)
+                     ),
+    TP_fast_assign(
+        __entry->chain = chain;
+        __entry->region = region;
+        __entry->freq = freq;
+        __entry->type = type;
+        __entry->pri = pri;
+                   ),
+    TP_printk("%s: region=%s type=%d freq=%dMHz (pri=%dus)",
+              __print_symbolic(__entry->chain,
+                               {RWNX_RADAR_RIU, "RIU"},
+                               {RWNX_RADAR_FCU, "FCU"}),
+              __print_symbolic(__entry->region,
+                               {NL80211_DFS_UNSET, "UNSET"},
+                               {NL80211_DFS_FCC, "FCC"},
+                               {NL80211_DFS_ETSI, "ETSI"},
+                               {NL80211_DFS_JP, "JP"}),
+              __entry->type, __entry->freq, __entry->pri)
+);
+
+TRACE_EVENT(
+    radar_set_region,
+    TP_PROTO(u8 region),
+    TP_ARGS(region),
+    TP_STRUCT__entry(
+        __field(u8, region)
+                     ),
+    TP_fast_assign(
+        __entry->region = region;
+                   ),
+    TP_printk("region=%s",
+              __print_symbolic(__entry->region,
+                               {NL80211_DFS_UNSET, "UNSET"},
+                               {NL80211_DFS_FCC, "FCC"},
+                               {NL80211_DFS_ETSI, "ETSI"},
+                               {NL80211_DFS_JP, "JP"}))
+);
+
+TRACE_EVENT(
+    radar_enable_detection,
+    TP_PROTO(u8 region, u8 enable, u8 chain),
+    TP_ARGS(region, enable, chain),
+    TP_STRUCT__entry(
+        __field(u8, region)
+        __field(u8, chain)
+        __field(u8, enable)
+                     ),
+    TP_fast_assign(
+        __entry->chain = chain;
+        __entry->enable = enable;
+        __entry->region = region;
+                   ),
+    TP_printk("%s: %s radar detection %s",
+               __print_symbolic(__entry->chain,
+                               {RWNX_RADAR_RIU, "RIU"},
+                               {RWNX_RADAR_FCU, "FCU"}),
+              __print_symbolic(__entry->enable,
+                               {RWNX_RADAR_DETECT_DISABLE, "Disable"},
+                               {RWNX_RADAR_DETECT_ENABLE, "Enable (no report)"},
+                               {RWNX_RADAR_DETECT_REPORT, "Enable"}),
+              __entry->enable == RWNX_RADAR_DETECT_DISABLE ? "" :
+              __print_symbolic(__entry->region,
+                               {NL80211_DFS_UNSET, "UNSET"},
+                               {NL80211_DFS_FCC, "FCC"},
+                               {NL80211_DFS_ETSI, "ETSI"},
+                               {NL80211_DFS_JP, "JP"}))
+);
+#endif /* CONFIG_RWNX_RADAR */
+
+/*****************************************************************************
+ * TRACE functions for IPC message
+ ****************************************************************************/
+#include "rwnx_strs.h"
+
+DECLARE_EVENT_CLASS(
+    ipc_msg_template,
+    TP_PROTO(u16 id),
+    TP_ARGS(id),
+    TP_STRUCT__entry(
+        __field(u16, id)
+                     ),
+    TP_fast_assign(
+        __entry->id  = id;
+                   ),
+
+    TP_printk("%s (%d - %d)", RWNX_ID2STR(__entry->id),
+              MSG_T(__entry->id), MSG_I(__entry->id))
+);
+
+DEFINE_EVENT(ipc_msg_template, msg_send,
+             TP_PROTO(u16 id),
+             TP_ARGS(id));
+
+DEFINE_EVENT(ipc_msg_template, msg_recv,
+             TP_PROTO(u16 id),
+             TP_ARGS(id));
+
+
+
+#endif /* !defined(_RWNX_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ) */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE rwnx_events
+#include <trace/define_trace.h>
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_fw_trace.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_fw_trace.c
new file mode 100755
index 0000000..fa96583
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_fw_trace.c
@@ -0,0 +1,47 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_fw_trace.c
+ *
+ * Copyright (C) RivieraWaves 2017-2019
+ *
+ ******************************************************************************
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include "rwnx_fw_trace.h"
+
+int rwnx_fw_log_init(struct rwnx_fw_log *fw_log)
+{
+	u8 *buf = kmalloc(FW_LOG_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	fw_log->buf.data = buf;
+	fw_log->buf.start = fw_log->buf.data;
+	fw_log->buf.size  = 0;
+	fw_log->buf.end   = fw_log->buf.data;
+	fw_log->buf.dataend = fw_log->buf.data + FW_LOG_SIZE;
+	spin_lock_init(&fw_log->lock);
+
+	printk("fw_log_init: %lx, %lx\n", (unsigned long)fw_log->buf.start, (unsigned long)(fw_log->buf.dataend));
+	return 0;
+}
+
+void rwnx_fw_log_deinit(struct rwnx_fw_log *fw_log)
+{
+	if (!fw_log)
+		return;
+
+	if (fw_log->buf.data)
+		kfree(fw_log->buf.data);
+	fw_log->buf.start = NULL;
+	fw_log->buf.end   = NULL;
+	fw_log->buf.size = 0;
+}
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_fw_trace.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_fw_trace.h
new file mode 100755
index 0000000..7956790
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_fw_trace.h
@@ -0,0 +1,35 @@
+/**
+ ******************************************************************************
+ *
+ * rwnx_fw_trace.h
+ *
+ * Copyright (C) RivieraWaves 2017-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_FW_TRACE_H_
+#define _RWNX_FW_TRACE_H_
+
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#define FW_LOG_SIZE (10240)
+
+struct rwnx_fw_log_buf {
+	uint8_t *data;
+	uint8_t *start;
+	uint8_t *end;
+	uint8_t *dataend;
+	uint32_t size;
+};
+
+struct rwnx_fw_log {
+	struct rwnx_fw_log_buf buf;
+	spinlock_t lock;
+};
+
+int rwnx_fw_log_init(struct rwnx_fw_log *fw_log);
+void rwnx_fw_log_deinit(struct rwnx_fw_log *fw_log);
+#endif /* _RWNX_FW_TRACE_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_gki.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_gki.c
new file mode 100755
index 0000000..ad6c52d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_gki.c
@@ -0,0 +1,408 @@
+#include <linux/version.h>
+#ifdef ANDROID_PLATFORM
+#include "net/wireless/core.h"
+#endif
+
+
+#undef NL80211_MCGRP_MLME
+#define NL80211_MCGRP_MLME 3
+#if IS_ENABLED(CONFIG_GKI_OPT_FEATURES) && IS_ENABLED(CONFIG_ANDROID) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0))
+
+
+static struct genl_family rwnx_nl80211_fam;
+
+static bool __rwnx_cfg80211_unexpected_frame(struct net_device *dev, u8 cmd,
+				const u8 *addr, gfp_t gfp)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+	struct sk_buff *msg;
+	void *hdr;
+	u32 nlportid = READ_ONCE(wdev->ap_unexpected_nlportid);
+
+	if (!nlportid)
+		return false;
+
+	msg = nlmsg_new(100, gfp);
+	if (!msg)
+		return true;
+
+	hdr = genlmsg_put(msg, 0, 0, &rwnx_nl80211_fam, 0, cmd);
+	if (!hdr) {
+		nlmsg_free(msg);
+		return true;
+	}
+
+	if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+		goto nla_put_failure;
+
+	genlmsg_end(msg, hdr);
+	genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
+	return true;
+
+ nla_put_failure:
+	nlmsg_free(msg);
+	return true;
+}
+
+bool rwnx_cfg80211_rx_spurious_frame(struct net_device *dev,
+				const u8 *addr, gfp_t gfp)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	bool ret;
+
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+		    wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
+		return false;
+	}
+	ret = __rwnx_cfg80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
+					addr, gfp);
+	return ret;
+}
+
+bool rwnx_cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
+				const u8 *addr, gfp_t gfp)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	bool ret;
+
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+		    wdev->iftype != NL80211_IFTYPE_P2P_GO &&
+		    wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
+		return false;
+	}
+	ret = __rwnx_cfg80211_unexpected_frame(dev,
+					 NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
+					 addr, gfp);
+	return ret;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
+void rwnx_cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
+				const u8 *ie, u8 ie_len,
+				int sig_dbm, gfp_t gfp)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+	struct sk_buff *msg;
+	void *hdr;
+
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))
+		return;
+
+	msg = nlmsg_new(100 + ie_len, gfp);
+	if (!msg)
+		return;
+
+	hdr = genlmsg_put(msg, 0, 0, &rwnx_nl80211_fam, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
+	if (!hdr) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    (ie_len && ie &&
+	     nla_put(msg, NL80211_ATTR_IE, ie_len, ie)) ||
+	    (sig_dbm &&
+	     nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)))
+		goto nla_put_failure;
+
+	genlmsg_end(msg, hdr);
+
+#define NL80211_MCGRP_MLME 3
+	genlmsg_multicast_netns(&rwnx_nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+				NL80211_MCGRP_MLME, gfp);
+	return;
+
+ nla_put_failure:
+	nlmsg_free(msg);
+}
+#endif
+
+void rwnx_cfg80211_report_obss_beacon(struct wiphy *wiphy,
+				const u8 *frame, size_t len,
+				int freq, int sig_dbm)
+{
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+	struct sk_buff *msg;
+	void *hdr;
+	struct cfg80211_beacon_registration *reg;
+
+	spin_lock_bh(&rdev->beacon_registrations_lock);
+	list_for_each_entry(reg, &rdev->beacon_registrations, list) {
+		msg = nlmsg_new(len + 100, GFP_ATOMIC);
+		if (!msg) {
+			spin_unlock_bh(&rdev->beacon_registrations_lock);
+			return;
+		}
+
+		hdr = genlmsg_put(msg, 0, 0, &rwnx_nl80211_fam, 0, NL80211_CMD_FRAME);
+		if (!hdr)
+			goto nla_put_failure;
+
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+		    (freq &&
+		     nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
+		    (sig_dbm &&
+		     nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
+		    nla_put(msg, NL80211_ATTR_FRAME, len, frame))
+			goto nla_put_failure;
+
+		genlmsg_end(msg, hdr);
+
+		genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, reg->nlportid);
+	}
+	spin_unlock_bh(&rdev->beacon_registrations_lock);
+	return;
+
+ nla_put_failure:
+	spin_unlock_bh(&rdev->beacon_registrations_lock);
+	nlmsg_free(msg);
+}
+
+static int rwnx_nl80211_send_chandef(struct sk_buff *msg,
+				const struct cfg80211_chan_def *chandef)
+{
+	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+		return -EINVAL;
+
+	if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+			chandef->chan->center_freq))
+		return -ENOBUFS;
+	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_20_NOHT:
+	case NL80211_CHAN_WIDTH_20:
+	case NL80211_CHAN_WIDTH_40:
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+				cfg80211_get_chandef_type(chandef)))
+			return -ENOBUFS;
+		break;
+	default:
+		break;
+	}
+	if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width))
+		return -ENOBUFS;
+	if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1))
+		return -ENOBUFS;
+	if (chandef->center_freq2 &&
+	    nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2))
+		return -ENOBUFS;
+	return 0;
+}
+
+void rwnx_cfg80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
+				struct net_device *netdev,
+				struct cfg80211_chan_def *chandef,
+				gfp_t gfp,
+				enum nl80211_commands notif,
+				u8 count)
+{
+	struct sk_buff *msg;
+	void *hdr;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+	if (!msg)
+		return;
+
+	hdr = genlmsg_put(msg, 0, 0, &rwnx_nl80211_fam, 0, notif);
+	if (!hdr) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
+		goto nla_put_failure;
+
+	if (rwnx_nl80211_send_chandef(msg, chandef))
+		goto nla_put_failure;
+
+	if ((notif == NL80211_CMD_CH_SWITCH_STARTED_NOTIFY) &&
+	    (nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT, count)))
+			goto nla_put_failure;
+
+	genlmsg_end(msg, hdr);
+
+	genlmsg_multicast_netns(&rwnx_nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+				NL80211_MCGRP_MLME, gfp);
+	return;
+
+ nla_put_failure:
+	nlmsg_free(msg);
+}
+
+void rwnx_cfg80211_ch_switch_started_notify(struct net_device *dev,
+				struct cfg80211_chan_def *chandef,
+				u8 count
+	#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
+				, bool quiet
+	#endif
+				)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct wiphy *wiphy = wdev->wiphy;
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+	rwnx_cfg80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
+				NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, count);
+}
+
+int rwnx_regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy,
+				struct ieee80211_regdomain *rd)
+{
+	wiphy_apply_custom_regulatory(wiphy, rd);
+	return 0;
+}
+
+void rwnx_skb_append(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list)
+{
+	unsigned long flags;
+	struct sk_buff *prev = old;
+	struct sk_buff *next = prev->next;
+	spin_lock_irqsave(&list->lock, flags);
+	WRITE_ONCE(newsk->next, next);
+	WRITE_ONCE(newsk->prev, prev);
+	WRITE_ONCE(next->prev, newsk);
+	WRITE_ONCE(prev->next, newsk);
+	list->qlen++;
+	spin_unlock_irqrestore(&list->lock, flags);
+}
+
+bool rwnx_ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
+					  u8 *op_class)
+{
+	u8 vht_opclass;
+	u32 freq = chandef->center_freq1;
+
+	if (freq >= 2412 && freq <= 2472) {
+		if (chandef->width > NL80211_CHAN_WIDTH_40)
+			return false;
+
+		/* 2.407 GHz, channels 1..13 */
+		if (chandef->width == NL80211_CHAN_WIDTH_40) {
+			if (freq > chandef->chan->center_freq)
+				*op_class = 83; /* HT40+ */
+			else
+				*op_class = 84; /* HT40- */
+		} else {
+			*op_class = 81;
+		}
+
+		return true;
+	}
+
+	if (freq == 2484) {
+		/* channel 14 is only for IEEE 802.11b */
+		if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT)
+			return false;
+
+		*op_class = 82; /* channel 14 */
+		return true;
+	}
+
+	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_80:
+		vht_opclass = 128;
+		break;
+	case NL80211_CHAN_WIDTH_160:
+		vht_opclass = 129;
+		break;
+	case NL80211_CHAN_WIDTH_80P80:
+		vht_opclass = 130;
+		break;
+	case NL80211_CHAN_WIDTH_10:
+	case NL80211_CHAN_WIDTH_5:
+		return false; /* unsupported for now */
+	default:
+		vht_opclass = 0;
+		break;
+	}
+
+	/* 5 GHz, channels 36..48 */
+	if (freq >= 5180 && freq <= 5240) {
+		if (vht_opclass) {
+			*op_class = vht_opclass;
+		} else if (chandef->width == NL80211_CHAN_WIDTH_40) {
+			if (freq > chandef->chan->center_freq)
+				*op_class = 116;
+			else
+				*op_class = 117;
+		} else {
+			*op_class = 115;
+		}
+
+		return true;
+	}
+
+	/* 5 GHz, channels 52..64 */
+	if (freq >= 5260 && freq <= 5320) {
+		if (vht_opclass) {
+			*op_class = vht_opclass;
+		} else if (chandef->width == NL80211_CHAN_WIDTH_40) {
+			if (freq > chandef->chan->center_freq)
+				*op_class = 119;
+			else
+				*op_class = 120;
+		} else {
+			*op_class = 118;
+		}
+
+		return true;
+	}
+
+	/* 5 GHz, channels 100..144 */
+	if (freq >= 5500 && freq <= 5720) {
+		if (vht_opclass) {
+			*op_class = vht_opclass;
+		} else if (chandef->width == NL80211_CHAN_WIDTH_40) {
+			if (freq > chandef->chan->center_freq)
+				*op_class = 122;
+			else
+				*op_class = 123;
+		} else {
+			*op_class = 121;
+		}
+
+		return true;
+	}
+
+	/* 5 GHz, channels 149..169 */
+	if (freq >= 5745 && freq <= 5845) {
+		if (vht_opclass) {
+			*op_class = vht_opclass;
+		} else if (chandef->width == NL80211_CHAN_WIDTH_40) {
+			if (freq > chandef->chan->center_freq)
+				*op_class = 126;
+			else
+				*op_class = 127;
+		} else if (freq <= 5805) {
+			*op_class = 124;
+		} else {
+			*op_class = 125;
+		}
+
+		return true;
+	}
+
+	/* 56.16 GHz, channel 1..4 */
+	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6) {
+		if (chandef->width >= NL80211_CHAN_WIDTH_40)
+			return false;
+
+		*op_class = 180;
+		return true;
+	}
+
+	/* not supported yet */
+	return false;
+}
+
+int rwnx_call_usermodehelper(const char *path, char **argv, char **envp, int wait)
+{
+	return -1;
+}
+
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_gki.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_gki.h
new file mode 100755
index 0000000..6a8fb3e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_gki.h
@@ -0,0 +1,71 @@
+#ifndef __RWNX_GKI_H
+#define __RWNX_GKI_H
+
+#ifdef ANDROID_PLATFORM
+#include "net/wireless/core.h"
+#endif
+
+#if IS_ENABLED(CONFIG_GKI_OPT_FEATURES) && IS_ENABLED(CONFIG_ANDROID) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0))
+
+
+bool rwnx_cfg80211_rx_spurious_frame(struct net_device *dev,
+				const u8 *addr, gfp_t gfp);
+
+bool rwnx_cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
+				const u8 *addr, gfp_t gfp);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
+void rwnx_cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
+				const u8 *ie, u8 ie_len,
+				int sig_dbm, gfp_t gfp);
+#endif
+
+void rwnx_cfg80211_report_obss_beacon(struct wiphy *wiphy,
+				const u8 *frame, size_t len,
+				int freq, int sig_dbm);
+
+void rwnx_cfg80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
+				struct net_device *netdev,
+				struct cfg80211_chan_def *chandef,
+				gfp_t gfp,
+				enum nl80211_commands notif,
+				u8 count);
+
+void rwnx_cfg80211_ch_switch_started_notify(struct net_device *dev,
+				struct cfg80211_chan_def *chandef,
+				u8 count
+			#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
+				, bool quiet
+			#endif
+				);
+
+int rwnx_regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy,
+				struct ieee80211_regdomain *rd);
+
+void rwnx_skb_append(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list);
+
+bool rwnx_ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
+				u8 *op_class);
+
+int rwnx_call_usermodehelper(const char *path, char **argv, char **envp, int wait);
+
+#else
+
+#define rwnx_cfg80211_rx_spurious_frame           cfg80211_rx_spurious_frame
+#define rwnx_cfg80211_rx_unexpected_4addr_frame   cfg80211_rx_unexpected_4addr_frame
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
+#define rwnx_cfg80211_notify_new_peer_candidate   cfg80211_notify_new_peer_candidate
+#endif
+
+#define rwnx_cfg80211_report_obss_beacon          cfg80211_report_obss_beacon
+#define rwnx_cfg80211_ch_switch_notify            cfg80211_ch_switch_notify
+#define rwnx_cfg80211_ch_switch_started_notify    cfg80211_ch_switch_started_notify
+#define rwnx_regulatory_set_wiphy_regd_sync_rtnl  regulatory_set_wiphy_regd_sync_rtnl
+#define rwnx_skb_append                           skb_append
+#define rwnx_ieee80211_chandef_to_operating_class ieee80211_chandef_to_operating_class
+#define rwnx_call_usermodehelper                  call_usermodehelper
+
+#endif
+
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_irqs.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_irqs.c
new file mode 100755
index 0000000..398e549
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_irqs.c
@@ -0,0 +1,66 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_irqs.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#include <linux/interrupt.h>
+
+#include "rwnx_defs.h"
+#include "ipc_host.h"
+#include "rwnx_prof.h"
+
+/**
+ * rwnx_irq_hdlr - IRQ handler
+ *
+ * Handler registerd by the platform driver
+ */
+irqreturn_t rwnx_irq_hdlr(int irq, void *dev_id)
+{
+    struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)dev_id;
+    disable_irq_nosync(irq);
+    tasklet_schedule(&rwnx_hw->task);
+    return IRQ_HANDLED;
+}
+
+/**
+ * rwnx_task - Bottom half for IRQ handler
+ *
+ * Read irq status and process accordingly
+ */
+void rwnx_task(unsigned long data)
+{
+	struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)data;
+
+#if 0
+    struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+    u32 status, statuses = 0;
+
+    /* Ack unconditionnally in case ipc_host_get_status does not see the irq */
+    rwnx_plat->ack_irq(rwnx_plat);
+
+    while ((status = ipc_host_get_status(rwnx_hw->ipc_env))) {
+        statuses |= status;
+        /* All kinds of IRQs will be handled in one shot (RX, MSG, DBG, ...)
+         * this will ack IPC irqs not the cfpga irqs */
+        ipc_host_irq(rwnx_hw->ipc_env, status);
+
+        rwnx_plat->ack_irq(rwnx_plat);
+    }
+#endif
+    //if (statuses & IPC_IRQ_E2A_RXDESC)
+    //    rwnx_hw->stats.last_rx = now;
+    //if (statuses & IPC_IRQ_E2A_TXCFM)
+    //    rwnx_hw->stats.last_tx = now;
+
+    //printk("rwnx_task\n");
+    spin_lock_bh(&rwnx_hw->tx_lock);
+    rwnx_hwq_process_all(rwnx_hw);
+    spin_unlock_bh(&rwnx_hw->tx_lock);
+#if 0
+    enable_irq(rwnx_platform_get_irq(rwnx_plat));
+#endif
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_irqs.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_irqs.h
new file mode 100755
index 0000000..d3fb451
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_irqs.h
@@ -0,0 +1,20 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_irqs.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#ifndef _RWNX_IRQS_H_
+#define _RWNX_IRQS_H_
+
+#include <linux/interrupt.h>
+
+/* IRQ handler to be registered by platform driver */
+irqreturn_t rwnx_irq_hdlr(int irq, void *dev_id);
+
+void rwnx_task(unsigned long data);
+
+#endif /* _RWNX_IRQS_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_main.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_main.c
new file mode 100755
index 0000000..5acd34e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_main.c
@@ -0,0 +1,8880 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_main.c
+ *
+ * @brief Entry point of the RWNX driver
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/inetdevice.h>
+#include <net/cfg80211.h>
+#include <net/ip.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <net/netlink.h>
+#include <linux/wireless.h>
+#include <linux/if_arp.h>
+#include <linux/ctype.h>
+#include <linux/random.h>
+#include "rwnx_defs.h"
+#include "rwnx_dini.h"
+#include "rwnx_msg_tx.h"
+#include "reg_access.h"
+#include "hal_desc.h"
+#include "rwnx_debugfs.h"
+#include "rwnx_cfgfile.h"
+#include "rwnx_irqs.h"
+#include "rwnx_radar.h"
+#include "rwnx_version.h"
+#ifdef CONFIG_RWNX_BFMER
+#include "rwnx_bfmer.h"
+#endif //(CONFIG_RWNX_BFMER)
+#include "rwnx_tdls.h"
+#include "rwnx_events.h"
+#include "rwnx_compat.h"
+#include "rwnx_version.h"
+#include "rwnx_main.h"
+#include "aicwf_txrxif.h"
+#ifdef AICWF_SDIO_SUPPORT
+#include "aicwf_sdio.h"
+#endif
+#ifdef AICWF_USB_SUPPORT
+#include "aicwf_usb.h"
+#endif
+
+//static u8 wifi_mac_addr[6] = {0};
+
+u8 chip_sub_id;
+u8 chip_mcu_id;
+
+extern const struct aicbsp_firmware *aicbsp_firmware_list;
+extern const struct aicbsp_firmware fw_8800dc_u01[];
+extern const struct aicbsp_firmware fw_8800dc_u02[];
+extern const struct aicbsp_firmware fw_8800dc_h_u02[];
+extern const struct aicbsp_firmware fw_8800d80_u01[];
+extern const struct aicbsp_firmware fw_8800d80_u02[];
+extern struct aicbsp_info_t aicbsp_info;
+
+
+#define RW_DRV_DESCRIPTION  "RivieraWaves 11nac driver for Linux cfg80211"
+#define RW_DRV_COPYRIGHT    "Copyright(c) 2015-2017 RivieraWaves"
+#define RW_DRV_AUTHOR       "RivieraWaves S.A.S"
+
+#define RWNX_PRINT_CFM_ERR(req) \
+        printk(KERN_CRIT "%s: Status Error(%d)\n", #req, (&req##_cfm)->status)
+
+#define RWNX_HT_CAPABILITIES                                    \
+{                                                               \
+    .ht_supported   = true,                                     \
+    .cap            = 0,                                        \
+    .ampdu_factor   = IEEE80211_HT_MAX_AMPDU_64K,               \
+    .ampdu_density  = IEEE80211_HT_MPDU_DENSITY_16,             \
+    .mcs        = {                                             \
+        .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },        \
+        .rx_highest = cpu_to_le16(65),                          \
+        .tx_params = IEEE80211_HT_MCS_TX_DEFINED,               \
+    },                                                          \
+}
+
+#define RWNX_VHT_CAPABILITIES                                   \
+{                                                               \
+    .vht_supported = false,                                     \
+    .cap       =                                                \
+      (7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT),\
+    .vht_mcs       = {                                          \
+        .rx_mcs_map = cpu_to_le16(                              \
+                      IEEE80211_VHT_MCS_SUPPORT_0_9    << 0  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 2  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 4  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 6  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 8  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 10 |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 12 |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 14),  \
+        .tx_mcs_map = cpu_to_le16(                              \
+                      IEEE80211_VHT_MCS_SUPPORT_0_9    << 0  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 2  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 4  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 6  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 8  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 10 |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 12 |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 14),  \
+    }                                                           \
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) || defined(CONFIG_HE_FOR_OLD_KERNEL)
+#define RWNX_HE_CAPABILITIES                                    \
+{                                                               \
+    .has_he = false,                                            \
+    .he_cap_elem = {                                            \
+        .mac_cap_info[0] = 0,                                   \
+        .mac_cap_info[1] = 0,                                   \
+        .mac_cap_info[2] = 0,                                   \
+        .mac_cap_info[3] = 0,                                   \
+        .mac_cap_info[4] = 0,                                   \
+        .mac_cap_info[5] = 0,                                   \
+        .phy_cap_info[0] = 0,                                   \
+        .phy_cap_info[1] = 0,                                   \
+        .phy_cap_info[2] = 0,                                   \
+        .phy_cap_info[3] = 0,                                   \
+        .phy_cap_info[4] = 0,                                   \
+        .phy_cap_info[5] = 0,                                   \
+        .phy_cap_info[6] = 0,                                   \
+        .phy_cap_info[7] = 0,                                   \
+        .phy_cap_info[8] = 0,                                   \
+        .phy_cap_info[9] = 0,                                   \
+        .phy_cap_info[10] = 0,                                  \
+    },                                                          \
+    .he_mcs_nss_supp = {                                        \
+        .rx_mcs_80 = cpu_to_le16(0xfffa),                       \
+        .tx_mcs_80 = cpu_to_le16(0xfffa),                       \
+        .rx_mcs_160 = cpu_to_le16(0xffff),                      \
+        .tx_mcs_160 = cpu_to_le16(0xffff),                      \
+        .rx_mcs_80p80 = cpu_to_le16(0xffff),                    \
+        .tx_mcs_80p80 = cpu_to_le16(0xffff),                    \
+    },                                                          \
+    .ppe_thres = {0x08, 0x1c, 0x07},                            \
+}
+#else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+#define RWNX_HE_CAPABILITIES                                    \
+{                                                               \
+    .has_he = false,                                            \
+    .he_cap_elem = {                                            \
+        .mac_cap_info[0] = 0,                                   \
+        .mac_cap_info[1] = 0,                                   \
+        .mac_cap_info[2] = 0,                                   \
+        .mac_cap_info[3] = 0,                                   \
+        .mac_cap_info[4] = 0,                                   \
+        .phy_cap_info[0] = 0,                                   \
+        .phy_cap_info[1] = 0,                                   \
+        .phy_cap_info[2] = 0,                                   \
+        .phy_cap_info[3] = 0,                                   \
+        .phy_cap_info[4] = 0,                                   \
+        .phy_cap_info[5] = 0,                                   \
+        .phy_cap_info[6] = 0,                                   \
+        .phy_cap_info[7] = 0,                                   \
+        .phy_cap_info[8] = 0,                                   \
+    },                                                          \
+    .he_mcs_nss_supp = {                                        \
+        .rx_mcs_80 = cpu_to_le16(0xfffa),                       \
+        .tx_mcs_80 = cpu_to_le16(0xfffa),                       \
+        .rx_mcs_160 = cpu_to_le16(0xffff),                      \
+        .tx_mcs_160 = cpu_to_le16(0xffff),                      \
+        .rx_mcs_80p80 = cpu_to_le16(0xffff),                    \
+        .tx_mcs_80p80 = cpu_to_le16(0xffff),                    \
+    },                                                          \
+    .ppe_thres = {0x08, 0x1c, 0x07},                            \
+}
+#endif
+#endif
+
+#define RATE(_bitrate, _hw_rate, _flags) {      \
+    .bitrate    = (_bitrate),                   \
+    .flags      = (_flags),                     \
+    .hw_value   = (_hw_rate),                   \
+}
+
+#define CHAN(_freq) {                           \
+    .center_freq    = (_freq),                  \
+    .max_power  = 30, /* FIXME */               \
+}
+
+static struct ieee80211_rate rwnx_ratetable[] = {
+    RATE(10,  0x00, 0),
+    RATE(20,  0x01, IEEE80211_RATE_SHORT_PREAMBLE),
+    RATE(55,  0x02, IEEE80211_RATE_SHORT_PREAMBLE),
+    RATE(110, 0x03, IEEE80211_RATE_SHORT_PREAMBLE),
+    RATE(60,  0x04, 0),
+    RATE(90,  0x05, 0),
+    RATE(120, 0x06, 0),
+    RATE(180, 0x07, 0),
+    RATE(240, 0x08, 0),
+    RATE(360, 0x09, 0),
+    RATE(480, 0x0A, 0),
+    RATE(540, 0x0B, 0),
+};
+
+/* The channels indexes here are not used anymore */
+static struct ieee80211_channel rwnx_2ghz_channels[] = {
+    CHAN(2412),
+    CHAN(2417),
+    CHAN(2422),
+    CHAN(2427),
+    CHAN(2432),
+    CHAN(2437),
+    CHAN(2442),
+    CHAN(2447),
+    CHAN(2452),
+    CHAN(2457),
+    CHAN(2462),
+    CHAN(2467),
+    CHAN(2472),
+    CHAN(2484),
+    // Extra channels defined only to be used for PHY measures.
+    // Enabled only if custregd and custchan parameters are set
+    CHAN(2390),
+    CHAN(2400),
+    CHAN(2410),
+    CHAN(2420),
+    CHAN(2430),
+    CHAN(2440),
+    CHAN(2450),
+    CHAN(2460),
+    CHAN(2470),
+    CHAN(2480),
+    CHAN(2490),
+    CHAN(2500),
+    CHAN(2510),
+};
+
+#ifdef USE_5G
+static struct ieee80211_channel rwnx_5ghz_channels[] = {
+    CHAN(5180),             // 36 -   20MHz
+    CHAN(5200),             // 40 -   20MHz
+    CHAN(5220),             // 44 -   20MHz
+    CHAN(5240),             // 48 -   20MHz
+    CHAN(5260),             // 52 -   20MHz
+    CHAN(5280),             // 56 -   20MHz
+    CHAN(5300),             // 60 -   20MHz
+    CHAN(5320),             // 64 -   20MHz
+    CHAN(5500),             // 100 -  20MHz
+    CHAN(5520),             // 104 -  20MHz
+    CHAN(5540),             // 108 -  20MHz
+    CHAN(5560),             // 112 -  20MHz
+    CHAN(5580),             // 116 -  20MHz
+    CHAN(5600),             // 120 -  20MHz
+    CHAN(5620),             // 124 -  20MHz
+    CHAN(5640),             // 128 -  20MHz
+    CHAN(5660),             // 132 -  20MHz
+    CHAN(5680),             // 136 -  20MHz
+    CHAN(5700),             // 140 -  20MHz
+    CHAN(5720),             // 144 -  20MHz
+    CHAN(5745),             // 149 -  20MHz
+    CHAN(5765),             // 153 -  20MHz
+    CHAN(5785),             // 157 -  20MHz
+    CHAN(5805),             // 161 -  20MHz
+    CHAN(5825),             // 165 -  20MHz
+    // Extra channels defined only to be used for PHY measures.
+    // Enabled only if custregd and custchan parameters are set
+    CHAN(5190),
+    CHAN(5210),
+    CHAN(5230),
+    CHAN(5250),
+    CHAN(5270),
+    CHAN(5290),
+    CHAN(5310),
+    CHAN(5330),
+    CHAN(5340),
+    CHAN(5350),
+    CHAN(5360),
+    CHAN(5370),
+    CHAN(5380),
+    CHAN(5390),
+    CHAN(5400),
+    CHAN(5410),
+    CHAN(5420),
+    CHAN(5430),
+    CHAN(5440),
+    CHAN(5450),
+    CHAN(5460),
+    CHAN(5470),
+    CHAN(5480),
+    CHAN(5490),
+    CHAN(5510),
+    CHAN(5530),
+    CHAN(5550),
+    CHAN(5570),
+    CHAN(5590),
+    CHAN(5610),
+    CHAN(5630),
+    CHAN(5650),
+    CHAN(5670),
+    CHAN(5690),
+    CHAN(5710),
+    CHAN(5730),
+    CHAN(5750),
+    CHAN(5760),
+    CHAN(5770),
+    CHAN(5780),
+    CHAN(5790),
+    CHAN(5800),
+    CHAN(5810),
+    CHAN(5820),
+    CHAN(5830),
+    CHAN(5840),
+    CHAN(5850),
+    CHAN(5860),
+    CHAN(5870),
+    CHAN(5880),
+    CHAN(5890),
+    CHAN(5900),
+    CHAN(5910),
+    CHAN(5920),
+    CHAN(5930),
+    CHAN(5940),
+    CHAN(5950),
+    CHAN(5960),
+    CHAN(5970),
+};
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) || defined(CONFIG_HE_FOR_OLD_KERNEL)
+struct ieee80211_sband_iftype_data rwnx_he_capa = {
+    .types_mask = BIT(NL80211_IFTYPE_STATION)|BIT(NL80211_IFTYPE_AP),
+    .he_cap = RWNX_HE_CAPABILITIES,
+};
+#endif
+
+static struct ieee80211_supported_band rwnx_band_2GHz = {
+    .channels   = rwnx_2ghz_channels,
+    .n_channels = ARRAY_SIZE(rwnx_2ghz_channels) - 13, // -13 to exclude extra channels
+    .bitrates   = rwnx_ratetable,
+    .n_bitrates = ARRAY_SIZE(rwnx_ratetable),
+    .ht_cap     = RWNX_HT_CAPABILITIES,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+    .vht_cap    = RWNX_VHT_CAPABILITIES,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+    .iftype_data = &rwnx_he_capa,
+    .n_iftype_data = 1,
+#endif
+};
+
+#ifdef USE_5G
+static struct ieee80211_supported_band rwnx_band_5GHz = {
+    .channels   = rwnx_5ghz_channels,
+    .n_channels = ARRAY_SIZE(rwnx_5ghz_channels) - 59, // -59 to exclude extra channels
+    .bitrates   = &rwnx_ratetable[4],
+    .n_bitrates = ARRAY_SIZE(rwnx_ratetable) - 4,
+    .ht_cap     = RWNX_HT_CAPABILITIES,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+    .vht_cap    = RWNX_VHT_CAPABILITIES,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+    .iftype_data = &rwnx_he_capa,
+    .n_iftype_data = 1,
+#endif
+};
+#endif
+
+static struct ieee80211_iface_limit rwnx_limits[] = {
+    { .max = 2,
+      .types = BIT(NL80211_IFTYPE_STATION)},
+    { .max = 2,
+      .types = BIT(NL80211_IFTYPE_AP)},
+    { .max = 1,
+      .types = BIT(NL80211_IFTYPE_P2P_CLIENT)},
+    { .max = 1,
+      .types = BIT(NL80211_IFTYPE_P2P_GO)},
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
+    { .max = 1,
+      .types = BIT(NL80211_IFTYPE_P2P_DEVICE),
+    }
+#endif
+};
+
+static struct ieee80211_iface_limit rwnx_limits_dfs[] = {
+    { .max = NX_VIRT_DEV_MAX, .types = BIT(NL80211_IFTYPE_AP)}
+};
+
+static const struct ieee80211_iface_combination rwnx_combinations[] = {
+    {
+        .limits                 = rwnx_limits,
+        .n_limits               = ARRAY_SIZE(rwnx_limits),
+        .num_different_channels = NX_CHAN_CTXT_CNT,
+        .max_interfaces         = NX_VIRT_DEV_MAX,
+    },
+    /* Keep this combination as the last one */
+    {
+        .limits                 = rwnx_limits_dfs,
+        .n_limits               = ARRAY_SIZE(rwnx_limits_dfs),
+        .num_different_channels = 1,
+        .max_interfaces         = NX_VIRT_DEV_MAX,
+#if 0
+        .radar_detect_widths = (BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+                                BIT(NL80211_CHAN_WIDTH_20) |
+                                BIT(NL80211_CHAN_WIDTH_40) |
+                                BIT(NL80211_CHAN_WIDTH_80)),
+#endif
+    }
+};
+
+/* There isn't a lot of sense in it, but you can transmit anything you like */
+static struct ieee80211_txrx_stypes
+rwnx_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
+    [NL80211_IFTYPE_STATION] = {
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+               BIT(IEEE80211_STYPE_AUTH >> 4)),
+    },
+    [NL80211_IFTYPE_AP] = {
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+               BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+               BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+               BIT(IEEE80211_STYPE_AUTH >> 4) |
+               BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+               BIT(IEEE80211_STYPE_ACTION >> 4)),
+    },
+    [NL80211_IFTYPE_AP_VLAN] = {
+        /* copy AP */
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+               BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+               BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+               BIT(IEEE80211_STYPE_AUTH >> 4) |
+               BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+               BIT(IEEE80211_STYPE_ACTION >> 4)),
+    },
+    [NL80211_IFTYPE_P2P_CLIENT] = {
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4)),
+    },
+    [NL80211_IFTYPE_P2P_GO] = {
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+               BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+               BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+               BIT(IEEE80211_STYPE_AUTH >> 4) |
+               BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+               BIT(IEEE80211_STYPE_ACTION >> 4)),
+    },
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
+    [NL80211_IFTYPE_P2P_DEVICE] = {
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4)),
+    },
+#endif
+    [NL80211_IFTYPE_MESH_POINT] = {
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_AUTH >> 4) |
+               BIT(IEEE80211_STYPE_DEAUTH >> 4)),
+    },
+};
+
+
+static u32 cipher_suites[] = {
+    WLAN_CIPHER_SUITE_WEP40,
+    WLAN_CIPHER_SUITE_WEP104,
+    WLAN_CIPHER_SUITE_TKIP,
+    WLAN_CIPHER_SUITE_CCMP,
+    WLAN_CIPHER_SUITE_AES_CMAC, // reserved entries to enable AES-CMAC and/or SMS4
+    0,
+};
+#define NB_RESERVED_CIPHER 1;
+
+static const int rwnx_ac2hwq[1][NL80211_NUM_ACS] = {
+    {
+        [NL80211_TXQ_Q_VO] = RWNX_HWQ_VO,
+        [NL80211_TXQ_Q_VI] = RWNX_HWQ_VI,
+        [NL80211_TXQ_Q_BE] = RWNX_HWQ_BE,
+        [NL80211_TXQ_Q_BK] = RWNX_HWQ_BK
+    }
+};
+
+const int rwnx_tid2hwq[IEEE80211_NUM_TIDS] = {
+    RWNX_HWQ_BE,
+    RWNX_HWQ_BK,
+    RWNX_HWQ_BK,
+    RWNX_HWQ_BE,
+    RWNX_HWQ_VI,
+    RWNX_HWQ_VI,
+    RWNX_HWQ_VO,
+    RWNX_HWQ_VO,
+    /* TID_8 is used for management frames */
+    RWNX_HWQ_VO,
+    /* At the moment, all others TID are mapped to BE */
+    RWNX_HWQ_BE,
+    RWNX_HWQ_BE,
+    RWNX_HWQ_BE,
+    RWNX_HWQ_BE,
+    RWNX_HWQ_BE,
+    RWNX_HWQ_BE,
+    RWNX_HWQ_BE,
+};
+
+static const int rwnx_hwq2uapsd[NL80211_NUM_ACS] = {
+    [RWNX_HWQ_VO] = IEEE80211_WMM_IE_STA_QOSINFO_AC_VO,
+    [RWNX_HWQ_VI] = IEEE80211_WMM_IE_STA_QOSINFO_AC_VI,
+    [RWNX_HWQ_BE] = IEEE80211_WMM_IE_STA_QOSINFO_AC_BE,
+    [RWNX_HWQ_BK] = IEEE80211_WMM_IE_STA_QOSINFO_AC_BK,
+};
+
+extern uint8_t scanning;
+bool_l func_flag = true;
+int testmode = 0;
+int adap_test = 0;
+
+u8 chip_id = 0;
+u8 chip_rom_id = 0;
+u8 btenable = 0;
+
+
+/*********************************************************************
+ * helper
+ *********************************************************************/
+struct rwnx_sta *rwnx_get_sta(struct rwnx_hw *rwnx_hw, const u8 *mac_addr)
+{
+    int i;
+
+    for (i = 0; i < NX_REMOTE_STA_MAX; i++) {
+        struct rwnx_sta *sta = &rwnx_hw->sta_table[i];
+        if (sta->valid && (memcmp(mac_addr, &sta->mac_addr, 6) == 0))
+            return sta;
+    }
+
+    return NULL;
+}
+
+void rwnx_enable_wapi(struct rwnx_hw *rwnx_hw)
+{
+    cipher_suites[rwnx_hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_SMS4;
+    rwnx_hw->wiphy->n_cipher_suites ++;
+    rwnx_hw->wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;
+}
+
+void rwnx_enable_mfp(struct rwnx_hw *rwnx_hw)
+{
+    cipher_suites[rwnx_hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_AES_CMAC;
+    rwnx_hw->wiphy->n_cipher_suites ++;
+}
+
+u8 *rwnx_build_bcn(struct rwnx_bcn *bcn, struct cfg80211_beacon_data *new)
+{
+    u8 *buf, *pos;
+
+    if (new->head) {
+        u8 *head = kmalloc(new->head_len, GFP_KERNEL);
+
+        if (!head)
+            return NULL;
+
+        if (bcn->head)
+            kfree(bcn->head);
+
+        bcn->head = head;
+        bcn->head_len = new->head_len;
+        memcpy(bcn->head, new->head, new->head_len);
+    }
+    if (new->tail) {
+        u8 *tail = kmalloc(new->tail_len, GFP_KERNEL);
+
+        if (!tail)
+            return NULL;
+
+        if (bcn->tail)
+            kfree(bcn->tail);
+
+        bcn->tail = tail;
+        bcn->tail_len = new->tail_len;
+        memcpy(bcn->tail, new->tail, new->tail_len);
+    }
+
+    if (!bcn->head)
+        return NULL;
+
+    bcn->tim_len = 6;
+    bcn->len = bcn->head_len + bcn->tail_len + bcn->ies_len + bcn->tim_len;
+
+    buf = kmalloc(bcn->len, GFP_KERNEL);
+    if (!buf)
+        return NULL;
+
+    // Build the beacon buffer
+    pos = buf;
+    memcpy(pos, bcn->head, bcn->head_len);
+    pos += bcn->head_len;
+    *pos++ = WLAN_EID_TIM;
+    *pos++ = 4;
+    *pos++ = 0;
+    *pos++ = bcn->dtim;
+    *pos++ = 0;
+    *pos++ = 0;
+    if (bcn->tail) {
+        memcpy(pos, bcn->tail, bcn->tail_len);
+        pos += bcn->tail_len;
+    }
+    if (bcn->ies) {
+        memcpy(pos, bcn->ies, bcn->ies_len);
+    }
+
+#if 1
+    u8* rate_ie;
+    int i = 0;
+    int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+    int len = bcn->len - var_offset;
+    u8 * var_pos = buf + var_offset;
+    #if 0
+    #define IS_BASIC_RATE(r) (r & 0x80) && ((r & ~0x80) <= (54 * 2))
+
+    rate_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
+    if (rate_ie) {
+        u8 *rates = rate_ie + 2;
+        for (i = 0; (i < rate_ie[1]) ; i++) {
+            if (i>=4){
+                rates[i] = rates[i] | CO_BIT(7);
+		//printk("rates[%d]=%d\n",i, rates[i]);
+	    }
+        }
+    }
+
+    u8* erp_info_ie = cfg80211_find_ie(42, var_pos, len);
+    if(erp_info_ie){
+    	//printk("ie found: %x\n", erp_info_ie[2]);
+	erp_info_ie[2] &=0xF9;//0xFD;
+    } 
+//    else
+// 	printk("ie not found\n");
+    #endif
+#endif
+    return buf;
+}
+
+
+static void rwnx_del_bcn(struct rwnx_bcn *bcn)
+{
+    if (bcn->head) {
+        kfree(bcn->head);
+        bcn->head = NULL;
+    }
+    bcn->head_len = 0;
+
+    if (bcn->tail) {
+        kfree(bcn->tail);
+        bcn->tail = NULL;
+    }
+    bcn->tail_len = 0;
+
+    if (bcn->ies) {
+        kfree(bcn->ies);
+        bcn->ies = NULL;
+    }
+    bcn->ies_len = 0;
+    bcn->tim_len = 0;
+    bcn->dtim = 0;
+    bcn->len = 0;
+}
+
+/**
+ * Link channel ctxt to a vif and thus increments count for this context.
+ */
+void rwnx_chanctx_link(struct rwnx_vif *vif, u8 ch_idx,
+                       struct cfg80211_chan_def *chandef)
+{
+    struct rwnx_chanctx *ctxt;
+
+    if (ch_idx >= NX_CHAN_CTXT_CNT) {
+        WARN(1, "Invalid channel ctxt id %d", ch_idx);
+        return;
+    }
+
+    vif->ch_index = ch_idx;
+    ctxt = &vif->rwnx_hw->chanctx_table[ch_idx];
+    ctxt->count++;
+
+    // For now chandef is NULL for STATION interface
+    if (chandef) {
+        if (!ctxt->chan_def.chan)
+            ctxt->chan_def = *chandef;
+        else {
+            // TODO. check that chandef is the same as the one already
+            // set for this ctxt
+        }
+    }
+}
+
+/**
+ * Unlink channel ctxt from a vif and thus decrements count for this context
+ */
+void rwnx_chanctx_unlink(struct rwnx_vif *vif)
+{
+    struct rwnx_chanctx *ctxt;
+
+    if (vif->ch_index == RWNX_CH_NOT_SET)
+        return;
+
+    ctxt = &vif->rwnx_hw->chanctx_table[vif->ch_index];
+
+    if (ctxt->count == 0) {
+        WARN(1, "Chan ctxt ref count is already 0");
+    } else {
+        ctxt->count--;
+    }
+
+    if (ctxt->count == 0) {
+        if (vif->ch_index == vif->rwnx_hw->cur_chanctx) {
+            /* If current chan ctxt is no longer linked to a vif
+               disable radar detection (no need to check if it was activated) */
+            rwnx_radar_detection_enable(&vif->rwnx_hw->radar,
+                                        RWNX_RADAR_DETECT_DISABLE,
+                                        RWNX_RADAR_RIU);
+        }
+        /* set chan to null, so that if this ctxt is relinked to a vif that
+           don't have channel information, don't use wrong information */
+        ctxt->chan_def.chan = NULL;
+    }
+    vif->ch_index = RWNX_CH_NOT_SET;
+}
+
+int rwnx_chanctx_valid(struct rwnx_hw *rwnx_hw, u8 ch_idx)
+{
+    if (ch_idx >= NX_CHAN_CTXT_CNT ||
+        rwnx_hw->chanctx_table[ch_idx].chan_def.chan == NULL) {
+        return 0;
+    }
+
+    return 1;
+}
+
+static void rwnx_del_csa(struct rwnx_vif *vif)
+{
+    struct rwnx_hw *rwnx_hw = vif->rwnx_hw;
+    struct rwnx_csa *csa = vif->ap.csa;
+
+    if (!csa)
+        return;
+
+    rwnx_del_bcn(&csa->bcn);
+    kfree(csa);
+    vif->ap.csa = NULL;
+}
+
+static void rwnx_csa_finish(struct work_struct *ws)
+{
+    struct rwnx_csa *csa = container_of(ws, struct rwnx_csa, work);
+    struct rwnx_vif *vif = csa->vif;
+    struct rwnx_hw *rwnx_hw = vif->rwnx_hw;
+    int error = csa->status;
+
+    if (!error)
+        error = rwnx_send_bcn_change(rwnx_hw, vif->vif_index, csa->elem.dma_addr,
+                                     csa->bcn.len, csa->bcn.head_len,
+                                     csa->bcn.tim_len, NULL);
+
+    if (error) {
+        #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
+        cfg80211_stop_iface(rwnx_hw->wiphy, &vif->wdev, GFP_KERNEL);
+        #else
+        cfg80211_disconnected(vif->ndev, 0, NULL, 0, 0, GFP_KERNEL);
+        #endif
+    } else {
+        mutex_lock(&vif->wdev.mtx);
+        __acquire(&vif->wdev.mtx);
+        spin_lock_bh(&rwnx_hw->cb_lock);
+        rwnx_chanctx_unlink(vif);
+        rwnx_chanctx_link(vif, csa->ch_idx, &csa->chandef);
+        if (rwnx_hw->cur_chanctx == csa->ch_idx) {
+            rwnx_radar_detection_enable_on_cur_channel(rwnx_hw);
+            rwnx_txq_vif_start(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+        } else
+            rwnx_txq_vif_stop(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+        spin_unlock_bh(&rwnx_hw->cb_lock);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
+        cfg80211_ch_switch_notify(vif->ndev, &csa->chandef);
+#endif
+        mutex_unlock(&vif->wdev.mtx);
+        __release(&vif->wdev.mtx);
+    }
+    rwnx_del_csa(vif);
+}
+
+/**
+ * rwnx_external_auth_enable - Enable external authentication on a vif
+ *
+ * @vif: VIF on which external authentication must be enabled
+ *
+ * External authentication requires to start TXQ for unknown STA in
+ * order to send auth frame pusehd by user space.
+ * Note: It is assumed that fw is on the correct channel.
+ */
+void rwnx_external_auth_enable(struct rwnx_vif *vif)
+{
+    vif->sta.external_auth = true;
+    rwnx_txq_unk_vif_init(vif);
+    rwnx_txq_start(rwnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE), 0);
+}
+
+/**
+ * rwnx_external_auth_disable - Disable external authentication on a vif
+ *
+ * @vif: VIF on which external authentication must be disabled
+ */
+void rwnx_external_auth_disable(struct rwnx_vif *vif)
+{
+    if (!vif->sta.external_auth)
+        return;
+
+    vif->sta.external_auth = false;
+    rwnx_txq_unk_vif_deinit(vif);
+}
+
+/**
+ * rwnx_update_mesh_power_mode -
+ *
+ * @vif: mesh VIF  for which power mode is updated
+ *
+ * Does nothing if vif is not a mesh point interface.
+ * Since firmware doesn't support one power save mode per link select the
+ * most "active" power mode among all mesh links.
+ * Indeed as soon as we have to be active on one link we might as well be
+ * active on all links.
+ *
+ * If there is no link then the power mode for next peer is used;
+ */
+void rwnx_update_mesh_power_mode(struct rwnx_vif *vif)
+{
+#if 0
+    enum nl80211_mesh_power_mode mesh_pm;
+    struct rwnx_sta *sta;
+    struct mesh_config mesh_conf;
+    struct mesh_update_cfm cfm;
+    u32 mask;
+
+    if (RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_MESH_POINT)
+        return;
+
+    if (list_empty(&vif->ap.sta_list)) {
+        mesh_pm = vif->ap.next_mesh_pm;
+    } else {
+        mesh_pm = NL80211_MESH_POWER_DEEP_SLEEP;
+        list_for_each_entry(sta, &vif->ap.sta_list, list) {
+            if (sta->valid && (sta->mesh_pm < mesh_pm)) {
+                mesh_pm = sta->mesh_pm;
+            }
+        }
+    }
+
+    if (mesh_pm == vif->ap.mesh_pm)
+        return;
+
+    mask = BIT(NL80211_MESHCONF_POWER_MODE - 1);
+    mesh_conf.power_mode = mesh_pm;
+    if (rwnx_send_mesh_update_req(vif->rwnx_hw, vif, mask, &mesh_conf, &cfm) ||
+        cfm.status)
+        return;
+
+    vif->ap.mesh_pm = mesh_pm;
+#endif
+}
+
+
+/*********************************************************************
+ * netdev callbacks
+ ********************************************************************/
+/**
+ * int (*ndo_open)(struct net_device *dev);
+ *     This function is called when network device transistions to the up
+ *     state.
+ *
+ * - Start FW if this is the first interface opened
+ * - Add interface at fw level
+ */
+static int rwnx_open(struct net_device *dev)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+    struct mm_add_if_cfm add_if_cfm;
+    int error = 0;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    // Check if it is the first opened VIF
+    if (rwnx_hw->vif_started == 0)
+    {
+        // Start the FW
+       if ((error = rwnx_send_start(rwnx_hw)))
+           return error;
+
+       /* Device is now started */
+       set_bit(RWNX_DEV_STARTED, &rwnx_hw->drv_flags);
+    }
+
+    #if 0
+    u16_l txop[4] = {0x3e, 0x4e, 0x5e, 0x6e};
+    printk("send txop\n");
+    rwnx_send_txop_req(rwnx_hw, txop, 1, 1);
+    #endif
+
+    if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP_VLAN) {
+        /* For AP_vlan use same fw and drv indexes. We ensure that this index
+           will not be used by fw for another vif by taking index >= NX_VIRT_DEV_MAX */
+        add_if_cfm.inst_nbr = rwnx_vif->drv_vif_index;
+        netif_tx_stop_all_queues(dev);
+
+        /* Save the index retrieved from LMAC */
+        spin_lock_bh(&rwnx_hw->cb_lock);
+        rwnx_vif->vif_index = add_if_cfm.inst_nbr;
+        rwnx_vif->up = true;
+        rwnx_hw->vif_started++;
+        rwnx_hw->vif_table[add_if_cfm.inst_nbr] = rwnx_vif;
+        spin_unlock_bh(&rwnx_hw->cb_lock);
+    } else {
+        /* Forward the information to the LMAC,
+         *     p2p value not used in FMAC configuration, iftype is sufficient */
+        if ((error = rwnx_send_add_if(rwnx_hw, rwnx_vif->address,
+                                      RWNX_VIF_TYPE(rwnx_vif), false, &add_if_cfm))) {
+            printk("add if fail\n");
+            return error;
+        }
+
+        if (add_if_cfm.status != 0) {
+            RWNX_PRINT_CFM_ERR(add_if);
+            return -EIO;
+        }
+
+        /* Save the index retrieved from LMAC */
+        spin_lock_bh(&rwnx_hw->cb_lock);
+        rwnx_vif->vif_index = add_if_cfm.inst_nbr;
+        rwnx_vif->up = true;
+        rwnx_hw->vif_started++;
+        rwnx_hw->vif_table[add_if_cfm.inst_nbr] = rwnx_vif;
+        spin_unlock_bh(&rwnx_hw->cb_lock);
+    }
+
+    if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP || RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_GO)
+    {
+        #ifdef CONFIG_COEX
+        rwnx_send_coex_req(rwnx_hw, 1, 0);
+        #endif
+    }
+
+    if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MONITOR){
+        rwnx_hw->monitor_vif = rwnx_vif->vif_index;
+        if (rwnx_vif->ch_index != RWNX_CH_NOT_SET){
+            //Configure the monitor channel
+            error = rwnx_send_config_monitor_req(rwnx_hw, &rwnx_hw->chanctx_table[rwnx_vif->ch_index].chan_def, NULL);
+        }
+        #if defined(CONFIG_RWNX_MON_XMIT)
+        rwnx_txq_unk_vif_init(rwnx_vif);
+        #endif
+    }
+
+    //netif_carrier_off(dev);
+    #if defined(CONFIG_RWNX_MON_XMIT)
+    netif_carrier_on(dev);
+    printk("monitor xmit: netif_carrier_on\n");
+    #endif
+    netif_start_queue(dev);
+
+    return error;
+}
+
+/**
+ * int (*ndo_stop)(struct net_device *dev);
+ *     This function is called when network device transistions to the down
+ *     state.
+ *
+ * - Remove interface at fw level
+ * - Reset FW if this is the last interface opened
+ */
+static int rwnx_close(struct net_device *dev)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+#if defined(AICWF_USB_SUPPORT)
+    struct aicwf_bus *bus_if = NULL;
+    struct aic_usb_dev *usbdev = NULL;
+    bus_if = dev_get_drvdata(rwnx_hw->dev);
+    usbdev = bus_if->bus_priv.usb;
+#elif defined(AICWF_SDIO_SUPPORT)
+    struct aicwf_bus *bus_if = NULL;
+    struct aic_sdio_dev *sdiodev = NULL;
+#else
+#endif
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#if defined(AICWF_USB_SUPPORT) || defined(AICWF_SDIO_SUPPORT)
+    if (scanning)
+        scanning = false;
+#endif
+
+    netdev_info(dev, "CLOSE");
+
+    rwnx_radar_cancel_cac(&rwnx_hw->radar);
+
+    /* Abort scan request on the vif */
+    if (rwnx_hw->scan_request
+#if 0
+	 &&
+        rwnx_hw->scan_request->wdev == &rwnx_vif->wdev
+#endif
+    ) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+        struct cfg80211_scan_info info = {
+            .aborted = true,
+        };
+
+        cfg80211_scan_done(rwnx_hw->scan_request, &info);
+#else
+        cfg80211_scan_done(rwnx_hw->scan_request, true);
+#endif
+        rwnx_hw->scan_request = NULL;
+    }
+
+#if defined(AICWF_USB_SUPPORT)
+    if (usbdev != NULL) {
+        if (usbdev->state != USB_DOWN_ST && testmode == 0)
+            rwnx_send_remove_if(rwnx_hw, rwnx_vif->vif_index, false);
+    }
+#elif defined(AICWF_SDIO_SUPPORT)
+    bus_if = dev_get_drvdata(rwnx_hw->dev);
+    if (bus_if) {
+        sdiodev = bus_if->bus_priv.sdio;
+    }
+    if (sdiodev != NULL ) {
+        if (sdiodev->bus_if->state != BUS_DOWN_ST && testmode == 0)
+            rwnx_send_remove_if(rwnx_hw, rwnx_vif->vif_index, false);
+    }
+#else
+#endif
+
+    if (rwnx_hw->roc_elem && (rwnx_hw->roc_elem->wdev == &rwnx_vif->wdev)) {
+        printk(KERN_CRIT "%s clear roc\n", __func__);
+        /* Initialize RoC element pointer to NULL, indicate that RoC can be started */
+        kfree(rwnx_hw->roc_elem);
+        rwnx_hw->roc_elem = NULL;
+    }
+
+    /* Ensure that we won't process disconnect ind */
+    spin_lock_bh(&rwnx_hw->cb_lock);
+
+    rwnx_vif->up = false;
+    if (netif_carrier_ok(dev)) {
+        if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_STATION ||
+            RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_CLIENT) {
+            cfg80211_disconnected(dev, WLAN_REASON_DEAUTH_LEAVING,
+                                  NULL, 0, true, GFP_ATOMIC);
+            netif_tx_stop_all_queues(dev);
+            netif_carrier_off(dev);
+        } else if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP_VLAN) {
+			netif_carrier_off(dev);
+        } else {
+            netdev_warn(dev, "AP not stopped when disabling interface");
+        }
+    }
+
+    rwnx_hw->vif_table[rwnx_vif->vif_index] = NULL;
+    spin_unlock_bh(&rwnx_hw->cb_lock);
+
+    rwnx_chanctx_unlink(rwnx_vif);
+
+    if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MONITOR)
+        rwnx_hw->monitor_vif = RWNX_INVALID_VIF;
+
+    if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP || RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_GO)
+    {
+    #ifdef CONFIG_COEX
+        rwnx_send_coex_req(rwnx_hw, 0, 1);
+    #endif
+    }
+
+    rwnx_hw->vif_started--;
+    if (rwnx_hw->vif_started == 0) {
+        /* This also lets both ipc sides remain in sync before resetting */
+        #if 0
+        rwnx_ipc_tx_drain(rwnx_hw);
+        #else
+        #if defined(AICWF_USB_SUPPORT)
+        if (usbdev->bus_if->state !=BUS_DOWN_ST)
+        #else
+        if (sdiodev->bus_if->state != BUS_DOWN_ST)
+        #endif
+        {
+            rwnx_send_reset(rwnx_hw);
+            if (testmode == 0) {
+                // Set parameters to firmware
+                rwnx_send_me_config_req(rwnx_hw);
+                // Set channel parameters to firmware
+                rwnx_send_me_chan_config_req(rwnx_hw);
+            }
+            #ifdef CONFIG_CHIP_REBOOT
+            rwnx_send_reboot(rwnx_hw);
+            #endif
+        }
+        #endif
+        clear_bit(RWNX_DEV_STARTED, &rwnx_hw->drv_flags);
+    }
+
+    return 0;
+}
+
+#ifdef CONFIG_RFTEST
+enum {
+    SET_TX,
+    SET_TXSTOP,
+    SET_TXTONE,
+    SET_RX,
+    GET_RX_RESULT,
+    SET_RXSTOP,
+    SET_RX_METER,
+    SET_POWER,
+    SET_XTAL_CAP,
+    SET_XTAL_CAP_FINE,
+    GET_EFUSE_BLOCK,
+    SET_FREQ_CAL,
+    SET_FREQ_CAL_FINE,
+    GET_FREQ_CAL,
+    SET_MAC_ADDR,
+    GET_MAC_ADDR,
+    SET_BT_MAC_ADDR,
+    GET_BT_MAC_ADDR,
+    SET_VENDOR_INFO,
+    GET_VENDOR_INFO,
+    RDWR_PWRMM,
+    RDWR_PWRLVL,
+    RDWR_PWROFST,
+    RDWR_DRVIBIT,
+    RDWR_EFUSE_PWROFST,
+    RDWR_EFUSE_DRVIBIT,
+    SET_PAPR,
+    SET_CAL_XTAL,
+    GET_CAL_XTAL_RES,
+    SET_COB_CAL,
+    GET_COB_CAL_RES,
+    RDWR_EFUSE_USRDATA,
+    SET_NOTCH,
+    RDWR_PWROFSTFINE,
+    RDWR_EFUSE_PWROFSTFINE,
+    RDWR_EFUSE_SDIOCFG,
+    RDWR_EFUSE_USBVIDPID,
+
+    #ifdef CONFIG_RFTEST_USB_BT
+    BT_CMD_BASE = 0x100,
+    BT_RESET,
+    BT_TXDH,
+    BT_RXDH,
+    BT_STOP,
+    GET_BT_RX_RESULT,
+    #endif
+};
+
+typedef struct
+{
+    u8_l chan;
+    u8_l bw;
+    u8_l mode;
+    u8_l rate;
+    u16_l length;
+}cmd_rf_settx_t;
+
+typedef struct
+{
+    u8_l val;
+}cmd_rf_setfreq_t;
+
+typedef struct
+{
+    u8_l chan;
+    u8_l bw;
+}cmd_rf_rx_t;
+
+typedef struct
+{
+    u8_l block;
+}cmd_rf_getefuse_t;
+#endif
+
+#define CMD_MAXARGS 30
+
+#if 0
+#define isblank(c)      ((c) == ' ' || (c) == '\t')
+#define isascii(c)      (((unsigned char)(c)) <= 0x7F)
+
+static int isdigit(unsigned char c)
+{
+    return ((c >= '0') && (c <='9'));
+}
+
+static int isxdigit(unsigned char c)
+{
+    if ((c >= '0') && (c <='9'))
+        return 1;
+    if ((c >= 'a') && (c <='f'))
+        return 1;
+    if ((c >= 'A') && (c <='F'))
+        return 1;
+    return 0;
+}
+
+static int islower(unsigned char c)
+{
+    return ((c >= 'a') && (c <='z'));
+}
+
+static unsigned char toupper(unsigned char c)
+{
+    if (islower(c))
+        c -= 'a'-'A';
+    return c;
+}
+#endif
+
+
+static int parse_line (char *line, char *argv[])
+{
+    int nargs = 0;
+
+    while (nargs < CMD_MAXARGS) {
+        /* skip any white space */
+        while ((*line == ' ') || (*line == '\t')) {
+            ++line;
+        }
+
+        if (*line == '\0') {    /* end of line, no more args    */
+            argv[nargs] = 0;
+            return (nargs);
+        }
+
+        /* Argument include space should be bracketed by quotation mark */
+        if (*line == '\"') {
+            /* Skip quotation mark */
+            line++;
+
+            /* Begin of argument string */
+            argv[nargs++] = line;
+
+            /* Until end of argument */
+            while(*line && (*line != '\"')) {
+                ++line;
+            }
+        } else {
+            argv[nargs++] = line;    /* begin of argument string    */
+
+            /* find end of string */
+            while(*line && (*line != ' ') && (*line != '\t')) {
+                ++line;
+            }
+        }
+
+        if (*line == '\0') {    /* end of line, no more args    */
+            argv[nargs] = 0;
+            return (nargs);
+        }
+
+        *line++ = '\0';         /* terminate current arg     */
+    }
+
+    printk("** Too many args (max. %d) **\n", CMD_MAXARGS);
+
+    return (nargs);
+}
+
+unsigned int command_strtoul(const char *cp, char **endp, unsigned int base)
+{
+    unsigned int result = 0, value, is_neg=0;
+
+    if (*cp == '0') {
+        cp++;
+        if ((*cp == 'x') && isxdigit(cp[1])) {
+            base = 16;
+            cp++;
+        }
+        if (!base) {
+            base = 8;
+        }
+    }
+    if (!base) {
+        base = 10;
+    }
+    if (*cp == '-') {
+        is_neg = 1;
+        cp++;
+    }
+    while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp - '0' : (islower(*cp) ? toupper(*cp) : *cp) - 'A' + 10) < base) {
+        result = result * base + value;
+        cp++;
+    }
+    if (is_neg)
+        result = (unsigned int)((int)result * (-1));
+
+    if (endp)
+        *endp = (char *)cp;
+    return result;
+}
+
+
+int handle_private_cmd(struct net_device *net, char *command, u32 cmd_len)
+{
+    int bytes_written = 0;
+    char *argv[CMD_MAXARGS + 1];
+    int argc;
+    #ifdef CONFIG_RFTEST
+    struct dbg_rftest_cmd_cfm cfm = {{0,}};
+    u8_l mac_addr[6];
+    cmd_rf_settx_t settx_param;
+    cmd_rf_rx_t setrx_param;
+    int freq;
+    cmd_rf_getefuse_t getefuse_param;
+    cmd_rf_setfreq_t cmd_setfreq;
+    u8_l pwr_lvl;
+    u8_l xtal_cap;
+    u8_l xtal_cap_fine;
+    u8_l vendor_info;
+    #ifdef CONFIG_RFTEST_USB_BT
+    int bt_index;
+    u8_l dh_cmd_reset[4];
+    u8_l dh_cmd_txdh[18];
+    u8_l dh_cmd_rxdh[17];
+    u8_l dh_cmd_stop[5];
+    #endif
+    #endif
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if ((argc = parse_line(command, argv)) == 0) {
+        return -1;
+    }
+
+    do {
+        #ifdef AICWF_SDIO_SUPPORT
+        struct rwnx_hw *p_rwnx_hw = g_rwnx_plat->sdiodev->rwnx_hw;
+        #endif
+        #ifdef AICWF_USB_SUPPORT
+        struct rwnx_hw *p_rwnx_hw = g_rwnx_plat->usbdev->rwnx_hw;
+        #endif
+        #ifdef CONFIG_RFTEST
+        if (strcasecmp(argv[0], "GET_RX_RESULT") ==0) {
+            printk("get_rx_result\n");
+            rwnx_send_rftest_req(p_rwnx_hw, GET_RX_RESULT, 0, NULL, &cfm);
+            memcpy(command, &cfm.rftest_result[0], 8);
+            bytes_written = 8;
+        } else if (strcasecmp(argv[0], "SET_TX") == 0) {
+            printk("set_tx\n");
+            if (argc < 6) {
+                printk("wrong param\n");
+                break;
+            }
+            settx_param.chan = command_strtoul(argv[1], NULL, 10);
+            settx_param.bw = command_strtoul(argv[2], NULL, 10);
+            settx_param.mode = command_strtoul(argv[3], NULL, 10);
+            settx_param.rate = command_strtoul(argv[4], NULL, 10);
+            settx_param.length = command_strtoul(argv[5], NULL, 10);
+            printk("txparam:%d,%d,%d,%d,%d\n", settx_param.chan, settx_param.bw,
+                settx_param.mode, settx_param.rate, settx_param.length);
+            rwnx_send_rftest_req(p_rwnx_hw, SET_TX, sizeof(cmd_rf_settx_t), (u8_l *)&settx_param, NULL);
+        } else if (strcasecmp(argv[0], "SET_TXSTOP") == 0) {
+            printk("settx_stop\n");
+            rwnx_send_rftest_req(p_rwnx_hw, SET_TXSTOP, 0, NULL, NULL);
+        } else if (strcasecmp(argv[0], "SET_TXTONE") == 0) {
+            printk("set_tx_tone,argc:%d\n",argc);
+            if ((argc == 2) || (argc == 3)) {
+                printk("argv 1:%s\n",argv[1]);
+                u8_l func = (u8_l)command_strtoul(argv[1], NULL, 16);
+                s8_l freq;
+                if (argc == 3) {
+                    printk("argv 2:%s\n",argv[2]);
+                    freq = (u8_l)command_strtoul(argv[2], NULL, 10);
+                } else {
+                    freq = 0;
+                };
+                u8_l buf[2] = {func, (u8_l)freq};
+                rwnx_send_rftest_req(p_rwnx_hw, SET_TXTONE, argc - 1, buf, NULL);
+            } else {
+                printk("wrong args\n");
+            }
+        } else if (strcasecmp(argv[0], "SET_RX") == 0) {
+            printk("set_rx\n");
+            if (argc < 3) {
+                printk("wrong param\n");
+                break;
+            }
+            setrx_param.chan = command_strtoul(argv[1], NULL, 10);
+            setrx_param.bw = command_strtoul(argv[2], NULL, 10);
+            rwnx_send_rftest_req(p_rwnx_hw, SET_RX, sizeof(cmd_rf_rx_t), (u8_l *)&setrx_param, NULL);
+        } else if (strcasecmp(argv[0], "SET_RXSTOP") == 0) {
+            printk("set_rxstop\n");
+            rwnx_send_rftest_req(p_rwnx_hw, SET_RXSTOP, 0, NULL, NULL);
+        } else if (strcasecmp(argv[0], "SET_RX_METER") == 0) {
+            printk("set_rx_meter\n");
+            freq = (int)command_strtoul(argv[1], NULL, 10);
+            rwnx_send_rftest_req(p_rwnx_hw, SET_RX_METER, sizeof(freq), (u8_l *)&freq, NULL);
+        } else if (strcasecmp(argv[0], "SET_FREQ_CAL") == 0) {
+            printk("set_freq_cal\n");
+            if (argc < 2) {
+                printk("wrong param\n");
+                break;
+            }
+            cmd_setfreq.val = command_strtoul(argv[1], NULL, 16);
+            printk("param:%x\r\n", cmd_setfreq.val);
+            rwnx_send_rftest_req(p_rwnx_hw, SET_FREQ_CAL, sizeof(cmd_rf_setfreq_t), (u8_l *)&cmd_setfreq, &cfm);
+            memcpy(command, &cfm.rftest_result[0], 4);
+            bytes_written = 4;
+        } else if (strcasecmp(argv[0], "SET_FREQ_CAL_FINE") == 0) {
+            printk("set_freq_cal_fine\n");
+            if (argc < 2) {
+                printk("wrong param\n");
+                break;
+            }
+            cmd_setfreq.val = command_strtoul(argv[1], NULL, 16);
+            printk("param:%x\r\n", cmd_setfreq.val);
+            rwnx_send_rftest_req(p_rwnx_hw, SET_FREQ_CAL_FINE, sizeof(cmd_rf_setfreq_t), (u8_l *)&cmd_setfreq, &cfm);
+            memcpy(command, &cfm.rftest_result[0], 4);
+            bytes_written = 4;
+        } else if (strcasecmp(argv[0], "GET_EFUSE_BLOCK") == 0) {
+            printk("get_efuse_block\n");
+            if (argc < 2) {
+                printk("wrong param\n");
+                break;
+            }
+            getefuse_param.block = command_strtoul(argv[1], NULL, 10);
+            rwnx_send_rftest_req(p_rwnx_hw, GET_EFUSE_BLOCK, sizeof(cmd_rf_getefuse_t), (u8_l *)&getefuse_param, &cfm);
+            printk("get val=%x\r\n", cfm.rftest_result[0]);
+            memcpy(command, &cfm.rftest_result[0], 4);
+            bytes_written = 4;
+        } else if (strcasecmp(argv[0], "SET_POWER") == 0) {
+            printk("set_power\n");
+            pwr_lvl = command_strtoul(argv[1], NULL, 10);
+            printk("pwr_lvl=%x\r\n", pwr_lvl);
+            rwnx_send_rftest_req(p_rwnx_hw, SET_POWER, sizeof(pwr_lvl), (u8_l *)&pwr_lvl, NULL);
+        } else if (strcasecmp(argv[0], "SET_XTAL_CAP")==0) {
+            printk("set_xtal_cap\n");
+            if (argc < 2) {
+                printk("wrong param\n");
+                break;
+            }
+            xtal_cap = command_strtoul(argv[1], NULL, 10);
+            printk("xtal_cap =%x\r\n", xtal_cap);
+            rwnx_send_rftest_req(p_rwnx_hw, SET_XTAL_CAP, sizeof(xtal_cap), (u8_l *)&xtal_cap, &cfm);
+            memcpy(command, &cfm.rftest_result[0], 4);
+            bytes_written = 4;
+        } else if (strcasecmp(argv[0], "SET_XTAL_CAP_FINE")==0) {
+            printk("set_xtal_cap_fine\n");
+            if (argc < 2) {
+                printk("wrong param\n");
+                break;
+            }
+            xtal_cap_fine = command_strtoul(argv[1], NULL, 10);
+            printk("xtal_cap_fine =%x\r\n", xtal_cap_fine);
+            rwnx_send_rftest_req(p_rwnx_hw, SET_XTAL_CAP_FINE, sizeof(xtal_cap_fine), (u8_l *)&xtal_cap_fine, &cfm);
+            memcpy(command, &cfm.rftest_result[0], 4);
+            bytes_written = 4;
+        } else if (strcasecmp(argv[0], "SET_MAC_ADDR")==0) {
+            printk("set_mac_addr\n");
+            if (argc < 7) {
+                printk("wrong param\n");
+                break;
+            }
+            mac_addr[5] = command_strtoul(argv[1], NULL, 16);
+            mac_addr[4] = command_strtoul(argv[2], NULL, 16);
+            mac_addr[3] = command_strtoul(argv[3], NULL, 16);
+            mac_addr[2] = command_strtoul(argv[4], NULL, 16);
+            mac_addr[1] = command_strtoul(argv[5], NULL, 16);
+            mac_addr[0] = command_strtoul(argv[6], NULL, 16);
+            printk("set macaddr:%x,%x,%x,%x,%x,%x\n", mac_addr[5], mac_addr[4], mac_addr[3], mac_addr[2], mac_addr[1], mac_addr[0]);
+            rwnx_send_rftest_req(p_rwnx_hw, SET_MAC_ADDR, sizeof(mac_addr), (u8_l *)&mac_addr, NULL);
+        } else if (strcasecmp(argv[0], "GET_MAC_ADDR")==0) {
+            u32_l addr0, addr1;
+            int rem_cnt;
+            printk("get mac addr\n");
+            rwnx_send_rftest_req(p_rwnx_hw, GET_MAC_ADDR, 0, NULL, &cfm);
+            memcpy(command, &cfm.rftest_result[0], 8);
+            bytes_written = 8;
+            addr0 = cfm.rftest_result[0];
+            rem_cnt = (cfm.rftest_result[1] >> 16) & 0x00FF;
+            addr1 = cfm.rftest_result[1] & 0x0000FFFF;
+            printk("0x%x,0x%x (remain:%x)\n", addr0, addr1, rem_cnt);
+        } else if (strcasecmp(argv[0], "SET_BT_MAC_ADDR") == 0) {
+            printk("set_bt_mac_addr\n");
+            if (argc < 7) {
+                printk("wrong param\n");
+                break;
+            }
+            mac_addr[5] = command_strtoul(argv[1], NULL, 16);
+            mac_addr[4] = command_strtoul(argv[2], NULL, 16);
+            mac_addr[3] = command_strtoul(argv[3], NULL, 16);
+            mac_addr[2] = command_strtoul(argv[4], NULL, 16);
+            mac_addr[1] = command_strtoul(argv[5], NULL, 16);
+            mac_addr[0] = command_strtoul(argv[6], NULL, 16);
+            printk("set bt macaddr:%x,%x,%x,%x,%x,%x\n", mac_addr[5], mac_addr[4], mac_addr[3], mac_addr[2], mac_addr[1], mac_addr[0]);
+            rwnx_send_rftest_req(p_rwnx_hw, SET_BT_MAC_ADDR, sizeof(mac_addr), (u8_l *)&mac_addr, NULL);
+        } else if (strcasecmp(argv[0], "GET_BT_MAC_ADDR")==0) {
+            printk("get bt mac addr\n");
+            rwnx_send_rftest_req(p_rwnx_hw, GET_BT_MAC_ADDR, 0, NULL, &cfm);
+            memcpy(command, &cfm.rftest_result[0], 8);
+            bytes_written = 8;
+            printk("0x%x,0x%x\n", cfm.rftest_result[0], cfm.rftest_result[1]);
+        } else if (strcasecmp(argv[0], "SET_VENDOR_INFO")==0) {
+            vendor_info = command_strtoul(argv[1], NULL, 16);
+            printk("set vendor info:%x\n", vendor_info);
+            rwnx_send_rftest_req(p_rwnx_hw, SET_VENDOR_INFO, 1, &vendor_info, &cfm);
+            memcpy(command, &cfm.rftest_result[0], 2);
+            bytes_written = 2;
+            printk("0x%x\n", cfm.rftest_result[0]);
+        } else if (strcasecmp(argv[0], "GET_VENDOR_INFO")==0) {
+            printk("get vendor info\n");
+            rwnx_send_rftest_req(p_rwnx_hw, GET_VENDOR_INFO, 0, NULL, &cfm);
+            memcpy(command, &cfm.rftest_result[0], 2);
+            bytes_written = 2;
+            printk("0x%x\n", cfm.rftest_result[0]);
+        } else if (strcasecmp(argv[0], "GET_FREQ_CAL") == 0) {
+            unsigned int val;
+            printk("get freq cal\n");
+            rwnx_send_rftest_req(p_rwnx_hw, GET_FREQ_CAL, 0, NULL, &cfm);
+            memcpy(command, &cfm.rftest_result[0], 4);
+            bytes_written = 4;
+            val = cfm.rftest_result[0];
+            printk("cap=0x%x (remain:%x), cap_fine=%x (remain:%x)\n",
+                    val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
+        } else if (strcasecmp(argv[0], "RDWR_PWRMM") == 0) {
+            printk("read/write txpwr manul mode\n");
+            if (argc <= 1) { // read cur
+                rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRMM, 0, NULL, &cfm);
+            } else { // write
+                u8_l pwrmm = (u8_l)command_strtoul(argv[1], NULL, 16);
+                pwrmm = (pwrmm) ? 1 : 0;
+                printk("set pwrmm = %x\r\n", pwrmm);
+                rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRMM, sizeof(pwrmm), (u8_l *)&pwrmm, &cfm);
+            }
+            memcpy(command, &cfm.rftest_result[0], 4);
+            bytes_written = 4;
+        } else if (strcasecmp(argv[0], "RDWR_PWRLVL") == 0) {
+            u8_l func = 0;
+            printk("read/write txpwr level\n");
+            if (argc > 1) {
+                func = (u8_l)command_strtoul(argv[1], NULL, 16);
+            }
+            if (func == 0) { // read cur
+                rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRLVL, 0, NULL, &cfm);
+            } else if (func <= 2) { // write 2.4g/5g pwr lvl
+                if (argc > 4) {
+                    u8_l grp = (u8_l)command_strtoul(argv[2], NULL, 16);
+                    u8_l idx, size;
+                    u8_l buf[14] = {func, grp,};
+                    if (argc > 12) { // set all grp
+                        printk("set pwrlvl %s:\n"
+                               "  [%x] =", (func == 1) ? "2.4g" : "5g", grp);
+                        if (grp == 1) { // TXPWR_LVL_GRP_11N_11AC
+                            size = 10;
+                        } else {
+                            size = 12;
+                        }
+                        for (idx = 0; idx < size; idx++) {
+                            s8_l pwrlvl = (s8_l)command_strtoul(argv[3 + idx], NULL, 10);
+                            buf[2 + idx] = (u8_l)pwrlvl;
+                            if (idx && !(idx & 0x3)) {
+                                printk(" ");
+                            }
+                            printk(" %2d", pwrlvl);
+                        }
+                        printk("\n");
+                        size += 2;
+                    } else { // set grp[idx]
+                        u8_l idx = (u8_l)command_strtoul(argv[3], NULL, 10);
+                        s8_l pwrlvl = (s8_l)command_strtoul(argv[4], NULL, 10);
+                        buf[2] = idx;
+                        buf[3] = (u8_l)pwrlvl;
+                        size = 4;
+                        printk("set pwrlvl %s:\n"
+                               "  [%x][%d] = %d\n", (func == 1) ? "2.4g" : "5g", grp, idx, pwrlvl);
+                    }
+                    rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRLVL, size, buf, &cfm);
+                } else {
+                    printk("wrong args\n");
+                }
+            } else {
+                printk("wrong func: %x\n", func);
+            }
+			if(g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+				memcpy(command, &cfm.rftest_result[0], 6 * 12);
+				bytes_written = 6 * 12;
+			} else {
+				memcpy(command, &cfm.rftest_result[0], 3 * 12);
+				bytes_written = 3 * 12;
+			}
+        } else if (strcasecmp(argv[0], "RDWR_PWROFST") == 0) {
+            u8_l func = 0;
+			int res_len = 0;
+            printk("read/write txpwr offset\n");
+            if (argc > 1) {
+                func = (u8_l)command_strtoul(argv[1], NULL, 16);
+            }
+            if (func == 0) { // read cur
+                rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWROFST, 0, NULL, &cfm);
+            } else if (func <= 2) { // write 2.4g/5g pwr ofst
+				if ((argc > 4) && (g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800D80)) {
+                    u8_l type = (u8_l)command_strtoul(argv[2], NULL, 16);
+                    u8_l chgrp = (u8_l)command_strtoul(argv[3], NULL, 16);
+                    s8_l pwrofst = (u8_l)command_strtoul(argv[4], NULL, 10);
+                    u8_l buf[4] = {func, type, chgrp, (u8_l)pwrofst};
+                    printk("set pwrofst_%s:[%x][%x]=%d\r\n", (func == 1) ? "2.4g" : "5g", type, chgrp, pwrofst);
+                    rwnx_send_rftest_req(g_rwnx_plat->sdiodev->rwnx_hw, RDWR_PWROFST, sizeof(buf), buf, &cfm);
+                }else if ((argc > 3) && (g_rwnx_plat->sdiodev->chipid != PRODUCT_ID_AIC8800D80)) {
+                    u8_l chgrp = (u8_l)command_strtoul(argv[2], NULL, 16);
+                    s8_l pwrofst = (u8_l)command_strtoul(argv[3], NULL, 10);
+                    u8_l buf[3] = {func, chgrp, (u8_l)pwrofst};
+                    printk("set pwrofst:[%x][%x]=%d\r\n", func, chgrp, pwrofst);
+                    rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWROFST, sizeof(buf), buf, &cfm);
+                } else {
+                    printk("wrong args\n");
+					bytes_written = -EINVAL;
+					break;
+                }
+            } else {
+                printk("wrong func: %x\n", func);
+				bytes_written = -EINVAL;
+				break;
+            }
+			if ((g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800DC) ||
+                (g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800DW)) {
+	            res_len = 7;
+            }else if (g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800D80) { // 3 * 2 (2.4g) + 3 * 6 (5g)
+                res_len = 3 * 3 + 3 * 6;
+            } else {
+                res_len = 3 + 4;
+            }
+			memcpy(command, &cfm.rftest_result[0], res_len);
+			bytes_written = res_len;
+        } else if (strcasecmp(argv[0], "RDWR_DRVIBIT") == 0) {
+            u8_l func = 0;
+            printk("read/write pa drv_ibit\n");
+            if (argc > 1) {
+                func = (u8_l)command_strtoul(argv[1], NULL, 16);
+            }
+            if (func == 0) { // read cur
+                rwnx_send_rftest_req(p_rwnx_hw, RDWR_DRVIBIT, 0, NULL, &cfm);
+            } else if (func == 1) { // write 2.4g pa drv_ibit
+                if (argc > 2) {
+                    u8_l ibit = (u8_l)command_strtoul(argv[2], NULL, 16);
+                    u8_l buf[2] = {func, ibit};
+                    printk("set drvibit:[%x]=%x\r\n", func, ibit);
+                    rwnx_send_rftest_req(p_rwnx_hw, RDWR_DRVIBIT, sizeof(buf), buf, &cfm);
+                } else {
+                    printk("wrong args\n");
+                }
+            } else {
+                printk("wrong func: %x\n", func);
+            }
+            memcpy(command, &cfm.rftest_result[0], 16);
+            bytes_written = 16;
+        } else if (strcasecmp(argv[0], "RDWR_EFUSE_PWROFST") == 0) {
+            u8_l func = 0;
+			int res_len = 0;
+            printk("read/write txpwr offset into efuse\n");
+            if (argc > 1) {
+                func = (u8_l)command_strtoul(argv[1], NULL, 16);
+            }
+            if (func == 0) { // read cur
+                rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_PWROFST, 0, NULL, &cfm);
+            } else if (func <= 2) { // write 2.4g/5g pwr ofst
+				if ((argc > 4) && (g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800D80)) {
+                    u8_l type = (u8_l)command_strtoul(argv[2], NULL, 16);
+                    u8_l chgrp = (u8_l)command_strtoul(argv[3], NULL, 16);
+                    s8_l pwrofst = (u8_l)command_strtoul(argv[4], NULL, 10);
+                    u8_l buf[4] = {func, type, chgrp, (u8_l)pwrofst};
+                    printk("set efuse pwrofst_%s:[%x][%x]=%d\r\n", (func == 1) ? "2.4g" : "5g", type, chgrp, pwrofst);
+                    rwnx_send_rftest_req(g_rwnx_plat->sdiodev->rwnx_hw, RDWR_EFUSE_PWROFST, sizeof(buf), buf, &cfm);
+                } else if ((argc > 3) && (g_rwnx_plat->sdiodev->chipid != PRODUCT_ID_AIC8800D80)) {
+                    u8_l chgrp = (u8_l)command_strtoul(argv[2], NULL, 16);
+                    s8_l pwrofst = (u8_l)command_strtoul(argv[3], NULL, 10);
+                    u8_l buf[3] = {func, chgrp, (u8_l)pwrofst};
+                    printk("set efuse pwrofst:[%x][%x]=%d\r\n", func, chgrp, pwrofst);
+                    rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_PWROFST, sizeof(buf), buf, &cfm);
+                } else {
+                    printk("wrong args\n");
+					bytes_written = -EINVAL;
+					break;
+                }
+            } else {
+                printk("wrong func: %x\n", func);
+				bytes_written = -EINVAL;
+				break;
+            }
+            if ((g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800DC) ||
+                (g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800DW)) { // 6 = 3 (2.4g) * 2
+                res_len = 3 * 2;
+            } else if (g_rwnx_plat->sdiodev->chipid == PRODUCT_ID_AIC8800D80) { // 3 * 2 (2.4g) + 3 * 6 (5g)
+                res_len = 3 * 3 + 3 * 6;
+            } else { // 7 = 3(2.4g) + 4(5g)
+                res_len = 3 + 4;
+            }
+            memcpy(command, &cfm.rftest_result[0], res_len);
+            bytes_written = res_len;
+        } else if (strcasecmp(argv[0], "RDWR_EFUSE_DRVIBIT") == 0) {
+            u8_l func = 0;
+            printk("read/write pa drv_ibit into efuse\n");
+            if (argc > 1) {
+                func = (u8_l)command_strtoul(argv[1], NULL, 16);
+            }
+            if (func == 0) { // read cur
+                rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_DRVIBIT, 0, NULL, &cfm);
+            } else if (func == 1) { // write 2.4g pa drv_ibit
+                if (argc > 2) {
+                u8_l ibit = (u8_l)command_strtoul(argv[2], NULL, 16);
+                u8_l buf[2] = {func, ibit};
+                printk("set efuse drvibit:[%x]=%x\r\n", func, ibit);
+                rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_DRVIBIT, sizeof(buf), buf, &cfm);
+                } else {
+                    printk("wrong args\n");
+                }
+            } else {
+                printk("wrong func: %x\n", func);
+            }
+            memcpy(command, &cfm.rftest_result[0], 4);
+            bytes_written = 4;
+        } else if (strcasecmp(argv[0], "SET_PAPR") == 0) {
+            printk("set papr\n");
+            if (argc > 1) {
+                u8_l func = (u8_l)command_strtoul(argv[1], NULL, 10);
+                printk("papr %d\r\n", func);
+                rwnx_send_rftest_req(p_rwnx_hw, SET_PAPR, sizeof(func), &func, NULL);
+            } else {
+                printk("wrong args\n");
+            }
+        }  else if (strcasecmp(argv[0], "SET_NOTCH") == 0) {
+            if (argc > 1) {
+                u8_l func = (u8_l)command_strtoul(argv[1], NULL, 10);
+                printk("set notch: %d\n", func);
+                rwnx_send_rftest_req(p_rwnx_hw, SET_NOTCH, sizeof(func), (u8_l *)&func, NULL);
+            } else {
+                printk("wrong args\n");
+                bytes_written = -EINVAL;
+                break;
+            }
+        }  else if (strcasecmp(argv[0], "SET_TXPWR_LOSS") == 0) {
+            if (argc > 1) {
+                s8_l func = (s8_l)command_strtoul(argv[1], NULL, 10);
+                printk("set txpwr loss: %d\n", func);
+                set_txpwr_loss_ofst(func);
+                rwnx_send_txpwr_lvl_req(p_rwnx_hw);
+            } else {
+                printk("wrong args\n");
+                bytes_written = -EINVAL;
+                break;
+            }
+        }
+
+        #ifdef CONFIG_RFTEST_USB_BT
+        else if (strcasecmp(argv[0], "BT_RESET") == 0) {
+            if (argc == 5) {
+                printk("btrf reset\n");
+                for (bt_index = 0; bt_index < 4; bt_index++) {
+                    dh_cmd_reset[bt_index] = command_strtoul(argv[bt_index+1], NULL, 16);
+                    printk("0x%x ",dh_cmd_reset[bt_index]);
+                }
+                printk("\n");
+            } else {
+                printk("wrong param\n");
+                break;
+            }
+            rwnx_send_rftest_req(p_rwnx_hw, BT_RESET, sizeof(dh_cmd_reset), (u8_l *)&dh_cmd_reset, NULL);
+        } else if (strcasecmp(argv[0], "BT_TXDH") == 0) {
+            if (argc == 19) {
+                printk("btrf txdh\n");
+                for (bt_index = 0; bt_index < 18; bt_index++) {
+                    dh_cmd_txdh[bt_index] = command_strtoul(argv[bt_index+1], NULL, 16);
+                    printk("0x%x ", dh_cmd_txdh[bt_index]);
+                }
+                printk("\n");
+            } else {
+                printk("wrong param\n");
+                break;
+            }
+            rwnx_send_rftest_req(p_rwnx_hw, BT_TXDH, sizeof(dh_cmd_txdh), (u8_l *)&dh_cmd_txdh, NULL);
+        } else if (strcasecmp(argv[0], "BT_RXDH") == 0) {
+            if (argc == 18) {
+                printk("btrf rxdh\n");
+                for (bt_index = 0; bt_index < 17; bt_index++) {
+                    dh_cmd_rxdh[bt_index] = command_strtoul(argv[bt_index+1], NULL, 16);
+                    printk("0x%x ", dh_cmd_rxdh[bt_index]);
+                }
+                printk("\n");
+            } else {
+                printk("wrong param\n");
+                break;
+            }
+            rwnx_send_rftest_req(p_rwnx_hw, BT_RXDH, sizeof(dh_cmd_rxdh), (u8_l *)&dh_cmd_rxdh, NULL);
+        } else if (strcasecmp(argv[0], "BT_STOP") == 0) {
+            if (argc == 6) {
+                printk("btrf stop\n");
+                for (bt_index = 0; bt_index < 5; bt_index++) {
+                    dh_cmd_stop[bt_index] = command_strtoul(argv[bt_index+1], NULL, 16);
+                    printk("0x%x ", dh_cmd_stop[bt_index]);
+                }
+                printk("\n");
+            } else {
+                printk("wrong param\n");
+                break;
+            }
+            rwnx_send_rftest_req(p_rwnx_hw, BT_STOP, sizeof(dh_cmd_stop), (u8_l *)&dh_cmd_stop, NULL);
+        } else if (strcasecmp(argv[0], "GET_BT_RX_RESULT") ==0) {
+            printk("get_bt_rx_result\n");
+            rwnx_send_rftest_req(p_rwnx_hw, GET_BT_RX_RESULT, 0, NULL, &cfm);
+            memcpy(command, &cfm.rftest_result[0], 12);
+            bytes_written = 12;
+        }
+        #endif
+        else {
+            printk("wrong cmd:%s in %s\n", command, __func__);
+        }
+        #endif
+    } while(0);
+    return bytes_written;
+}
+
+//Android private command
+
+#define RWNX_COUNTRY_CODE_LEN 2
+#define CMD_SET_COUNTRY "COUNTRY"
+#define CMD_SET_VENDOR_EX_IE "SET_VENDOR_EX_IE"
+
+struct ieee80211_regdomain *getRegdomainFromRwnxDB(struct wiphy *wiphy, char *alpha2);
+struct ieee80211_regdomain *getRegdomainFromRwnxDBIndex(struct wiphy *wiphy, int index);
+extern int reg_regdb_size1;
+
+#ifdef CONFIG_SET_VENDOR_EXTENSION_IE
+extern u8_l vendor_extension_data[256];
+extern int vendor_extension_len;
+
+void set_vendor_extension_ie(char *command){
+
+	char databyte[3]={0x00, 0x00, 0x00};
+	int skip = strlen(CMD_SET_VENDOR_EX_IE) + 1;
+	int command_index = skip;
+	int data_index = 0;
+
+	memset(vendor_extension_data, 0, 256);
+	vendor_extension_len = 0;
+	memcpy(databyte, command + command_index, 2);
+	vendor_extension_len = command_strtoul(databyte, NULL, 16);
+	printk("%s len:%d \r\n", __func__, vendor_extension_len);
+
+	//parser command and save data in vendor_extension_data
+	for(data_index = 0;data_index < vendor_extension_len; data_index++){
+		command_index = command_index + 3;
+		memcpy(databyte, command + command_index, 2);
+		vendor_extension_data[data_index] = command_strtoul(databyte, NULL, 16);
+	}
+	
+}
+#endif//CONFIG_SET_VENDOR_EXTENSION_IE
+
+int android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
+{
+#define PRIVATE_COMMAND_MAX_LEN 8192
+#define PRIVATE_COMMAND_DEF_LEN 4096
+
+	struct rwnx_vif *vif = netdev_priv(net);
+	int ret = 0;
+	char *command = NULL;
+	int bytes_written = 0;
+	android_wifi_priv_cmd priv_cmd;
+	int buf_size = 0;
+	int skip = 0;
+	char *country = NULL;
+	struct ieee80211_regdomain *regdomain;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    ///todo: add our lock
+    //net_os_wake_lock(net);
+
+
+/*    if (!capable(CAP_NET_ADMIN)) {
+        ret = -EPERM;
+        goto exit;
+    }*/
+    if (!ifr->ifr_data) {
+        ret = -EINVAL;
+        goto exit;
+    }
+
+#ifdef CONFIG_COMPAT
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0))
+    if (in_compat_syscall())
+#else
+    if (is_compat_task())
+#endif
+    {
+        compat_android_wifi_priv_cmd compat_priv_cmd;
+        if (copy_from_user(&compat_priv_cmd, ifr->ifr_data, sizeof(compat_android_wifi_priv_cmd))) {
+	    ret = -EFAULT;
+            goto exit;
+        }
+        priv_cmd.buf = compat_ptr(compat_priv_cmd.buf);
+        priv_cmd.used_len = compat_priv_cmd.used_len;
+        priv_cmd.total_len = compat_priv_cmd.total_len;
+    } else
+#endif /* CONFIG_COMPAT */
+    {
+        if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) {
+	    ret = -EFAULT;
+            goto exit;
+        }
+    }
+    if ((priv_cmd.total_len > PRIVATE_COMMAND_MAX_LEN) || (priv_cmd.total_len < 0)) {
+        printk("%s: buf length invalid:%d\n", __FUNCTION__, priv_cmd.total_len);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    buf_size = max(priv_cmd.total_len, PRIVATE_COMMAND_DEF_LEN);
+    command = kmalloc((buf_size + 1), GFP_KERNEL);
+
+    if (!command)
+    {
+        printk("%s: failed to allocate memory\n", __FUNCTION__);
+        ret = -ENOMEM;
+        goto exit;
+    }
+    if (copy_from_user(command, priv_cmd.buf, priv_cmd.total_len)) {
+        ret = -EFAULT;
+        goto exit;
+    }
+    command[priv_cmd.total_len] = '\0';
+
+    /* outputs */
+    printk("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name);
+    printk("cmd = %d\n", cmd);
+    printk("buf_size=%d\n", buf_size);
+
+#if 1//Handle Android command
+		if(!strncasecmp(command, CMD_SET_COUNTRY, strlen(CMD_SET_COUNTRY))) {
+			skip = strlen(CMD_SET_COUNTRY) + 1;
+			country = command + skip;
+			if (!country || strlen(country) < RWNX_COUNTRY_CODE_LEN) {
+				printk("%s: invalid country code\n", __func__);
+				ret = -EINVAL;
+				goto exit;
+			}
+#if 0
+			for(index = 0; index < reg_regdb_size1; index++){
+				regdomain = getRegdomainFromRwnxDBIndex(vif->rwnx_hw->wiphy, index);
+				if((ret = regulatory_set_wiphy_regd(vif->rwnx_hw->wiphy, regdomain))){
+					printk("regulatory_set_wiphy_regd fail \r\n");
+				}else{
+					printk("regulatory_set_wiphy_regd ok \r\n");
+				}
+			}
+#endif
+			printk("%s country code:%c%c\n", __func__, toupper(country[0]), toupper(country[1]));
+			regdomain = getRegdomainFromRwnxDB(vif->rwnx_hw->wiphy, country);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+			if((ret = regulatory_set_wiphy_regd(vif->rwnx_hw->wiphy, regdomain))){
+				printk("regulatory_set_wiphy_regd fail \r\n");
+			}
+#else
+			wiphy_apply_custom_regulatory(vif->rwnx_hw->wiphy, regdomain);
+#endif
+		}
+#ifdef CONFIG_SET_VENDOR_EXTENSION_IE
+		else if(!strncasecmp(command, CMD_SET_VENDOR_EX_IE, strlen(CMD_SET_VENDOR_EX_IE))){
+			set_vendor_extension_ie(command);
+		}
+#endif//CONFIG_SET_VENDOR_EXTENSION_IE
+#endif//Handle Android command
+
+    bytes_written = handle_private_cmd(net, command, priv_cmd.total_len);
+    if (bytes_written >= 0) {
+        if ((bytes_written == 0) && (priv_cmd.total_len > 0)) {
+            command[0] = '\0';
+        }
+        if (bytes_written >= priv_cmd.total_len) {
+            printk("%s: err. bytes_written:%d >= buf_size:%d \n",
+                __FUNCTION__, bytes_written, buf_size);
+            goto exit;
+        }
+        bytes_written++;
+        priv_cmd.used_len = bytes_written;
+        if (copy_to_user(priv_cmd.buf, command, bytes_written)) {
+            printk("%s: failed to copy data to user buffer\n", __FUNCTION__);
+            ret = -EFAULT;
+        }
+    }
+    else {
+        /* Propagate the error */
+        ret = bytes_written;
+    }
+
+exit:
+    ///todo: add our unlock
+    //net_os_wake_unlock(net);
+    kfree(command);
+    return ret;
+}
+
+#ifdef CONFIG_MCU_MESSAGE
+#define CMD_GET_VERSION_STR "GET_VERSION"
+#define CMD_GET_SSID_STR    "GET_SSID"
+#define CMD_SET_SSID_STR    "SET_SSID"
+#define CMD_GET_PASS_STR    "GET_PASS"
+#define CMD_SET_PASS_STR    "SET_PASS"
+#define CMD_GET_VAR_STR     "GET_VAR"
+#define CMD_SET_VAR_STR     "SET_VAR"
+
+enum custmsg_cmd_tag
+{
+    CUST_CMD_GET_VERSION = 0,
+    CUST_CMD_GET_SSID,
+    CUST_CMD_SET_SSID,
+    CUST_CMD_GET_PASS,
+    CUST_CMD_SET_PASS,
+    CUST_CMD_GET_VAR,
+    CUST_CMD_SET_VAR,
+    CUST_CMD_MAX
+};
+
+int handle_custom_msg(char *command, u32 cmd_len)
+{
+    int bytes_read = 0, max_bytes_to_read = 0;
+    struct rwnx_hw *p_rwnx_hw = NULL;
+    u32 cmd, len = 0, flags = 0;
+    char *buf = NULL;
+    struct dbg_custom_msg_cfm *cust_msg_cfm;
+    printk("cmd,%s,%ld\n",command,strlen(command));
+    if (strncasecmp(command, CMD_GET_VERSION_STR, strlen(CMD_GET_VERSION_STR)) == 0) {
+        cmd = CUST_CMD_GET_VERSION;
+        max_bytes_to_read = 32; // max str len for version
+    } else if (strncasecmp(command, CMD_GET_SSID_STR, strlen(CMD_GET_SSID_STR)) == 0) {
+        cmd = CUST_CMD_GET_SSID;
+        max_bytes_to_read = 48; // max str len for ssid
+    } else if (strncasecmp(command, CMD_SET_SSID_STR, strlen(CMD_SET_SSID_STR)) == 0) {
+        cmd = CUST_CMD_SET_SSID;
+        len = cmd_len - (strlen(CMD_SET_SSID_STR) + 1);
+        buf = command + (strlen(CMD_SET_SSID_STR) + 1);
+        max_bytes_to_read = 0;
+    } else if (strncasecmp(command, CMD_GET_PASS_STR, strlen(CMD_GET_PASS_STR)) == 0) {
+        cmd = CUST_CMD_GET_PASS;
+        max_bytes_to_read = 64; // max str len for PASS
+    } else if (strncasecmp(command, CMD_SET_PASS_STR, strlen(CMD_SET_PASS_STR)) == 0) {
+        cmd = CUST_CMD_SET_PASS;
+        len = cmd_len - (strlen(CMD_SET_PASS_STR) + 1);
+        buf = command + (strlen(CMD_SET_PASS_STR) + 1);
+        max_bytes_to_read = 0;
+    } else if (strncasecmp(command, CMD_GET_VAR_STR, strlen(CMD_GET_VAR_STR)) == 0) {
+        cmd = CUST_CMD_GET_VAR;
+        max_bytes_to_read = 64; // max str len for VAR
+    } else if (strncasecmp(command, CMD_SET_VAR_STR, strlen(CMD_SET_VAR_STR)) == 0) {
+        cmd = CUST_CMD_SET_VAR;
+        len = cmd_len - (strlen(CMD_SET_VAR_STR) + 1);
+        buf = command + (strlen(CMD_SET_VAR_STR) + 1);
+        max_bytes_to_read = 0;
+    } else {
+        printk("invalid cmd: %s\r\n", command);
+        return -1;
+    }
+    if (len < 0) {
+        printk("invalid len: %d\r\n", len);
+        return -3;
+    }
+    #ifdef AICWF_SDIO_SUPPORT
+    p_rwnx_hw = g_rwnx_plat->sdiodev->rwnx_hw;
+    #endif
+    #ifdef AICWF_USB_SUPPORT
+    p_rwnx_hw = g_rwnx_plat->usbdev->rwnx_hw;
+    #endif
+    cust_msg_cfm = (struct dbg_custom_msg_cfm *)kmalloc((offsetof(struct dbg_custom_msg_cfm, buf) + max_bytes_to_read), GFP_KERNEL);
+    if (cust_msg_cfm == NULL) {
+        printk("msg cfm alloc fail\r\n");
+        return -2;
+    }
+    rwnx_send_dbg_custom_msg_req(p_rwnx_hw, cmd, buf, len, flags, cust_msg_cfm);
+    bytes_read = cust_msg_cfm->len;
+    printk("Custom msg cfm: cmd=%d, len=%d, status=%x\n", cust_msg_cfm->cmd, bytes_read, cust_msg_cfm->status);
+    if (bytes_read) {
+        memcpy(command, cust_msg_cfm->buf, bytes_read);
+        command[bytes_read] = '\0';
+    } else {
+        command[0] = '\0';
+    }
+    if (cust_msg_cfm->status) {
+        printk("cfm status: %x", cust_msg_cfm->status);
+    }
+    return bytes_read;
+}
+
+int devipc_cust_msg(struct net_device *net, struct ifreq *ifr, int cmd)
+{
+#ifdef PRIVATE_COMMAND_MAX_LEN
+#undef PRIVATE_COMMAND_MAX_LEN
+#undef PRIVATE_COMMAND_DEF_LEN
+#define PRIVATE_COMMAND_MAX_LEN 8192
+#define PRIVATE_COMMAND_DEF_LEN 4096
+#endif
+    int ret = 0;
+    char *command = NULL;
+    int bytes_written = 0;
+    android_wifi_priv_cmd priv_cmd;
+    int buf_size = 0;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    ///todo: add our lock
+    //net_os_wake_lock(net);
+
+
+/*    if (!capable(CAP_NET_ADMIN)) {
+        ret = -EPERM;
+        goto exit;
+    }*/
+    if (!ifr->ifr_data) {
+        ret = -EINVAL;
+        goto exit;
+    }
+
+#ifdef CONFIG_COMPAT
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0))
+    if (in_compat_syscall())
+#else
+    if (is_compat_task())
+#endif
+    {
+        compat_android_wifi_priv_cmd compat_priv_cmd;
+        if (copy_from_user(&compat_priv_cmd, ifr->ifr_data, sizeof(compat_android_wifi_priv_cmd))) {
+	    ret = -EFAULT;
+            goto exit;
+        }
+        priv_cmd.buf = compat_ptr(compat_priv_cmd.buf);
+        priv_cmd.used_len = compat_priv_cmd.used_len;
+        priv_cmd.total_len = compat_priv_cmd.total_len;
+    } else
+#endif /* CONFIG_COMPAT */
+    {
+        if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) {
+	    ret = -EFAULT;
+            goto exit;
+        }
+    }
+    if ((priv_cmd.total_len > PRIVATE_COMMAND_MAX_LEN) || (priv_cmd.total_len < 0)) {
+        printk("%s: buf length invalid:%d\n", __FUNCTION__, priv_cmd.total_len);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    buf_size = max(priv_cmd.total_len, PRIVATE_COMMAND_DEF_LEN);
+    command = kmalloc((buf_size + 1), GFP_KERNEL);
+
+    if (!command)
+    {
+        printk("%s: failed to allocate memory\n", __FUNCTION__);
+        ret = -ENOMEM;
+        goto exit;
+    }
+    if (copy_from_user(command, priv_cmd.buf, priv_cmd.used_len)) {
+        ret = -EFAULT;
+        goto exit;
+    }
+    command[priv_cmd.used_len] = '\0';
+
+    /* outputs */
+    printk("%s: Devipc custom msg \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name);
+    printk("cmd = %x\n", cmd);
+    printk("buf_size=%d\n", buf_size);
+
+
+    bytes_written = handle_custom_msg(command, priv_cmd.used_len);
+    if (bytes_written >= 0) {
+        if ((bytes_written == 0) && (priv_cmd.total_len > 0)) {
+            command[0] = '\0';
+        }
+        if (bytes_written >= priv_cmd.total_len) {
+            printk("%s: err. bytes_written:%d >= buf_size:%d \n",
+                __FUNCTION__, bytes_written, buf_size);
+            goto exit;
+        }
+        bytes_written++;
+        priv_cmd.used_len = bytes_written;
+        if (copy_to_user(priv_cmd.buf, command, bytes_written)) {
+            printk("%s: failed to copy data to user buffer\n", __FUNCTION__);
+            ret = -EFAULT;
+        }
+    }
+    else {
+        /* Propagate the error */
+        ret = bytes_written;
+    }
+
+exit:
+    ///todo: add our unlock
+    //net_os_wake_unlock(net);
+    kfree(command);
+    return ret;
+}
+#endif
+
+#define IOCTL_HOSTAPD   (SIOCIWFIRSTPRIV+28)
+#define IOCTL_WPAS      (SIOCIWFIRSTPRIV+30)
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
+static int rwnx_do_ioctl(struct net_device *net, struct ifreq *req, void __user *data, int cmd)
+#else
+static int rwnx_do_ioctl(struct net_device *net, struct ifreq *req, int cmd)
+#endif
+{
+    int ret = 0;
+    ///TODO: add ioctl command handler later
+    switch(cmd)
+    {
+        case IOCTL_HOSTAPD:
+            printk("IOCTL_HOSTAPD\n");
+            break;
+        case IOCTL_WPAS:
+            printk("IOCTL_WPAS\n");
+            break;
+        case SIOCDEVPRIVATE:
+            printk("IOCTL SIOCDEVPRIVATE\n");
+            break;
+        case (SIOCDEVPRIVATE+1):
+            printk("IOCTL PRIVATE\n");
+            android_priv_cmd(net, req, cmd);
+            break;
+        case (SIOCDEVPRIVATE+2):
+            printk("IOCTL PRIVATE+2\n");
+            #ifdef CONFIG_MCU_MESSAGE
+            devipc_cust_msg(net, req, cmd);
+            #endif
+            break;
+        default:
+            ret = -EOPNOTSUPP;
+    }
+    return ret;
+}
+
+/**
+ * struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
+ *	Called when a user wants to get the network device usage
+ *	statistics. Drivers must do one of the following:
+ *	1. Define @ndo_get_stats64 to fill in a zero-initialised
+ *	   rtnl_link_stats64 structure passed by the caller.
+ *	2. Define @ndo_get_stats to update a net_device_stats structure
+ *	   (which should normally be dev->stats) and return a pointer to
+ *	   it. The structure may be changed asynchronously only if each
+ *	   field is written atomically.
+ *	3. Update dev->stats asynchronously and atomically, and define
+ *	   neither operation.
+ */
+static struct net_device_stats *rwnx_get_stats(struct net_device *dev)
+{
+    struct rwnx_vif *vif = netdev_priv(dev);
+
+    return &vif->net_stats;
+}
+
+/**
+ * u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb,
+ *                         struct net_device *sb_dev);
+ *	Called to decide which queue to when device supports multiple
+ *	transmit queues.
+ */
+u16 rwnx_select_queue(struct net_device *dev, struct sk_buff *skb,
+                      struct net_device *sb_dev)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    return rwnx_select_txq(rwnx_vif, skb);
+}
+
+/**
+ * int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
+ *	This function  is called when the Media Access Control address
+ *	needs to be changed. If this interface is not defined, the
+ *	mac address can not be changed.
+ */
+static int rwnx_set_mac_address(struct net_device *dev, void *addr)
+{
+    struct sockaddr *sa = addr;
+    int ret;
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+
+    ret = eth_mac_addr(dev, sa);
+    memcpy(rwnx_vif->address, dev->dev_addr, 6);
+
+    return ret;
+}
+
+static const struct net_device_ops rwnx_netdev_ops = {
+    .ndo_open               = rwnx_open,
+    .ndo_stop               = rwnx_close,
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
+    .ndo_siocdevprivate     = rwnx_do_ioctl,
+    #else
+    .ndo_do_ioctl           = rwnx_do_ioctl,
+    #endif
+    .ndo_start_xmit         = rwnx_start_xmit,
+    .ndo_get_stats          = rwnx_get_stats,
+#ifndef CONFIG_ONE_TXQ
+    .ndo_select_queue       = rwnx_select_queue,
+#endif
+    .ndo_set_mac_address    = rwnx_set_mac_address
+//    .ndo_set_features       = rwnx_set_features,
+//    .ndo_set_rx_mode        = rwnx_set_multicast_list,
+};
+
+static const struct net_device_ops rwnx_netdev_monitor_ops = {
+    .ndo_open               = rwnx_open,
+    .ndo_stop               = rwnx_close,
+    #ifdef CONFIG_RWNX_MON_XMIT
+    .ndo_start_xmit         = rwnx_start_monitor_if_xmit,
+    .ndo_select_queue       = rwnx_select_queue,
+    #endif
+    .ndo_get_stats          = rwnx_get_stats,
+    .ndo_set_mac_address    = rwnx_set_mac_address,
+};
+
+static void rwnx_netdev_setup(struct net_device *dev)
+{
+    ether_setup(dev);
+    dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+    dev->netdev_ops = &rwnx_netdev_ops;
+#if LINUX_VERSION_CODE <  KERNEL_VERSION(4, 12, 0)
+    dev->destructor = free_netdev;
+#else
+    dev->needs_free_netdev = true;
+#endif
+    dev->watchdog_timeo = RWNX_TX_LIFETIME_MS;
+
+    dev->needed_headroom = sizeof(struct rwnx_txhdr) + RWNX_SWTXHDR_ALIGN_SZ - 14;
+#ifdef CONFIG_RWNX_AMSDUS_TX
+    dev->needed_headroom = max(dev->needed_headroom,
+                               (unsigned short)(sizeof(struct rwnx_amsdu_txhdr)
+                                                + sizeof(struct ethhdr) + 4
+                                                + sizeof(rfc1042_header) + 2));
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+
+    dev->hw_features = 0;
+}
+
+/*********************************************************************
+ * Cfg80211 callbacks (and helper)
+ *********************************************************************/
+static struct rwnx_vif *rwnx_interface_add(struct rwnx_hw *rwnx_hw,
+                                               const char *name,
+                                               unsigned char name_assign_type,
+                                               enum nl80211_iftype type,
+                                               struct vif_params *params,
+                                               struct rwnx_conf_file conf_fl)
+{
+    struct net_device *ndev;
+    struct rwnx_vif *vif;
+    int min_idx, max_idx;
+    int vif_idx = -1;
+    int i;
+
+    printk("rwnx_interface_add: %s, %d, %d", name, type, NL80211_IFTYPE_P2P_DEVICE);
+    // Look for an available VIF
+    if (type == NL80211_IFTYPE_AP_VLAN) {
+        min_idx = NX_VIRT_DEV_MAX;
+        max_idx = NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX;
+    } else {
+        min_idx = 0;
+        max_idx = NX_VIRT_DEV_MAX;
+    }
+
+    for (i = min_idx; i < max_idx; i++) {
+        if ((rwnx_hw->avail_idx_map) & BIT(i)) {
+            vif_idx = i;
+            break;
+        }
+    }
+    if (vif_idx < 0)
+        return NULL;
+
+    #ifndef CONFIG_RWNX_MON_DATA
+    list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+        // Check if monitor interface already exists or type is monitor
+        if ((RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_MONITOR) ||
+           (type == NL80211_IFTYPE_MONITOR)) {
+            wiphy_err(rwnx_hw->wiphy,
+                    "Monitor+Data interface support (MON_DATA) disabled\n");
+            return NULL;
+        }
+    }
+    #endif
+
+#ifndef CONFIG_ONE_TXQ
+    ndev = alloc_netdev_mqs(sizeof(*vif), name, name_assign_type,
+                            rwnx_netdev_setup, NX_NB_NDEV_TXQ, 1);
+#else
+    ndev = alloc_netdev_mqs(sizeof(*vif), name, name_assign_type,
+                                rwnx_netdev_setup, 1, 1);
+#endif
+    if (!ndev)
+        return NULL;
+
+    vif = netdev_priv(ndev);
+    vif->key_has_add = 0;
+    ndev->ieee80211_ptr = &vif->wdev;
+    vif->wdev.wiphy = rwnx_hw->wiphy;
+    vif->rwnx_hw = rwnx_hw;
+    vif->ndev = ndev;
+    vif->drv_vif_index = vif_idx;
+    SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
+    vif->wdev.netdev = ndev;
+    vif->wdev.iftype = type;
+    vif->up = false;
+    vif->ch_index = RWNX_CH_NOT_SET;
+    memset(&vif->net_stats, 0, sizeof(vif->net_stats));
+    vif->is_p2p_vif = 0;
+
+    switch (type) {
+    case NL80211_IFTYPE_STATION:
+        vif->sta.ap = NULL;
+        vif->sta.tdls_sta = NULL;
+        vif->sta.external_auth = false;
+        break;
+    case NL80211_IFTYPE_P2P_CLIENT:
+        vif->sta.ap = NULL;
+        vif->sta.tdls_sta = NULL;
+        vif->sta.external_auth = false;
+        vif->is_p2p_vif = 1;
+        break;
+    case NL80211_IFTYPE_MESH_POINT:
+        INIT_LIST_HEAD(&vif->ap.mpath_list);
+        INIT_LIST_HEAD(&vif->ap.proxy_list);
+        vif->ap.create_path = false;
+        vif->ap.generation = 0;
+        vif->ap.mesh_pm = NL80211_MESH_POWER_ACTIVE;
+        vif->ap.next_mesh_pm = NL80211_MESH_POWER_ACTIVE;
+        // no break
+    case NL80211_IFTYPE_AP:
+        INIT_LIST_HEAD(&vif->ap.sta_list);
+        memset(&vif->ap.bcn, 0, sizeof(vif->ap.bcn));
+        break;
+    case NL80211_IFTYPE_P2P_GO:
+        INIT_LIST_HEAD(&vif->ap.sta_list);
+        memset(&vif->ap.bcn, 0, sizeof(vif->ap.bcn));
+        vif->is_p2p_vif = 1;
+        break;
+    case NL80211_IFTYPE_AP_VLAN:
+    {
+        struct rwnx_vif *master_vif;
+        bool found = false;
+#if 0
+        list_for_each_entry(master_vif, &rwnx_hw->vifs, list) {
+            if ((RWNX_VIF_TYPE(master_vif) == NL80211_IFTYPE_AP) &&
+                !(!memcmp(master_vif->ndev->dev_addr, params->macaddr,
+                           ETH_ALEN))) {
+                 found=true;
+                 break;
+            }
+        }
+#endif
+        if (!found)
+            goto err;
+
+         vif->ap_vlan.master = master_vif;
+         vif->ap_vlan.sta_4a = NULL;
+         break;
+    }
+    case NL80211_IFTYPE_MONITOR:
+        ndev->type = ARPHRD_IEEE80211_RADIOTAP;
+        ndev->netdev_ops = &rwnx_netdev_monitor_ops;
+        break;
+    default:
+        break;
+    }
+
+    if (params == NULL) {
+        if (type == NL80211_IFTYPE_AP_VLAN) {
+            memcpy(ndev->dev_addr, conf_fl.mac_addr, ETH_ALEN);
+            memcpy(vif->address, conf_fl.mac_addr, ETH_ALEN);
+        }
+        else {
+            memcpy(ndev->dev_addr, conf_fl.mac_addr, ETH_ALEN);
+            //ndev->dev_addr[5] ^= vif_idx;
+            memcpy(vif->address, ndev->dev_addr, ETH_ALEN);
+        }
+    } else {
+        if (type == NL80211_IFTYPE_AP_VLAN) {
+            #if 0
+            memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN);
+            memcpy(vif->address, params->macaddr, ETH_ALEN);
+            #endif
+        }
+        else {
+            memcpy(ndev->dev_addr, rwnx_hw->wiphy->perm_addr, ETH_ALEN);
+            ndev->dev_addr[5] ^= vif_idx;
+            memcpy(vif->address, ndev->dev_addr, ETH_ALEN);
+        }
+    }
+
+    printk("interface add:%x %x %x %x %x %x\n", vif->address[0], vif->address[1], \
+        vif->address[2], vif->address[3], vif->address[4], vif->address[5]);
+
+    if (params) {
+        vif->use_4addr = params->use_4addr;
+        ndev->ieee80211_ptr->use_4addr = params->use_4addr;
+    } else
+        vif->use_4addr = false;
+
+    if (register_netdevice(ndev))
+        goto err;
+
+    spin_lock_bh(&rwnx_hw->cb_lock);
+    list_add_tail(&vif->list, &rwnx_hw->vifs);
+    spin_unlock_bh(&rwnx_hw->cb_lock);
+    rwnx_hw->avail_idx_map &= ~BIT(vif_idx);
+
+    return vif;
+
+err:
+    free_netdev(ndev);
+    return NULL;
+}
+
+#define P2P_ALIVE_TIME_MS       (1*1000)
+#define P2P_ALIVE_TIME_COUNT    200
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+void aicwf_p2p_alive_timeout(ulong data)
+#else
+void aicwf_p2p_alive_timeout(struct timer_list *t)
+#endif
+{
+    struct rwnx_hw *rwnx_hw;
+    struct rwnx_vif *rwnx_vif;
+    struct rwnx_vif *rwnx_vif1, *tmp;
+    u8_l p2p = 0;
+    #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+    rwnx_vif = (struct rwnx_vif *)data;
+    rwnx_hw = rwnx_vif->rwnx_hw;
+    #else
+    rwnx_hw = from_timer(rwnx_hw, t, p2p_alive_timer);
+    rwnx_vif = rwnx_hw->p2p_dev_vif;
+    #endif
+
+    list_for_each_entry_safe(rwnx_vif1, tmp, &rwnx_hw->vifs, list) {
+        if ((rwnx_hw->avail_idx_map & BIT(rwnx_vif1->drv_vif_index)) == 0) {
+            switch(RWNX_VIF_TYPE(rwnx_vif1)) {
+            case NL80211_IFTYPE_P2P_CLIENT:
+            case NL80211_IFTYPE_P2P_GO:
+                rwnx_hw->is_p2p_alive = 1;
+                p2p = 1;
+                break;
+            default:
+                break;
+            }
+       }
+    }
+
+    if (p2p)
+        atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+    else
+        atomic_inc(&rwnx_hw->p2p_alive_timer_count);
+
+    if (atomic_read(&rwnx_hw->p2p_alive_timer_count) < P2P_ALIVE_TIME_COUNT) {
+        mod_timer(&rwnx_hw->p2p_alive_timer,
+            jiffies + msecs_to_jiffies(P2P_ALIVE_TIME_MS));
+        return;
+    } else
+        atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+
+    rwnx_hw->is_p2p_alive = 0;
+    if(rwnx_vif->up) {
+        rwnx_send_remove_if(rwnx_hw, rwnx_vif->vif_index, true);
+
+        /* Ensure that we won't process disconnect ind */
+        spin_lock_bh(&rwnx_hw->cb_lock);
+
+        rwnx_vif->up = false;
+        rwnx_hw->vif_table[rwnx_vif->vif_index] = NULL;
+        rwnx_hw->vif_started--;
+        spin_unlock_bh(&rwnx_hw->cb_lock);
+    }
+}
+
+
+/*********************************************************************
+ * Cfg80211 callbacks (and helper)
+ *********************************************************************/
+static struct wireless_dev *rwnx_virtual_interface_add(struct rwnx_hw *rwnx_hw,
+                                               const char *name,
+                                               unsigned char name_assign_type,
+                                               enum nl80211_iftype type,
+                                               struct vif_params *params)
+{
+    struct wireless_dev *wdev = NULL;
+    struct rwnx_vif *vif;
+    int min_idx, max_idx;
+    int vif_idx = -1;
+    int i;
+
+    printk("rwnx_virtual_interface_add: %d, %s\n", type, name);
+
+    if (type == NL80211_IFTYPE_AP_VLAN) {
+        min_idx = NX_VIRT_DEV_MAX;
+        max_idx = NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX;
+    } else {
+        min_idx = 0;
+        max_idx = NX_VIRT_DEV_MAX;
+    }
+
+    for (i = min_idx; i < max_idx; i++) {
+        if ((rwnx_hw->avail_idx_map) & BIT(i)) {
+            vif_idx = i;
+            break;
+        }
+    }
+
+    if (vif_idx < 0) {
+        printk("virtual_interface_add %s fail\n", name);
+        return NULL;
+    }
+
+    vif = kzalloc(sizeof(struct rwnx_vif), GFP_KERNEL);
+    if (unlikely(!vif)) {
+        printk("Could not allocate wireless device\n");
+        return NULL;
+    }
+    wdev = &vif->wdev;
+    wdev->wiphy = rwnx_hw->wiphy;
+    wdev->iftype = type;
+
+    printk("rwnx_virtual_interface_add, ifname=%s, wdev=%p, vif_idx=%d\n", name, wdev, vif_idx);
+
+    #if 1
+    vif->is_p2p_vif = 1;
+    vif->rwnx_hw = rwnx_hw;
+    vif->vif_index = vif_idx;
+    vif->wdev.wiphy = rwnx_hw->wiphy;
+    vif->drv_vif_index = vif_idx;
+    vif->up = false;
+    vif->ch_index = RWNX_CH_NOT_SET;
+    memset(&vif->net_stats, 0, sizeof(vif->net_stats));
+    vif->use_4addr = false;
+
+    spin_lock_bh(&rwnx_hw->cb_lock);
+    list_add_tail(&vif->list, &rwnx_hw->vifs);
+    spin_unlock_bh(&rwnx_hw->cb_lock);
+
+    if (rwnx_hw->is_p2p_alive == 0) {
+        #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+        init_timer(&rwnx_hw->p2p_alive_timer);
+        rwnx_hw->p2p_alive_timer.data = (unsigned long)vif;
+        rwnx_hw->p2p_alive_timer.function = aicwf_p2p_alive_timeout;
+        #else
+        timer_setup(&rwnx_hw->p2p_alive_timer, aicwf_p2p_alive_timeout, 0);
+        #endif
+        rwnx_hw->is_p2p_alive = 0;
+        rwnx_hw->is_p2p_connected = 0;
+        rwnx_hw->p2p_dev_vif = vif;
+        atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+    }
+    #endif
+    rwnx_hw->avail_idx_map &= ~BIT(vif_idx);
+
+    memcpy(vif->address, rwnx_hw->wiphy->perm_addr, ETH_ALEN);
+    vif->address[5] ^= vif_idx;
+    printk("p2p dev addr=%x %x %x %x %x %x\n", vif->address[0], vif->address[1], \
+        vif->address[2], vif->address[3], vif->address[4], vif->address[5]);
+
+    return wdev;
+}
+
+/*
+ * @brief Retrieve the rwnx_sta object allocated for a given MAC address
+ * and a given role.
+ */
+static struct rwnx_sta *rwnx_retrieve_sta(struct rwnx_hw *rwnx_hw,
+                                          struct rwnx_vif *rwnx_vif, u8 *addr,
+                                          __le16 fc, bool ap)
+{
+    if (ap) {
+        /* only deauth, disassoc and action are bufferable MMPDUs */
+        bool bufferable = ieee80211_is_deauth(fc) ||
+                          ieee80211_is_disassoc(fc) ||
+                          ieee80211_is_action(fc);
+
+        /* Check if the packet is bufferable or not */
+        if (bufferable)
+        {
+            /* Check if address is a broadcast or a multicast address */
+            if (is_broadcast_ether_addr(addr) || is_multicast_ether_addr(addr)) {
+                /* Returned STA pointer */
+                struct rwnx_sta *rwnx_sta = &rwnx_hw->sta_table[rwnx_vif->ap.bcmc_index];
+
+                if (rwnx_sta->valid)
+                    return rwnx_sta;
+            } else {
+                /* Returned STA pointer */
+                struct rwnx_sta *rwnx_sta;
+
+                /* Go through list of STAs linked with the provided VIF */
+				spin_lock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+                list_for_each_entry(rwnx_sta, &rwnx_vif->ap.sta_list, list) {
+                    if (rwnx_sta->valid &&
+                        !memcmp(rwnx_sta->mac_addr, addr, 6)) {
+                        /* Return the found STA */
+						spin_unlock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+                        return rwnx_sta;
+                    }
+                }
+				spin_unlock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+            }
+        }
+    } else {
+        return rwnx_vif->sta.ap;
+    }
+
+    return NULL;
+}
+
+/**
+ * @add_virtual_intf: create a new virtual interface with the given name,
+ *	must set the struct wireless_dev's iftype. Beware: You must create
+ *	the new netdev in the wiphy's network namespace! Returns the struct
+ *	wireless_dev, or an ERR_PTR. For P2P device wdevs, the driver must
+ *	also set the address member in the wdev.
+ */
+static struct wireless_dev *rwnx_cfg80211_add_iface(struct wiphy *wiphy,
+                                                    const char *name,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
+	unsigned char name_assign_type, enum nl80211_iftype type,
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
+        unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags,
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
+        enum nl80211_iftype type, u32 *flags,
+#endif
+        struct vif_params *params)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct wireless_dev *wdev;
+    struct rwnx_conf_file init_conf;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
+    unsigned char name_assign_type = NET_NAME_UNKNOWN;
+#endif
+
+    if (type != NL80211_IFTYPE_P2P_DEVICE) {
+        struct rwnx_vif *vif= rwnx_interface_add(rwnx_hw, name, name_assign_type, type, params, init_conf);
+        if (!vif)
+            return ERR_PTR(-EINVAL);
+        return &vif->wdev;
+
+    } else {
+        wdev = rwnx_virtual_interface_add(rwnx_hw, name, name_assign_type, type, params);
+        if (!wdev)
+            return ERR_PTR(-EINVAL);
+        return wdev;
+    }
+}
+
+/**
+ * @del_virtual_intf: remove the virtual interface
+ */
+static int rwnx_cfg80211_del_iface(struct wiphy *wiphy,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
+ struct wireless_dev *wdev
+#else
+ struct net_device *dev
+#endif
+)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
+    struct net_device *dev = wdev->netdev;
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = container_of(wdev, struct rwnx_vif, wdev);
+#else
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+#endif
+   // printk("del_iface: %p\n",wdev);
+
+#if 0
+    if(!dev || !rwnx_vif->ndev) {
+        cfg80211_unregister_wdev(wdev);
+        spin_lock_bh(&rwnx_hw->cb_lock);
+        list_del(&rwnx_vif->list);
+        spin_unlock_bh(&rwnx_hw->cb_lock);
+        rwnx_hw->avail_idx_map |= BIT(rwnx_vif->drv_vif_index);
+        rwnx_vif->ndev = NULL;
+        kfree(rwnx_vif);
+        return 0;
+    }
+#endif
+
+    netdev_info(dev, "Remove Interface");
+
+    if (dev->reg_state == NETREG_REGISTERED) {
+        /* Will call rwnx_close if interface is UP */
+        unregister_netdevice(dev);
+    }
+
+    spin_lock_bh(&rwnx_hw->cb_lock);
+    list_del(&rwnx_vif->list);
+    spin_unlock_bh(&rwnx_hw->cb_lock);
+    rwnx_hw->avail_idx_map |= BIT(rwnx_vif->drv_vif_index);
+    rwnx_vif->ndev = NULL;
+
+    /* Clear the priv in adapter */
+    dev->ieee80211_ptr = NULL;
+
+    return 0;
+}
+
+/**
+ * @change_virtual_intf: change type/configuration of virtual interface,
+ *	keep the struct wireless_dev's iftype updated.
+ */
+static int rwnx_cfg80211_change_iface(struct wiphy *wiphy,
+                                      struct net_device *dev,
+                                      enum nl80211_iftype type,
+				      #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0))
+					u32 *flags,
+				      #endif
+                                      struct vif_params *params)
+{
+#ifndef CONFIG_RWNX_MON_DATA
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+#endif
+    struct rwnx_vif *vif = netdev_priv(dev);
+    struct mm_add_if_cfm add_if_cfm;
+    bool_l p2p = false;
+    int ret;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+    printk("change_if: %d to %d, %d, %d", vif->wdev.iftype, type, NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_STATION);
+
+#ifdef CONFIG_COEX
+    if (type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_P2P_GO)
+        rwnx_send_coex_req(vif->rwnx_hw, 1, 0);
+    if (RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP || RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_GO)
+        rwnx_send_coex_req(vif->rwnx_hw, 0, 1);
+#endif
+#ifndef CONFIG_RWNX_MON_DATA
+    if ((type == NL80211_IFTYPE_MONITOR) &&
+       (RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_MONITOR)) {
+        struct rwnx_vif *vif_el;
+        list_for_each_entry(vif_el, &rwnx_hw->vifs, list) {
+            // Check if data interface already exists
+            if ((vif_el != vif) &&
+               (RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_MONITOR)) {
+                wiphy_err(rwnx_hw->wiphy,
+                        "Monitor+Data interface support (MON_DATA) disabled\n");
+                return -EIO;
+            }
+        }
+    }
+#endif
+
+    // Reset to default case (i.e. not monitor)
+    dev->type = ARPHRD_ETHER;
+    dev->netdev_ops = &rwnx_netdev_ops;
+
+    switch (type) {
+    case NL80211_IFTYPE_STATION:
+    case NL80211_IFTYPE_P2P_CLIENT:
+        vif->sta.ap = NULL;
+        vif->sta.tdls_sta = NULL;
+        vif->sta.external_auth = false;
+        break;
+    case NL80211_IFTYPE_MESH_POINT:
+        INIT_LIST_HEAD(&vif->ap.mpath_list);
+        INIT_LIST_HEAD(&vif->ap.proxy_list);
+        vif->ap.create_path = false;
+        vif->ap.generation = 0;
+        // no break
+    case NL80211_IFTYPE_AP:
+    case NL80211_IFTYPE_P2P_GO:
+        INIT_LIST_HEAD(&vif->ap.sta_list);
+        memset(&vif->ap.bcn, 0, sizeof(vif->ap.bcn));
+        break;
+    case NL80211_IFTYPE_AP_VLAN:
+        return -EPERM;
+    case NL80211_IFTYPE_MONITOR:
+        dev->type = ARPHRD_IEEE80211_RADIOTAP;
+        dev->netdev_ops = &rwnx_netdev_monitor_ops;
+        break;
+    default:
+        break;
+    }
+
+    vif->wdev.iftype = type;
+    if (params->use_4addr != -1)
+        vif->use_4addr = params->use_4addr;
+    if (type == NL80211_IFTYPE_P2P_CLIENT || type == NL80211_IFTYPE_P2P_GO)
+        p2p = true;
+
+    if ((type == NL80211_IFTYPE_AP) || (type == NL80211_IFTYPE_P2P_GO) ||
+        (type == NL80211_IFTYPE_MONITOR)) {
+        if (vif->up) {
+        /* Abort scan request on the vif */
+        if (vif->rwnx_hw->scan_request
+#if 0
+ &&
+            vif->rwnx_hw->scan_request->wdev == &vif->wdev
+#endif
+	) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+            struct cfg80211_scan_info info = {
+                .aborted = true,
+            };
+            cfg80211_scan_done(vif->rwnx_hw->scan_request, &info);
+#else
+            cfg80211_scan_done(vif->rwnx_hw->scan_request, true);
+#endif
+            if ((ret = rwnx_send_scanu_cancel_req(vif->rwnx_hw, NULL))) {
+                printk("scanu_cancel fail\n");
+                return ret;
+            }
+            vif->rwnx_hw->scan_request = NULL;
+        }
+        if ((ret = rwnx_send_remove_if(vif->rwnx_hw, vif->vif_index, false))) {
+            printk("remove_if fail\n");
+            return ret;
+        }
+		vif->rwnx_hw->vif_table[vif->vif_index] = NULL;
+        printk("change_if from %d \n", vif->vif_index);
+        if ((ret = rwnx_send_add_if(vif->rwnx_hw, vif->address,
+                                                  RWNX_VIF_TYPE(vif), p2p, &add_if_cfm))) {
+                printk("add if fail\n");
+                return ret;
+        }
+        if (add_if_cfm.status != 0) {
+                printk("add if status fail\n");
+                return -EIO;
+        }
+        printk("change_if to %d \n", add_if_cfm.inst_nbr);
+        /* Save the index retrieved from LMAC */
+        spin_lock_bh(&vif->rwnx_hw->cb_lock);
+        vif->vif_index = add_if_cfm.inst_nbr;
+        vif->rwnx_hw->vif_table[add_if_cfm.inst_nbr] = vif;
+        spin_unlock_bh(&vif->rwnx_hw->cb_lock);
+        }
+    }
+
+    if (type == NL80211_IFTYPE_MONITOR) {
+        vif->rwnx_hw->monitor_vif = vif->vif_index;
+        #if defined(CONFIG_RWNX_MON_XMIT)
+        rwnx_txq_unk_vif_init(vif);
+        #endif
+    } else {
+        vif->rwnx_hw->monitor_vif = RWNX_INVALID_VIF;
+    }
+
+    return 0;
+}
+
+static int rwnx_cfgp2p_start_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+    int ret = 0;
+
+    //do nothing
+    printk("P2P interface started\n");
+
+    return ret;
+}
+
+static void rwnx_cfgp2p_stop_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+#if 0
+    int ret = 0;
+    struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+
+    if (!cfg)
+        return;
+
+    CFGP2P_DBG(("Enter\n"));
+
+    ret = wl_cfg80211_scan_stop(cfg, wdev);
+    if (unlikely(ret < 0)) {
+        CFGP2P_ERR(("P2P scan stop failed, ret=%d\n", ret));
+    }
+
+    if (!cfg->p2p)
+        return;
+
+    /* Cancel any on-going listen */
+    wl_cfgp2p_cancel_listen(cfg, bcmcfg_to_prmry_ndev(cfg), wdev, TRUE);
+
+    ret = wl_cfgp2p_disable_discovery(cfg);
+    if (unlikely(ret < 0)) {
+        CFGP2P_ERR(("P2P disable discovery failed, ret=%d\n", ret));
+    }
+
+    p2p_on(cfg) = false;
+#endif
+    printk("Exit. P2P interface stopped\n");
+
+    return;
+}
+
+
+/**
+ * @scan: Request to do a scan. If returning zero, the scan request is given
+ *	the driver, and will be valid until passed to cfg80211_scan_done().
+ *	For scan results, call cfg80211_inform_bss(); you can call this outside
+ *	the scan/scan_done bracket too.
+ */
+static int rwnx_cfg80211_scan(struct wiphy *wiphy,
+	#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+ 	struct net_device *dev,
+	#endif
+                              struct cfg80211_scan_request *request)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+    struct rwnx_vif *rwnx_vif = container_of(request->wdev, struct rwnx_vif,
+                                            wdev);
+#else
+    struct rwnx_vif* rwnx_vif = netdev_priv(request->dev);
+#endif
+    int error;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (testmode) {
+        return 0;
+    }
+    if (scanning) {
+        printk("is scanning, abort\n");
+        if ((error =  rwnx_send_scanu_cancel_req(rwnx_hw, NULL)))
+            return error;
+        msleep(150);
+    }
+
+    if((RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_STATION || RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_CLIENT) &&  rwnx_vif->sta.external_auth){
+        printk("scan about: external auth\r\n");
+        return -EBUSY;
+    }
+
+    if ((error = rwnx_send_scanu_req(rwnx_hw, rwnx_vif, request)))
+        return error;
+
+    rwnx_hw->scan_request = request;
+
+    return 0;
+}
+
+/**
+ * @add_key: add a key with the given parameters. @mac_addr will be %NULL
+ *	when adding a group key.
+ */
+static int rwnx_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev,
+                                 u8 key_index, bool pairwise, const u8 *mac_addr,
+                                 struct key_params *params)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *vif = netdev_priv(netdev);
+    int i, error = 0;
+    struct mm_key_add_cfm key_add_cfm;
+    u8_l cipher = 0;
+    struct rwnx_sta *sta = NULL;
+    struct rwnx_key *rwnx_key;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (mac_addr) {
+        sta = rwnx_get_sta(rwnx_hw, mac_addr);
+        if (!sta)
+            return -EINVAL;
+        rwnx_key = &sta->key;
+        if (vif->wdev.iftype == NL80211_IFTYPE_STATION || vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
+            vif->sta.paired_cipher_type = params->cipher;
+    }
+    else {
+        rwnx_key = &vif->key[key_index];
+        vif->key_has_add = 1;
+        if (vif->wdev.iftype == NL80211_IFTYPE_STATION || vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
+            vif->sta.group_cipher_type = params->cipher;
+    }
+
+    /* Retrieve the cipher suite selector */
+    switch (params->cipher) {
+    case WLAN_CIPHER_SUITE_WEP40:
+        cipher = MAC_CIPHER_WEP40;
+        break;
+    case WLAN_CIPHER_SUITE_WEP104:
+        cipher = MAC_CIPHER_WEP104;
+        break;
+    case WLAN_CIPHER_SUITE_TKIP:
+        cipher = MAC_CIPHER_TKIP;
+        break;
+    case WLAN_CIPHER_SUITE_CCMP:
+        cipher = MAC_CIPHER_CCMP;
+        break;
+    case WLAN_CIPHER_SUITE_AES_CMAC:
+        cipher = MAC_CIPHER_BIP_CMAC_128;
+        break;
+    case WLAN_CIPHER_SUITE_SMS4:
+    {
+        // Need to reverse key order
+        u8 tmp, *key = (u8 *)params->key;
+        cipher = MAC_CIPHER_WPI_SMS4;
+        for (i = 0; i < WPI_SUBKEY_LEN/2; i++) {
+            tmp = key[i];
+            key[i] = key[WPI_SUBKEY_LEN - 1 - i];
+            key[WPI_SUBKEY_LEN - 1 - i] = tmp;
+        }
+        for (i = 0; i < WPI_SUBKEY_LEN/2; i++) {
+            tmp = key[i + WPI_SUBKEY_LEN];
+            key[i + WPI_SUBKEY_LEN] = key[WPI_KEY_LEN - 1 - i];
+            key[WPI_KEY_LEN - 1 - i] = tmp;
+        }
+        break;
+    }
+    default:
+        return -EINVAL;
+    }
+
+    if ((error = rwnx_send_key_add(rwnx_hw, vif->vif_index,
+                                   (sta ? sta->sta_idx : 0xFF), pairwise,
+                                   (u8 *)params->key, params->key_len,
+                                   key_index, cipher, &key_add_cfm)))
+        return error;
+
+    if (key_add_cfm.status != 0) {
+        RWNX_PRINT_CFM_ERR(key_add);
+        return -EIO;
+    }
+
+    /* Save the index retrieved from LMAC */
+    rwnx_key->hw_idx = key_add_cfm.hw_key_idx;
+
+    return 0;
+}
+
+/**
+ * @get_key: get information about the key with the given parameters.
+ *	@mac_addr will be %NULL when requesting information for a group
+ *	key. All pointers given to the @callback function need not be valid
+ *	after it returns. This function should return an error if it is
+ *	not possible to retrieve the key, -ENOENT if it doesn't exist.
+ *
+ */
+static int rwnx_cfg80211_get_key(struct wiphy *wiphy, struct net_device *netdev,
+                                 u8 key_index, bool pairwise, const u8 *mac_addr,
+                                 void *cookie,
+                                 void (*callback)(void *cookie, struct key_params*))
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    return -1;
+}
+
+
+/**
+ * @del_key: remove a key given the @mac_addr (%NULL for a group key)
+ *	and @key_index, return -ENOENT if the key doesn't exist.
+ */
+static int rwnx_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev,
+                                 u8 key_index, bool pairwise, const u8 *mac_addr)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *vif = netdev_priv(netdev);
+    int error;
+    struct rwnx_sta *sta = NULL;
+    struct rwnx_key *rwnx_key;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+    if (mac_addr) {
+        sta = rwnx_get_sta(rwnx_hw, mac_addr);
+        if (!sta)
+            return -EINVAL;
+        rwnx_key = &sta->key;
+        if (vif->wdev.iftype == NL80211_IFTYPE_STATION || vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
+            vif->sta.paired_cipher_type = 0xff;
+    }
+    else {
+        rwnx_key = &vif->key[key_index];
+        vif->key_has_add = 0;
+        if (vif->wdev.iftype == NL80211_IFTYPE_STATION || vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
+            vif->sta.group_cipher_type = 0xff;
+    }
+
+    error = rwnx_send_key_del(rwnx_hw, rwnx_key->hw_idx);
+
+    rwnx_key->hw_idx = 0;
+    return error;
+}
+
+/**
+ * @set_default_key: set the default key on an interface
+ */
+static int rwnx_cfg80211_set_default_key(struct wiphy *wiphy,
+                                         struct net_device *netdev,
+                                         u8 key_index, bool unicast, bool multicast)
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    return 0;
+}
+
+/**
+ * @set_default_mgmt_key: set the default management frame key on an interface
+ */
+static int rwnx_cfg80211_set_default_mgmt_key(struct wiphy *wiphy,
+                                              struct net_device *netdev,
+                                              u8 key_index)
+{
+    return 0;
+}
+
+/**
+ * @connect: Connect to the ESS with the specified parameters. When connected,
+ *	call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS.
+ *	If the connection fails for some reason, call cfg80211_connect_result()
+ *	with the status from the AP.
+ *	(invoked with the wireless_dev mutex held)
+ */
+static int rwnx_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+                                 struct cfg80211_connect_params *sme)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct sm_connect_cfm sm_connect_cfm;
+    int error = 0;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (rwnx_vif->wep_enabled && rwnx_vif->wep_auth_err && (sme->auth_type == rwnx_vif->last_auth_type)) {
+        if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
+            sme->auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+            printk("start connect, auth_type changed, shared --> open\n");
+        }
+        if (sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) {
+            sme->auth_type = NL80211_AUTHTYPE_SHARED_KEY;
+            printk("start connect, auth_type changed, open --> shared\n");
+        }
+    }
+
+	/* For SHARED-KEY authentication, must install key first */
+	if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY && sme->key) {
+		struct key_params key_params;
+		key_params.key = (u8*)sme->key;
+		key_params.seq = NULL;
+		key_params.key_len = sme->key_len;
+		key_params.seq_len = 0;
+		key_params.cipher = sme->crypto.cipher_group;
+		rwnx_cfg80211_add_key(wiphy, dev, sme->key_idx, false, NULL, &key_params);
+	}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_WPA3_FOR_OLD_KERNEL)
+	else if ((sme->auth_type == NL80211_AUTHTYPE_SAE) &&
+			 !(sme->flags & CONNECT_REQ_EXTERNAL_AUTH_SUPPORT)) {
+		netdev_err(dev, "Doesn't support SAE without external authentication\n");
+		return -EINVAL;
+	}
+#endif
+
+    if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
+        rwnx_hw->is_p2p_connected = 1;
+    }
+
+    if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION || rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
+        rwnx_vif->sta.paired_cipher_type = 0xff;
+        rwnx_vif->sta.group_cipher_type = 0xff;
+    }
+
+    /* Forward the information to the LMAC */
+    if ((error = rwnx_send_sm_connect_req(rwnx_hw, rwnx_vif, sme, &sm_connect_cfm)))
+        return error;
+
+    // Check the status
+    switch (sm_connect_cfm.status)
+    {
+        case CO_OK:
+            error = 0;
+            break;
+        case CO_BUSY:
+            error = -EINPROGRESS;
+            break;
+        case CO_OP_IN_PROGRESS:
+            error = -EALREADY;
+            break;
+        default:
+            error = -EIO;
+            break;
+    }
+
+    return error;
+}
+
+/**
+ * @disconnect: Disconnect from the BSS/ESS.
+ *	(invoked with the wireless_dev mutex held)
+ */
+static int rwnx_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
+                                    u16 reason_code)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    return(rwnx_send_sm_disconnect_req(rwnx_hw, rwnx_vif, reason_code));
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_WPA3_FOR_OLD_KERNEL)
+/**
+ * @external_auth: indicates result of offloaded authentication processing from
+ *     user space
+ */
+static int rwnx_cfg80211_external_auth(struct wiphy *wiphy, struct net_device *dev,
+                                       struct cfg80211_external_auth_params *params)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+
+    if (!rwnx_vif->sta.external_auth)
+        return -EINVAL;
+
+    rwnx_external_auth_disable(rwnx_vif);
+    return rwnx_send_sm_external_auth_required_rsp(rwnx_hw, rwnx_vif,
+                                                   params->status);
+}
+#endif
+
+/**
+ * @add_station: Add a new station.
+ */
+static int rwnx_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
+                                     const u8 *mac, struct station_parameters *params)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct me_sta_add_cfm me_sta_add_cfm;
+    int error = 0;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    WARN_ON(RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP_VLAN);
+
+    /* Do not add TDLS station */
+    if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+        return 0;
+
+    /* Indicate we are in a STA addition process - This will allow handling
+     * potential PS mode change indications correctly
+     */
+    rwnx_hw->adding_sta = true;
+
+    /* Forward the information to the LMAC */
+    if ((error = rwnx_send_me_sta_add(rwnx_hw, params, mac, rwnx_vif->vif_index,
+                                      &me_sta_add_cfm)))
+        return error;
+
+    // Check the status
+    switch (me_sta_add_cfm.status)
+    {
+        case CO_OK:
+        {
+            struct rwnx_sta *sta = &rwnx_hw->sta_table[me_sta_add_cfm.sta_idx];
+            int tid;
+            sta->aid = params->aid;
+
+            sta->sta_idx = me_sta_add_cfm.sta_idx;
+            sta->ch_idx = rwnx_vif->ch_index;
+            sta->vif_idx = rwnx_vif->vif_index;
+            sta->vlan_idx = sta->vif_idx;
+            sta->qos = (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME)) != 0;
+            sta->ht = params->ht_capa ? 1 : 0;
+            sta->vht = 0;// params->vht_capa ? 1 : 0;
+            sta->acm = 0;
+            sta->key.hw_idx = 0;
+
+#if 0
+            if (params->local_pm != NL80211_MESH_POWER_UNKNOWN)
+                sta->mesh_pm = params->local_pm;
+            else
+                sta->mesh_pm = rwnx_vif->ap.next_mesh_pm;
+            rwnx_update_mesh_power_mode(rwnx_vif);
+#endif
+            for (tid = 0; tid < NX_NB_TXQ_PER_STA; tid++) {
+                int uapsd_bit = rwnx_hwq2uapsd[rwnx_tid2hwq[tid]];
+                if (params->uapsd_queues & uapsd_bit)
+                    sta->uapsd_tids |= 1 << tid;
+                else
+                    sta->uapsd_tids &= ~(1 << tid);
+            }
+            memcpy(sta->mac_addr, mac, ETH_ALEN);
+            #ifdef CONFIG_DEBUG_FS_AIC
+            rwnx_dbgfs_register_rc_stat(rwnx_hw, sta);
+            #endif
+
+            /* Ensure that we won't process PS change or channel switch ind*/
+            spin_lock_bh(&rwnx_hw->cb_lock);
+            rwnx_txq_sta_init(rwnx_hw, sta, rwnx_txq_vif_get_status(rwnx_vif));
+            list_add_tail(&sta->list, &rwnx_vif->ap.sta_list);
+            sta->valid = true;
+            rwnx_ps_bh_enable(rwnx_hw, sta, sta->ps.active || me_sta_add_cfm.pm_state);
+            spin_unlock_bh(&rwnx_hw->cb_lock);
+	    		
+            error = 0;
+
+#if 0
+            if(rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP || rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
+                struct station_info sinfo;
+                memset(&sinfo, 0, sizeof(struct station_info));
+                sinfo.assoc_req_ies = NULL;
+                sinfo.assoc_req_ies_len = 0;
+                #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+                sinfo.filled |= STATION_INFO_ASSOC_REQ_IES;
+                #endif
+		        cfg80211_new_sta(rwnx_vif->ndev, sta->mac_addr, &sinfo, GFP_KERNEL);
+            }
+#endif
+#ifdef CONFIG_RWNX_BFMER
+            if (rwnx_hw->mod_params->bfmer)
+                rwnx_send_bfmer_enable(rwnx_hw, sta, params->vht_capa);
+
+            rwnx_mu_group_sta_init(sta, params->vht_capa);
+#endif /* CONFIG_RWNX_BFMER */
+
+            #define PRINT_STA_FLAG(f)                               \
+                (params->sta_flags_set & BIT(NL80211_STA_FLAG_##f) ? "["#f"]" : "")
+
+            netdev_info(dev, "Add sta %d (%pM)",
+                        sta->sta_idx, mac);
+            #undef PRINT_STA_FLAG
+            break;
+        }
+        default:
+            error = -EBUSY;
+            break;
+    }
+
+    rwnx_hw->adding_sta = false;
+
+    return error;
+}
+
+/**
+ * @del_station: Remove a station
+ */
+static int rwnx_cfg80211_del_station_compat(struct wiphy *wiphy,
+                                            struct net_device *dev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+            	struct station_del_parameters *params
+#else
+		const u8 *mac
+#endif
+)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_sta *cur, *tmp;
+    int error = 0, found = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+    const u8 *mac = NULL;
+#endif
+#ifdef AICWF_RX_REORDER
+    struct reord_ctrl_info *reord_info, *reord_tmp;
+    u8 *macaddr;
+    struct aicwf_rx_priv *rx_priv;
+#endif
+
+    //RWNX_DBG(RWNX_FN_ENTRY_STR);
+    printk("%s: %pM\n", __func__, mac);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+    if (params)
+        mac = params->mac;
+#endif
+
+	do {
+		spin_lock_bh(&rwnx_hw->cb_lock);
+		if(list_empty(&rwnx_vif->ap.sta_list)) {
+			spin_unlock_bh(&rwnx_hw->cb_lock);
+			break;
+		}
+
+	    list_for_each_entry_safe(cur, tmp, &rwnx_vif->ap.sta_list, list) {
+	        if ((!mac) || (!memcmp(cur->mac_addr, mac, ETH_ALEN)))  {
+				found = 1;
+				break;
+	    	}
+	    }
+
+        if(found) {
+            cur->ps.active = false;
+            cur->valid = false;
+            list_del(&cur->list);
+        }
+		spin_unlock_bh(&rwnx_hw->cb_lock);
+
+		if(found) {
+			netdev_info(dev, "Del sta %d (%pM)", cur->sta_idx, cur->mac_addr);
+			if (cur->vif_idx != cur->vlan_idx) {
+				struct rwnx_vif *vlan_vif;
+				vlan_vif = rwnx_hw->vif_table[cur->vlan_idx];
+				if (vlan_vif->up) {
+					if ((RWNX_VIF_TYPE(vlan_vif) == NL80211_IFTYPE_AP_VLAN) &&
+						(vlan_vif->use_4addr)) {
+						vlan_vif->ap_vlan.sta_4a = NULL;
+					} else {
+						WARN(1, "Deleting sta belonging to VLAN other than AP_VLAN 4A");
+					}
+				}
+			}
+
+#ifdef AICWF_RX_REORDER
+#ifdef AICWF_SDIO_SUPPORT
+			rx_priv = rwnx_hw->sdiodev->rx_priv;
+#else
+			rx_priv = rwnx_hw->usbdev->rx_priv;
+#endif
+			if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) {
+				BUG();//should be other function
+			}
+			else if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)){
+				macaddr = cur->mac_addr;
+				printk("deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0],macaddr[1],macaddr[2], \
+									   macaddr[3],macaddr[4],macaddr[5]);
+				spin_lock_bh(&rx_priv->stas_reord_lock);
+				list_for_each_entry_safe(reord_info, reord_tmp,
+					&rx_priv->stas_reord_list, list) {
+					printk("reord_mac:%x,%x,%x,%x,%x,%x\r\n", reord_info->mac_addr[0],reord_info->mac_addr[1],reord_info->mac_addr[2], \
+										   reord_info->mac_addr[3],reord_info->mac_addr[4],reord_info->mac_addr[5]);
+					if (!memcmp(reord_info->mac_addr, macaddr, 6)) {
+						reord_deinit_sta(rx_priv, reord_info);
+						break;
+					}
+				}
+				spin_unlock_bh(&rx_priv->stas_reord_lock);
+			}
+#endif
+
+			rwnx_txq_sta_deinit(rwnx_hw, cur);
+			error = rwnx_send_me_sta_del(rwnx_hw, cur->sta_idx, false);
+			if ((error != 0) && (error != -EPIPE))
+				return error;
+
+#ifdef CONFIG_RWNX_BFMER
+			// Disable Beamformer if supported
+			rwnx_bfmer_report_del(rwnx_hw, cur);
+			rwnx_mu_group_sta_del(rwnx_hw, cur);
+#endif /* CONFIG_RWNX_BFMER */
+			
+#if CONFIG_DEBUG_FS_AIC
+			rwnx_dbgfs_unregister_rc_stat(rwnx_hw, cur);
+#endif
+		}
+
+		if(mac)
+			break;
+	}	while (1);
+
+    rwnx_update_mesh_power_mode(rwnx_vif);
+
+	if(!found && mac != NULL)
+		return -ENOENT;
+	else
+    	return 0;
+}
+
+void apm_staloss_work_process(struct work_struct *work)
+{
+	struct rwnx_hw *rwnx_hw = container_of(work, struct rwnx_hw, apmStalossWork);
+	struct rwnx_sta *cur, *tmp;
+	int error = 0;
+
+#ifdef AICWF_RX_REORDER
+	struct reord_ctrl_info *reord_info, *reord_tmp;
+	u8 *macaddr;
+	struct aicwf_rx_priv *rx_priv;
+#endif
+	struct rwnx_vif *rwnx_vif;
+    bool_l found = false;
+	const u8 *mac = rwnx_hw->sta_mac_addr;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    // Look for VIF entry
+    list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+        if (rwnx_vif->vif_index == rwnx_hw->apm_vif_idx) {
+            found = true;
+            break;
+        }
+    }
+
+    printk("apm vif idx=%d, found=%d, mac addr=%pM\n", rwnx_hw->apm_vif_idx, found, mac);
+    if (!found || !rwnx_vif || (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_AP && RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_P2P_GO))
+    {
+        return;
+    }
+
+    found = false;
+    spin_lock_bh(&rwnx_hw->cb_lock);
+    list_for_each_entry_safe(cur, tmp, &rwnx_vif->ap.sta_list, list) {
+        if ((mac) && (!memcmp(cur->mac_addr, mac, ETH_ALEN))) {
+            found = true;
+            break;
+        }
+    }
+
+    if(found) {
+        cur->ps.active = false;
+        cur->valid = false;
+        list_del(&cur->list);
+    }
+    spin_unlock_bh(&rwnx_hw->cb_lock);
+
+	if(found) {
+		netdev_info(rwnx_vif->ndev, "Del sta %d (%pM)", cur->sta_idx, cur->mac_addr);
+		if (cur->vif_idx != cur->vlan_idx) {
+			struct rwnx_vif *vlan_vif;
+			vlan_vif = rwnx_hw->vif_table[cur->vlan_idx];
+			if (vlan_vif->up) {
+				if ((RWNX_VIF_TYPE(vlan_vif) == NL80211_IFTYPE_AP_VLAN) &&
+					(vlan_vif->use_4addr)) {
+					vlan_vif->ap_vlan.sta_4a = NULL;
+				} else {
+					WARN(1, "Deleting sta belonging to VLAN other than AP_VLAN 4A");
+				}
+			}
+		}
+
+#ifdef AICWF_RX_REORDER
+#ifdef AICWF_SDIO_SUPPORT
+		rx_priv = rwnx_hw->sdiodev->rx_priv;
+#else
+		rx_priv = rwnx_hw->usbdev->rx_priv;
+#endif
+		if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) {
+			BUG();//should be other function
+		} else if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) {
+			macaddr = cur->mac_addr;
+			printk("deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0], macaddr[1], macaddr[2], \
+								   macaddr[3], macaddr[4], macaddr[5]);
+			spin_lock_bh(&rx_priv->stas_reord_lock);
+			list_for_each_entry_safe(reord_info, reord_tmp,
+				&rx_priv->stas_reord_list, list) {
+				printk("reord_mac:%x,%x,%x,%x,%x,%x\r\n", reord_info->mac_addr[0], reord_info->mac_addr[1], reord_info->mac_addr[2], \
+									   reord_info->mac_addr[3], reord_info->mac_addr[4], reord_info->mac_addr[5]);
+				if (!memcmp(reord_info->mac_addr, macaddr, 6)) {
+					reord_deinit_sta(rx_priv, reord_info);
+					break;
+				}
+			}
+			spin_unlock_bh(&rx_priv->stas_reord_lock);
+		}
+#endif
+
+		rwnx_txq_sta_deinit(rwnx_hw, cur);
+		error = rwnx_send_me_sta_del(rwnx_hw, cur->sta_idx, false);
+		if ((error != 0) && (error != -EPIPE))
+			return;
+
+#ifdef CONFIG_RWNX_BFMER
+		// Disable Beamformer if supported
+		rwnx_bfmer_report_del(rwnx_hw, cur);
+		rwnx_mu_group_sta_del(rwnx_hw, cur);
+#endif /* CONFIG_RWNX_BFMER */
+
+#ifdef CONFIG_DEBUG_FS_AIC
+		rwnx_dbgfs_unregister_rc_stat(rwnx_hw, cur);
+#endif
+	}else {
+		printk("sta not found: %pM\n", mac);
+		return;
+	}
+
+	rwnx_update_mesh_power_mode(rwnx_vif);
+}
+
+void apm_probe_sta_work_process(struct work_struct *work)
+{
+       struct apm_probe_sta *probe_sta = container_of(work, struct apm_probe_sta, apmprobestaWork);
+       struct rwnx_vif *rwnx_vif = container_of(probe_sta, struct rwnx_vif, sta_probe);
+       bool found = false;
+       struct rwnx_sta *cur, *tmp;
+
+       u8 *mac = rwnx_vif->sta_probe.sta_mac_addr;
+
+       RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	  spin_lock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+       list_for_each_entry_safe(cur, tmp, &rwnx_vif->ap.sta_list, list) {
+               if (!memcmp(cur->mac_addr, mac, ETH_ALEN)) {
+                       found = true;
+                       break;
+               }
+       }
+	   spin_unlock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+
+       printk("sta %pM found = %d\n", mac, found);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
+       if(found)
+               cfg80211_probe_status(rwnx_vif->ndev, mac, (u64)rwnx_vif->sta_probe.probe_id, 1, 0, false, GFP_ATOMIC);
+       else
+               cfg80211_probe_status(rwnx_vif->ndev, mac, (u64)rwnx_vif->sta_probe.probe_id, 0, 0, false, GFP_ATOMIC);
+#else
+	if(found)
+                cfg80211_probe_status(rwnx_vif->ndev, mac, (u64)rwnx_vif->sta_probe.probe_id, 1, GFP_ATOMIC);
+        else
+                cfg80211_probe_status(rwnx_vif->ndev, mac, (u64)rwnx_vif->sta_probe.probe_id, 0, GFP_ATOMIC);
+ 
+#endif
+       rwnx_vif->sta_probe.probe_id ++;
+}
+
+/**
+ * @change_station: Modify a given station. Note that flags changes are not much
+ *	validated in cfg80211, in particular the auth/assoc/authorized flags
+ *	might come to the driver in invalid combinations -- make sure to check
+ *	them, also against the existing state! Drivers must call
+ *	cfg80211_check_station_change() to validate the information.
+ */
+static int rwnx_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
+                                        const u8 *mac, struct station_parameters *params)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *vif = netdev_priv(dev);
+    struct rwnx_sta *sta;
+
+    sta = rwnx_get_sta(rwnx_hw, mac);
+    if (!sta)
+    {
+        /* Add the TDLS station */
+        if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+        {
+            struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+            struct me_sta_add_cfm me_sta_add_cfm;
+            int error = 0;
+
+            /* Indicate we are in a STA addition process - This will allow handling
+             * potential PS mode change indications correctly
+             */
+            rwnx_hw->adding_sta = true;
+
+            /* Forward the information to the LMAC */
+            if ((error = rwnx_send_me_sta_add(rwnx_hw, params, mac, rwnx_vif->vif_index,
+                                              &me_sta_add_cfm)))
+                return error;
+
+            // Check the status
+            switch (me_sta_add_cfm.status)
+            {
+                case CO_OK:
+                {
+                    int tid;
+                    sta = &rwnx_hw->sta_table[me_sta_add_cfm.sta_idx];
+                    sta->aid = params->aid;
+                    sta->sta_idx = me_sta_add_cfm.sta_idx;
+                    sta->ch_idx = rwnx_vif->ch_index;
+                    sta->vif_idx = rwnx_vif->vif_index;
+                    sta->vlan_idx = sta->vif_idx;
+                    sta->qos = (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME)) != 0;
+                    sta->ht = params->ht_capa ? 1 : 0;
+                    sta->vht = 0;//params->vht_capa ? 1 : 0;
+                    sta->acm = 0;
+                    for (tid = 0; tid < NX_NB_TXQ_PER_STA; tid++) {
+                        int uapsd_bit = rwnx_hwq2uapsd[rwnx_tid2hwq[tid]];
+                        if (params->uapsd_queues & uapsd_bit)
+                            sta->uapsd_tids |= 1 << tid;
+                        else
+                            sta->uapsd_tids &= ~(1 << tid);
+                    }
+                    memcpy(sta->mac_addr, mac, ETH_ALEN);
+#ifdef CONFIG_DEBUG_FS_AIC
+                    rwnx_dbgfs_register_rc_stat(rwnx_hw, sta);
+#endif
+                    /* Ensure that we won't process PS change or channel switch ind*/
+                    spin_lock_bh(&rwnx_hw->cb_lock);
+                    rwnx_txq_sta_init(rwnx_hw, sta, rwnx_txq_vif_get_status(rwnx_vif));
+                    if (rwnx_vif->tdls_status == TDLS_SETUP_RSP_TX) {
+                        rwnx_vif->tdls_status = TDLS_LINK_ACTIVE;
+                        sta->tdls.initiator = true;
+                        sta->tdls.active = true;
+                    }
+                    /* Set TDLS channel switch capability */
+#if 0
+                    if ((params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) &&
+                        !rwnx_vif->tdls_chsw_prohibited)
+                        sta->tdls.chsw_allowed = true;
+#endif
+                    rwnx_vif->sta.tdls_sta = sta;
+                    sta->valid = true;
+                    spin_unlock_bh(&rwnx_hw->cb_lock);
+#ifdef CONFIG_RWNX_BFMER
+                    if (rwnx_hw->mod_params->bfmer)
+                        rwnx_send_bfmer_enable(rwnx_hw, sta, params->vht_capa);
+
+                    rwnx_mu_group_sta_init(sta, NULL);
+#endif /* CONFIG_RWNX_BFMER */
+
+                    #define PRINT_STA_FLAG(f)                               \
+                        (params->sta_flags_set & BIT(NL80211_STA_FLAG_##f) ? "["#f"]" : "")
+
+                    netdev_info(dev, "Add %s TDLS sta %d (%pM) ",
+                                sta->tdls.initiator ? "initiator" : "responder",
+                                sta->sta_idx, mac);
+                    #undef PRINT_STA_FLAG
+
+                    break;
+                }
+                default:
+                    error = -EBUSY;
+                    break;
+            }
+
+            rwnx_hw->adding_sta = false;
+        } else  {
+            return -EINVAL;
+        }
+    }
+
+    if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))
+        rwnx_send_me_set_control_port_req(rwnx_hw,
+                (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) != 0,
+                sta->sta_idx);
+
+#if 0
+    if (RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_MESH_POINT) {
+        if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
+            if (params->plink_state < NUM_NL80211_PLINK_STATES) {
+                rwnx_send_mesh_peer_update_ntf(rwnx_hw, vif, sta->sta_idx, params->plink_state);
+            }
+        }
+
+        if (params->local_pm != NL80211_MESH_POWER_UNKNOWN) {
+            sta->mesh_pm = params->local_pm;
+            rwnx_update_mesh_power_mode(vif);
+        }
+    }
+#endif
+
+    if (params->vlan) {
+        uint8_t vlan_idx;
+
+        vif = netdev_priv(params->vlan);
+        vlan_idx = vif->vif_index;
+
+        if (sta->vlan_idx != vlan_idx) {
+            struct rwnx_vif *old_vif;
+            old_vif = rwnx_hw->vif_table[sta->vlan_idx];
+            rwnx_txq_sta_switch_vif(sta, old_vif, vif);
+            sta->vlan_idx = vlan_idx;
+
+            if ((RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP_VLAN) &&
+                (vif->use_4addr)) {
+                WARN((vif->ap_vlan.sta_4a),
+                     "4A AP_VLAN interface with more than one sta");
+                vif->ap_vlan.sta_4a = sta;
+            }
+
+            if ((RWNX_VIF_TYPE(old_vif) == NL80211_IFTYPE_AP_VLAN) &&
+                (old_vif->use_4addr)) {
+                old_vif->ap_vlan.sta_4a = NULL;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * @start_ap: Start acting in AP mode defined by the parameters.
+ */
+static int rwnx_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
+                                  struct cfg80211_ap_settings *settings)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct apm_start_cfm apm_start_cfm;
+    struct rwnx_ipc_elem_var elem;
+    struct rwnx_sta *sta;
+    int error = 0;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    INIT_WORK(&rwnx_vif->sta_probe.apmprobestaWork, apm_probe_sta_work_process);
+    rwnx_vif->sta_probe.apmprobesta_wq = create_singlethread_workqueue("apmprobe_wq");
+    if (!rwnx_vif->sta_probe.apmprobesta_wq) {
+            txrx_err("insufficient memory to create apmprobe_wq.\n");
+            return -ENOBUFS;
+    }
+    if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)
+	    rwnx_hw->is_p2p_connected = 1;
+    /* Forward the information to the LMAC */
+    if ((error = rwnx_send_apm_start_req(rwnx_hw, rwnx_vif, settings,
+                                         &apm_start_cfm, &elem)))
+        goto end;
+
+    // Check the status
+    switch (apm_start_cfm.status)
+    {
+        case CO_OK:
+        {
+            u8 txq_status = 0;
+            rwnx_vif->ap.bcmc_index = apm_start_cfm.bcmc_idx;
+            rwnx_vif->ap.flags = 0;
+            #if (defined CONFIG_HE_FOR_OLD_KERNEL) || (defined CONFIG_VHT_FOR_OLD_KERNEL)
+    		rwnx_vif->ap.aic_index = 0;
+            #endif
+            sta = &rwnx_hw->sta_table[apm_start_cfm.bcmc_idx];
+            sta->valid = true;
+            sta->aid = 0;
+            sta->sta_idx = apm_start_cfm.bcmc_idx;
+            sta->ch_idx = apm_start_cfm.ch_idx;
+            sta->vif_idx = rwnx_vif->vif_index;
+            sta->qos = false;
+            sta->acm = 0;
+            sta->ps.active = false;
+            rwnx_mu_group_sta_init(sta, NULL);
+            spin_lock_bh(&rwnx_hw->cb_lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
+	    rwnx_chanctx_link(rwnx_vif, apm_start_cfm.ch_idx,
+                              NULL);
+#else
+            rwnx_chanctx_link(rwnx_vif, apm_start_cfm.ch_idx,
+                              &settings->chandef);
+#endif
+            if (rwnx_hw->cur_chanctx != apm_start_cfm.ch_idx) {
+                txq_status = RWNX_TXQ_STOP_CHAN;
+            }
+            rwnx_txq_vif_init(rwnx_hw, rwnx_vif, txq_status);
+            spin_unlock_bh(&rwnx_hw->cb_lock);
+
+            netif_tx_start_all_queues(dev);
+            netif_carrier_on(dev);
+            error = 0;
+            /* If the AP channel is already the active, we probably skip radar
+               activation on MM_CHANNEL_SWITCH_IND (unless another vif use this
+               ctxt). In anycase retest if radar detection must be activated
+             */
+            if (txq_status == 0) {
+                rwnx_radar_detection_enable_on_cur_channel(rwnx_hw);
+            }
+            break;
+        }
+        case CO_BUSY:
+            error = -EINPROGRESS;
+            break;
+        case CO_OP_IN_PROGRESS:
+            error = -EALREADY;
+            break;
+        default:
+            error = -EIO;
+            break;
+    }
+
+    if (error) {
+        netdev_info(dev, "Failed to start AP (%d)", error);
+    } else {
+        netdev_info(dev, "AP started: ch=%d, bcmc_idx=%d",
+                    rwnx_vif->ch_index, rwnx_vif->ap.bcmc_index);
+    }
+
+  end:
+    //rwnx_ipc_elem_var_deallocs(rwnx_hw, &elem);
+
+    return error;
+}
+
+
+/**
+ * @change_beacon: Change the beacon parameters for an access point mode
+ *	interface. This should reject the call when AP mode wasn't started.
+ */
+static int rwnx_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
+                                       struct cfg80211_beacon_data *info)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *vif = netdev_priv(dev);
+    struct rwnx_bcn *bcn = &vif->ap.bcn;
+    struct rwnx_ipc_elem_var elem;
+    u8 *buf;
+    int error = 0;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    // Build the beacon
+    buf = rwnx_build_bcn(bcn, info);
+    if (!buf)
+        return -ENOMEM;
+
+    rwnx_send_bcn(rwnx_hw, buf, vif->vif_index, bcn->len);
+
+#if 0
+    // Sync buffer for FW
+    if ((error = rwnx_ipc_elem_var_allocs(rwnx_hw, &elem, bcn->len, DMA_TO_DEVICE,
+                                          buf, NULL, NULL)))
+        return error;
+#endif
+    // Forward the information to the LMAC
+    error = rwnx_send_bcn_change(rwnx_hw, vif->vif_index, elem.dma_addr,
+                                 bcn->len, bcn->head_len, bcn->tim_len, NULL);
+
+#if 0
+    rwnx_ipc_elem_var_deallocs(rwnx_hw, &elem);
+#else
+
+
+#endif
+    return error;
+}
+
+/**
+ * * @stop_ap: Stop being an AP, including stopping beaconing.
+ */
+static int rwnx_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_sta *sta;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    netif_tx_stop_all_queues(dev);
+    netif_carrier_off(dev);
+
+    if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)
+        rwnx_hw->is_p2p_connected = 0;
+    rwnx_radar_cancel_cac(&rwnx_hw->radar);
+    rwnx_send_apm_stop_req(rwnx_hw, rwnx_vif);
+    spin_lock_bh(&rwnx_hw->cb_lock);
+    rwnx_chanctx_unlink(rwnx_vif);
+    spin_unlock_bh(&rwnx_hw->cb_lock);
+
+    /* delete any remaining STA*/
+    while (!list_empty(&rwnx_vif->ap.sta_list)) {
+        rwnx_cfg80211_del_station_compat(wiphy, dev, NULL);
+    }
+
+    /* delete BC/MC STA */
+    sta = &rwnx_hw->sta_table[rwnx_vif->ap.bcmc_index];
+    rwnx_txq_vif_deinit(rwnx_hw, rwnx_vif);
+    rwnx_del_bcn(&rwnx_vif->ap.bcn);
+    rwnx_del_csa(rwnx_vif);
+
+    flush_workqueue(rwnx_vif->sta_probe.apmprobesta_wq);
+    destroy_workqueue(rwnx_vif->sta_probe.apmprobesta_wq);
+
+    //netif_tx_stop_all_queues(dev);
+    //netif_carrier_off(dev);
+
+    netdev_info(dev, "AP Stopped");
+
+    return 0;
+}
+
+/**
+ * @set_monitor_channel: Set the monitor mode channel for the device. If other
+ *	interfaces are active this callback should reject the configuration.
+ *	If no interfaces are active or the device is down, the channel should
+ *	be stored for when a monitor interface becomes active.
+ *
+ * Also called internaly with chandef set to NULL simply to retrieve the channel
+ * configured at firmware level.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+static inline bool
+cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1,
+			   const struct cfg80211_chan_def *chandef2)
+{
+	return (chandef1->chan == chandef2->chan &&
+		chandef1->width == chandef2->width &&
+		chandef1->center_freq1 == chandef2->center_freq1 &&
+		chandef1->center_freq2 == chandef2->center_freq2);
+}
+#endif
+
+static int rwnx_cfg80211_set_monitor_channel(struct wiphy *wiphy,
+                                             struct cfg80211_chan_def *chandef)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif;
+    struct me_config_monitor_cfm cfm;
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (rwnx_hw->monitor_vif == RWNX_INVALID_VIF)
+        return -EINVAL;
+
+    rwnx_vif = rwnx_hw->vif_table[rwnx_hw->monitor_vif];
+
+    // Do nothing if monitor interface is already configured with the requested channel
+    if (rwnx_chanctx_valid(rwnx_hw, rwnx_vif->ch_index)) {
+        struct rwnx_chanctx *ctxt;
+        ctxt = &rwnx_vif->rwnx_hw->chanctx_table[rwnx_vif->ch_index];
+        if (chandef && cfg80211_chandef_identical(&ctxt->chan_def, chandef))
+            return 0;
+    }
+
+    // Always send command to firmware. It allows to retrieve channel context index
+    // and its configuration.
+    if (rwnx_send_config_monitor_req(rwnx_hw, chandef, &cfm))
+        return -EIO;
+
+    // Always re-set channel context info
+    rwnx_chanctx_unlink(rwnx_vif);
+
+
+
+    // If there is also a STA interface not yet connected then monitor interface
+    // will only have a channel context after the connection of the STA interface.
+    if (cfm.chan_index != RWNX_CH_NOT_SET)
+    {
+        struct cfg80211_chan_def mon_chandef;
+
+        if (rwnx_hw->vif_started > 1) {
+            // In this case we just want to update the channel context index not
+            // the channel configuration
+            rwnx_chanctx_link(rwnx_vif, cfm.chan_index, NULL);
+            return -EBUSY;
+        }
+
+        mon_chandef.chan = ieee80211_get_channel(wiphy, cfm.chan.prim20_freq);
+        mon_chandef.center_freq1 = cfm.chan.center1_freq;
+        mon_chandef.center_freq2 = cfm.chan.center2_freq;
+        mon_chandef.width =  chnl2bw[cfm.chan.type];
+        rwnx_chanctx_link(rwnx_vif, cfm.chan_index, &mon_chandef);
+    }
+
+    return 0;
+}
+
+/**
+ * @probe_client: probe an associated client, must return a cookie that it
+ *	later passes to cfg80211_probe_status().
+ */
+int rwnx_cfg80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
+            const u8 *peer, u64 *cookie)
+{
+       struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+       struct rwnx_vif *vif = netdev_priv(dev);
+       struct rwnx_sta *sta = NULL;
+
+       RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+       if((RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_AP) && (RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_P2P_GO) && 
+               (RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_AP_VLAN))
+               return -EINVAL;
+
+	   spin_lock_bh(&vif->rwnx_hw->cb_lock);
+       list_for_each_entry(sta, &vif->ap.sta_list, list){
+           if (sta->valid && (!memcmp(sta->mac_addr, peer, ETH_ALEN)))
+               break;
+       }
+	   spin_unlock_bh(&vif->rwnx_hw->cb_lock);
+
+       if (!sta)
+           return -ENOENT;
+
+
+       memcpy(vif->sta_probe.sta_mac_addr, peer, 6);
+       queue_work(vif->sta_probe.apmprobesta_wq, &vif->sta_probe.apmprobestaWork);
+
+       *cookie = vif->sta_probe.probe_id;
+
+       return 0;
+
+}
+
+/**
+ * @mgmt_frame_register: Notify driver that a management frame type was
+ *	registered. Note that this callback may not sleep, and cannot run
+ *	concurrently with itself.
+ */
+void rwnx_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
+		   struct net_device *dev, 
+#else
+                   struct wireless_dev *wdev,
+#endif
+                   u16 frame_type, bool reg)
+{
+}
+
+/**
+ * @set_wiphy_params: Notify that wiphy parameters have changed;
+ *	@changed bitfield (see &enum wiphy_params_flags) describes which values
+ *	have changed. The actual parameter values are available in
+ *	struct wiphy. If returning an error, no value should be changed.
+ */
+static int rwnx_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+    return 0;
+}
+
+
+/**
+ * @set_tx_power: set the transmit power according to the parameters,
+ *	the power passed is in mBm, to get dBm use MBM_TO_DBM(). The
+ *	wdev may be %NULL if power was set for the wiphy, and will
+ *	always be %NULL unless the driver supports per-vif TX power
+ *	(as advertised by the nl80211 feature flag.)
+ */
+static int rwnx_cfg80211_set_tx_power(struct wiphy *wiphy,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ struct wireless_dev *wdev,
+#endif
+                                      enum nl80211_tx_power_setting type, int mbm)
+{
+    #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+    struct wireless_dev *wdev = NULL;
+    #endif
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *vif;
+    s8 pwr;
+    int res = 0;
+
+    if (type == NL80211_TX_POWER_AUTOMATIC) {
+        pwr = 0x7f;
+    } else {
+        pwr = MBM_TO_DBM(mbm);
+    }
+
+    if (wdev) {
+        vif = container_of(wdev, struct rwnx_vif, wdev);
+        res = rwnx_send_set_power(rwnx_hw, vif->vif_index, pwr, NULL);
+    } else {
+        list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+            res = rwnx_send_set_power(rwnx_hw, vif->vif_index, pwr, NULL);
+            if (res)
+                break;
+        }
+    }
+
+    return res;
+}
+
+#if 0
+/**
+ * @set_power_mgmt: set the power save to one of those two modes:
+ *  Power-save off
+ *  Power-save on - Dynamic mode
+ */
+static int rwnx_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+                                        struct net_device *dev,
+                                        bool enabled, int timeout)
+{
+#if 0
+	struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+	u8 ps_mode;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+    if (timeout >= 0)
+        netdev_info(dev, "Ignore timeout value %d", timeout);
+
+    if (!(rwnx_hw->version_cfm.features & BIT(MM_FEAT_PS_BIT)))
+        enabled = false;
+
+    if (enabled) {
+        /* Switch to Dynamic Power Save */
+        ps_mode = MM_PS_MODE_ON_DYN;
+    } else {
+        /* Exit Power Save */
+        ps_mode = MM_PS_MODE_OFF;
+    }
+
+	return rwnx_send_me_set_ps_mode(rwnx_hw, ps_mode);
+#else
+	/* TODO
+	 * Add handle in the feature!
+	 */
+	return 0;
+#endif
+}
+#endif
+
+static int rwnx_cfg80211_set_txq_params(struct wiphy *wiphy, struct net_device *dev,
+                                        struct ieee80211_txq_params *params)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    u8 hw_queue, aifs, cwmin, cwmax;
+    u32 param;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+    hw_queue = rwnx_ac2hwq[0][params->queue];
+#else
+    hw_queue = rwnx_ac2hwq[0][params->ac];
+#endif
+
+    aifs  = params->aifs;
+    cwmin = fls(params->cwmin);
+    cwmax = fls(params->cwmax);
+
+    /* Store queue information in general structure */
+    param  = (u32) (aifs << 0);
+    param |= (u32) (cwmin << 4);
+    param |= (u32) (cwmax << 8);
+    param |= (u32) (params->txop) << 12;
+
+    /* Send the MM_SET_EDCA_REQ message to the FW */
+    return rwnx_send_set_edca(rwnx_hw, hw_queue, param, false, rwnx_vif->vif_index);
+}
+
+
+/**
+ * @remain_on_channel: Request the driver to remain awake on the specified
+ *	channel for the specified duration to complete an off-channel
+ *	operation (e.g., public action frame exchange). When the driver is
+ *	ready on the requested channel, it must indicate this with an event
+ *	notification by calling cfg80211_ready_on_channel().
+ */
+static int
+rwnx_cfg80211_remain_on_channel(struct wiphy *wiphy,
+							#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+								struct wireless_dev *wdev,
+							#else
+								struct net_device *dev,
+							#endif
+                                struct ieee80211_channel *chan,
+							#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+								enum nl80211_channel_type channel_type,
+							#endif
+                                unsigned int duration, u64 *cookie)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+    struct rwnx_vif *rwnx_vif = container_of(wdev, struct rwnx_vif, wdev);
+#else
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct wireless_dev *wdev = &rwnx_vif->wdev;
+#endif
+    struct rwnx_roc_elem *roc_elem;
+    struct mm_add_if_cfm add_if_cfm;
+	struct mm_remain_on_channel_cfm roc_cfm;
+    int error;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* For debug purpose (use ftrace kernel option) */
+#ifdef CREATE_TRACE_POINTS
+    trace_roc(rwnx_vif->vif_index, chan->center_freq, duration);
+#endif
+    /* Check that no other RoC procedure has been launched */
+    if (rwnx_hw->roc_elem) {
+        msleep(2);
+        if (rwnx_hw->roc_elem) {
+            printk("remain_on_channel fail\n");
+            return -EBUSY;
+        }
+    }
+
+    printk("remain:%d,%d,%d\n",rwnx_vif->vif_index, rwnx_vif->is_p2p_vif, rwnx_hw->is_p2p_alive);
+    if (rwnx_vif->is_p2p_vif) {
+        if (rwnx_vif == rwnx_hw->p2p_dev_vif && !rwnx_vif->up) {
+            if ((error = rwnx_send_add_if(rwnx_hw, rwnx_vif->address, //wdev->netdev->dev_addr,
+                                  RWNX_VIF_TYPE(rwnx_vif), false, &add_if_cfm)))
+                return -EIO;
+
+            if (add_if_cfm.status != 0) {
+                return -EIO;
+            }
+
+            /* Save the index retrieved from LMAC */
+            spin_lock_bh(&rwnx_hw->cb_lock);
+            rwnx_vif->vif_index = add_if_cfm.inst_nbr;
+            rwnx_vif->up = true;
+            rwnx_hw->vif_started++;
+            rwnx_hw->vif_table[add_if_cfm.inst_nbr] = rwnx_vif;
+            spin_unlock_bh(&rwnx_hw->cb_lock);
+            rwnx_hw->is_p2p_alive = 1;
+            mod_timer(&rwnx_hw->p2p_alive_timer, jiffies + msecs_to_jiffies(1000));
+            atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+        }
+        else {
+            mod_timer(&rwnx_hw->p2p_alive_timer, jiffies + msecs_to_jiffies(1000));
+            atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+        }
+    }
+
+    /* Allocate a temporary RoC element */
+    roc_elem = kmalloc(sizeof(struct rwnx_roc_elem), GFP_KERNEL);
+
+    /* Verify that element has well been allocated */
+    if (!roc_elem) {
+        return -ENOMEM;
+    }
+
+    /* Initialize the RoC information element */
+    roc_elem->wdev = wdev;
+    roc_elem->chan = chan;
+    roc_elem->duration = duration;
+    roc_elem->mgmt_roc = false;
+    roc_elem->on_chan = false;
+
+    /* Initialize the OFFCHAN TX queue to allow off-channel transmissions */
+    rwnx_txq_offchan_init(rwnx_vif);
+
+    /* Forward the information to the FMAC */
+    rwnx_hw->roc_elem = roc_elem;
+	error = rwnx_send_roc(rwnx_hw, rwnx_vif, chan, duration, &roc_cfm);
+
+    /* If no error, keep all the information for handling of end of procedure */
+    if (error == 0) {
+
+        /* Set the cookie value */
+        *cookie = (u64)(rwnx_hw->roc_cookie_cnt);
+        if(roc_cfm.status) {
+            // failed to roc
+            rwnx_hw->roc_elem = NULL;
+            kfree(roc_elem);
+            rwnx_txq_offchan_deinit(rwnx_vif);
+            return -EBUSY;
+        }
+    } else {
+        /* Free the allocated element */
+        rwnx_hw->roc_elem = NULL;
+        kfree(roc_elem);
+        rwnx_txq_offchan_deinit(rwnx_vif);
+    }
+
+    return error;
+}
+
+/**
+ * @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation.
+ *	This allows the operation to be terminated prior to timeout based on
+ *	the duration value.
+ */
+static int rwnx_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
+											#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+						  					      struct net_device *dev,
+											#else
+                                                  struct wireless_dev *wdev,
+											#endif
+                                                  u64 cookie)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+#ifdef CREATE_TRACE_POINTS
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+#else
+    struct rwnx_vif *rwnx_vif = container_of(wdev, struct rwnx_vif, wdev);
+#endif
+#endif
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* For debug purpose (use ftrace kernel option) */
+#ifdef CREATE_TRACE_POINTS
+    trace_cancel_roc(rwnx_vif->vif_index);
+#endif
+    /* Check if a RoC procedure is pending */
+    if (!rwnx_hw->roc_elem) {
+        return 0;
+    }
+
+    /* Forward the information to the FMAC */
+    return rwnx_send_cancel_roc(rwnx_hw);
+}
+
+/**
+ * @dump_survey: get site survey information.
+ */
+static int rwnx_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *netdev,
+                                     int idx, struct survey_info *info)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct ieee80211_supported_band *sband;
+    struct rwnx_survey_info *rwnx_survey;
+
+    //RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (idx >= ARRAY_SIZE(rwnx_hw->survey))
+        return -ENOENT;
+
+    rwnx_survey = &rwnx_hw->survey[idx];
+
+    // Check if provided index matches with a supported 2.4GHz channel
+    sband = wiphy->bands[NL80211_BAND_2GHZ];
+    if (sband && idx >= sband->n_channels) {
+        idx -= sband->n_channels;
+        sband = NULL;
+    }
+
+	#ifdef USE_5G
+    if (!sband) {
+        // Check if provided index matches with a supported 5GHz channel
+        sband = wiphy->bands[NL80211_BAND_5GHZ];
+
+        if (!sband || idx >= sband->n_channels)
+            return -ENOENT;
+    }
+	#else
+		if (!sband || idx >= sband->n_channels)
+            return -ENOENT;
+	#endif
+
+    // Fill the survey
+    info->channel = &sband->channels[idx];
+    info->filled = rwnx_survey->filled;
+
+    if (rwnx_survey->filled != 0) {
+        SURVEY_TIME(info) = (u64)rwnx_survey->chan_time_ms;
+        SURVEY_TIME_BUSY(info) = (u64)rwnx_survey->chan_time_busy_ms;
+        info->noise = rwnx_survey->noise_dbm;
+
+        // Set the survey report as not used
+        rwnx_survey->filled = 0;
+    }
+
+    return 0;
+}
+
+/**
+ * @get_channel: Get the current operating channel for the virtual interface.
+ *	For monitor interfaces, it should return %NULL unless there's a single
+ *	current monitoring channel.
+ */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
+static int rwnx_cfg80211_get_channel(struct wiphy *wiphy,
+                                     struct wireless_dev *wdev,
+                                     struct cfg80211_chan_def *chandef)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = container_of(wdev, struct rwnx_vif, wdev);
+    struct rwnx_chanctx *ctxt;
+
+    if (!rwnx_vif->up) {
+        return -ENODATA;
+    }
+
+    if (rwnx_vif->vif_index == rwnx_hw->monitor_vif)
+    {
+        //retrieve channel from firmware
+        rwnx_cfg80211_set_monitor_channel(wiphy, NULL);
+    }
+
+    //Check if channel context is valid
+    if (!rwnx_chanctx_valid(rwnx_hw, rwnx_vif->ch_index)){
+        return -ENODATA;
+    }
+
+    ctxt = &rwnx_hw->chanctx_table[rwnx_vif->ch_index];
+    *chandef = ctxt->chan_def;
+
+    return 0;
+}
+#else
+struct ieee80211_channel *rwnx_cfg80211_get_channel(struct wiphy *wiphy)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct ieee80211_channel *chan = NULL;
+    struct rwnx_vif *vif;
+    bool_l found = false;
+    //may TBD
+
+    list_for_each_entry(vif, &rwnx_hw->vifs, list)
+    {
+        if(vif->wdev.iftype == NL80211_IFTYPE_AP)
+        {
+            found = true;
+            break;
+        }
+    }
+
+    if(found && rwnx_hw->set_chan.center_freq) {
+        chan = kzalloc(sizeof(struct ieee80211_channel), GFP_KERNEL);
+        memcpy((u8 *)chan, (u8 *)&rwnx_hw->set_chan, sizeof(struct ieee80211_channel));
+    }
+
+    return chan;
+}
+#endif
+
+/**
+ * @mgmt_tx: Transmit a management frame.
+ */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+static int rwnx_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+                                 struct cfg80211_mgmt_tx_params *params,
+                                 u64 *cookie)
+#else
+static int rwnx_cfg80211_mgmt_tx(struct wiphy *wiphy,
+							#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
+								struct net_device *dev,
+							#else
+								struct wireless_dev *wdev,
+							#endif
+								struct ieee80211_channel *channel, bool offchan,
+							#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
+								enum nl80211_channel_type channel_type,
+								bool channel_type_valid,
+							#endif
+                                unsigned int wait, const u8* buf, size_t len,
+							#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
+                                bool no_cck,
+                           	#endif
+                            	#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
+                                bool dont_wait_for_ack,
+                            	#endif
+                                u64 *cookie)
+#endif
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct wireless_dev *wdev = &rwnx_vif->wdev;
+#else
+    struct rwnx_vif *rwnx_vif = container_of(wdev, struct rwnx_vif, wdev);
+#endif
+    struct rwnx_sta *rwnx_sta;
+    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+    struct ieee80211_channel *channel = params->chan;
+    const u8 *buf = params->buf;
+    //size_t len = params->len;
+    #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+    struct ieee80211_mgmt *mgmt = (void *)buf;
+    bool ap = false;
+    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+    bool offchan = false;
+    #endif
+
+    /* Check if provided VIF is an AP or a STA one */
+    switch (RWNX_VIF_TYPE(rwnx_vif)) {
+        case NL80211_IFTYPE_AP_VLAN:
+            rwnx_vif = rwnx_vif->ap_vlan.master;
+        case NL80211_IFTYPE_AP:
+        case NL80211_IFTYPE_P2P_GO:
+        case NL80211_IFTYPE_MESH_POINT:
+            ap = true;
+            break;
+        case NL80211_IFTYPE_STATION:
+        case NL80211_IFTYPE_P2P_CLIENT:
+        default:
+            break;
+    }
+
+    /* Get STA on which management frame has to be sent */
+    rwnx_sta = rwnx_retrieve_sta(rwnx_hw, rwnx_vif, mgmt->da,
+                                 mgmt->frame_control, ap);
+#ifdef CREATE_TRACE_POINTS
+    trace_mgmt_tx((channel) ? channel->center_freq : 0,
+                  rwnx_vif->vif_index, (rwnx_sta) ? rwnx_sta->sta_idx : 0xFF,
+                  mgmt);
+#endif
+    if (ap || rwnx_sta)
+        goto send_frame;
+
+    /* Not an AP interface sending frame to unknown STA:
+     * This is allowed for external authetication */
+    if (rwnx_vif->sta.external_auth && ieee80211_is_auth(mgmt->frame_control))
+        goto send_frame;
+
+    /* Otherwise ROC is needed */
+    if (!channel) {
+        printk("mgmt_tx fail since channel\n");
+        return -EINVAL;
+    }
+
+    /* Check that a RoC is already pending */
+    if (rwnx_hw->roc_elem) {
+        /* Get VIF used for current ROC */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
+		struct rwnx_vif *rwnx_roc_vif = netdev_priv(rwnx_hw->roc_elem->wdev->netdev);
+#else
+        struct rwnx_vif *rwnx_roc_vif = container_of(rwnx_hw->roc_elem->wdev, struct rwnx_vif, wdev);//netdev_priv(rwnx_hw->roc_elem->wdev->netdev);
+#endif
+
+        /* Check if RoC channel is the same than the required one */
+        if ((rwnx_hw->roc_elem->chan->center_freq != channel->center_freq)
+            || (rwnx_vif->vif_index != rwnx_roc_vif->vif_index)) {
+            printk("mgmt rx chan invalid: %d, %d", rwnx_hw->roc_elem->chan->center_freq, channel->center_freq);
+            return -EINVAL;
+        }
+    } else {
+        u64 cookie;
+        int error;
+
+        printk("mgmt rx remain on chan\n");
+
+        /* Start a ROC procedure for 30ms */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
+		error = rwnx_cfg80211_remain_on_channel(wiphy, wdev, channel,
+                                                30, &cookie);
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
+		error = rwnx_cfg80211_remain_on_channel(wiphy, wdev, channel, NL80211_CHAN_NO_HT,
+                                                30, &cookie);
+#else
+	error = rwnx_cfg80211_remain_on_channel(wiphy, dev, channel, NL80211_CHAN_NO_HT,
+                                                30, &cookie);
+#endif
+
+        if (error) {
+            printk("mgmt rx chan err\n");
+            return error;
+        }
+        /* Need to keep in mind that RoC has been launched internally in order to
+         * avoid to call the cfg80211 callback once expired */
+        rwnx_hw->roc_elem->mgmt_roc = true;
+    }
+
+    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+    offchan = true;
+    #endif
+
+send_frame:
+    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+    return rwnx_start_mgmt_xmit(rwnx_vif, rwnx_sta, params, offchan, cookie);
+    #else
+    return rwnx_start_mgmt_xmit(rwnx_vif, rwnx_sta, channel, offchan, wait, buf, len, no_cck, dont_wait_for_ack, cookie);
+    #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+}
+
+/**
+ * @start_radar_detection: Start radar detection in the driver.
+ */
+static
+int rwnx_cfg80211_start_radar_detection(struct wiphy *wiphy,
+                                        struct net_device *dev,
+                                        struct cfg80211_chan_def *chandef
+                                    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
+                                        , u32 cac_time_ms
+                                    #endif
+                                        )
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct apm_start_cac_cfm cfm;
+
+    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
+    rwnx_radar_start_cac(&rwnx_hw->radar, cac_time_ms, rwnx_vif);
+    #endif
+    rwnx_send_apm_start_cac_req(rwnx_hw, rwnx_vif, chandef, &cfm);
+
+    if (cfm.status == CO_OK) {
+        spin_lock_bh(&rwnx_hw->cb_lock);
+        rwnx_chanctx_link(rwnx_vif, cfm.ch_idx, chandef);
+        if (rwnx_hw->cur_chanctx == rwnx_vif->ch_index)
+            rwnx_radar_detection_enable(&rwnx_hw->radar,
+                                        RWNX_RADAR_DETECT_REPORT,
+                                        RWNX_RADAR_RIU);
+        spin_unlock_bh(&rwnx_hw->cb_lock);
+    } else {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+/**
+ * @update_ft_ies: Provide updated Fast BSS Transition information to the
+ *	driver. If the SME is in the driver/firmware, this information can be
+ *	used in building Authentication and Reassociation Request frames.
+ */
+static
+int rwnx_cfg80211_update_ft_ies(struct wiphy *wiphy,
+                            struct net_device *dev,
+                            struct cfg80211_update_ft_ies_params *ftie)
+{
+    return 0;
+}
+
+/**
+ * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
+ */
+static
+int rwnx_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
+                                  struct net_device *dev,
+                                  int32_t rssi_thold, uint32_t rssi_hyst)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+
+    return rwnx_send_cfg_rssi_req(rwnx_hw, rwnx_vif->vif_index, rssi_thold, rssi_hyst);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+/**
+ *
+ * @channel_switch: initiate channel-switch procedure (with CSA). Driver is
+ *	responsible for veryfing if the switch is possible. Since this is
+ *	inherently tricky driver may decide to disconnect an interface later
+ *	with cfg80211_stop_iface(). This doesn't mean driver can accept
+ *	everything. It should do it's best to verify requests and reject them
+ *	as soon as possible.
+ */
+int rwnx_cfg80211_channel_switch(struct wiphy *wiphy,
+                                 struct net_device *dev,
+                                 struct cfg80211_csa_settings *params)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *vif = netdev_priv(dev);
+    struct rwnx_ipc_elem_var elem;
+    struct rwnx_bcn *bcn, *bcn_after;
+    struct rwnx_csa *csa;
+    u16 csa_oft[BCN_MAX_CSA_CPT];
+    u8 *buf;
+    int i, error = 0;
+
+
+    if (vif->ap.csa)
+        return -EBUSY;
+
+    if (params->n_counter_offsets_beacon > BCN_MAX_CSA_CPT)
+        return -EINVAL;
+
+    /* Build the new beacon with CSA IE */
+    bcn = &vif->ap.bcn;
+    buf = rwnx_build_bcn(bcn, &params->beacon_csa);
+    if (!buf)
+        return -ENOMEM;
+
+    memset(csa_oft, 0, sizeof(csa_oft));
+    for (i = 0; i < params->n_counter_offsets_beacon; i++)
+    {
+        csa_oft[i] = params->counter_offsets_beacon[i] + bcn->head_len +
+            bcn->tim_len;
+    }
+
+    /* If count is set to 0 (i.e anytime after this beacon) force it to 2 */
+    if (params->count == 0) {
+        params->count = 2;
+        for (i = 0; i < params->n_counter_offsets_beacon; i++)
+        {
+            buf[csa_oft[i]] = 2;
+        }
+    }
+
+    if ((error = rwnx_ipc_elem_var_allocs(rwnx_hw, &elem, bcn->len,
+                                          DMA_TO_DEVICE, buf, NULL, NULL))) {
+        goto end;
+    }
+
+    /* Build the beacon to use after CSA. It will only be sent to fw once
+       CSA is over, but do it before sending the beacon as it must be ready
+       when CSA is finished. */
+    csa = kzalloc(sizeof(struct rwnx_csa), GFP_KERNEL);
+    if (!csa) {
+        error = -ENOMEM;
+        goto end;
+    }
+
+    bcn_after = &csa->bcn;
+    buf = rwnx_build_bcn(bcn_after, &params->beacon_after);
+    if (!buf) {
+        error = -ENOMEM;
+        rwnx_del_csa(vif);
+        goto end;
+    }
+
+    if ((error = rwnx_ipc_elem_var_allocs(rwnx_hw, &csa->elem, bcn_after->len,
+                                          DMA_TO_DEVICE, buf, NULL, NULL))) {
+        goto end;
+    }
+
+    vif->ap.csa = csa;
+    csa->vif = vif;
+    csa->chandef = params->chandef;
+
+    /* Send new Beacon. FW will extract channel and count from the beacon */
+    error = rwnx_send_bcn_change(rwnx_hw, vif->vif_index, elem.dma_addr,
+                                 bcn->len, bcn->head_len, bcn->tim_len, csa_oft);
+
+    if (error) {
+        rwnx_del_csa(vif);
+        goto end;
+    } else {
+        INIT_WORK(&csa->work, rwnx_csa_finish);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
+        cfg80211_ch_switch_started_notify(dev, &csa->chandef, params->count, false);
+#else
+        cfg80211_ch_switch_started_notify(dev, &csa->chandef, params->count);
+#endif
+    }
+
+  end:
+    return error;
+}
+#endif
+
+
+/*
+ * @tdls_mgmt: prepare TDLS action frame packets and forward them to FW
+ */
+static int
+rwnx_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                        const u8 *peer, u8 action_code,  u8 dialog_token,
+                        u16 status_code,
+					#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
+ 						u32 peer_capability,
+					#endif
+					#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
+ 						bool initiator,
+					#endif
+			 			const u8 *buf, size_t len)
+{
+    #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)
+    u32 peer_capability = 0;
+    #endif
+    #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+    bool initiator = false;
+    #endif
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    int ret = 0;
+
+    /* make sure we support TDLS */
+    if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+        return -ENOTSUPP;
+
+    /* make sure we are in station mode (and connected) */
+    if ((RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_STATION) ||
+        (!rwnx_vif->up) || (!rwnx_vif->sta.ap))
+        return -ENOTSUPP;
+
+    /* only one TDLS link is supported */
+    if ((action_code == WLAN_TDLS_SETUP_REQUEST) &&
+        (rwnx_vif->sta.tdls_sta) &&
+        (rwnx_vif->tdls_status == TDLS_LINK_ACTIVE)) {
+        printk("%s: only one TDLS link is supported!\n", __func__);
+        return -ENOTSUPP;
+    }
+
+    if ((action_code == WLAN_TDLS_DISCOVERY_REQUEST) &&
+        (rwnx_hw->mod_params->ps_on)) {
+        printk("%s: discovery request is not supported when "
+                "power-save is enabled!\n", __func__);
+        return -ENOTSUPP;
+    }
+
+    switch (action_code) {
+    case WLAN_TDLS_SETUP_RESPONSE:
+        /* only one TDLS link is supported */
+        if ((status_code == 0) &&
+            (rwnx_vif->sta.tdls_sta) &&
+            (rwnx_vif->tdls_status == TDLS_LINK_ACTIVE)) {
+            printk("%s: only one TDLS link is supported!\n", __func__);
+            status_code = WLAN_STATUS_REQUEST_DECLINED;
+        }
+        /* fall-through */
+    case WLAN_TDLS_SETUP_REQUEST:
+    case WLAN_TDLS_TEARDOWN:
+    case WLAN_TDLS_DISCOVERY_REQUEST:
+    case WLAN_TDLS_SETUP_CONFIRM:
+    case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+        ret = rwnx_tdls_send_mgmt_packet_data(rwnx_hw, rwnx_vif, peer, action_code,
+                dialog_token, status_code, peer_capability, initiator, buf, len, 0, NULL);
+        break;
+
+    default:
+        printk("%s: Unknown TDLS mgmt/action frame %pM\n",
+                __func__, peer);
+        ret = -EOPNOTSUPP;
+        break;
+    }
+
+    if (action_code == WLAN_TDLS_SETUP_REQUEST) {
+        rwnx_vif->tdls_status = TDLS_SETUP_REQ_TX;
+    } else if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
+        rwnx_vif->tdls_status = TDLS_SETUP_RSP_TX;
+    } else if ((action_code == WLAN_TDLS_SETUP_CONFIRM) && (ret == CO_OK)) {
+        rwnx_vif->tdls_status = TDLS_LINK_ACTIVE;
+        /* Set TDLS active */
+        rwnx_vif->sta.tdls_sta->tdls.active = true;
+    }
+
+    return ret;
+}
+
+/*
+ * @tdls_oper: execute TDLS operation
+ */
+static int
+rwnx_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+        const u8 *peer, enum nl80211_tdls_operation oper)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    int error;
+
+    if (oper != NL80211_TDLS_DISABLE_LINK)
+        return 0;
+
+    if (!rwnx_vif->sta.tdls_sta) {
+        printk("%s: TDLS station %pM does not exist\n", __func__, peer);
+        return -ENOLINK;
+    }
+
+    if (memcmp(rwnx_vif->sta.tdls_sta->mac_addr, peer, ETH_ALEN) == 0) {
+        /* Disable Channel Switch */
+        if (!rwnx_send_tdls_cancel_chan_switch_req(rwnx_hw, rwnx_vif,
+                                                   rwnx_vif->sta.tdls_sta,
+                                                   NULL))
+            rwnx_vif->sta.tdls_sta->tdls.chsw_en = false;
+
+        netdev_info(dev, "Del TDLS sta %d (%pM)",
+                rwnx_vif->sta.tdls_sta->sta_idx,
+                rwnx_vif->sta.tdls_sta->mac_addr);
+        /* Ensure that we won't process PS change ind */
+        spin_lock_bh(&rwnx_hw->cb_lock);
+        rwnx_vif->sta.tdls_sta->ps.active = false;
+        rwnx_vif->sta.tdls_sta->valid = false;
+        spin_unlock_bh(&rwnx_hw->cb_lock);
+        rwnx_txq_sta_deinit(rwnx_hw, rwnx_vif->sta.tdls_sta);
+        error = rwnx_send_me_sta_del(rwnx_hw, rwnx_vif->sta.tdls_sta->sta_idx, true);
+        if ((error != 0) && (error != -EPIPE))
+            return error;
+
+#ifdef CONFIG_RWNX_BFMER
+            // Disable Beamformer if supported
+            rwnx_bfmer_report_del(rwnx_hw, rwnx_vif->sta.tdls_sta);
+            rwnx_mu_group_sta_del(rwnx_hw, rwnx_vif->sta.tdls_sta);
+#endif /* CONFIG_RWNX_BFMER */
+
+        /* Set TDLS not active */
+        rwnx_vif->sta.tdls_sta->tdls.active = false;
+#ifdef CONFIG_DEBUG_FS_AIC
+        rwnx_dbgfs_unregister_rc_stat(rwnx_hw, rwnx_vif->sta.tdls_sta);
+#endif
+        // Remove TDLS station
+        rwnx_vif->tdls_status = TDLS_LINK_IDLE;
+        rwnx_vif->sta.tdls_sta = NULL;
+    }
+
+    return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+/*
+ * @tdls_channel_switch: enable TDLS channel switch
+ */
+static int
+rwnx_cfg80211_tdls_channel_switch(struct wiphy *wiphy,
+                                      struct net_device *dev,
+                                      const u8 *addr, u8 oper_class,
+                                      struct cfg80211_chan_def *chandef)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_sta *rwnx_sta = rwnx_vif->sta.tdls_sta;
+    struct tdls_chan_switch_cfm cfm;
+    int error;
+
+    if ((!rwnx_sta) || (memcmp(addr, rwnx_sta->mac_addr, ETH_ALEN))) {
+        printk("%s: TDLS station %pM doesn't exist\n", __func__, addr);
+        return -ENOLINK;
+    }
+
+    if (!rwnx_sta->tdls.chsw_allowed) {
+        printk("%s: TDLS station %pM does not support TDLS channel switch\n", __func__, addr);
+        return -ENOTSUPP;
+    }
+
+    error = rwnx_send_tdls_chan_switch_req(rwnx_hw, rwnx_vif, rwnx_sta,
+                                           rwnx_sta->tdls.initiator,
+                                           oper_class, chandef, &cfm);
+    if (error)
+        return error;
+
+    if (!cfm.status) {
+        rwnx_sta->tdls.chsw_en = true;
+        return 0;
+    } else {
+        printk("%s: TDLS channel switch already enabled and only one is supported\n", __func__);
+        return -EALREADY;
+    }
+}
+
+/*
+ * @tdls_cancel_channel_switch: disable TDLS channel switch
+ */
+static void
+rwnx_cfg80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
+                                              struct net_device *dev,
+                                              const u8 *addr)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_sta *rwnx_sta = rwnx_vif->sta.tdls_sta;
+    struct tdls_cancel_chan_switch_cfm cfm;
+
+    if (!rwnx_sta)
+        return;
+
+    if (!rwnx_send_tdls_cancel_chan_switch_req(rwnx_hw, rwnx_vif,
+                                               rwnx_sta, &cfm))
+        rwnx_sta->tdls.chsw_en = false;
+}
+#endif /* version >= 3.19 */
+
+/**
+ * @change_bss: Modify parameters for a given BSS (mainly for AP mode).
+ */
+int rwnx_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev,
+                             struct bss_parameters *params)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    int res =  -EOPNOTSUPP;
+
+    if (((RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP) ||
+         (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_GO)) &&
+        (params->ap_isolate > -1)) {
+
+        if (params->ap_isolate)
+            rwnx_vif->ap.flags |= RWNX_AP_ISOLATE;
+        else
+            rwnx_vif->ap.flags &= ~RWNX_AP_ISOLATE;
+
+        res = 0;
+    }
+
+    return res;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
+static int rwnx_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev,
+               struct cfg80211_chan_def *chandef)
+#else
+static int rwnx_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev,
+               struct ieee80211_channel *chan,
+               enum nl80211_channel_type channel_type)
+#endif
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+
+    struct mac_chan_op chan_op;
+    chan_op.band = chan->band;
+
+    if(channel_type == 0 || channel_type == 1)
+	chan_op.type = 0;
+    else
+    	chan_op.type = 1;
+    printk("chantype:%d\n", chan_op.type);
+    chan_op.prim20_freq = chan->center_freq;
+    chan_op.center1_freq = chan->center_freq;
+    chan_op.tx_power = chan->max_power;
+    chan_op.flags = 0;
+
+    memcpy((u8 *)&rwnx_hw->set_chan, (u8 *)chan, sizeof(struct ieee80211_channel));
+
+    if(rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP)
+    {
+        memcpy((u8 *)&rwnx_hw->ap_chan, (u8 *)&chan_op, sizeof(struct mac_chan_op));
+    }
+
+    return rwnx_send_set_channel_new(rwnx_hw, &chan_op, NULL);
+}
+
+static int rwnx_fill_station_info(struct rwnx_sta *sta, struct rwnx_vif *vif,
+                                  struct station_info *sinfo)
+{
+#if 1
+    struct rwnx_sta_stats *stats = &sta->stats;
+    struct rx_vector_1 *rx_vect1 = &stats->last_rx.rx_vect1;
+    union rwnx_rate_ctrl_info *rate_info;
+    struct mm_get_sta_info_cfm cfm;
+
+	rwnx_send_get_sta_info_req(vif->rwnx_hw, sta->sta_idx, &cfm);
+	sinfo->tx_failed = cfm.txfailed;
+	rate_info = (union rwnx_rate_ctrl_info *)&cfm.rate_info;
+
+	switch (rate_info->formatModTx) {
+	case FORMATMOD_NON_HT:
+	case FORMATMOD_NON_HT_DUP_OFDM:
+		sinfo->txrate.flags = 0;
+		sinfo->txrate.legacy = tx_legrates_lut_rate[rate_info->mcsIndexTx];
+		break;
+	case FORMATMOD_HT_MF:
+	case FORMATMOD_HT_GF:
+		sinfo->txrate.flags = RATE_INFO_FLAGS_MCS;
+		sinfo->txrate.mcs = rate_info->mcsIndexTx;
+		break;
+	case FORMATMOD_VHT:
+		sinfo->txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+		sinfo->txrate.mcs = rate_info->mcsIndexTx;
+		break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+	case FORMATMOD_HE_MU:
+	case FORMATMOD_HE_SU:
+	case FORMATMOD_HE_ER:
+		sinfo->txrate.flags = RATE_INFO_FLAGS_HE_MCS;
+		sinfo->txrate.mcs = rate_info->mcsIndexTx;
+		break;
+#else
+	//kernel not support he
+	case FORMATMOD_HE_MU:
+	case FORMATMOD_HE_SU:
+	case FORMATMOD_HE_ER:
+		sinfo->txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+		sinfo->txrate.mcs = rate_info->mcsIndexTx;
+		break;
+#endif
+	default:
+		return -EINVAL;
+	}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+	switch (rate_info->bwTx) {
+	case PHY_CHNL_BW_20:
+		sinfo->txrate.bw = RATE_INFO_BW_20;
+		break;
+	case PHY_CHNL_BW_40:
+		sinfo->txrate.bw = RATE_INFO_BW_40;
+		break;
+	case PHY_CHNL_BW_80:
+		sinfo->txrate.bw = RATE_INFO_BW_80;
+		break;
+	case PHY_CHNL_BW_160:
+		sinfo->txrate.bw = RATE_INFO_BW_160;
+		break;
+	default:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+		sinfo->txrate.bw = RATE_INFO_BW_HE_RU;
+#else
+		sinfo->txrate.bw = RATE_INFO_BW_20;
+#endif
+		break;
+	}
+#endif
+
+	//sinfo->txrate.nss = 1;
+	sinfo->filled |= (BIT(NL80211_STA_INFO_TX_BITRATE) | BIT(NL80211_STA_INFO_TX_FAILED));
+
+	sinfo->inactive_time = jiffies_to_msecs(jiffies - vif->rwnx_hw->stats.last_tx);
+	sinfo->rx_bytes = vif->net_stats.rx_bytes;
+	sinfo->tx_bytes = vif->net_stats.tx_bytes;
+	sinfo->tx_packets = vif->net_stats.tx_packets;
+	sinfo->rx_packets = vif->net_stats.rx_packets;
+	sinfo->signal = (s8)cfm.rssi;
+	//sinfo->rxrate.nss = 1;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+	switch (rx_vect1->ch_bw) {
+	case PHY_CHNL_BW_20:
+		sinfo->rxrate.bw = RATE_INFO_BW_20;
+		break;
+	case PHY_CHNL_BW_40:
+		sinfo->rxrate.bw = RATE_INFO_BW_40;
+		break;
+	case PHY_CHNL_BW_80:
+		sinfo->rxrate.bw = RATE_INFO_BW_80;
+		break;
+	case PHY_CHNL_BW_160:
+		sinfo->rxrate.bw = RATE_INFO_BW_160;
+		break;
+	default:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+		sinfo->rxrate.bw = RATE_INFO_BW_HE_RU;
+#else
+		sinfo->rxrate.bw = RATE_INFO_BW_20;
+#endif
+		break;
+	}
+#endif
+
+	switch (rx_vect1->format_mod) {
+	case FORMATMOD_NON_HT:
+	case FORMATMOD_NON_HT_DUP_OFDM:
+		sinfo->rxrate.flags = 0;
+		sinfo->rxrate.legacy = legrates_lut_rate[legrates_lut[rx_vect1->leg_rate]];
+		break;
+	case FORMATMOD_HT_MF:
+	case FORMATMOD_HT_GF:
+		sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS;
+		if (rx_vect1->ht.short_gi)
+			sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+		sinfo->rxrate.mcs = rx_vect1->ht.mcs;
+		break;
+	case FORMATMOD_VHT:
+		sinfo->rxrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+		if (rx_vect1->vht.short_gi)
+			sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+		sinfo->rxrate.mcs = rx_vect1->vht.mcs;
+		break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+	case FORMATMOD_HE_MU:
+		sinfo->rxrate.he_ru_alloc = rx_vect1->he.ru_size;
+	case FORMATMOD_HE_SU:
+	case FORMATMOD_HE_ER:
+		sinfo->rxrate.flags = RATE_INFO_FLAGS_HE_MCS;
+		sinfo->rxrate.mcs = rx_vect1->he.mcs;
+		sinfo->rxrate.he_gi = rx_vect1->he.gi_type;
+		sinfo->rxrate.he_dcm = rx_vect1->he.dcm;
+		break;
+#else
+	//kernel not support he
+	case FORMATMOD_HE_MU:
+	case FORMATMOD_HE_SU:
+	case FORMATMOD_HE_ER:
+		sinfo->rxrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+		sinfo->rxrate.mcs = rx_vect1->he.mcs;
+		break;
+#endif
+	default:
+		return -EINVAL;
+	}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+	sinfo->filled |= (STATION_INFO_INACTIVE_TIME |
+					 STATION_INFO_RX_BYTES |
+					 STATION_INFO_TX_BYTES |
+					 STATION_INFO_RX_PACKETS |
+					 STATION_INFO_TX_PACKETS |
+					 STATION_INFO_SIGNAL |
+					 STATION_INFO_RX_BITRATE);
+#else
+	sinfo->filled |= (BIT(NL80211_STA_INFO_INACTIVE_TIME) |
+					 BIT(NL80211_STA_INFO_RX_BYTES64)    |
+					 BIT(NL80211_STA_INFO_TX_BYTES64)    |
+					 BIT(NL80211_STA_INFO_RX_PACKETS)    |
+					 BIT(NL80211_STA_INFO_TX_PACKETS)    |
+					 BIT(NL80211_STA_INFO_SIGNAL)        |
+					 BIT(NL80211_STA_INFO_RX_BITRATE));
+#endif
+
+#endif
+	return 0;
+}
+
+/**
+ * @get_station: get station information for the station identified by @mac
+ */
+static int rwnx_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
+                                     const u8 *mac, struct station_info *sinfo)
+{
+    struct rwnx_vif *vif = netdev_priv(dev);
+    struct rwnx_sta *sta = NULL;
+
+    if (RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_MONITOR)
+        return -EINVAL;
+    else if ((RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_STATION) ||
+             (RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_CLIENT)) {
+        if (vif->sta.ap && (!memcmp(vif->sta.ap->mac_addr, mac, 6)))
+            sta = vif->sta.ap;
+    }
+    else
+    {
+        struct rwnx_sta *sta_iter;
+		spin_lock_bh(&vif->rwnx_hw->cb_lock);
+        list_for_each_entry(sta_iter, &vif->ap.sta_list, list) {
+            if (sta_iter->valid && (!memcmp(sta_iter->mac_addr, mac, 6))) {
+                sta = sta_iter;
+                break;
+            }
+        }
+		spin_unlock_bh(&vif->rwnx_hw->cb_lock);
+    }
+
+    if (sta)
+        return rwnx_fill_station_info(sta, vif, sinfo);
+
+    return -ENOENT;
+}
+
+
+/**
+ * @dump_station: dump station callback -- resume dump at index @idx
+ */
+static int rwnx_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
+                                      int idx, u8 *mac, struct station_info *sinfo)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_sta *sta_iter, *sta = NULL;
+    struct mesh_peer_info_cfm peer_info_cfm;
+    int i = 0;
+
+    if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    list_for_each_entry(sta_iter, &rwnx_vif->ap.sta_list, list) {
+        if (i < idx) {
+            i++;
+            continue;
+        }
+
+        sta = sta_iter;
+        break;
+    }
+
+    if (sta == NULL)
+        return -ENOENT;
+
+    /* Forward the information to the UMAC */
+    if (rwnx_send_mesh_peer_info_req(rwnx_hw, rwnx_vif, sta->sta_idx,
+                                     &peer_info_cfm))
+        return -ENOMEM;
+
+    /* Copy peer MAC address */
+    memcpy(mac, &sta->mac_addr, ETH_ALEN);
+
+    /* Fill station information */
+    sinfo->llid = peer_info_cfm.local_link_id;
+    sinfo->plid = peer_info_cfm.peer_link_id;
+    sinfo->plink_state = peer_info_cfm.link_state;
+#if 0
+    sinfo->local_pm = peer_info_cfm.local_ps_mode;
+    sinfo->peer_pm = peer_info_cfm.peer_ps_mode;
+    sinfo->nonpeer_pm = peer_info_cfm.non_peer_ps_mode;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+    sinfo->filled = (STATION_INFO_LLID |
+                     STATION_INFO_PLID |
+                     STATION_INFO_PLINK_STATE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+                  |  STATION_INFO_LOCAL_PM |
+                     STATION_INFO_PEER_PM |
+                     STATION_INFO_NONPEER_PM
+#endif
+);
+#else
+    sinfo->filled = (BIT(NL80211_STA_INFO_LLID) |
+                     BIT(NL80211_STA_INFO_PLID) |
+                     BIT(NL80211_STA_INFO_PLINK_STATE) |
+                     BIT(NL80211_STA_INFO_LOCAL_PM) |
+                     BIT(NL80211_STA_INFO_PEER_PM) |
+                     BIT(NL80211_STA_INFO_NONPEER_PM));
+#endif
+
+    return 0;
+}
+
+/**
+ * @add_mpath: add a fixed mesh path
+ */
+static int rwnx_cfg80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                   const u8 *dst, const u8 *next_hop)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct mesh_path_update_cfm cfm;
+
+    if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    return rwnx_send_mesh_path_update_req(rwnx_hw, rwnx_vif, dst, next_hop, &cfm);
+}
+
+/**
+ * @del_mpath: delete a given mesh path
+ */
+static int rwnx_cfg80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                   const u8 *dst)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct mesh_path_update_cfm cfm;
+
+    if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    return rwnx_send_mesh_path_update_req(rwnx_hw, rwnx_vif, dst, NULL, &cfm);
+}
+
+/**
+ * @change_mpath: change a given mesh path
+ */
+static int rwnx_cfg80211_change_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                      const u8 *dst, const u8 *next_hop)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct mesh_path_update_cfm cfm;
+
+    if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    return rwnx_send_mesh_path_update_req(rwnx_hw, rwnx_vif, dst, next_hop, &cfm);
+}
+
+/**
+ * @get_mpath: get a mesh path for the given parameters
+ */
+static int rwnx_cfg80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                   u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_mesh_path *mesh_path = NULL;
+    struct rwnx_mesh_path *cur;
+
+    if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    list_for_each_entry(cur, &rwnx_vif->ap.mpath_list, list) {
+        /* Compare the path target address and the provided destination address */
+        if (memcmp(dst, &cur->tgt_mac_addr, ETH_ALEN)) {
+            continue;
+        }
+
+        mesh_path = cur;
+        break;
+    }
+
+    if (mesh_path == NULL)
+        return -ENOENT;
+
+    /* Copy next HOP MAC address */
+    if (mesh_path->p_nhop_sta)
+        memcpy(next_hop, &mesh_path->p_nhop_sta->mac_addr, ETH_ALEN);
+
+    /* Fill path information */
+    pinfo->filled = 0;
+    pinfo->generation = rwnx_vif->ap.generation;
+
+    return 0;
+}
+
+/**
+ * @dump_mpath: dump mesh path callback -- resume dump at index @idx
+ */
+static int rwnx_cfg80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                    int idx, u8 *dst, u8 *next_hop,
+                                    struct mpath_info *pinfo)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_mesh_path *mesh_path = NULL;
+    struct rwnx_mesh_path *cur;
+    int i = 0;
+
+    if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    list_for_each_entry(cur, &rwnx_vif->ap.mpath_list, list) {
+        if (i < idx) {
+            i++;
+            continue;
+        }
+
+        mesh_path = cur;
+        break;
+    }
+
+    if (mesh_path == NULL)
+        return -ENOENT;
+
+    /* Copy target and next hop MAC address */
+    memcpy(dst, &mesh_path->tgt_mac_addr, ETH_ALEN);
+    if (mesh_path->p_nhop_sta)
+        memcpy(next_hop, &mesh_path->p_nhop_sta->mac_addr, ETH_ALEN);
+
+    /* Fill path information */
+    pinfo->filled = 0;
+    pinfo->generation = rwnx_vif->ap.generation;
+
+    return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+/**
+ * @get_mpp: get a mesh proxy path for the given parameters
+ */
+static int rwnx_cfg80211_get_mpp(struct wiphy *wiphy, struct net_device *dev,
+                                 u8 *dst, u8 *mpp, struct mpath_info *pinfo)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_mesh_proxy *mesh_proxy = NULL;
+    struct rwnx_mesh_proxy *cur;
+
+    if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    list_for_each_entry(cur, &rwnx_vif->ap.proxy_list, list) {
+        if (cur->local) {
+            continue;
+        }
+
+        /* Compare the path target address and the provided destination address */
+        if (memcmp(dst, &cur->ext_sta_addr, ETH_ALEN)) {
+            continue;
+        }
+
+        mesh_proxy = cur;
+        break;
+    }
+
+    if (mesh_proxy == NULL)
+        return -ENOENT;
+
+    memcpy(mpp, &mesh_proxy->proxy_addr, ETH_ALEN);
+
+    /* Fill path information */
+    pinfo->filled = 0;
+    pinfo->generation = rwnx_vif->ap.generation;
+
+    return 0;
+}
+
+/**
+ * @dump_mpp: dump mesh proxy path callback -- resume dump at index @idx
+ */
+static int rwnx_cfg80211_dump_mpp(struct wiphy *wiphy, struct net_device *dev,
+                                  int idx, u8 *dst, u8 *mpp, struct mpath_info *pinfo)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_mesh_proxy *mesh_proxy = NULL;
+    struct rwnx_mesh_proxy *cur;
+    int i = 0;
+
+    if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    list_for_each_entry(cur, &rwnx_vif->ap.proxy_list, list) {
+        if (cur->local) {
+            continue;
+        }
+
+        if (i < idx) {
+            i++;
+            continue;
+        }
+
+        mesh_proxy = cur;
+        break;
+    }
+
+    if (mesh_proxy == NULL)
+        return -ENOENT;
+
+    /* Copy target MAC address */
+    memcpy(dst, &mesh_proxy->ext_sta_addr, ETH_ALEN);
+    memcpy(mpp, &mesh_proxy->proxy_addr, ETH_ALEN);
+
+    /* Fill path information */
+    pinfo->filled = 0;
+    pinfo->generation = rwnx_vif->ap.generation;
+
+    return 0;
+}
+#endif /* version >= 3.19 */
+
+/**
+ * @get_mesh_config: Get the current mesh configuration
+ */
+static int rwnx_cfg80211_get_mesh_config(struct wiphy *wiphy, struct net_device *dev,
+                                         struct mesh_config *conf)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+
+    if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    return 0;
+}
+
+/**
+ * @update_mesh_config: Update mesh parameters on a running mesh.
+ */
+static int rwnx_cfg80211_update_mesh_config(struct wiphy *wiphy, struct net_device *dev,
+                                            u32 mask, const struct mesh_config *nconf)
+{
+#if 0
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct mesh_update_cfm cfm;
+    int status;
+
+    if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    if (mask & CO_BIT(NL80211_MESHCONF_POWER_MODE - 1)) {
+        rwnx_vif->ap.next_mesh_pm = nconf->power_mode;
+
+        if (!list_empty(&rwnx_vif->ap.sta_list)) {
+            // If there are mesh links we don't want to update the power mode
+            // It will be updated with rwnx_update_mesh_power_mode() when the
+            // ps mode of a link is updated or when a new link is added/removed
+            mask &= ~BIT(NL80211_MESHCONF_POWER_MODE - 1);
+
+            if (!mask)
+                return 0;
+        }
+    }
+
+    status = rwnx_send_mesh_update_req(rwnx_hw, rwnx_vif, mask, nconf, &cfm);
+
+    if (!status && (cfm.status != 0))
+        status = -EINVAL;
+
+    return status;
+#else
+    return 0;
+#endif
+}
+
+/**
+ * @join_mesh: join the mesh network with the specified parameters
+ * (invoked with the wireless_dev mutex held)
+ */
+static int rwnx_cfg80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
+                                   const struct mesh_config *conf, const struct mesh_setup *setup)
+{
+#if 0
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct mesh_start_cfm mesh_start_cfm;
+    int error = 0;
+    u8 txq_status = 0;
+    /* STA for BC/MC traffic */
+    struct rwnx_sta *sta;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    /* Forward the information to the UMAC */
+    if ((error = rwnx_send_mesh_start_req(rwnx_hw, rwnx_vif, conf, setup, &mesh_start_cfm))) {
+        return error;
+    }
+
+    /* Check the status */
+    switch (mesh_start_cfm.status) {
+        case CO_OK:
+            rwnx_vif->ap.bcmc_index = mesh_start_cfm.bcmc_idx;
+            rwnx_vif->ap.flags = 0;
+            rwnx_vif->use_4addr = true;
+            rwnx_vif->user_mpm = setup->user_mpm;
+
+            sta = &rwnx_hw->sta_table[mesh_start_cfm.bcmc_idx];
+            sta->valid = true;
+            sta->aid = 0;
+            sta->sta_idx = mesh_start_cfm.bcmc_idx;
+            sta->ch_idx = mesh_start_cfm.ch_idx;
+            sta->vif_idx = rwnx_vif->vif_index;
+            sta->qos = true;
+            sta->acm = 0;
+            sta->ps.active = false;
+            rwnx_mu_group_sta_init(sta, NULL);
+            spin_lock_bh(&rwnx_hw->cb_lock);
+            rwnx_chanctx_link(rwnx_vif, mesh_start_cfm.ch_idx,
+                              (struct cfg80211_chan_def *)(&setup->chandef));
+            if (rwnx_hw->cur_chanctx != mesh_start_cfm.ch_idx) {
+                txq_status = RWNX_TXQ_STOP_CHAN;
+            }
+            rwnx_txq_vif_init(rwnx_hw, rwnx_vif, txq_status);
+            spin_unlock_bh(&rwnx_hw->cb_lock);
+
+            netif_tx_start_all_queues(dev);
+            netif_carrier_on(dev);
+
+            /* If the AP channel is already the active, we probably skip radar
+               activation on MM_CHANNEL_SWITCH_IND (unless another vif use this
+               ctxt). In anycase retest if radar detection must be activated
+             */
+            if (rwnx_hw->cur_chanctx == mesh_start_cfm.ch_idx) {
+                rwnx_radar_detection_enable_on_cur_channel(rwnx_hw);
+            }
+            break;
+
+        case CO_BUSY:
+            error = -EINPROGRESS;
+            break;
+
+        default:
+            error = -EIO;
+            break;
+    }
+
+    /* Print information about the operation */
+    if (error) {
+        netdev_info(dev, "Failed to start MP (%d)", error);
+    } else {
+        netdev_info(dev, "MP started: ch=%d, bcmc_idx=%d",
+                    rwnx_vif->ch_index, rwnx_vif->ap.bcmc_index);
+    }
+
+    return error;
+#else
+    return 0;
+#endif
+}
+
+/**
+ * @leave_mesh: leave the current mesh network
+ * (invoked with the wireless_dev mutex held)
+ */
+static int rwnx_cfg80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct mesh_stop_cfm mesh_stop_cfm;
+    int error = 0;
+
+    error = rwnx_send_mesh_stop_req(rwnx_hw, rwnx_vif, &mesh_stop_cfm);
+
+    if (error == 0) {
+        /* Check the status */
+        switch (mesh_stop_cfm.status) {
+            case CO_OK:
+                spin_lock_bh(&rwnx_hw->cb_lock);
+                rwnx_chanctx_unlink(rwnx_vif);
+                rwnx_radar_cancel_cac(&rwnx_hw->radar);
+                spin_unlock_bh(&rwnx_hw->cb_lock);
+                /* delete BC/MC STA */
+                rwnx_txq_vif_deinit(rwnx_hw, rwnx_vif);
+                rwnx_del_bcn(&rwnx_vif->ap.bcn);
+
+                netif_tx_stop_all_queues(dev);
+                netif_carrier_off(dev);
+
+                break;
+
+            default:
+                error = -EIO;
+                break;
+        }
+    }
+
+    if (error) {
+        netdev_info(dev, "Failed to stop MP");
+    } else {
+        netdev_info(dev, "MP Stopped");
+    }
+
+    return 0;
+}
+
+static struct cfg80211_ops rwnx_cfg80211_ops = {
+    .add_virtual_intf = rwnx_cfg80211_add_iface,
+    .del_virtual_intf = rwnx_cfg80211_del_iface,
+    .change_virtual_intf = rwnx_cfg80211_change_iface,
+//    .start_p2p_device = rwnx_cfgp2p_start_p2p_device,
+//    .stop_p2p_device = rwnx_cfgp2p_stop_p2p_device,
+    .scan = rwnx_cfg80211_scan,
+    .connect = rwnx_cfg80211_connect,
+    .disconnect = rwnx_cfg80211_disconnect,
+    .add_key = rwnx_cfg80211_add_key,
+    .get_key = rwnx_cfg80211_get_key,
+    .del_key = rwnx_cfg80211_del_key,
+    .set_default_key = rwnx_cfg80211_set_default_key,
+    .set_default_mgmt_key = rwnx_cfg80211_set_default_mgmt_key,
+    .add_station = rwnx_cfg80211_add_station,
+    .del_station = rwnx_cfg80211_del_station_compat,
+    .change_station = rwnx_cfg80211_change_station,
+    .mgmt_tx = rwnx_cfg80211_mgmt_tx,
+    .start_ap = rwnx_cfg80211_start_ap,
+    .change_beacon = rwnx_cfg80211_change_beacon,
+    .stop_ap = rwnx_cfg80211_stop_ap,
+//    .set_monitor_channel = rwnx_cfg80211_set_monitor_channel,
+    .probe_client = rwnx_cfg80211_probe_client,
+    //.mgmt_frame_register = rwnx_cfg80211_mgmt_frame_register,
+    .set_wiphy_params = rwnx_cfg80211_set_wiphy_params,
+    .set_txq_params = rwnx_cfg80211_set_txq_params,
+    .set_tx_power = rwnx_cfg80211_set_tx_power,
+//    .get_tx_power = rwnx_cfg80211_get_tx_power,
+//    .set_power_mgmt = rwnx_cfg80211_set_power_mgmt,
+    .get_station = rwnx_cfg80211_get_station,
+    .remain_on_channel = rwnx_cfg80211_remain_on_channel,
+    .cancel_remain_on_channel = rwnx_cfg80211_cancel_remain_on_channel,
+    .dump_survey = rwnx_cfg80211_dump_survey,
+    .get_channel = rwnx_cfg80211_get_channel,
+//    .start_radar_detection = rwnx_cfg80211_start_radar_detection,
+//    .update_ft_ies = rwnx_cfg80211_update_ft_ies,
+    .set_cqm_rssi_config = rwnx_cfg80211_set_cqm_rssi_config,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+    .channel_switch = rwnx_cfg80211_channel_switch,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+    .tdls_channel_switch = rwnx_cfg80211_tdls_channel_switch,
+    .tdls_cancel_channel_switch = rwnx_cfg80211_tdls_cancel_channel_switch,
+#endif
+    .tdls_mgmt = rwnx_cfg80211_tdls_mgmt,
+    .tdls_oper = rwnx_cfg80211_tdls_oper,
+    .change_bss = rwnx_cfg80211_change_bss,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_WPA3_FOR_OLD_KERNEL)
+    .external_auth = rwnx_cfg80211_external_auth,
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)
+    .set_channel = rwnx_cfg80211_set_channel,
+#endif
+};
+
+
+/*********************************************************************
+ * Init/Exit functions
+ *********************************************************************/
+static void rwnx_wdev_unregister(struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_vif *rwnx_vif, *tmp;
+
+    rtnl_lock();
+    list_for_each_entry_safe(rwnx_vif, tmp, &rwnx_hw->vifs, list) {
+	#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+        rwnx_cfg80211_del_iface(rwnx_hw->wiphy, &rwnx_vif->wdev);
+	#else
+		rwnx_cfg80211_del_iface(rwnx_hw->wiphy, rwnx_vif->ndev);
+	#endif
+    }
+    rtnl_unlock();
+}
+
+static void rwnx_set_vers(struct rwnx_hw *rwnx_hw)
+{
+    u32 vers = rwnx_hw->version_cfm.version_lmac;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    snprintf(rwnx_hw->wiphy->fw_version,
+             sizeof(rwnx_hw->wiphy->fw_version), "%d.%d.%d.%d",
+             (vers & (0xff << 24)) >> 24, (vers & (0xff << 16)) >> 16,
+             (vers & (0xff <<  8)) >>  8, (vers & (0xff <<  0)) >>  0);
+}
+
+static void rwnx_reg_notifier(struct wiphy *wiphy,
+                              struct regulatory_request *request)
+{
+    struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+
+    // For now trust all initiator
+    rwnx_radar_set_domain(&rwnx_hw->radar, request->dfs_region);
+    if (testmode == 0) {
+        rwnx_send_me_chan_config_req(rwnx_hw);
+    }
+}
+
+static void rwnx_enable_mesh(struct rwnx_hw *rwnx_hw)
+{
+    struct wiphy *wiphy = rwnx_hw->wiphy;
+
+    if (!rwnx_mod_params.mesh)
+        return;
+
+    rwnx_cfg80211_ops.get_station = rwnx_cfg80211_get_station;
+    rwnx_cfg80211_ops.dump_station = rwnx_cfg80211_dump_station;
+    rwnx_cfg80211_ops.add_mpath = rwnx_cfg80211_add_mpath;
+    rwnx_cfg80211_ops.del_mpath = rwnx_cfg80211_del_mpath;
+    rwnx_cfg80211_ops.change_mpath = rwnx_cfg80211_change_mpath;
+    rwnx_cfg80211_ops.get_mpath = rwnx_cfg80211_get_mpath;
+    rwnx_cfg80211_ops.dump_mpath = rwnx_cfg80211_dump_mpath;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+    rwnx_cfg80211_ops.get_mpp = rwnx_cfg80211_get_mpp;
+    rwnx_cfg80211_ops.dump_mpp = rwnx_cfg80211_dump_mpp;
+#endif
+    rwnx_cfg80211_ops.get_mesh_config = rwnx_cfg80211_get_mesh_config;
+    rwnx_cfg80211_ops.update_mesh_config = rwnx_cfg80211_update_mesh_config;
+    rwnx_cfg80211_ops.join_mesh = rwnx_cfg80211_join_mesh;
+    rwnx_cfg80211_ops.leave_mesh = rwnx_cfg80211_leave_mesh;
+#if 0
+    wiphy->flags |= (WIPHY_FLAG_MESH_AUTH | WIPHY_FLAG_IBSS_RSN);
+    wiphy->features |= NL80211_FEATURE_USERSPACE_MPM;
+    wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT);
+
+    rwnx_limits[0].types |= BIT(NL80211_IFTYPE_MESH_POINT);
+    rwnx_limits_dfs[0].types |= BIT(NL80211_IFTYPE_MESH_POINT);
+#endif 
+}
+
+extern int rwnx_init_aic(struct rwnx_hw *rwnx_hw);
+
+u32 func_tbl[] =
+{};
+
+uint32_t ldpc_cfg_ram[] = {
+#if 0//def CONFIG_FPGA_VERIFICATION
+    0x00363638,
+    0x1DF8F834,
+    0x1DF8F834,
+    0x1DF8F834,
+    0x1DF8F834,
+    0x002F2F31,
+    0x1DF8F82C,
+    0x1DF8F82C,
+    0x1DF8F82C,
+    0x1DF8F82C,
+    0x00363639,
+    0x1AA5F834,
+    0x1AA5F834,
+    0x1ADEF834,
+    0x1ADEF834,
+    0x003A3A3E,
+    0x1578F436,
+    0x1578F436,
+    0x1578F436,
+    0x15B6F436,
+    0x003B3B40,
+    0x1DF8F838,
+    0x1DF8F838,
+    0x1DF8F838,
+    0x1DF8F838,
+    0x003B3B41,
+    0x1DC4F838,
+    0x1DC4F838,
+    0x1DF8F838,
+    0x1DF8F838,
+    0x003B3B40,
+    0x1781F838,
+    0x1781F838,
+    0x1781F838,
+    0x17C4F838,
+    0x003B3B40,
+    0x0E81F838,
+    0x0E81F838,
+    0x0E81F838,
+    0x0E82F838,
+    0x003F3F43,
+    0x1A92F83D,
+    0x1A92F83E,
+    0x1A92F83D,
+    0x1ADDF83D,
+    0x00272729,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x1DF8F843,
+    0x1DF8F843,
+    0x00272729,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x1DF8F842,
+    0x1DF8F842,
+    0x00262628,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x00252528,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x00262628,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x00242427,
+    0x1DF8F821,
+    0x1DF8F821,
+    0x1DF8F821,
+    0x1DF8F821,
+    0x00232326,
+    0x1DF8F821,
+    0x1DF8F820,
+    0x1DF8F820,
+    0x1DF8F820,
+    0x00262628,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x00242427,
+    0x1DF8F821,
+    0x1DF8F821,
+    0x1DF8F821,
+    0x1DF8F821,
+    0x001F1F21,
+    0x1DF8F81D,
+    0x1DF8F81D,
+    0x1DF8F81D,
+    0x1DF8F81D,
+    0x00262643,
+    0x1DF8F822,
+    0x1DF8F821,
+    0x1DF8F821,
+    0x1DF8F821,
+    0x0018182B,
+    0x1DF8F816,
+    0x1DBDF815,
+    0x1DF8F815,
+    0x1DF8F815,
+    0x0018182A,
+    0x1195F836,
+    0x1195F815,
+    0x1195F815,
+    0x1196F815,
+    0x0028282C,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x0027272C,
+    0x1DF8F824,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x1DF8F823,
+    0x0082824A,
+    0x1ADFF841,
+    0x1ADDF822,
+    0x1ADEF822,
+    0x1ADFF822,
+    0x003E3E40,
+    0x09D1F81D,
+    0x095BF81D,
+    0x095BF81D,
+    0x095BF81D,
+    0x0029292D,
+    0x1DF8F825,
+    0x1DF8F825,
+    0x1DF8F825,
+    0x1DF8F825,
+    0x0028282C,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x0029292D,
+    0x1DF8F825,
+    0x1DF8F825,
+    0x1DF8F825,
+    0x1DF8F825,
+    0x0028282E,
+    0x1DF8F825,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x0026262C,
+    0x1DF8F823,
+    0x1DF8F822,
+    0x1DF8F822,
+    0x1DF8F822,
+    0x0028282D,
+    0x1DF8F825,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x00282852,
+    0x1DF8F827,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x1DF8F824,
+    0x0029294E,
+    0x1DF8F823,
+    0x1DF8F822,
+    0x1DF8F822,
+    0x1DF8F822,
+    0x00212143,
+    0x1DF8F821,
+    0x1DECF81D,
+    0x1DF4F81D,
+    0x1DF8F81D,
+    0x0086864D,
+    0x1CF0F844,
+    0x1CEDF823,
+    0x1CEFF822,
+    0x1CF0F822,
+    0x0047474D,
+    0x1BE8F823,
+    0x1BE8F823,
+    0x1BE9F822,
+    0x1BEAF822,
+    0x0018182F,
+    0x14B0F83C,
+    0x14B0F814,
+    0x14B0F814,
+    0x14B0F814,
+    0x00404040,
+    0x0AE1F81E,
+    0x0A61F81D,
+    0x0A61F81D,
+    0x0A61F81D,
+    0x002C2C40,
+    0x09555526,
+    0x09555512,
+    0x09555513,
+    0x09555512,
+    0x00181840,
+    0x06333329,
+    0x06333314,
+    0x06333314,
+    0x06333314,
+    0x002B2B2F,
+    0x1DF8F828,
+    0x1DF8F828,
+    0x1DF8F828,
+    0x1DF8F828,
+    0x002B2B32,
+    0x1DF8F829,
+    0x1DF8F828,
+    0x1DF8F828,
+    0x1DF8F828,
+    0x002A2A2F,
+    0x1DF8F827,
+    0x1DF8F827,
+    0x1DF8F827,
+    0x1DF8F827,
+    0x002A2A57,
+    0x1DF8F82B,
+    0x1DF8F827,
+    0x1DF8F827,
+    0x1DF8F827,
+    0x00919152,
+    0x1DF8F84B,
+    0x1DF8F825,
+    0x1DF8F825,
+    0x1DF8F825,
+    0x004C4C51,
+    0x1DF8F826,
+    0x1DF8F825,
+    0x1DF8F825,
+    0x1DF8F825,
+    0x00444440,
+    0x0CF8F820,
+    0x0C6EF81F,
+    0x0C6EF81F,
+    0x0C6EF81F,
+    0x00424240,
+    0x0D75753E,
+    0x0D75751E,
+    0x0D75751E,
+    0x0D75751E,
+    0x00191940,
+    0x0539392E,
+    0x05393914,
+    0x05393914,
+    0x05393914,
+    0x002F2F32,
+    0x1AA5F82C,
+    0x1AA5F82C,
+    0x1ADEF82C,
+    0x1ADEF82C,
+    0x002F2F40,
+    0x0C6EDE2C,
+    0x0C6EDE2C,
+    0x0C6EDE2C,
+    0x0C6EDE2C,
+    0x00323240,
+    0x053BB62E,
+    0x053BB62E,
+    0x053BB62E,
+    0x053BB62E,
+    0x00333339,
+    0x1DC4F82F,
+    0x1DC4F82F,
+    0x1DF8F82F,
+    0x1DF8F82F,
+    0x00333340,
+    0x0E81F82F,
+    0x0E81F82F,
+    0x0E81F82F,
+    0x0E82F82F,
+    0x00333340,
+    0x063FC42F,
+    0x063FC42F,
+    0x063FC42F,
+    0x063FC42F,
+    0x00404040,
+    0x063FC42F,
+    0x063FC42F,
+    0x063FC42F,
+    0x063FC42F,
+    0x00363640,
+    0x0747DD33,
+    0x0747DD33,
+    0x0747DD33,
+    0x0747DD33,
+    0x00404040,
+    0x0747DD33,
+    0x0747DD33,
+    0x0747DD33,
+    0x0747DD33,
+    0x00292940,
+    0x07484825,
+    0x07484812,
+    0x07484812,
+    0x07484812,
+    0x00404040,
+    0x07343428,
+    0x07343414,
+    0x07343414,
+    0x07343414,
+    0x00404040,
+    0x0538382A,
+    0x05383814,
+    0x05383814,
+    0x05383814,
+    0x00404040,
+    0x05292914,
+    0x05292909,
+    0x05292909,
+    0x05292909,
+    0x000B0B40,
+    0x02111108,
+    0x0211110E,
+    0x02111108,
+    0x02111108,
+    0x00404040,
+    0x063E3E2E,
+    0x063E3E15,
+    0x063E3E14,
+    0x063E3E14,
+    0x00404040,
+    0x062E2E14,
+    0x062E2E09,
+    0x062E2E09,
+    0x062E2E09,
+    0x000B0B40,
+    0x02131308,
+    0x0213130F,
+    0x02131308,
+    0x02131308
+#else
+    0x00767679,
+    0x1DF8F870,
+    0x1DF8F870,
+    0x1DF8F870,
+    0x1DF8F870,
+    0x006E6E72,
+    0x1DF8F869,
+    0x1DF8F869,
+    0x1DF8F869,
+    0x1DF8F869,
+    0x0076767B,
+    0x1DF8F870,
+    0x1DF8F870,
+    0x1DF8F870,
+    0x1DF8F870,
+    0x007E7E85,
+    0x1DF4F876,
+    0x1DF4F876,
+    0x1DF4F876,
+    0x1DF8F876,
+    0x0081818A,
+    0x1DF8F87B,
+    0x1DF8F87B,
+    0x1DF8F87B,
+    0x1DF8F87B,
+    0x0081818D,
+    0x1DF8F87B,
+    0x1DF8F87B,
+    0x1DF8F87B,
+    0x1DF8F87B,
+    0x0081818A,
+    0x1DF8F87B,
+    0x1DF8F87C,
+    0x1DF8F87B,
+    0x1DF8F87B,
+    0x007E7E40,
+    0x1DF8F87B,
+    0x1DF8F87B,
+    0x1DF8F87B,
+    0x1DF8F87B,
+    0x008B8B92,
+    0x1DF8F887,
+    0x1DF8F889,
+    0x1DF8F887,
+    0x1DF8F887,
+    0x00515155,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x1DF8F889,
+    0x1DF8F889,
+    0x00515154,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x1DF8F888,
+    0x1DF8F888,
+    0x004F4F53,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x004F4F53,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x004F4F53,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x004E4E53,
+    0x1DF8F849,
+    0x1DF8F848,
+    0x1DF8F848,
+    0x1DF8F848,
+    0x004D4D52,
+    0x1DF8F847,
+    0x1DF8F847,
+    0x1DF8F847,
+    0x1DF8F847,
+    0x004F4F55,
+    0x1DF8F84B,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x004E4E53,
+    0x1DF8F849,
+    0x1DF8F848,
+    0x1DF8F848,
+    0x1DF8F848,
+    0x0049494D,
+    0x1DF8F844,
+    0x1DF8F844,
+    0x1DF8F844,
+    0x1DF8F844,
+    0x0051518F,
+    0x1DF8F849,
+    0x1DF8F848,
+    0x1DF8F848,
+    0x1DF8F848,
+    0x00424277,
+    0x1DF8F83F,
+    0x1DF8F83C,
+    0x1DF8F83C,
+    0x1DF8F83C,
+    0x00424275,
+    0x1DF8F89E,
+    0x1DF8F83C,
+    0x1DF8F83C,
+    0x1DF8F83C,
+    0x0055555C,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x0053535C,
+    0x1DF8F84C,
+    0x1DF8F84B,
+    0x1DF8F84B,
+    0x1DF8F84B,
+    0x00F8F89E,
+    0x1DF8F88C,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x00898940,
+    0x18F8F846,
+    0x18CFF845,
+    0x18CFF844,
+    0x18CFF844,
+    0x0056565F,
+    0x1DF8F84F,
+    0x1DF8F84F,
+    0x1DF8F84F,
+    0x1DF8F84F,
+    0x0055555E,
+    0x1DF8F84E,
+    0x1DF8F84E,
+    0x1DF8F84E,
+    0x1DF8F84E,
+    0x0056565F,
+    0x1DF8F84F,
+    0x1DF8F84F,
+    0x1DF8F84F,
+    0x1DF8F84F,
+    0x00555561,
+    0x1DF8F850,
+    0x1DF8F84E,
+    0x1DF8F84E,
+    0x1DF8F84E,
+    0x0053535F,
+    0x1DF8F84D,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x0055555F,
+    0x1DF8F84F,
+    0x1DF8F84E,
+    0x1DF8F84E,
+    0x1DF8F84E,
+    0x005555AA,
+    0x1DF8F854,
+    0x1DF8F84E,
+    0x1DF8F84E,
+    0x1DF8F84E,
+    0x005959A6,
+    0x1DF8F84D,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x004F4F9B,
+    0x1DF8F84E,
+    0x1DF8F846,
+    0x1DF8F846,
+    0x1DF8F846,
+    0x00F8F8A5,
+    0x1DF8F894,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x009898A4,
+    0x1DF8F84D,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x00464686,
+    0x1DF8F8B3,
+    0x1DF8F83D,
+    0x1DF8F83D,
+    0x1DF8F83D,
+    0x008E8E40,
+    0x1AF8F848,
+    0x1ADFF848,
+    0x1ADFF846,
+    0x1ADFF846,
+    0x007F7F40,
+    0x18D2D275,
+    0x18D2D23A,
+    0x18D2D23A,
+    0x18D2D239,
+    0x00454540,
+    0x0F868664,
+    0x0F86863E,
+    0x0F86863D,
+    0x0F86863D,
+    0x005C5C64,
+    0x1DF8F856,
+    0x1DF8F855,
+    0x1DF8F855,
+    0x1DF8F855,
+    0x005B5B68,
+    0x1DF8F858,
+    0x1DF8F855,
+    0x1DF8F855,
+    0x1DF8F855,
+    0x005A5A64,
+    0x1DF8F855,
+    0x1DF8F854,
+    0x1DF8F854,
+    0x1DF8F854,
+    0x005A5AB5,
+    0x1DF8F85B,
+    0x1DF8F855,
+    0x1DF8F854,
+    0x1DF8F854,
+    0x00F8F8B0,
+    0x1DF8F8A3,
+    0x1DF8F852,
+    0x1DF8F852,
+    0x1DF8F852,
+    0x00A4A4AE,
+    0x1DF8F854,
+    0x1DF8F852,
+    0x1DF8F852,
+    0x1DF8F852,
+    0x009A9A40,
+    0x1DF8F84E,
+    0x1DF8F84D,
+    0x1DF8F84C,
+    0x1DF8F84C,
+    0x009C9C40,
+    0x1DF8F895,
+    0x1DF8F849,
+    0x1DF8F84A,
+    0x1DF8F84A,
+    0x00494940,
+    0x1197976F,
+    0x11979742,
+    0x11979741,
+    0x11979741,
+    0x006E6E74,
+    0x1DF8F869,
+    0x1DF8F869,
+    0x1DF8F869,
+    0x1DF8F869,
+    0x006E6E40,
+    0x1ADEF869,
+    0x1ADEF869,
+    0x1ADEF869,
+    0x1ADEF869,
+    0x00757540,
+    0x0D78F86E,
+    0x0D78F86E,
+    0x0D78F86E,
+    0x0D79F86E,
+    0x00787885,
+    0x1DF8F873,
+    0x1DF8F873,
+    0x1DF8F873,
+    0x1DF8F873,
+    0x00787840,
+    0x1DF8F873,
+    0x1DF8F873,
+    0x1DF8F873,
+    0x1DF8F873,
+    0x00787840,
+    0x0E81F873,
+    0x0E81F873,
+    0x0E81F873,
+    0x0E82F873,
+    0x00404040,
+    0x0E82F873,
+    0x0E82F873,
+    0x0E82F873,
+    0x0E82F873,
+    0x00818140,
+    0x1092F87E,
+    0x1092F87E,
+    0x1092F87E,
+    0x1092F87E,
+    0x00404040,
+    0x1092F87E,
+    0x1092F87E,
+    0x1092F87E,
+    0x1092F87E,
+    0x00737340,
+    0x14B2B26B,
+    0x14B2B235,
+    0x14B2B235,
+    0x14B2B235,
+    0x00404040,
+    0x0E828260,
+    0x0E82823D,
+    0x0E82823C,
+    0x0E82823C,
+    0x00404040,
+    0x0F8B8B66,
+    0x0F8B8B3F,
+    0x0F8B8B3D,
+    0x0F8B8B3D,
+    0x00404040,
+    0x0B68683D,
+    0x0B68681E,
+    0x0B68681E,
+    0x0B68681E,
+    0x00222240,
+    0x06434318,
+    0x06434329,
+    0x06434318,
+    0x06434318,
+    0x00404040,
+    0x129D9D72,
+    0x129D9D43,
+    0x129D9D41,
+    0x129D9D41,
+    0x00404040,
+    0x0D757542,
+    0x0D757520,
+    0x0D757520,
+    0x0D757520,
+    0x00232340,
+    0x084C4C19,
+    0x084C4C2C,
+    0x084C4C19,
+    0x084C4C19
+#endif
+};
+
+uint32_t agc_cfg_ram[] = {
+    0x20000000,
+    0x0400000E,
+    0x3000200E,
+    0x5B000000,
+    0x0400004B,
+    0x3000008E,
+    0x32000000,
+    0x0400007B,
+    0x40000000,
+    0xF8000026,
+    0x04000011,
+    0x4819008E,
+    0x9C000020,
+    0x08000191,
+    0x38008000,
+    0x0A000000,
+    0x08104411,
+    0x38018000,
+    0x0C004641,
+    0x08D00014,
+    0x30000000,
+    0x01000000,
+    0x04000017,
+    0x30000000,
+    0x3C000000,
+    0x0400001A,
+    0x38020000,
+    0x40000001,
+    0x0800001D,
+    0x3808008E,
+    0x14000050,
+    0x08000020,
+    0x4000008E,
+    0xA400007B,
+    0x00000101,
+    0x3000339F,
+    0x41000700,
+    0x04104420,
+    0x90000000,
+    0x49000000,
+    0xF00E842F,
+    0xEC0E842C,
+    0xEC0E842C,
+    0x04000032,
+    0x30000000,
+    0x48000101,
+    0x04000032,
+    0x30000000,
+    0x48000202,
+    0x04000032,
+    0x30000000,
+    0x46000000,
+    0x04000011,
+    0x58010006,
+    0x3D040472,
+    0xDC204439,
+    0x081DD4D2,
+    0x480A0006,
+    0xDC2044DC,
+    0x081DD43C,
+    0x38050004,
+    0x0EF1F1C3,
+    0x342044DC,
+    0x30000000,
+    0x01000000,
+    0x04000042,
+    0x30000000,
+    0x33000000,
+    0x04104445,
+    0x38008000,
+    0x2200109C,
+    0x08104448,
+    0x38008000,
+    0x23D4509C,
+    0x08104417,
+    0x9000A000,
+    0x32000000,
+    0x18000063,
+    0x14000060,
+    0x1C000051,
+    0x10000057,
+    0x38028000,
+    0x0C000001,
+    0x08D04466,
+    0x3000200F,
+    0x00000000,
+    0x00000000,
+    0x38030000,
+    0x0C002601,
+    0x08D0445A,
+    0x30000000,
+    0x3D020230,
+    0x0400005D,
+    0x30000000,
+    0x3E000100,
+    0x04000066,
+    0x38028000,
+    0x0C001601,
+    0x34204466,
+    0x38028000,
+    0x0C000A01,
+    0x34204466,
+    0x38008004,
+    0xFF000000,
+    0x0800007B,
+    0x3800802F,
+    0x26000000,
+    0x0800006C,
+    0x380404AF,
+    0x1F191010,
+    0x0800006F,
+    0x20000CAF,
+    0x04000071,
+    0x60000CAF,
+    0x18700079,
+    0x14000077,
+    0x10000075,
+    0x28140CAF,
+    0x09B00084,
+    0x280A0CAF,
+    0x09B00084,
+    0x28060CAF,
+    0x09B00084,
+    0x28048086,
+    0x0800007D,
+    0x38000086,
+    0x22800000,
+    0x04000080,
+    0x30000000,
+    0x0EF1F101,
+    0x36004883,
+    0x28020000,
+    0x08000085,
+    0x3802008E,
+    0x3D040431,
+    0x08000088,
+    0x3805008E,
+    0x1F241821,
+    0x0800008B,
+    0x3000008E,
+    0xA0163021,
+    0x0400008E,
+    0x3000008E,
+    0x0EF10012,
+    0x34000091,
+    0x300000CC,
+    0x50000000,
+    0x04000094,
+    0x380095FE,
+    0x32010000,
+    0x04000097,
+    0x50001FFE,
+    0x5A010000,
+    0x6DC9989B,
+    0xFC19D4B9,
+    0x30000186,
+    0x3D840373,
+    0x0400009E,
+    0x3000008E,
+    0x0A000000,
+    0x040000A1,
+    0x3000008E,
+    0x22C00000,
+    0x040000A4,
+    0x9000028E,
+    0x32010001,
+    0x8E4000AA,
+    0xC80000B0,
+    0x00000000,
+    0x00000000,
+    0x3000008E,
+    0x32010001,
+    0x040000CB,
+    0x3000008E,
+    0x29000000,
+    0x94045011,
+    0x300019B6,
+    0x32010000,
+    0x040000B3,
+    0x300019B6,
+    0x3D040431,
+    0x040000B6,
+    0x300019B6,
+    0x22800000,
+    0x04000097,
+    0x30000186,
+    0x3D840473,
+    0x040000BC,
+    0x3000008E,
+    0x29030000,
+    0x040000BF,
+    0x9AEE028E,
+    0x32010100,
+    0x7C0000C5,
+    0xCC0000B0,
+    0x080000B0,
+    0x00000000,
+    0x3000008E,
+    0x32010100,
+    0x040000C8,
+    0x3000028E,
+    0x29000000,
+    0x94045011,
+    0x5000038E,
+    0x29000000,
+    0x94045011,
+    0xC0000035,
+    0x38010006,
+    0x3D040472,
+    0x080000D2,
+    0x30000004,
+    0x0EF1F141,
+    0x340000D5,
+    0x28040004,
+    0x080000D7,
+    0x2808000E,
+    0x080000D9,
+    0x3000018E,
+    0x0EF10052,
+    0x340000DC,
+    0x3000038E,
+    0x29000000,
+    0x94045011,
+    0x38020000,
+    0x32000000,
+    0x080000E2,
+    0x60000000,
+    0xD80000E6,
+    0xD40000E9,
+    0x040000EC,
+    0x30000000,
+    0x0EF1F121,
+    0x360048EF,
+    0x30000000,
+    0x0C002421,
+    0x360048EF,
+    0x30000000,
+    0x0C000021,
+    0x360048EF,
+    0x28020000,
+    0x0800007B,
+    0x50001EFE,
+    0x5A010000,
+    0x6DC998F5,
+    0xFC19D4F8,
+    0x3000028E,
+    0x32000040,
+    0x040000FB,
+    0x3AEE028E,
+    0x32000080,
+    0x040000FB,
+    0x30000000,
+    0x0EF1F101,
+    0x360048FE,
+    0x28020000,
+    0x08000100,
+    0x3802008E,
+    0x3D040431,
+    0x08000103,
+    0x3805008E,
+    0x1F241821,
+    0x08000106,
+    0x3000008E,
+    0xA0163021,
+    0x04000109,
+    0x3000008E,
+    0x0EF10012,
+    0x3400010C,
+    0x300014F6,
+    0x32010000,
+    0x04000114,
+    0x20000000,
+    0x04000111,
+    0x300000EC,
+    0x50000000,
+    0x040000F1,
+    0x300014F6,
+    0x32030000,
+    0x04000117,
+    0x30001086,
+    0x3D840473,
+    0x0400011A,
+    0x5000108E,
+    0x22C00000,
+    0x8E47C0CB,
+    0xCB30011E,
+    0x300019B6,
+    0x32040000,
+    0x04000121,
+    0x300019B6,
+    0x3D040431,
+    0x04000124,
+    0x300019B6,
+    0x22800000,
+    0x04000111,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x30000186,
+    0x3D840473,
+    0x0400012D,
+    0x5000038E,
+    0x29000000,
+    0x94045011,
+    0xC0000131,
+    0x380C800E,
+    0xFF000000,
+    0x08000134,
+    0x30000004,
+    0x0FF1F103,
+    0x34000137,
+    0x28020000,
+    0x08000139,
+    0x3000038E,
+    0x29000000,
+    0x94045011,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x58010006,
+    0x3D040472,
+    0xDC204543,
+    0x081DD4D2,
+    0x480A0006,
+    0xDC2044DC,
+    0x081DD546,
+    0x38050004,
+    0x0EF1F141,
+    0x342044DC,
+    0x2802800E,
+    0x080000DC,
+    0x48000035,
+    0x0400014A,
+    0x7896638F,
+    0x4100000F,
+    0x8C00014F,
+    0x080450C4,
+    0x90104574,
+    0x88C8620F,
+    0xC000015A,
+    0x90104574,
+    0x08104554,
+    0x94104557,
+    0x3000628F,
+    0x29000000,
+    0x9404517A,
+    0x3000638F,
+    0x29000000,
+    0x0410457A,
+    0x3800E005,
+    0x3D010131,
+    0x0810455D,
+    0xA832600F,
+    0x90104574,
+    0x08000154,
+    0x94104557,
+    0xC6104567,
+    0xC4185563,
+    0x5802E00F,
+    0x0FEEEA07,
+    0x80000174,
+    0x3420456B,
+    0x5802E00F,
+    0x0EEEEA07,
+    0x80000174,
+    0x3420456B,
+    0x30004000,
+    0x33000001,
+    0x0400016E,
+    0x38034005,
+    0x3D030373,
+    0x08000171,
+    0x30006007,
+    0x33000000,
+    0x04000174,
+    0x3000608F,
+    0x29000000,
+    0x94045177,
+    0x4000608F,
+    0xA010457D,
+    0x0410457A,
+    0x3000608F,
+    0x64000101,
+    0x04104411,
+    0x3000608F,
+    0x64000101,
+    0x04104580,
+    0x3000618F,
+    0x42000001,
+    0x04000183,
+    0x38028000,
+    0x32000000,
+    0x08104586,
+    0x280A618F,
+    0x08000188,
+    0x480A618F,
+    0xBC00018B,
+    0x0800018E,
+    0x3000618F,
+    0x34000001,
+    0x04000005,
+    0x3000618F,
+    0x34000000,
+    0x04000008,
+    0x3000008F,
+    0x0EEAED0F,
+    0x36000194,
+    0x38038000,
+    0x34000000,
+    0x08000197,
+    0x38028005,
+    0x29010002,
+    0x0800019A,
+    0x3000028F,
+    0x2200209C,
+    0x0400019D,
+    0x3000028F,
+    0x23D4509C,
+    0x040001A0,
+    0x2814028F,
+    0x080001A2,
+    0x3000028F,
+    0x43010201,
+    0x040001A5,
+    0x3000128F,
+    0x32000100,
+    0x040001A8,
+    0x5AEE138F,
+    0x4100000F,
+    0x7C0001AC,
+    0x080000F9,
+    0x592C138F,
+    0x29000000,
+    0x8C0001B0,
+    0x080000F9,
+    0x2000138F,
+    0x94045011,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000,
+    0x00000000
+};
+
+
+uint32_t txgain_map[96] =  {
+#ifdef CONFIG_FPGA_VERIFICATION
+    0x20c0c971,
+    0x20c0c980,
+    0x20c0c992,
+    0x20c0c9a6,
+    0x20c0c9bf,
+    0x20c0caa5,
+    0x20c0cabd,
+    0x20c0cba0,
+    0x20c0cbb6,
+    0x20c0cbea,
+    0x20c0ccc5,
+    0x20c0cdac,
+    0x20c0cdd0,
+    0x20c0ceb2,
+    0x20c0ceff,
+    0x20c0cfff,
+    0x20c0c922,
+    0x20c0c922,
+    0x20c0c922,
+    0x20c0c922,
+    0x20c0c922,
+    0x20c0c922,
+    0x20c0c922,
+    0x20c0c927,
+    0x20c0c92c,
+    0x20c0c931,
+    0x20c0c937,
+    0x20c0c93f,
+    0x20c0c946,
+    0x20c0c94f,
+    0x20c0c959,
+    0x20c0c964,
+    0x20c0cbee,
+    0x20c0cce0,
+    0x20c0ccff,
+    0x20c0cde2,
+    0x20c0cdfe,
+    0x20c0cede,
+    0x20c0cefc,
+    0x20c0cfd9,
+    0x20c0cff8,
+    0x20c0cfff,
+    0x20c0cfff,
+    0x20c0cfff,
+    0x20c0cfff,
+    0x20c0cfff,
+    0x20c0cfff,
+    0x20c0cfff,
+    0x20c0c97c,
+    0x20c0c97c,
+    0x20c0c97c,
+    0x20c0c97c,
+    0x20c0c97c,
+    0x20c0c97c,
+    0x20c0c97c,
+    0x20c0c98c,
+    0x20c0ca79,
+    0x20c0ca89,
+    0x20c0cb74,
+    0x20c0cb84,
+    0x20c0cb94,
+    0x20c0cba8,
+    0x20c0cbbb,
+    0x20c0cbd2,
+    0x20c0cbee,
+    0x20c0cce0,
+    0x20c0ccff,
+    0x20c0cde2,
+    0x20c0cdfe,
+    0x20c0cede,
+    0x20c0cefc,
+    0x20c0cfd9,
+    0x20c0cff8,
+    0x20c0cfff,
+    0x20c0cfff,
+    0x20c0cfff,
+    0x20c0cfff,
+    0x20c0cfff,
+    0x20c0cfff,
+    0x20c0cfff,
+    0x20c0c97c,
+    0x20c0c97c,
+    0x20c0c97c,
+    0x20c0c97c,
+    0x20c0c97c,
+    0x20c0c97c,
+    0x20c0c97c,
+    0x20c0c98c,
+    0x20c0ca79,
+    0x20c0ca89,
+    0x20c0cb74,
+    0x20c0cb84,
+    0x20c0cb94,
+    0x20c0cba8,
+    0x20c0cbbb,
+    0x20c0cbd2,
+#else
+    //11b
+    0x00ffd780,
+    0x00ffd872,
+    0x00ffd880,
+    0x00ffd972,
+    0x00ffd980,
+    0x00ffda75,
+    0x00ffda86,
+    0x00ffdb77,
+    0x00ffdb86,
+    0x00ffdc78,
+    0x00ffdc89,
+    0x00ffdd79,
+    0x00ffdd89,
+    0x00ffde83,
+    0x00ffdf79,
+    0x00ffdf8b,
+    0x00ffd072,
+    0x00ffd072,
+    0x00ffd080,
+    0x00ffd172,
+    0x00ffd180,
+    0x00ffd272,
+    0x00ffd280,
+    0x00ffd36d,
+    0x00ffd379,
+    0x00ffd46d,
+    0x00ffd479,
+    0x00ffd572,
+    0x00ffd580,
+    0x00ffd672,
+    0x00ffd680,
+    0x00ffd772,
+    //high
+    0x00ffc87d,
+    0x00ffc88b,
+    0x00ffc979,
+    0x00ffc989,
+    0x00ffca7d,
+    0x00ffca88,
+    0x00ffcc5e,
+    0x00ffcc69,
+    0x00ffcc78,
+    0x00ffcc85,
+    0x00ffcd70,
+    0x00ffcd80,
+    0x00ffce70,
+    0x00ffce80,
+    0x00ffce93,
+    0x00ffcf90,
+    0x00ffc080,
+    0x00ffc090,
+    0x00ffc180,
+    0x00ffc190,
+    0x00ffc27b,
+    0x00ffc28b,
+    0x00ffc37b,
+    0x00ffc390,
+    0x00ffc485,
+    0x00ffc495,
+    0x00ffc579,
+    0x00ffc589,
+    0x00ffc679,
+    0x00ffc689,
+    0x00ffc780,
+    0x00ffc790,
+    //low
+    0x00ffc87d,
+    0x00ffc88b,
+    0x00ffc979,
+    0x00ffc989,
+    0x00ffca7d,
+    0x00ffca88,
+    0x00ffcc5e,
+    0x00ffcc69,
+    0x00ffcc78,
+    0x00ffcc85,
+    0x00ffcd70,
+    0x00ffcd80,
+    0x00ffcd90,
+    0x00ffce80,
+    0x00ffce93,
+    0x00ffcf90,
+    0x00ffc080,
+    0x00ffc090,
+    0x00ffc180,
+    0x00ffc190,
+    0x00ffc27b,
+    0x00ffc28b,
+    0x00ffc37b,
+    0x00ffc390,
+    0x00ffc485,
+    0x00ffc495,
+    0x00ffc579,
+    0x00ffc589,
+    0x00ffc679,
+    0x00ffc689,
+    0x00ffc780,
+    0x00ffc790,
+#endif
+};
+
+const uint32_t txgain_map_h[96] =
+{
+    //11b
+    0xffd888, //11
+    0xffd979, //12
+    0xffd988, //13
+    0xffda79, //14
+    0xffda88, //15
+    0xffdb79, //16
+    0xffdb88, //17
+    0xffdc72, //18
+    0xffdc80, //19
+    0xffdd80, //20
+    0xffde66, //21
+    0xffde72, //22
+    0xffde80, //23
+    0xffdf79, //24
+    0xffdf88, //25
+    0xffdf98, //26
+    0xffd079, //-5
+    0xffd088, //-4
+    0xffd179, //-3
+    0xffd188, //-2
+    0xffd288, //-1
+    0xffd36c, //0
+    0xffd379, //1
+    0xffd388, //2
+    0xffd479, //3
+    0xffd488, //4
+    0xffd579, //5
+    0xffd588, //6
+    0xffd679, //7
+    0xffd688, //8
+    0xffd779, //9
+    0xffd879, //10
+    //high
+    0xffc879, //8
+    0xffc96b, //9
+    0xffc979, //10
+    0xffca6b, //11
+    0xffca79, //12
+    0xffcc56, //13
+    0xffcc60, //14
+    0xffcc6b, //15
+    0xffcc79, //16
+    0xffcd72, //17
+    0xffce60, //18
+    0xffce72, //19
+    0xffcf72, //20
+    0xffcf80, //21
+    0xffcf90, //22
+    0xffcf90, //23
+    0xffc079, //-8
+    0xffc16b, //-7
+    0xffc179, //-6
+    0xffc26b, //-5
+    0xffc279, //-4
+    0xffc36b, //-3
+    0xffc379, //-2
+    0xffc46b, //-1
+    0xffc479, //0
+    0xffc56b, //1
+    0xffc579, //2
+    0xffc66b, //3
+    0xffc679, //4
+    0xffc76b, //5
+    0xffc779, //6
+    0xffc86b, //7
+    //low
+    0xffc879, //8
+    0xffc96b, //9
+    0xffc979, //10
+    0xffca6b, //11
+    0xffca79, //12
+    0xffcc56, //13
+    0xffcc60, //14
+    0xffcc6b, //15
+    0xffcc79, //16
+    0xffcd72, //17
+    0xffce60, //18
+    0xffce72, //19
+    0xffcf72, //20
+    0xffcf80, //21
+    0xffcf90, //22
+    0xffcf90, //23
+    0xffc079, //-8
+    0xffc16b, //-7
+    0xffc179, //-6
+    0xffc26b, //-5
+    0xffc279, //-4
+    0xffc36b, //-3
+    0xffc379, //-2
+    0xffc46b, //-1
+    0xffc479, //0
+    0xffc56b, //1
+    0xffc579, //2
+    0xffc66b, //3
+    0xffc679, //4
+    0xffc76b, //5
+    0xffc779, //6
+    0xffc86b, //7
+};
+
+u32 wifi_txgain_table_24g_8800dcdw[32] =
+{
+    0xA4B22189, //index 0
+    0x00007825,
+    0xA4B2214B, //index 1
+    0x00007825,
+    0xA4B2214F, //index 2
+    0x00007825,
+    0xA4B221D5, //index 3
+    0x00007825,
+    0xA4B221DC, //index 4
+    0x00007825,
+    0xA4B221E5, //index 5
+    0x00007825,
+    0xAC9221E5, //index 6
+    0x00006825,
+    0xAC9221EF, //index 7
+    0x00006825,
+    0xBC9221EE, //index 8
+    0x00006825,
+    0xBC9221FF, //index 9
+    0x00006825,
+    0xBC9221FF, //index 10
+    0x00004025,
+    0xB792203F, //index 11
+    0x00004026,
+    0xDC92203F, //index 12
+    0x00004025,
+    0xE692203F, //index 13
+    0x00004025,
+    0xFF92203F, //index 14
+    0x00004035,
+    0xFFFE203F, //index 15
+    0x00004832
+};
+
+u32 wifi_txgain_table_24g_1_8800dcdw[32] =
+{
+    0x096E2011, //index 0
+    0x00004001,
+    0x096E2015, //index 1
+    0x00004001,
+    0x096E201B, //index 2
+    0x00004001,
+    0x116E2018, //index 3
+    0x00004001,
+    0x116E201E, //index 4
+    0x00004001,
+    0x116E2023, //index 5
+    0x00004001,
+    0x196E2021, //index 6
+    0x00004001,
+    0x196E202B, //index 7
+    0x00004001,
+    0x216E202B, //index 8
+    0x00004001,
+    0x236E2027, //index 9
+    0x00004001,
+    0x236E2031, //index 10
+    0x00004001,
+    0x246E2039, //index 11
+    0x00004001,
+    0x26922039, //index 12
+    0x00004001,
+    0x2E92203F, //index 13
+    0x00004001,
+    0x3692203F, //index 14
+    0x00004001,
+    0x3FF2203F, //index 15
+    0x00004001,
+};
+
+u32 wifi_txgain_table_24g_8800dcdw_h[32] =
+{
+    0xA55629C9, //index 0
+    0x00005825,
+    0xAE5629C9, //index 1
+    0x00005825,
+    0xAD5629CD, //index 2
+    0x00005825,
+    0xAD5629D1, //index 3
+    0x00005825,
+    0xAD5629D7, //index 4
+    0x00005825,
+    0xAD5629DE, //index 5
+    0x00005825,
+    0xAD5629E6, //index 6
+    0x00005825,
+    0xBD5629E6, //index 7
+    0x00005825,
+    0xBD5629F0, //index 8
+    0x00005825,
+    0xCD5629F0, //index 9
+    0x00005825,
+    0xE55629F0, //index 10
+    0x00005825,
+    0xE55629FF, //index 11
+    0x00005825,
+    0xE55629FF, //index 12
+    0x00002825,
+    0xE75629FF, //index 13
+    0x00002825,
+    0xFF5629FF, //index 14
+    0x00001825,
+    0xFF5628FF, //index 15
+    0x00001025,
+};
+
+u32 wifi_txgain_table_24g_1_8800dcdw_h[32] =
+{
+    0x941A2048, //index 0
+    0x00001825,
+    0x961A2048, //index 1
+    0x00001825,
+    0x9D1A2048, //index 2
+    0x00001825,
+    0x9A1A204F, //index 3
+    0x00001825,
+    0x961A204F, //index 4
+    0x00001825,
+    0x9A1A2057, //index 5
+    0x00001825,
+    0x9C1A2057, //index 6
+    0x00001825,
+    0xA31A205B, //index 7
+    0x00001825,
+    0xAB1A205B, //index 8
+    0x00001825,
+    0xAD1A205B, //index 9
+    0x00001825,
+    0xA71A2064, //index 10
+    0x00001825,
+    0xAD1A2070, //index 11
+    0x00001825,
+    0xAD72207F, //index 12
+    0x00001825,
+    0xBCAE207F, //index 13
+    0x00001825,
+    0xBFB2207F, //index 14
+    0x00001825,
+    0xD73A207F, //index 15
+    0x00001825,
+};
+
+u32 wifi_rxgain_table_24g_20m_8800dcdw[64] = {
+    0x82f282d1,//index 0
+    0x9591a324,
+    0x80808419,
+    0x000000f0,
+    0x42f282d1,//index 1
+    0x95923524,
+    0x80808419,
+    0x000000f0,
+    0x22f282d1,//index 2
+    0x9592c724,
+    0x80808419,
+    0x000000f0,
+    0x02f282d1,//index 3
+    0x9591a324,
+    0x80808419,
+    0x000000f0,
+    0x06f282d1,//index 4
+    0x9591a324,
+    0x80808419,
+    0x000000f0,
+    0x0ef29ad1,//index 5
+    0x9591a324,
+    0x80808419,
+    0x000000f0,
+    0x0ef29ad3,//index 6
+    0x95923524,
+    0x80808419,
+    0x000000f0,
+    0x0ef29ad7,//index 7
+    0x9595a324,
+    0x80808419,
+    0x000000f0,
+    0x02f282d2,//index 8
+    0x95951124,
+    0x80808419,
+    0x000000f0,
+    0x02f282f4,//index 9
+    0x95951124,
+    0x80808419,
+    0x000000f0,
+    0x02f282e6,//index 10
+    0x9595a324,
+    0x80808419,
+    0x000000f0,
+    0x02f282e6,//index 11
+    0x9599a324,
+    0x80808419,
+    0x000000f0,
+    0x02f282e6,//index 12
+    0x959da324,
+    0x80808419,
+    0x000000f0,
+    0x02f282e6,//index 13
+    0x959f5924,
+    0x80808419,
+    0x000000f0,
+    0x06f282e6,//index 14
+    0x959f5924,
+    0x80808419,
+    0x000000f0,
+    0x0ef29ae6,//index 15
+    0x959f5924,           //loft [35:34]=3
+    0x80808419,
+    0x000000f0
+};
+
+u32 wifi_rxgain_table_24g_40m_8800dcdw[64] = {
+    0x83428151,//index 0
+    0x9631a328,
+    0x80808419,
+    0x000000f0,
+    0x43428151,//index 1
+    0x96323528,
+    0x80808419,
+    0x000000f0,
+    0x23428151,//index 2
+    0x9632c728,
+    0x80808419,
+    0x000000f0,
+    0x03428151,//index 3
+    0x9631a328,
+    0x80808419,
+    0x000000f0,
+    0x07429951,//index 4
+    0x9631a328,
+    0x80808419,
+    0x000000f0,
+    0x0f42d151,//index 5
+    0x9631a328,
+    0x80808419,
+    0x000000f0,
+    0x0f42d153,//index 6
+    0x96323528,
+    0x80808419,
+    0x000000f0,
+    0x0f42d157,//index 7
+    0x9635a328,
+    0x80808419,
+    0x000000f0,
+    0x03428152,//index 8
+    0x96351128,
+    0x80808419,
+    0x000000f0,
+    0x03428174,//index 9
+    0x96351128,
+    0x80808419,
+    0x000000f0,
+    0x03428166,//index 10
+    0x9635a328,
+    0x80808419,
+    0x000000f0,
+    0x03428166,//index 11
+    0x9639a328,
+    0x80808419,
+    0x000000f0,
+    0x03428166,//index 12
+    0x963da328,
+    0x80808419,
+    0x000000f0,
+    0x03428166,//index 13
+    0x963f5928,
+    0x80808419,
+    0x000000f0,
+    0x07429966,//index 14
+    0x963f5928,
+    0x80808419,
+    0x000000f0,
+    0x0f42d166,//index 15
+    0x963f5928,
+    0x80808419,
+    0x000000f0
+};
+
+u32 patch_tbl_wifisetting[][2] =
+{
+    #if !defined(CONFIG_FPGA_VERIFICATION)
+    #ifdef CONFIG_AGGRESSIVE_TX
+    {0x0098, 0x445},
+    {0x009c, 0x5e332},
+    #endif
+    {0x0090, 0x0013FC00}, //rx_ringbuf_start2
+    #endif
+#ifdef CONFIG_USB_TX_AGGR
+    {0x00E8, 0x03021714}, //usb fc params(rx msg fc recover, rx msg fc trigger, wifi fc recover, wifi fc trigger)
+    {0x0108, 0x140A0100}, //usb agg tx params(total cnt, aggr cnt, out en, global out nak)
+#endif //CONFIG_USB_TX_AGGR
+#ifdef CONFIG_SDIO_AGGR
+    {0x010C, 0x01001E01}, //sdio_func_config, gpio_wakeup_en
+#else
+    {0x010C, 0x00001E01}, //sdio_func_config, gpio_wakeup_en
+    {0x00C0, 0x0F000A00},
+#endif
+};
+u32 patch_tbl_wifisetting_u02[][2] =
+{
+    #if !defined(CONFIG_FPGA_VERIFICATION)
+    #ifdef CONFIG_AGGRESSIVE_TX
+    {0x0098, 0x445},
+    {0x009c, 0x5e332},
+    #endif
+    {0x0090, 0x0013FC00}, //rx_ringbuf_start2
+    #endif
+#ifdef CONFIG_USB_TX_AGGR
+    {0x00E8, 0x03021714}, //usb fc params(rx msg fc recover, rx msg fc trigger, wifi fc recover, wifi fc trigger)
+    {0x0108, 0x140A0100}, //usb agg tx params(total cnt, aggr cnt, out en, global out nak)
+#endif //CONFIG_USB_TX_AGGR
+#ifdef CONFIG_SDIO_AGGR
+    {0x0124, 0x01001E01}, //sdio_func_config, gpio_wakeup_en
+#else
+    {0x0124, 0x00001E01}, //sdio_func_config, gpio_wakeup_en
+    {0x00C0, 0x0F000A00},
+#endif
+};
+
+u32 patch_tbl_rf_func[][2] =
+{
+    {0x00110bf0, 0x00181001},
+};
+
+//adap test
+u32 adaptivity_patch_tbl[][2] = {
+    {0x000C, 0x0000320A}, //linkloss_thd
+    {0x009C, 0x00000000}, //ac_param_conf
+    {0x0128, 0xF6140001}, //tx_adaptivity_en
+};
+//adap test
+
+static void patch_config(struct rwnx_hw *rwnx_hw)
+{
+    #ifdef CONFIG_ROM_PATCH_EN
+    int ret = 0;
+    int cnt = 0;
+    if (testmode == 0) {
+        const u32 cfg_base        = 0x10164;
+
+        u32 wifisetting_cfg_addr;
+        u32 ldpc_cfg_addr;
+        u32 agc_cfg_addr;
+        u32 txgain_cfg_addr;
+
+        u32 patch_tbl_wifisetting_num = sizeof(patch_tbl_wifisetting)/sizeof(u32)/2;
+        u32 patch_tbl_wifisetting_u02_num = sizeof(patch_tbl_wifisetting_u02)/sizeof(u32)/2;
+        u32 ldpc_cfg_size = sizeof(ldpc_cfg_ram);
+        u32 agc_cfg_size = sizeof(agc_cfg_ram);
+        u32 txgain_cfg_size, *txgain_cfg_array;
+
+
+        struct dbg_mem_read_cfm cfm;
+        int i;
+	int adap_patch_num = 0;
+
+        if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base, &cfm))) {
+            printk("setting base[0x%x] rd fail: %d\n", cfg_base, ret);
+        }
+        wifisetting_cfg_addr = cfm.memdata;
+
+        if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 8, &cfm))) {
+            printk("setting base[0x%x] rd fail: %d\n", cfg_base + 8, ret);
+        }
+        ldpc_cfg_addr = cfm.memdata;
+
+        if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0xc, &cfm))) {
+            printk("setting base[0x%x] rd fail: %d\n", cfg_base + 0xc, ret);
+        }
+        agc_cfg_addr = cfm.memdata;
+
+        if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0x10, &cfm))) {
+            printk("setting base[0x%x] rd fail: %d\n", cfg_base + 0x10, ret);
+        }
+        txgain_cfg_addr = cfm.memdata;
+
+        printk("wifisetting_cfg_addr=%x, ldpc_cfg_addr=%x, agc_cfg_addr=%x, txgain_cfg_addr=%x\n", wifisetting_cfg_addr, ldpc_cfg_addr, agc_cfg_addr, txgain_cfg_addr);
+
+        if ((chip_sub_id == 0) && (chip_mcu_id == 0)) {
+            for (cnt = 0; cnt < patch_tbl_wifisetting_num; cnt++) {
+                if ((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, wifisetting_cfg_addr + patch_tbl_wifisetting[cnt][0], patch_tbl_wifisetting[cnt][1]))) {
+                    printk("wifisetting %x write fail\n", patch_tbl_wifisetting[cnt][0]);
+                }
+            }
+        } else {
+            for (cnt = 0; cnt < patch_tbl_wifisetting_u02_num; cnt++) {
+                if ((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, wifisetting_cfg_addr + patch_tbl_wifisetting_u02[cnt][0], patch_tbl_wifisetting_u02[cnt][1]))) {
+                    printk("wifisetting %x write fail\n", patch_tbl_wifisetting_u02[cnt][0]);
+                }
+            }
+        }
+        if (ldpc_cfg_size > 512) {// > 0.5KB data
+            for (i = 0; i < (ldpc_cfg_size - 512); i += 512) {//each time write 0.5KB
+                ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, ldpc_cfg_addr + i, 512, ldpc_cfg_ram + i / 4);
+                if (ret) {
+                    printk("ldpc upload fail: %x, err:%d\r\n", ldpc_cfg_addr + i, ret);
+                    break;
+                }
+            }
+        }
+
+        if (!ret && (i < ldpc_cfg_size)) {// < 0.5KB data
+            ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, ldpc_cfg_addr + i, ldpc_cfg_size - i, ldpc_cfg_ram + i / 4);
+            if (ret) {
+                printk("ldpc upload fail: %x, err:%d\r\n", ldpc_cfg_addr + i, ret);
+            }
+        }
+
+//adap test
+        if(adap_test){
+            adap_patch_num = sizeof(adaptivity_patch_tbl)/sizeof(u32)/2;
+        	for(cnt = 0; cnt < adap_patch_num; cnt++)
+        	{
+        		if((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, wifisetting_cfg_addr + adaptivity_patch_tbl[cnt][0], adaptivity_patch_tbl[cnt][1]))) {
+        			printk("%x write fail\n", wifisetting_cfg_addr + adaptivity_patch_tbl[cnt][0]);
+        		}
+        	}
+        }
+//adap test
+
+        if (agc_cfg_size > 512) {// > 0.5KB data
+            for (i = 0; i < (agc_cfg_size - 512); i += 512) {//each time write 0.5KB
+                ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, agc_cfg_addr + i, 512, agc_cfg_ram + i / 4);
+                if (ret) {
+                    printk("agc upload fail: %x, err:%d\r\n", agc_cfg_addr + i, ret);
+                    break;
+                }
+            }
+        }
+
+        if (!ret && (i < agc_cfg_size)) {// < 0.5KB data
+            ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, agc_cfg_addr + i, agc_cfg_size - i, agc_cfg_ram + i / 4);
+            if (ret) {
+                printk("agc upload fail: %x, err:%d\r\n", agc_cfg_addr + i, ret);
+            }
+        }
+
+        #if !defined(CONFIG_FPGA_VERIFICATION)
+        if ((IS_CHIP_ID_H())) {
+            txgain_cfg_size = sizeof(txgain_map_h);
+            txgain_cfg_array = (u32 *)txgain_map_h;
+        } else {
+            txgain_cfg_size = sizeof(txgain_map);
+            txgain_cfg_array = (u32 *)txgain_map;
+        }
+        ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, txgain_cfg_addr, txgain_cfg_size, txgain_cfg_array);
+
+        if (ret) {
+            printk("txgain upload fail: %x, err:%d\r\n", txgain_cfg_addr, ret);
+        }
+	if ((chip_sub_id == 0) && (chip_mcu_id == 0)) {
+		ret = aicwf_patch_table_load(rwnx_hw, RWNX_MAC_PATCH_TABLE_8800DC);
+	} else if (chip_sub_id == 1) {
+		ret = aicwf_patch_table_load(rwnx_hw, RWNX_MAC_PATCH_TABLE_8800DC_U02);
+	} else if (chip_sub_id == 2) {
+		ret = aicwf_patch_table_load(rwnx_hw, RWNX_MAC_PATCH_TABLE_8800DC_H_U02);
+	} else {
+		printk("unsupported id: %d\n", chip_sub_id);
+	}
+
+	if (ret) {
+	    printk("patch_tbl upload fail: err:%d\r\n", ret);
+	}
+	#endif
+    } else {
+	if ((chip_sub_id == 0) && (chip_mcu_id == 0)) {
+            u32 patch_tbl_rf_func_num = sizeof(patch_tbl_rf_func)/sizeof(u32)/2;
+            for (cnt = 0; cnt < patch_tbl_rf_func_num; cnt++) {
+                if ((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, patch_tbl_rf_func[cnt][0], patch_tbl_rf_func[cnt][1]))) {
+                    printk("patch_tbl_rf_func %x write fail\n", patch_tbl_rf_func[cnt][0]);
+                }
+            }
+        }
+    }
+    #endif
+}
+
+#ifdef AICWF_USB_SUPPORT
+u32 usbcfg_tbl[][2] = {
+    {0x40200028, 0x0021047e},
+    {0x40200024, 0x0000011d},
+};
+
+static void usb_config(struct rwnx_hw *rwnx_hw)
+{
+    int usbcfg_num = 0;
+    int ret = 0, cnt = 0;
+    struct dbg_mem_read_cfm rd_mem_addr_cfm;
+    const u32 mem_addr = 0x40200024;
+
+    ret = rwnx_send_dbg_mem_read_req(rwnx_hw, mem_addr, &rd_mem_addr_cfm);
+    if (ret) {
+        printk("%x rd fail: %d\n", mem_addr, ret);
+        return;
+    }
+    printk("usb config read %x\n", rd_mem_addr_cfm.memdata);
+    if ((rd_mem_addr_cfm.memdata & 0xffff) == 0x119) {
+        cnt = 0;
+        usbcfg_num = sizeof(usbcfg_tbl) / sizeof(u32) / 2;
+        for (cnt = cnt; cnt < usbcfg_num; cnt++) {
+            ret = rwnx_send_dbg_mem_write_req(rwnx_hw, usbcfg_tbl[cnt][0], usbcfg_tbl[cnt][1]);
+            if (ret) {
+                printk("%x write fail: %d\n", usbcfg_tbl[cnt][0], ret);
+                return;
+            }
+        }
+    }
+}
+#endif // (AICWF_USB_SUPPORT)
+
+u32 syscfg_tbl[][2] = {
+    {0x40500010, 0x00000004},
+    {0x40500010, 0x00000006},//160m clk
+};
+
+u32 syscfg_tbl_8800dc_sdio_u01[][2] = {
+    {0x40030000, 0x00036724}, // for assert_err
+    {0x0011E800, 0xE7FE4070},
+    {0x40030084, 0x0011E800},
+    {0x40030080, 0x00000001},
+    {0x4010001C, 0x00000000},
+};
+
+u32 syscfg_tbl_8800dc_sdio_u02[][2] = {
+    {0x40030000, 0x00036DA4}, // loop forever after assert_err
+    {0x0011E800, 0xE7FE4070},
+    {0x40030084, 0x0011E800},
+    {0x40030080, 0x00000001},
+    {0x4010001C, 0x00000000},
+};
+
+u32 syscfg_tbl_masked_8800dc[][3] = {
+    //#ifdef CONFIG_PMIC_SETTING
+    #if defined(CONFIG_VRF_DCDC_MODE)
+    {0x7000216C, (0x3 << 2), (0x1 << 2)}, // pmic_pmu_init
+    {0x700021BC, (0x3 << 2), (0x1 << 2)},
+    {0x70002118, ((0x7 << 4) | (0x1 << 7)), ((0x2 << 4) | (0x1 << 7))},
+    {0x70002104, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))},
+    {0x7000210C, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))},
+    {0x70002170, (0xF << 0), (0x1 << 0)},
+    {0x70002190, (0x3F << 0), (24 << 0)},
+    {0x700021CC, ((0x7 << 4) | (0x1 << 7)), ((0x0 << 4) | (0x0 << 7))},
+    {0x700010A0, (0x1 << 11), (0x1 << 11)},
+    {0x70001034, ((0x1 << 20) | (0x7 << 26)), ((0x0 << 20) | (0x2 << 26))},
+    {0x70001038, (0x1 << 8), (0x1 << 8)},
+    {0x70001094, (0x3 << 2), (0x0 << 2)},
+    {0x700021D0, ((0x1 << 5) | (0x1 << 6)), ((0x1 << 5) | (0x1 << 6))},
+    {0x70001000, ((0x1 << 0) | (0x1 << 20) | (0x1 << 22)),
+                 ((0x1 << 0) | (0x1 << 20) | (0x0 << 22))},
+    {0x70001028, (0xf << 2), (0x1 << 2)},
+    #else
+    {0x7000216C, (0x3 << 2), (0x1 << 2)}, // pmic_pmu_init
+    {0x700021BC, (0x3 << 2), (0x1 << 2)},
+    {0x70002118, ((0x7 << 4) | (0x1 << 7)), ((0x2 << 4) | (0x1 << 7))},
+    {0x70002104, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))},
+    {0x7000210C, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))},
+    {0x70002170, (0xF << 0), (0x1 << 0)},
+    {0x70002190, (0x3F << 0), (24 << 0)},
+    {0x700021CC, ((0x7 << 4) | (0x1 << 7)), ((0x0 << 4) | (0x0 << 7))},
+    {0x700010A0, (0x1 << 11), (0x1 << 11)},
+    {0x70001034, ((0x1 << 20) | (0x7 << 26)), ((0x0 << 20) | (0x2 << 26))},
+    {0x70001038, (0x1 << 8), (0x1 << 8)},
+    {0x70001094, (0x3 << 2), (0x0 << 2)},
+    {0x700021D0, ((0x1 << 5) | (0x1 << 6)), ((0x1 << 5) | (0x1 << 6))},
+    {0x70001000, ((0x1 << 0) | (0x1 << 20) | (0x1 << 22)),
+                 ((0x0 << 0) | (0x1 << 20) | (0x0 << 22))},
+    {0x70001028, (0xf << 2), (0x1 << 2)},
+    #endif
+    //#endif /* CONFIG_PMIC_SETTING */
+    {0x00000000, 0x00000000, 0x00000000}, // last one
+};
+
+u32 syscfg_tbl_masked_8800dc_h[][3] = {
+    {0x7000216C, ((0x3 << 2) | (0x3 << 4)), ((0x2 << 2) | (0x2 << 4))}, // pmic_pmu_init
+    {0x70002138, (0xFF << 0), (0xFF << 0)},
+    {0x7000213C, (0xFF << 0), (0xFF << 0)},
+    {0x70002144, (0xFF << 0), (0xFF << 0)},
+    {0x700021BC, (0x3 << 2), (0x1 << 2)},
+    {0x70002118, ((0x7 << 4) | (0x1 << 7)), ((0x2 << 4) | (0x1 << 7))},
+    {0x70002104, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))},
+    {0x7000210C, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))},
+    {0x70002170, (0xF << 0), (0x1 << 0)},
+    {0x70002190, (0x3F << 0), (24 << 0)},
+    {0x700021CC, ((0x7 << 4) | (0x1 << 7)), ((0x0 << 4) | (0x0 << 7))},
+    {0x700010A0, (0x1 << 11), (0x1 << 11)},
+    //{0x70001034, ((0x1 << 20) | (0x7 << 26)), ((0x0 << 20) | (0x2 << 26))},
+    {0x70001038, (0x1 << 8), (0x1 << 8)},
+    {0x70001094, (0x3 << 2), (0x0 << 2)},
+    {0x700021D0, ((0x1 << 5) | (0x1 << 6)), ((0x1 << 5) | (0x1 << 6))},
+    #if defined(CONFIG_VRF_DCDC_MODE)
+    {0x70001000, ((0x1 << 0) | (0x1 << 20) | (0x1 << 22)),
+                 ((0x1 << 0) | (0x1 << 20) | (0x0 << 22))},
+    #else
+    {0x70001000, ((0x1 << 0) | (0x1 << 20) | (0x1 << 22)),
+                 ((0x0 << 0) | (0x1 << 20) | (0x0 << 22))},
+    #endif
+    {0x70001028, (0xf << 2), (0x1 << 2)},
+
+    {0x00000000, 0x00000000, 0x00000000}, // last one
+};
+
+u32 rf_tbl_masked[][3] = {
+    {0x40344058, 0x00800000, 0x00000000},// pll trx
+};
+
+static void system_config(struct rwnx_hw *rwnx_hw)
+{
+    int syscfg_num;
+    array3_tbl_t p_syscfg_msk_tbl;
+    int ret, cnt;
+    const u32 mem_addr = 0x40500000;
+    struct dbg_mem_read_cfm rd_mem_addr_cfm;
+    ret = rwnx_send_dbg_mem_read_req(rwnx_hw, mem_addr, &rd_mem_addr_cfm);
+    if (ret) {
+        printk("%x rd fail: %d\n", mem_addr, ret);
+        return;
+    }
+    chip_id = (u8)(rd_mem_addr_cfm.memdata >> 16);
+	btenable = (u8)((rd_mem_addr_cfm.memdata >> 26) & 0x1);
+	aicbsp_info.chip_rev = (u8)((rd_mem_addr_cfm.memdata >> 16) & 0x3F);
+	aicbsp_info.cpmode = (testmode > 0 ? 1 : 0);
+	printk("system_config btenable %x bsp cpmode %d  testmode %d \n", btenable, aicbsp_info.cpmode, testmode);
+	if(btenable){
+		printk(" now is using AIC8800DC \n");
+	}
+    //printk("%x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata);
+    if (((rd_mem_addr_cfm.memdata >> 25) & 0x01UL) == 0x00UL) {
+		chip_mcu_id = 1;
+    }
+    ret = rwnx_send_dbg_mem_read_req(rwnx_hw, 0x00000020, &rd_mem_addr_cfm);
+    if (ret) {
+        printk("[0x00000020] rd fail: %d\n", ret);
+        return;
+    }
+	
+    chip_sub_id = (u8)(rd_mem_addr_cfm.memdata);
+    //printk("%x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata);
+	printk("chip_id=%x, chip_sub_id=%x\n", chip_id, chip_sub_id);
+    if (IS_CHIP_ID_H()){
+		printk("IS_CHIP_ID_H\n");
+		aicbsp_firmware_list = fw_8800dc_h_u02;
+    }else{
+		if(aicbsp_info.chip_rev == CHIP_REV_U01){
+			aicbsp_firmware_list = fw_8800dc_u01;
+			printk("dc u01 \n");
+		}else{
+			aicbsp_firmware_list = fw_8800dc_u02;
+			printk("dc u02 \n");
+		}
+	}
+    ret = rwnx_send_dbg_mem_read_req(rwnx_hw, 0x40500010, &rd_mem_addr_cfm);
+	printk("[0x40500010]=%x\n", rd_mem_addr_cfm.memdata);
+    if (ret) {
+        printk("[0x40500010] rd fail: %d\n", ret);
+        return;
+    }
+
+    syscfg_num = sizeof(syscfg_tbl) / sizeof(u32) / 2;
+	cnt = 0;
+	if (rd_mem_addr_cfm.memdata == 0x6){
+		cnt =2;
+	}
+    for (cnt = cnt; cnt < syscfg_num; cnt++) {
+        if (syscfg_tbl[0][0] == 0x00000000) {
+            break;
+        }
+        ret = rwnx_send_dbg_mem_write_req(rwnx_hw, syscfg_tbl[cnt][0], syscfg_tbl[cnt][1]);
+        if (ret) {
+            printk("%x write fail: %d\n", syscfg_tbl[cnt][0], ret);
+            return;
+        }
+    }
+
+    if (chip_mcu_id == 0) {
+        if (chip_sub_id == 0) {
+            syscfg_num = sizeof(syscfg_tbl_8800dc_sdio_u01) / sizeof(u32) / 2;
+            for (cnt = 0; cnt < syscfg_num; cnt++) {
+                ret = rwnx_send_dbg_mem_write_req(rwnx_hw, syscfg_tbl_8800dc_sdio_u01[cnt][0], syscfg_tbl_8800dc_sdio_u01[cnt][1]);
+                if (ret) {
+                     printk("%x write fail: %d\n", syscfg_tbl_8800dc_sdio_u01[cnt][0], ret);
+                    return;
+                }
+            }
+        } else if (chip_sub_id == 1) {
+            syscfg_num = sizeof(syscfg_tbl_8800dc_sdio_u02) / sizeof(u32) / 2;
+            for (cnt = 0; cnt < syscfg_num; cnt++) {
+                ret = rwnx_send_dbg_mem_write_req(rwnx_hw, syscfg_tbl_8800dc_sdio_u02[cnt][0], syscfg_tbl_8800dc_sdio_u02[cnt][1]);
+                if (ret) {
+                    printk("%x write fail: %d\n", syscfg_tbl_8800dc_sdio_u02[cnt][0], ret);
+                    return;
+                }
+            }
+        }
+    }
+
+    if (IS_CHIP_ID_H()) {
+        syscfg_num = sizeof(syscfg_tbl_masked_8800dc_h) / sizeof(u32) / 3;
+        p_syscfg_msk_tbl = syscfg_tbl_masked_8800dc_h;
+    } else {
+        syscfg_num = sizeof(syscfg_tbl_masked_8800dc) / sizeof(u32) / 3;
+        p_syscfg_msk_tbl = syscfg_tbl_masked_8800dc;
+    }
+    for (cnt = 0; cnt < syscfg_num; cnt++) {
+        if (p_syscfg_msk_tbl[cnt][0] == 0x00000000) {
+            break;
+        } else if (p_syscfg_msk_tbl[cnt][0] == 0x70001000) {
+            if (chip_mcu_id == 0) {
+                p_syscfg_msk_tbl[cnt][1] |= ((0x1 << 8) | (0x1 << 15)); // mask
+                p_syscfg_msk_tbl[cnt][2] |= ((0x1 << 8) | (0x1 << 15));
+            }
+        }
+
+        ret = rwnx_send_dbg_mem_mask_write_req(rwnx_hw,
+            p_syscfg_msk_tbl[cnt][0], p_syscfg_msk_tbl[cnt][1], p_syscfg_msk_tbl[cnt][2]);
+        if (ret) {
+            printk("%x mask write fail: %d\n", p_syscfg_msk_tbl[cnt][0], ret);
+            return;
+        }
+    }
+
+}
+
+u32 aicbsp_syscfg_tbl_8800d80[][2] = {
+};
+
+int aicbsp_system_config_8800d80(struct rwnx_hw *rwnx_hw)
+{
+	int syscfg_num = sizeof(aicbsp_syscfg_tbl_8800d80) / sizeof(u32) / 2;
+	int ret, cnt;
+	for (cnt = 0; cnt < syscfg_num; cnt++) {
+		ret = rwnx_send_dbg_mem_write_req(rwnx_hw, aicbsp_syscfg_tbl_8800d80[cnt][0], aicbsp_syscfg_tbl_8800d80[cnt][1]);
+		if (ret) {
+			printk("%x write fail: %d\n", aicbsp_syscfg_tbl_8800d80[cnt][0], ret);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static void system_config_d80(struct rwnx_hw *rwnx_hw){
+	u32 mem_addr;
+	struct dbg_mem_read_cfm rd_mem_addr_cfm;
+	u32 btenable = 0;
+	u8 is_chip_id_h = 0;
+	int ret = 0;
+
+	mem_addr = 0x40500000;
+
+	if (rwnx_send_dbg_mem_read_req(rwnx_hw, mem_addr, &rd_mem_addr_cfm))
+		return -1;
+
+	aicbsp_info.chip_rev = (u8)(rd_mem_addr_cfm.memdata >> 16);
+	btenable = 1;
+	aicbsp_info.cpmode = (testmode > 0 ? 1 : 0);
+	
+	printk("%s chip_rev %u cpmode %u \n",__func__,aicbsp_info.chip_rev,aicbsp_info.cpmode);
+	if (aicbsp_info.chip_rev == CHIP_REV_U01)
+        aicbsp_firmware_list = fw_8800d80_u01;
+    if (aicbsp_info.chip_rev == CHIP_REV_U02 || aicbsp_info.chip_rev == CHIP_REV_U03)
+        aicbsp_firmware_list = fw_8800d80_u02;
+    if (aicbsp_system_config_8800d80(rwnx_hw))
+        return -1;
+
+	
+}
+
+extern int aicwf_dpd_result_apply_8800dc(struct rwnx_hw *rwnx_hw, rf_misc_ram_lite_t *dpd_res);
+extern int aicwf_dpd_result_load_8800dc(struct rwnx_hw *rwnx_hw, rf_misc_ram_lite_t *dpd_res);
+
+int rf_config(struct rwnx_hw *rwnx_hw)
+{
+	int ret =0;
+	struct mm_set_rf_calib_cfm cfm;
+    if ((ret = rwnx_send_txpwr_lvl_req(rwnx_hw))) {
+        return -1;
+    }
+
+    if ((ret = rwnx_send_txpwr_ofst_req(rwnx_hw))) {
+        return -1;
+    }
+
+	if (testmode == 0) {
+		if (IS_CHIP_ID_H()) {
+			if ((ret = rwnx_send_rf_config_req(rwnx_hw, 0,    1, (u8_l *)wifi_txgain_table_24g_8800dcdw_h, 128)))
+				return -1;
+
+			if ((ret = rwnx_send_rf_config_req(rwnx_hw, 16,    1, (u8_l *)wifi_txgain_table_24g_1_8800dcdw_h, 128)))
+				return -1;
+		} else {
+			if ((ret = rwnx_send_rf_config_req(rwnx_hw, 0,	1, (u8_l *)wifi_txgain_table_24g_8800dcdw, 128)))
+				return -1;
+
+			if ((ret = rwnx_send_rf_config_req(rwnx_hw, 16,	1, (u8_l *)wifi_txgain_table_24g_1_8800dcdw, 128)))
+				return -1;
+		}
+		if ((ret = rwnx_send_rf_config_req(rwnx_hw, 0,	0, (u8_l *)wifi_rxgain_table_24g_20m_8800dcdw, 256)))
+			return -1;
+
+		if ((ret = rwnx_send_rf_config_req(rwnx_hw, 32,  0, (u8_l *)wifi_rxgain_table_24g_40m_8800dcdw, 256)))
+			return -1;
+
+		if ((ret = rwnx_send_rf_calib_req(rwnx_hw, &cfm))) {
+			return -1;
+		}
+
+	}else if (testmode == 1) {
+		if (chip_sub_id >= 1) {
+#ifdef CONFIG_DPD
+#ifndef CONFIG_FORCE_DPD_CALIB
+            if (is_file_exist(FW_DPDRESULT_NAME_8800DC) == 1) {
+                printk("%s load dpd bin\n", __func__);
+                ret = aicwf_dpd_result_load_8800dc(rwnx_hw, &dpd_res);
+                if (ret) {
+                    printk("load dpd bin fail: %d\n", ret);
+                    return ret;
+                }
+            }
+#endif
+            if (dpd_res.bit_mask[1]) {
+                ret = aicwf_dpd_result_apply_8800dc(rwnx_hw, &dpd_res);
+                if (ret) {
+                    printk("apply dpd bin fail: %d\n", ret);
+                    return ret;
+                }
+            }
+#else
+            {
+                ret = aicwf_misc_ram_init_8800dc(rwnx_hw);
+                if (ret) {
+                    printk("misc ram init fail: %d\n", ret);
+                    return ret;
+                }
+            }
+#endif
+	   		ret = rwnx_send_rf_calib_req(rwnx_hw, &cfm);
+			if (ret) {
+				printk("rf calib req fail: %d\n", ret);
+		   		return -1;
+	   		}
+	   }
+	}
+
+	return 0;
+}
+
+int rf_config_d80(struct rwnx_hw *rwnx_hw){
+	int ret = 0;
+	struct mm_set_rf_calib_cfm cfm;
+
+	if ((ret = rwnx_send_txpwr_lvl_v3_req(rwnx_hw))) {
+		return -1;
+	}
+
+	if ((ret = rwnx_send_txpwr_lvl_adj_req(rwnx_hw))) {
+		return -1;
+	}
+
+	if ((ret = rwnx_send_txpwr_ofst2x_req(rwnx_hw))) {
+		return -1;
+	}
+
+    if ((ret = rwnx_send_rf_calib_req(rwnx_hw, &cfm))) {
+		return -1;
+	}
+
+	return 0 ;
+}
+
+static int start_from_bootrom(struct rwnx_hw *rwnx_hw)
+{
+    int ret = 0;
+
+	/* memory access */
+#ifdef CONFIG_ROM_PATCH_EN
+	const u32 rd_addr = RAM_LMAC_FW_ADDR;
+	const u32 fw_addr = RAM_LMAC_FW_ADDR;
+#else
+	const u32 rd_addr = RAM_FMAC_FW_ADDR;
+	const u32 fw_addr = RAM_FMAC_FW_ADDR;
+#endif
+    u32 boot_type;
+	struct dbg_mem_read_cfm rd_cfm;
+	printk("Read FW mem: %08x\n", rd_addr);
+	if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, rd_addr, &rd_cfm))) {
+		return -1;
+	}
+	printk("cfm: [%08x] = %08x\n", rd_cfm.memaddr, rd_cfm.memdata);
+
+    if (testmode == 0) {
+        boot_type = HOST_START_APP_DUMMY;
+    } else {
+        boot_type = HOST_START_APP_AUTO;
+    }
+	/* fw start */
+	printk("Start app: %08x, %d\n", fw_addr, boot_type);
+	if ((ret = rwnx_send_dbg_start_app_req(rwnx_hw, fw_addr, boot_type))) {
+		return -1;
+	}
+	return 0;
+}
+
+static int start_from_bootrom_d80(struct rwnx_hw *rwnx_hw)
+{
+	int ret = 0;
+
+	/* memory access */
+	const u32 fw_addr = RAM_FMAC_FW_ADDR;
+	//struct dbg_start_app_cfm start_app_cfm;
+
+	/* fw start */
+	ret = rwnx_send_dbg_start_app_req(rwnx_hw, fw_addr, HOST_START_APP_AUTO);
+	if (ret) {
+		return -1;
+	}
+	//aicbsp_info.hwinfo_r = start_app_cfm.bootstatus & 0xFF;
+
+	return 0;
+}
+
+/**
+ *
+ */
+int rwnx_cfg80211_init(struct rwnx_plat *rwnx_plat, void **platform_data)
+{
+    struct rwnx_hw *rwnx_hw;
+    struct rwnx_conf_file init_conf[3];
+    int ret = 0;
+    struct wiphy *wiphy;
+    struct rwnx_vif *vif;
+    int i;
+    u8 dflt_mac[ETH_ALEN] = { 0x88, 0x18, 0x33, 0x22, 0x32, 0x44};
+    struct mm_set_rf_calib_cfm cfm;
+	struct mm_get_fw_version_cfm fw_version;
+    u8_l mac_addr_efuse[ETH_ALEN];
+    struct mm_set_stack_start_cfm set_start_cfm;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* create a new wiphy for use with cfg80211 */
+    wiphy = wiphy_new(&rwnx_cfg80211_ops, sizeof(struct rwnx_hw));
+
+    if (!wiphy) {
+        dev_err(rwnx_platform_get_dev(rwnx_plat), "Failed to create new wiphy\n");
+        ret = -ENOMEM;
+        goto err_out;
+    }
+
+    rwnx_hw = wiphy_priv(wiphy);
+    rwnx_hw->wiphy = wiphy;
+    rwnx_hw->plat = rwnx_plat;
+    rwnx_hw->dev = rwnx_platform_get_dev(rwnx_plat);
+#ifdef AICWF_SDIO_SUPPORT
+    rwnx_hw->sdiodev = rwnx_plat->sdiodev;
+    rwnx_plat->sdiodev->rwnx_hw = rwnx_hw;
+    rwnx_hw->cmd_mgr = &rwnx_plat->sdiodev->cmd_mgr;
+#else
+    rwnx_hw->usbdev = rwnx_plat->usbdev;
+    rwnx_plat->usbdev->rwnx_hw = rwnx_hw;
+    rwnx_hw->cmd_mgr = &rwnx_plat->usbdev->cmd_mgr;
+#endif
+    rwnx_hw->mod_params = &rwnx_mod_params;
+    rwnx_hw->tcp_pacing_shift = 7;
+
+    rwnx_init_aic(rwnx_hw);
+    /* set device pointer for wiphy */
+    set_wiphy_dev(wiphy, rwnx_hw->dev);
+
+    /* Create cache to allocate sw_txhdr */
+    rwnx_hw->sw_txhdr_cache = KMEM_CACHE(rwnx_sw_txhdr, 0);
+    if (!rwnx_hw->sw_txhdr_cache) {
+        wiphy_err(wiphy, "Cannot allocate cache for sw TX header\n");
+        ret = -ENOMEM;
+        goto err_cache;
+    }
+#ifdef CONFIG_FILTER_TCP_ACK
+	tcp_ack_init(rwnx_hw);
+#endif
+
+//#if 1
+	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW
+	 || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+		if ((ret = rwnx_parse_configfile(rwnx_hw, AIC_MACCONFIG_NAME, &init_conf[0], "MAC_A1="))) {
+	        printk("read macconfig fail, use random macaddr\n");
+	        get_random_bytes(&dflt_mac[4], 2);
+	        memcpy(init_conf[0].mac_addr, dflt_mac, ETH_ALEN);
+	    }
+	    if ((ret = rwnx_parse_configfile(rwnx_hw, AIC_MACCONFIG_NAME, &init_conf[1], "MAC_A2="))) {
+	        printk("read macconfig fail, use random macaddr\n");
+	        get_random_bytes(&dflt_mac[4], 2);
+	        memcpy(init_conf[1].mac_addr, dflt_mac, ETH_ALEN);
+	    }
+	    if ((ret = rwnx_parse_configfile(rwnx_hw, AIC_MACCONFIG_NAME, &init_conf[2], "MAC_A3="))) {
+	        printk("read macconfig fail, use random macaddr\n");
+	        get_random_bytes(&dflt_mac[4], 2);
+	        memcpy(init_conf[2].mac_addr, dflt_mac, ETH_ALEN);
+	    }
+	}
+//#else
+	// if(wifi_mac_addr[0] != 0) 
+	// 	memcpy(init_conf[0].mac_addr, wifi_mac_addr, ETH_ALEN);
+	// else
+    // 	memcpy(init_conf[0].mac_addr, dflt_mac, ETH_ALEN);
+//#endif
+
+    rwnx_hw->vif_started = 0;
+    rwnx_hw->monitor_vif = RWNX_INVALID_VIF;
+    rwnx_hw->adding_sta = false;
+
+    rwnx_hw->scan_ie.addr = NULL;
+
+    for (i = 0; i < NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX; i++)
+        rwnx_hw->avail_idx_map |= BIT(i);
+
+    rwnx_hwq_init(rwnx_hw);
+
+    for (i = 0; i < NX_NB_TXQ; i++) {
+        rwnx_hw->txq[i].idx = TXQ_INACTIVE;
+    }
+
+    rwnx_mu_group_init(rwnx_hw);
+
+    /* Initialize RoC element pointer to NULL, indicate that RoC can be started */
+    rwnx_hw->roc_elem = NULL;
+    /* Cookie can not be 0 */
+    rwnx_hw->roc_cookie_cnt = 1;
+
+	INIT_LIST_HEAD(&rwnx_hw->vifs);
+	INIT_LIST_HEAD(&rwnx_hw->defrag_list);
+	spin_lock_init(&rwnx_hw->defrag_lock);
+	mutex_init(&rwnx_hw->mutex);
+	mutex_init(&rwnx_hw->dbgdump_elem.mutex);
+	spin_lock_init(&rwnx_hw->tx_lock);
+	spin_lock_init(&rwnx_hw->cb_lock);
+
+    INIT_WORK(&rwnx_hw->apmStalossWork, apm_staloss_work_process);
+    rwnx_hw->apmStaloss_wq = create_singlethread_workqueue("apmStaloss_wq");
+    if (!rwnx_hw->apmStaloss_wq) {
+        txrx_err("insufficient memory to create apmStaloss workqueue.\n");
+        goto err_cache;
+    }
+
+#ifdef AICWF_USB_SUPPORT
+    usb_config(rwnx_hw);
+#endif
+
+	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
+    	system_config(rwnx_hw);
+	}else if(rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+		system_config_d80(rwnx_hw);
+	}
+
+    if ((ret = rwnx_platform_on(rwnx_hw, NULL)))
+        goto err_platon;
+#if 0//!defined(CONFIG_DPD)
+	aicwf_misc_ram_init_8800dc(rwnx_hw);
+#endif
+
+	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
+    	patch_config(rwnx_hw);
+	}
+
+#if defined(CONFIG_START_FROM_BOOTROM)
+	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
+    	if ((ret = start_from_bootrom(rwnx_hw)))
+			goto err_lmac_reqs;
+	}else if(rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+		if ((ret = start_from_bootrom_d80(rwnx_hw)))
+			goto err_lmac_reqs;
+	}
+#endif
+
+	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
+				rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
+		if (testmode == 0){
+		    func_flag = false;
+		    aicwf_sdio_release_func2(rwnx_hw->sdiodev);
+		}
+		ret = rwnx_send_set_stack_start_req(rwnx_hw, 1, 0, 0, 0, &set_start_cfm);
+		set_start_cfm.is_5g_support = false;
+	} else {
+		ret = rwnx_send_set_stack_start_req(rwnx_hw, 1, 0, CO_BIT(5), 0, &set_start_cfm);
+	}
+
+	if (ret)
+		goto err_lmac_reqs;
+
+	printk("is 5g support = %d, vendor_info = 0x%02X\n", set_start_cfm.is_5g_support, set_start_cfm.vendor_info);
+	rwnx_hw->band_5g_support = set_start_cfm.is_5g_support;
+	ret = rwnx_send_get_fw_version_req(rwnx_hw, &fw_version);
+	memcpy(wiphy->fw_version, fw_version.fw_version, fw_version.fw_version_len>32? 32 : fw_version.fw_version_len);
+	printk("Firmware Version: %s\r\n", fw_version.fw_version);
+
+    wiphy->mgmt_stypes = rwnx_default_mgmt_stypes;
+
+    wiphy->bands[NL80211_BAND_2GHZ] = &rwnx_band_2GHz;
+
+	#ifdef USE_5G
+	if (rwnx_hw->band_5g_support)
+    	wiphy->bands[NL80211_BAND_5GHZ] = &rwnx_band_5GHz;
+	#endif
+
+    wiphy->interface_modes =
+    BIT(NL80211_IFTYPE_STATION)     |
+    BIT(NL80211_IFTYPE_AP)          |
+    BIT(NL80211_IFTYPE_AP_VLAN)     |
+    BIT(NL80211_IFTYPE_P2P_CLIENT)  |
+    BIT(NL80211_IFTYPE_P2P_GO)      |
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
+    BIT(NL80211_IFTYPE_P2P_DEVICE)  |
+    #endif
+    BIT(NL80211_IFTYPE_MONITOR);
+
+    wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+        #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))
+        WIPHY_FLAG_HAS_CHANNEL_SWITCH |
+        #endif
+        WIPHY_FLAG_4ADDR_STATION |
+        WIPHY_FLAG_4ADDR_AP;
+
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+    wiphy->max_num_csa_counters = BCN_MAX_CSA_CPT;
+    #endif
+
+    wiphy->max_remain_on_channel_duration = rwnx_hw->mod_params->roc_dur_max;
+
+    wiphy->features |= NL80211_FEATURE_NEED_OBSS_SCAN |
+        NL80211_FEATURE_SK_TX_STATUS |
+        NL80211_FEATURE_VIF_TXPOWER |
+        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+        NL80211_FEATURE_ACTIVE_MONITOR |
+        #endif
+        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+        NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
+        #endif
+        0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_WPA3_FOR_OLD_KERNEL)
+    wiphy->features |= NL80211_FEATURE_SAE;
+#endif
+
+    if (rwnx_mod_params.tdls)
+        /* TDLS support */
+        wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+
+    wiphy->iface_combinations   = rwnx_combinations;
+    /* -1 not to include combination with radar detection, will be re-added in
+       rwnx_handle_dynparams if supported */
+    wiphy->n_iface_combinations = ARRAY_SIZE(rwnx_combinations) - 1;
+    wiphy->reg_notifier = rwnx_reg_notifier;
+
+    wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+    wiphy->cipher_suites = cipher_suites;
+    wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites) - NB_RESERVED_CIPHER;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
+	wiphy->support_mbssid = 1;
+#endif
+    rwnx_hw->ext_capa[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
+    rwnx_hw->ext_capa[2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT;
+#endif
+    rwnx_hw->ext_capa[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+    wiphy->extended_capabilities = rwnx_hw->ext_capa;
+    wiphy->extended_capabilities_mask = rwnx_hw->ext_capa;
+    wiphy->extended_capabilities_len = ARRAY_SIZE(rwnx_hw->ext_capa);
+#endif
+    tasklet_init(&rwnx_hw->task, rwnx_task, (unsigned long)rwnx_hw);
+
+	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
+		if (ret = rf_config(rwnx_hw))
+			goto err_lmac_reqs;
+	}else if(rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+		if (ret = rf_config_d80(rwnx_hw))
+			goto err_lmac_reqs;
+	}
+
+    if ((ret = rwnx_send_get_macaddr_req(rwnx_hw, (struct mm_get_mac_addr_cfm *)mac_addr_efuse)))
+        goto err_lmac_reqs;
+    if (mac_addr_efuse[0] | mac_addr_efuse[1] | mac_addr_efuse[2] | mac_addr_efuse[3])
+    {
+		if(rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+			printk("not use mac_addr_efuse\n");
+		}
+		else {
+        	memcpy(init_conf[0].mac_addr, mac_addr_efuse, ETH_ALEN);
+		}
+    }
+
+    printk("get macaddr:%x,%x\r\n", mac_addr_efuse[0], mac_addr_efuse[5]);
+
+  memcpy(wiphy->perm_addr, init_conf[0].mac_addr, ETH_ALEN);
+	
+	/* Reset FW */
+	ret = rwnx_send_reset(rwnx_hw);
+	if (ret)
+		goto err_lmac_reqs;
+	ret = rwnx_send_version_req(rwnx_hw, &rwnx_hw->version_cfm);
+	if (ret)
+		goto err_lmac_reqs;
+	rwnx_set_vers(rwnx_hw);
+
+	ret = rwnx_handle_dynparams(rwnx_hw, rwnx_hw->wiphy);
+	if (ret)
+		goto err_lmac_reqs;
+
+    rwnx_enable_mesh(rwnx_hw);
+    rwnx_radar_detection_init(&rwnx_hw->radar);
+
+    if (testmode == 0) {
+        /* Set parameters to firmware */
+        rwnx_send_me_config_req(rwnx_hw);
+    }
+
+    /* Only monitor mode supported when custom channels are enabled */
+    if (rwnx_mod_params.custchan) {
+        rwnx_limits[0].types = BIT(NL80211_IFTYPE_MONITOR);
+        rwnx_limits_dfs[0].types = BIT(NL80211_IFTYPE_MONITOR);
+    }
+
+	ret = wiphy_register(wiphy);
+	if (ret) {
+        wiphy_err(wiphy, "Could not register wiphy device\n");
+        goto err_register_wiphy;
+    }
+
+    /* Update regulatory (if needed) and set channel parameters to firmware
+       (must be done after WiPHY registration) */
+    rwnx_custregd(rwnx_hw, wiphy);
+    if (testmode == 0) {
+        rwnx_send_me_chan_config_req(rwnx_hw);
+        #ifdef CONFIG_COEX
+        rwnx_send_coex_req(rwnx_hw, 0, 1);
+        #endif
+    }
+    *platform_data = rwnx_hw;
+
+#ifdef CONFIG_DEBUG_FS_AIC
+    if ((ret = rwnx_dbgfs_register(rwnx_hw, "rwnx"))) {
+        wiphy_err(wiphy, "Failed to register debugfs entries");
+        goto err_debugfs;
+    }
+#endif
+
+    rtnl_lock();
+
+    /* Add an initial station interface */
+    vif = rwnx_interface_add(rwnx_hw, "wlan%d", NET_NAME_UNKNOWN,
+                            NL80211_IFTYPE_STATION, NULL, init_conf[0]);
+    vif = rwnx_interface_add(rwnx_hw, "wlan%d-vxd", NET_NAME_UNKNOWN,
+                            NL80211_IFTYPE_STATION, NULL, init_conf[1]);
+    //vif = rwnx_interface_add(rwnx_hw, "wlan%d-va1", NET_NAME_UNKNOWN,
+    //                        NL80211_IFTYPE_STATION, NULL, init_conf[2]);
+
+    rtnl_unlock();
+
+    if (!vif) {
+        wiphy_err(wiphy, "Failed to instantiate a network device\n");
+        ret = -ENOMEM;
+        goto err_add_interface;
+    }
+
+    wiphy_info(wiphy, "New interface create %s", vif->ndev->name);
+
+    return 0;
+
+err_add_interface:
+#ifdef CONFIG_DEBUG_FS_AIC
+    rwnx_dbgfs_unregister(rwnx_hw);
+err_debugfs:
+#endif
+    wiphy_unregister(rwnx_hw->wiphy);
+err_register_wiphy:
+err_lmac_reqs:
+	printk("err_lmac_reqs\n");
+    //rwnx_fw_trace_dump(rwnx_hw);
+    rwnx_platform_off(rwnx_hw, NULL);
+err_platon:
+err_config:
+    kmem_cache_destroy(rwnx_hw->sw_txhdr_cache);
+err_cache:
+    wiphy_free(wiphy);
+err_out:
+    return ret;
+}
+
+/**
+ *
+ */
+void rwnx_cfg80211_deinit(struct rwnx_hw *rwnx_hw)
+{
+	struct mm_set_stack_start_cfm set_start_cfm;
+	struct defrag_ctrl_info *defrag_ctrl = NULL;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef AICWF_USB_SUPPORT
+    if (rwnx_hw->usbdev->bus_if->state != BUS_DOWN_ST)
+#else
+    if (rwnx_hw->sdiodev->bus_if->state != BUS_DOWN_ST)
+#endif
+    {
+        rwnx_send_set_stack_start_req(rwnx_hw, 0, 0, 0, 0, &set_start_cfm);
+    }
+	
+	spin_lock_bh(&rwnx_hw->defrag_lock);
+	if (!list_empty(&rwnx_hw->defrag_list)) {
+		list_for_each_entry(defrag_ctrl, &rwnx_hw->defrag_list, list) {
+			list_del_init(&defrag_ctrl->list);
+			if (timer_pending(&defrag_ctrl->defrag_timer))
+				del_timer_sync(&defrag_ctrl->defrag_timer);
+			dev_kfree_skb(defrag_ctrl->skb);
+			kfree(defrag_ctrl);
+		}
+	}
+	spin_unlock_bh(&rwnx_hw->defrag_lock);
+#ifdef CONFIG_DEBUG_FS_AIC
+    rwnx_dbgfs_unregister(rwnx_hw);
+#endif
+    flush_workqueue(rwnx_hw->apmStaloss_wq);
+    destroy_workqueue(rwnx_hw->apmStaloss_wq);
+    rwnx_wdev_unregister(rwnx_hw);
+    if(rwnx_hw->wiphy && rwnx_hw->wiphy->registered){
+        printk("%s wiphy_unregister \r\n", __func__);
+        wiphy_unregister(rwnx_hw->wiphy);
+    }
+    rwnx_radar_detection_deinit(&rwnx_hw->radar);
+    rwnx_platform_off(rwnx_hw, NULL);
+    kmem_cache_destroy(rwnx_hw->sw_txhdr_cache);
+#ifdef CONFIG_FILTER_TCP_ACK
+	tcp_ack_deinit(rwnx_hw);
+#endif
+    //wiphy_free(rwnx_hw->wiphy);
+}
+
+static void aicsmac_driver_register(void)
+{
+#ifdef AICWF_SDIO_SUPPORT
+    aicwf_sdio_register();
+#endif
+#ifdef AICWF_USB_SUPPORT
+    aicwf_usb_register();
+#endif
+#ifdef AICWF_PCIE_SUPPORT
+    aicwf_pcie_register();
+#endif
+}
+
+//static DECLARE_WORK(aicsmac_driver_work, aicsmac_driver_register);
+
+struct completion hostif_register_done;
+static int rwnx_driver_err = -1;
+
+#define REGISTRATION_TIMEOUT                     9000
+
+void aicwf_hostif_ready(void)
+{
+	rwnx_driver_err = 0;
+	complete(&hostif_register_done);
+}
+
+void aicwf_hostif_fail(void)
+{
+	rwnx_driver_err = 1;
+	complete(&hostif_register_done);
+}
+
+int rwnx_mod_init(void)
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+    rwnx_print_version();
+
+	init_completion(&hostif_register_done);
+
+	aicsmac_driver_register();
+
+	if ((wait_for_completion_timeout(&hostif_register_done, msecs_to_jiffies(REGISTRATION_TIMEOUT)) == 0) || rwnx_driver_err) {
+		printk("register_driver timeout or error\n");
+#ifdef AICWF_SDIO_SUPPORT
+        aicwf_sdio_exit();
+#endif /* AICWF_SDIO_SUPPORT */
+#ifdef AICWF_USB_SUPPORT
+       aicwf_usb_exit();
+#endif /*AICWF_USB_SUPPORT */
+        return -ENODEV;
+    }
+
+#ifdef AICWF_PCIE_SUPPORT
+    return rwnx_platform_register_drv();
+#else
+    return 0;
+#endif
+}
+
+/**
+ *
+ */
+void rwnx_mod_exit(void)
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef AICWF_PCIE_SUPPORT
+    rwnx_platform_unregister_drv();
+#endif
+
+#ifdef AICWF_SDIO_SUPPORT
+    aicwf_sdio_exit();
+#endif
+
+#ifdef AICWF_USB_SUPPORT
+    aicwf_usb_exit();
+#endif
+}
+
+
+//module_param_array(wifi_mac_addr,byte, NULL, 0);
+//MODULE_PARM_DESC(wifi_mac_addr, "Configures mac addr.");
+
+//module_init(rwnx_mod_init);
+//module_exit(rwnx_mod_exit);
+
+module_param(testmode, int, 0660);
+
+MODULE_FIRMWARE(RWNX_CONFIG_FW_NAME);
+
+MODULE_DESCRIPTION(RW_DRV_DESCRIPTION);
+MODULE_VERSION(RWNX_VERS_MOD);
+MODULE_AUTHOR(RW_DRV_COPYRIGHT " " RW_DRV_AUTHOR);
+MODULE_LICENSE("GPL");
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_main.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_main.h
new file mode 100755
index 0000000..7a8f166
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_main.h
@@ -0,0 +1,38 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_main.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_MAIN_H_
+#define _RWNX_MAIN_H_
+
+#include "rwnx_defs.h"
+
+typedef struct _android_wifi_priv_cmd {
+    char *buf;
+    int used_len;
+    int total_len;
+} android_wifi_priv_cmd;
+
+#ifdef CONFIG_COMPAT
+typedef struct _compat_android_wifi_priv_cmd {
+    compat_caddr_t buf;
+    int used_len;
+    int total_len;
+} compat_android_wifi_priv_cmd;
+#endif /* CONFIG_COMPAT */
+
+extern int testmode;
+typedef u32 (*array3_tbl_t)[3];
+#define CHIP_ID_H_MASK  0xC0
+#define IS_CHIP_ID_H()  ((chip_id & CHIP_ID_H_MASK) == CHIP_ID_H_MASK)
+
+int rwnx_cfg80211_init(struct rwnx_plat *rwnx_plat, void **platform_data);
+void rwnx_cfg80211_deinit(struct rwnx_hw *rwnx_hw);
+
+#endif /* _RWNX_MAIN_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mesh.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mesh.c
new file mode 100755
index 0000000..791abab
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mesh.c
@@ -0,0 +1,42 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_mesh.c
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ****************************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+
+#include "rwnx_mesh.h"
+
+/**
+ * FUNCTION DEFINITIONS
+ ****************************************************************************************
+ */
+
+struct rwnx_mesh_proxy *rwnx_get_mesh_proxy_info(struct rwnx_vif *p_rwnx_vif, u8 *p_sta_addr, bool local)
+{
+    struct rwnx_mesh_proxy *p_mesh_proxy = NULL;
+    struct rwnx_mesh_proxy *p_cur_proxy;
+
+    /* Look for proxied devices with provided address */
+    list_for_each_entry(p_cur_proxy, &p_rwnx_vif->ap.proxy_list, list) {
+        if (p_cur_proxy->local != local) {
+            continue;
+        }
+
+        if (!memcmp(&p_cur_proxy->ext_sta_addr, p_sta_addr, ETH_ALEN)) {
+            p_mesh_proxy = p_cur_proxy;
+            break;
+        }
+    }
+
+    /* Return the found information */
+    return p_mesh_proxy;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mesh.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mesh.h
new file mode 100755
index 0000000..db8950f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mesh.h
@@ -0,0 +1,45 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_mesh.h
+ *
+ * @brief VHT Beamformer function declarations
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_MESH_H_
+#define _RWNX_MESH_H_
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+
+#include "rwnx_defs.h"
+
+/**
+ * DEFINES
+ ****************************************************************************************
+ */
+
+/**
+ * TYPE DEFINITIONS
+ ****************************************************************************************
+ */
+
+/**
+ * FUNCTION DECLARATIONS
+ ****************************************************************************************
+ */
+
+/**
+ ****************************************************************************************
+ * @brief TODO [LT]
+ ****************************************************************************************
+ */
+struct rwnx_mesh_proxy *rwnx_get_mesh_proxy_info(struct rwnx_vif *p_rwnx_vif, u8 *p_sta_addr, bool local);
+
+#endif /* _RWNX_MESH_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mod_params.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mod_params.c
new file mode 100755
index 0000000..7d6644f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mod_params.c
@@ -0,0 +1,1742 @@
+/**
+******************************************************************************
+*
+* @file rwnx_mod_params.c
+*
+* @brief Set configuration according to modules parameters
+*
+* Copyright (C) RivieraWaves 2012-2019
+*
+******************************************************************************
+*/
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+
+#include "rwnx_defs.h"
+#include "rwnx_tx.h"
+#include "hal_desc.h"
+#include "rwnx_cfgfile.h"
+#include "rwnx_dini.h"
+#include "reg_access.h"
+#include "rwnx_compat.h"
+
+#ifdef CONFIG_RWNX_FULLMAC
+#define COMMON_PARAM(name, default_softmac, default_fullmac)    \
+    .name = default_fullmac,
+#define SOFTMAC_PARAM(name, default)
+#define FULLMAC_PARAM(name, default) .name = default,
+#endif /* CONFIG_RWNX_FULLMAC */
+
+struct rwnx_mod_params rwnx_mod_params = {
+	/* common parameters */
+	COMMON_PARAM(ht_on, true, true)
+	COMMON_PARAM(vht_on, true, true)
+	COMMON_PARAM(he_on, true, true)
+	COMMON_PARAM(mcs_map, IEEE80211_VHT_MCS_SUPPORT_0_9, IEEE80211_VHT_MCS_SUPPORT_0_9)
+	COMMON_PARAM(he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_11, IEEE80211_HE_MCS_SUPPORT_0_11)
+	COMMON_PARAM(he_ul_on, false, false)
+	COMMON_PARAM(ldpc_on, true, true)
+	COMMON_PARAM(stbc_on, true, true)
+	COMMON_PARAM(gf_rx_on, false, false)
+	COMMON_PARAM(phy_cfg, 2, 2)
+	COMMON_PARAM(uapsd_timeout, 300, 300)
+	COMMON_PARAM(ap_uapsd_on, true, true)
+	COMMON_PARAM(sgi, true, true)
+	COMMON_PARAM(sgi80, false, false)
+	COMMON_PARAM(use_2040, 1, 1)
+	COMMON_PARAM(nss, 1, 1)
+	COMMON_PARAM(amsdu_rx_max, 2, 2)
+	COMMON_PARAM(bfmee, true, true)
+	COMMON_PARAM(bfmer, false, false)
+	COMMON_PARAM(mesh, true, true)
+	COMMON_PARAM(murx, true, true)
+	COMMON_PARAM(mutx, true, true)
+	COMMON_PARAM(mutx_on, true, true)
+	COMMON_PARAM(use_80, false, false)
+	COMMON_PARAM(custregd, true, true)
+	COMMON_PARAM(custchan, false, false)
+	COMMON_PARAM(roc_dur_max, 500, 500)
+	COMMON_PARAM(listen_itv, 0, 0)
+	COMMON_PARAM(listen_bcmc, true, true)
+	COMMON_PARAM(lp_clk_ppm, 20, 20)
+	COMMON_PARAM(ps_on, true, true)
+	COMMON_PARAM(tx_lft, RWNX_TX_LIFETIME_MS, RWNX_TX_LIFETIME_MS)
+	COMMON_PARAM(amsdu_maxnb, NX_TX_PAYLOAD_MAX, NX_TX_PAYLOAD_MAX)
+	// By default, only enable UAPSD for Voice queue (see IEEE80211_DEFAULT_UAPSD_QUEUE comment)
+	COMMON_PARAM(uapsd_queues, IEEE80211_WMM_IE_STA_QOSINFO_AC_VO, IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+	COMMON_PARAM(tdls, false, false)
+	COMMON_PARAM(uf, false, false)
+	COMMON_PARAM(auto_reply, false, false)
+	COMMON_PARAM(ftl, "", "")
+	COMMON_PARAM(dpsm, false, false)
+
+    /* SOFTMAC only parameters */
+    SOFTMAC_PARAM(mfp_on, false)
+    SOFTMAC_PARAM(gf_on, false)
+    SOFTMAC_PARAM(bwsig_on, true)
+    SOFTMAC_PARAM(dynbw_on, true)
+    SOFTMAC_PARAM(agg_tx, true)
+    SOFTMAC_PARAM(amsdu_force, 2)
+    SOFTMAC_PARAM(rc_probes_on, false)
+    SOFTMAC_PARAM(cmon, true)
+    SOFTMAC_PARAM(hwscan, true)
+    SOFTMAC_PARAM(autobcn, true)
+    SOFTMAC_PARAM(dpsm, true)
+
+    /* FULLMAC only parameters */
+    FULLMAC_PARAM(ant_div, true)
+};
+
+#ifdef CONFIG_RWNX_FULLMAC
+/* FULLMAC specific parameters*/
+module_param_named(ant_div, rwnx_mod_params.ant_div, bool, S_IRUGO);
+MODULE_PARM_DESC(ant_div, "Enable Antenna Diversity (Default: 1)");
+#endif /* CONFIG_RWNX_FULLMAC */
+
+module_param_named(ht_on, rwnx_mod_params.ht_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ht_on, "Enable HT (Default: 1)");
+
+module_param_named(vht_on, rwnx_mod_params.vht_on, bool, S_IRUGO);
+MODULE_PARM_DESC(vht_on, "Enable VHT (Default: 1)");
+
+module_param_named(he_on, rwnx_mod_params.he_on, bool, S_IRUGO);
+MODULE_PARM_DESC(he_on, "Enable HE (Default: 1)");
+
+module_param_named(mcs_map, rwnx_mod_params.mcs_map, int, S_IRUGO);
+MODULE_PARM_DESC(mcs_map,  "VHT MCS map value  0: MCS0_7, 1: MCS0_8, 2: MCS0_9"
+                 " (Default: 2)");
+
+module_param_named(he_mcs_map, rwnx_mod_params.he_mcs_map, int, S_IRUGO);
+MODULE_PARM_DESC(he_mcs_map,  "HE MCS map value  0: MCS0_7, 1: MCS0_9, 2: MCS0_11"
+                 " (Default: 2)");
+
+module_param_named(he_ul_on, rwnx_mod_params.he_ul_on, bool, S_IRUGO);
+MODULE_PARM_DESC(he_ul_on, "Enable HE OFDMA UL (Default: 0)");
+
+module_param_named(amsdu_maxnb, rwnx_mod_params.amsdu_maxnb, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(amsdu_maxnb, "Maximum number of MSDUs inside an A-MSDU in TX: (Default: NX_TX_PAYLOAD_MAX)");
+
+module_param_named(ps_on, rwnx_mod_params.ps_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ps_on, "Enable PowerSaving (Default: 1-Enabled)");
+
+module_param_named(tx_lft, rwnx_mod_params.tx_lft, int, 0644);
+MODULE_PARM_DESC(tx_lft, "Tx lifetime (ms) - setting it to 0 disables retries "
+                 "(Default: "__stringify(RWNX_TX_LIFETIME_MS)")");
+
+module_param_named(ldpc_on, rwnx_mod_params.ldpc_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ldpc_on, "Enable LDPC (Default: 1)");
+
+module_param_named(stbc_on, rwnx_mod_params.stbc_on, bool, S_IRUGO);
+MODULE_PARM_DESC(stbc_on, "Enable STBC in RX (Default: 1)");
+
+module_param_named(gf_rx_on, rwnx_mod_params.gf_rx_on, bool, S_IRUGO);
+MODULE_PARM_DESC(gf_rx_on, "Enable HT greenfield in reception (Default: 1)");
+
+module_param_named(phycfg, rwnx_mod_params.phy_cfg, int, S_IRUGO);
+MODULE_PARM_DESC(phycfg,
+                 "0 <= phycfg <= 5 : RF Channel Conf (Default: 2(C0-A1-B2))");
+
+module_param_named(uapsd_timeout, rwnx_mod_params.uapsd_timeout, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uapsd_timeout,
+                 "UAPSD Timer timeout, in ms (Default: 300). If 0, UAPSD is disabled");
+
+module_param_named(uapsd_queues, rwnx_mod_params.uapsd_queues, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uapsd_queues, "UAPSD Queues, integer value, must be seen as a bitfield\n"
+                 "        Bit 0 = VO\n"
+                 "        Bit 1 = VI\n"
+                 "        Bit 2 = BK\n"
+                 "        Bit 3 = BE\n"
+                 "     -> uapsd_queues=7 will enable uapsd for VO, VI and BK queues");
+
+module_param_named(ap_uapsd_on, rwnx_mod_params.ap_uapsd_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ap_uapsd_on, "Enable UAPSD in AP mode (Default: 1)");
+
+module_param_named(sgi, rwnx_mod_params.sgi, bool, S_IRUGO);
+MODULE_PARM_DESC(sgi, "Advertise Short Guard Interval support (Default: 1)");
+
+module_param_named(sgi80, rwnx_mod_params.sgi80, bool, S_IRUGO);
+MODULE_PARM_DESC(sgi80, "Advertise Short Guard Interval support for 80MHz (Default: 1)");
+
+module_param_named(use_2040, rwnx_mod_params.use_2040, bool, S_IRUGO);
+MODULE_PARM_DESC(use_2040, "Use tweaked 20-40MHz mode (Default: 1)");
+
+module_param_named(use_80, rwnx_mod_params.use_80, bool, S_IRUGO);
+MODULE_PARM_DESC(use_80, "Enable 80MHz (Default: 1)");
+
+module_param_named(custregd, rwnx_mod_params.custregd, bool, S_IRUGO);
+MODULE_PARM_DESC(custregd,
+                 "Use permissive custom regulatory rules (for testing ONLY) (Default: 0)");
+
+module_param_named(custchan, rwnx_mod_params.custchan, bool, S_IRUGO);
+MODULE_PARM_DESC(custchan,
+                 "Extend channel set to non-standard channels (for testing ONLY) (Default: 0)");
+
+module_param_named(nss, rwnx_mod_params.nss, int, S_IRUGO);
+MODULE_PARM_DESC(nss, "1 <= nss <= 2 : Supported number of Spatial Streams (Default: 1)");
+
+module_param_named(amsdu_rx_max, rwnx_mod_params.amsdu_rx_max, int, S_IRUGO);
+MODULE_PARM_DESC(amsdu_rx_max, "0 <= amsdu_rx_max <= 2 : Maximum A-MSDU size supported in RX\n"
+                 "        0: 3895 bytes\n"
+                 "        1: 7991 bytes\n"
+                 "        2: 11454 bytes\n"
+                 "        This value might be reduced according to the FW capabilities.\n"
+                 "        Default: 2");
+
+module_param_named(bfmee, rwnx_mod_params.bfmee, bool, S_IRUGO);
+MODULE_PARM_DESC(bfmee, "Enable Beamformee Capability (Default: 1-Enabled)");
+
+module_param_named(bfmer, rwnx_mod_params.bfmer, bool, S_IRUGO);
+MODULE_PARM_DESC(bfmer, "Enable Beamformer Capability (Default: 0-Disabled)");
+
+module_param_named(mesh, rwnx_mod_params.mesh, bool, S_IRUGO);
+MODULE_PARM_DESC(mesh, "Enable Meshing Capability (Default: 1-Enabled)");
+
+module_param_named(murx, rwnx_mod_params.murx, bool, S_IRUGO);
+MODULE_PARM_DESC(murx, "Enable MU-MIMO RX Capability (Default: 1-Enabled)");
+
+module_param_named(mutx, rwnx_mod_params.mutx, bool, S_IRUGO);
+MODULE_PARM_DESC(mutx, "Enable MU-MIMO TX Capability (Default: 1-Enabled)");
+
+module_param_named(mutx_on, rwnx_mod_params.mutx_on, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(mutx_on, "Enable MU-MIMO transmissions (Default: 1-Enabled)");
+
+module_param_named(roc_dur_max, rwnx_mod_params.roc_dur_max, int, S_IRUGO);
+MODULE_PARM_DESC(roc_dur_max, "Maximum Remain on Channel duration");
+
+module_param_named(listen_itv, rwnx_mod_params.listen_itv, int, S_IRUGO);
+MODULE_PARM_DESC(listen_itv, "Maximum listen interval");
+
+module_param_named(listen_bcmc, rwnx_mod_params.listen_bcmc, bool, S_IRUGO);
+MODULE_PARM_DESC(listen_bcmc, "Wait for BC/MC traffic following DTIM beacon");
+
+module_param_named(lp_clk_ppm, rwnx_mod_params.lp_clk_ppm, int, S_IRUGO);
+MODULE_PARM_DESC(lp_clk_ppm, "Low Power Clock accuracy of the local device");
+
+module_param_named(tdls, rwnx_mod_params.tdls, bool, S_IRUGO);
+MODULE_PARM_DESC(tdls, "Enable TDLS (Default: 1-Enabled)");
+
+module_param_named(uf, rwnx_mod_params.uf, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uf, "Enable Unsupported HT Frame Logging (Default: 0-Disabled)");
+
+module_param_named(auto_reply, rwnx_mod_params.auto_reply, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(auto_reply, "Enable Monitor MacAddr Auto-Reply (Default: 0-Disabled)");
+
+module_param_named(ftl, rwnx_mod_params.ftl, charp, S_IRUGO);
+MODULE_PARM_DESC(ftl, "Firmware trace level  (Default: \"\")");
+
+module_param_named(dpsm, rwnx_mod_params.dpsm, bool, S_IRUGO);
+MODULE_PARM_DESC(dpsm, "Enable Dynamic PowerSaving (Default: 1-Enabled)");
+
+#ifdef DEFAULT_COUNTRY_CODE
+char default_ccode[4] = DEFAULT_COUNTRY_CODE;
+#else
+char default_ccode[4] = "00";
+#endif
+
+char country_code[4];
+module_param_string(country_code, country_code, 4, 0600);
+
+#if 0
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+/* Regulatory rules */
+static struct ieee80211_regdomain rwnx_regdom = {
+    .n_reg_rules = 2,
+    .alpha2 = "99",
+    .reg_rules = {
+        REG_RULE(2390 - 10, 2510 + 10, 40, 0, 1000, 0),
+        REG_RULE(5150 - 10, 5970 + 10, 80, 0, 1000, 0),
+    }
+};
+#endif
+#endif
+
+static const int mcs_map_to_rate[4][3] = {
+    [PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_7] = 65,
+    [PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_8] = 78,
+    [PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_9] = 78,
+    [PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_7] = 135,
+    [PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_8] = 162,
+    [PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_9] = 180,
+    [PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_7] = 292,
+    [PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_8] = 351,
+    [PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_9] = 390,
+    [PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_7] = 585,
+    [PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_8] = 702,
+    [PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_9] = 780,
+};
+
+#define MAX_VHT_RATE(map, nss, bw) (mcs_map_to_rate[bw][map] * (nss))
+
+extern struct ieee80211_regdomain *reg_regdb1[];
+
+char ccode_channels[200];
+int index_for_channel_list = 0;
+module_param_string(ccode_channels, ccode_channels, 200, 0600);
+
+void rwnx_get_countrycode_channels(struct wiphy *wiphy,
+		struct ieee80211_regdomain *regdomain){
+	enum nl80211_band band;
+	struct ieee80211_supported_band *sband;
+	int channel_index;
+	int rule_index;
+	int band_num = 0;
+	int rule_num = regdomain->n_reg_rules;
+	int start_freq = 0;
+	int end_freq = 0;
+	int center_freq = 0;
+	char channel[4];
+
+	band_num = NUM_NL80211_BANDS;
+
+	memset(ccode_channels, 0, 200);
+	index_for_channel_list = 0;
+
+	for (band = 0; band < band_num; band++) {
+		sband = wiphy->bands[band];// bands: 0:2.4G 1:5G 2:60G
+		if (!sband)
+			continue;
+
+		for (channel_index = 0; channel_index < sband->n_channels; channel_index++) {
+			for(rule_index = 0; rule_index < rule_num; rule_index++){
+				start_freq = regdomain->reg_rules[rule_index].freq_range.start_freq_khz/1000;
+				end_freq = regdomain->reg_rules[rule_index].freq_range.end_freq_khz/1000;
+				center_freq = sband->channels[channel_index].center_freq;
+				if(center_freq >= start_freq && center_freq <= end_freq){
+					sprintf(channel, "%d",ieee80211_frequency_to_channel(center_freq));
+					memcpy(ccode_channels + index_for_channel_list, channel, strlen(channel));
+					index_for_channel_list += strlen(channel);
+					memcpy(ccode_channels + index_for_channel_list, " ", 1);
+					index_for_channel_list += 1;
+					break;
+				}
+			}
+		}
+	}
+	printk("%s support channel:%s\r\n", __func__, ccode_channels);
+}
+
+
+struct ieee80211_regdomain *getRegdomainFromRwnxDBIndex(struct wiphy *wiphy,
+	int index)
+{
+	u8 idx;
+
+	idx = index;
+
+	memset(country_code, 0, 4);
+	country_code[0] = reg_regdb1[idx]->alpha2[0];
+	country_code[1] = reg_regdb1[idx]->alpha2[1];
+
+	printk("%s set ccode:%s \r\n", __func__, country_code);
+
+	rwnx_get_countrycode_channels(wiphy, reg_regdb1[idx]);
+
+	return reg_regdb1[idx];
+}
+
+
+struct ieee80211_regdomain *getRegdomainFromRwnxDB(struct wiphy *wiphy,
+	char *alpha2)
+{
+	u8 idx;
+
+	memset(country_code, 0, 4);
+
+	printk("%s set ccode:%s \r\n", __func__, alpha2);
+
+	idx = 0;
+
+	while (reg_regdb1[idx]){
+		if((reg_regdb1[idx]->alpha2[0] == alpha2[0]) &&
+			(reg_regdb1[idx]->alpha2[1] == alpha2[1])){
+			memcpy(country_code, alpha2, 2);
+			rwnx_get_countrycode_channels(wiphy, reg_regdb1[idx]);
+			return reg_regdb1[idx];
+		}
+		idx++;
+	}
+
+	printk("%s(): Error, wrong country = %s\n",
+			__func__, alpha2);
+	printk("Set as default 00\n");
+
+	memcpy(country_code, default_ccode, sizeof(default_ccode));
+	rwnx_get_countrycode_channels(wiphy, reg_regdb1[0]);
+
+	return reg_regdb1[0];
+}
+
+
+/**
+ * Do some sanity check
+ *
+ */
+#if 0
+static int rwnx_check_fw_hw_feature(struct rwnx_hw *rwnx_hw,
+                                    struct wiphy *wiphy)
+{
+    u32_l sys_feat = rwnx_hw->version_cfm.features;
+    u32_l mac_feat = rwnx_hw->version_cfm.version_machw_1;
+    u32_l phy_feat = rwnx_hw->version_cfm.version_phy_1;
+    u32_l phy_vers = rwnx_hw->version_cfm.version_phy_2;
+    u16_l max_sta_nb = rwnx_hw->version_cfm.max_sta_nb;
+    u8_l max_vif_nb = rwnx_hw->version_cfm.max_vif_nb;
+    int bw, res = 0;
+    int amsdu_rx;
+
+    if (!rwnx_hw->mod_params->custregd)
+        rwnx_hw->mod_params->custchan = false;
+
+    if (rwnx_hw->mod_params->custchan) {
+        rwnx_hw->mod_params->mesh = false;
+        rwnx_hw->mod_params->tdls = false;
+    }
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+    if (!(sys_feat & BIT(MM_FEAT_UMAC_BIT))) {
+        wiphy_err(wiphy,
+                  "Loading softmac firmware with fullmac driver\n");
+        res = -1;
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_ANT_DIV_BIT))) {
+        rwnx_hw->mod_params->ant_div = false;
+    }
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    if (!(sys_feat & BIT(MM_FEAT_VHT_BIT))) {
+        rwnx_hw->mod_params->vht_on = false;
+    }
+
+    // Check if HE is supported
+    if (!(sys_feat & BIT(MM_FEAT_HE_BIT))) {
+        rwnx_hw->mod_params->he_on = false;
+        rwnx_hw->mod_params->he_ul_on = false;
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_PS_BIT))) {
+        rwnx_hw->mod_params->ps_on = false;
+    }
+
+    /* AMSDU (non)support implies different shared structure definition
+       so insure that fw and drv have consistent compilation option */
+    if (sys_feat & BIT(MM_FEAT_AMSDU_BIT)) {
+#ifndef CONFIG_RWNX_SPLIT_TX_BUF
+        wiphy_err(wiphy,
+                  "AMSDU enabled in firmware but support not compiled in driver\n");
+        res = -1;
+#else
+        if (rwnx_hw->mod_params->amsdu_maxnb > NX_TX_PAYLOAD_MAX)
+            rwnx_hw->mod_params->amsdu_maxnb = NX_TX_PAYLOAD_MAX;
+#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
+    } else {
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+        wiphy_err(wiphy,
+                  "AMSDU disabled in firmware but support compiled in driver\n");
+        res = -1;
+#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_UAPSD_BIT))) {
+        rwnx_hw->mod_params->uapsd_timeout = 0;
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_BFMEE_BIT))) {
+        rwnx_hw->mod_params->bfmee = false;
+    }
+
+    if ((sys_feat & BIT(MM_FEAT_BFMER_BIT))) {
+#ifndef CONFIG_RWNX_BFMER
+        wiphy_err(wiphy,
+                  "BFMER enabled in firmware but support not compiled in driver\n");
+        res = -1;
+#endif /* CONFIG_RWNX_BFMER */
+        // Check PHY and MAC HW BFMER support and update parameter accordingly
+        if (!(phy_feat & MDM_BFMER_BIT) || !(mac_feat & NXMAC_BFMER_BIT)) {
+            rwnx_hw->mod_params->bfmer = false;
+            // Disable the feature in the bitfield so that it won't be displayed
+            sys_feat &= ~BIT(MM_FEAT_BFMER_BIT);
+        }
+    } else {
+#ifdef CONFIG_RWNX_BFMER
+        wiphy_err(wiphy,
+                  "BFMER disabled in firmware but support compiled in driver\n");
+        res = -1;
+#else
+        rwnx_hw->mod_params->bfmer = false;
+#endif /* CONFIG_RWNX_BFMER */
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_MESH_BIT))) {
+        rwnx_hw->mod_params->mesh = false;
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_TDLS_BIT))) {
+        rwnx_hw->mod_params->tdls = false;
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_UF_BIT))) {
+        rwnx_hw->mod_params->uf = false;
+    }
+
+#ifdef CONFIG_RWNX_FULLMAC
+    if ((sys_feat & BIT(MM_FEAT_MON_DATA_BIT))) {
+#ifndef CONFIG_RWNX_MON_DATA
+        wiphy_err(wiphy,
+                  "Monitor+Data interface support (MON_DATA) is enabled in firmware but support not compiled in driver\n");
+        res = -1;
+#endif /* CONFIG_RWNX_MON_DATA */
+    } else {
+#ifdef CONFIG_RWNX_MON_DATA
+        wiphy_err(wiphy,
+                  "Monitor+Data interface support (MON_DATA) disabled in firmware but support compiled in driver\n");
+        res = -1;
+#endif /* CONFIG_RWNX_MON_DATA */
+    }
+#endif
+
+    // Check supported AMSDU RX size
+    amsdu_rx = (sys_feat >> MM_AMSDU_MAX_SIZE_BIT0) & 0x03;
+    if (amsdu_rx < rwnx_hw->mod_params->amsdu_rx_max) {
+        rwnx_hw->mod_params->amsdu_rx_max = amsdu_rx;
+    }
+
+    // Check supported BW
+    bw = (phy_feat & MDM_CHBW_MASK) >> MDM_CHBW_LSB;
+    // Check if 80MHz BW is supported
+    if (bw < 2) {
+        rwnx_hw->mod_params->use_80 = false;
+    }
+    // Check if 40MHz BW is supported
+    if (bw < 1)
+        rwnx_hw->mod_params->use_2040 = false;
+
+    // 80MHz BW shall be disabled if 40MHz is not enabled
+    if (!rwnx_hw->mod_params->use_2040)
+        rwnx_hw->mod_params->use_80 = false;
+
+    // Check if HT is supposed to be supported. If not, disable VHT/HE too
+    if (!rwnx_hw->mod_params->ht_on)
+    {
+        rwnx_hw->mod_params->vht_on = false;
+        rwnx_hw->mod_params->he_on = false;
+        rwnx_hw->mod_params->he_ul_on = false;
+        rwnx_hw->mod_params->use_80 = false;
+        rwnx_hw->mod_params->use_2040 = false;
+    }
+
+    // LDPC is mandatory for HE40 and above, so if LDPC is not supported, then disable
+    // HE to use HT/VHT only
+    if (rwnx_hw->mod_params->use_2040 && !rwnx_hw->mod_params->ldpc_on)
+    {
+        rwnx_hw->mod_params->he_on = false;
+        rwnx_hw->mod_params->he_ul_on = false;
+    }
+
+    // HT greenfield is not supported in modem >= 3.0
+    if (__MDM_MAJOR_VERSION(phy_vers) > 0) {
+        rwnx_hw->mod_params->gf_rx_on = false;
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_MU_MIMO_RX_BIT)) ||
+        !rwnx_hw->mod_params->bfmee) {
+        rwnx_hw->mod_params->murx = false;
+    }
+
+    if ((sys_feat & BIT(MM_FEAT_MU_MIMO_TX_BIT))) {
+#ifndef CONFIG_RWNX_MUMIMO_TX
+        wiphy_err(wiphy,
+                  "MU-MIMO TX enabled in firmware but support not compiled in driver\n");
+        res = -1;
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+        if (!rwnx_hw->mod_params->bfmer)
+            rwnx_hw->mod_params->mutx = false;
+        // Check PHY and MAC HW MU-MIMO TX support and update parameter accordingly
+        else if (!(phy_feat & MDM_MUMIMOTX_BIT) || !(mac_feat & NXMAC_MU_MIMO_TX_BIT)) {
+                rwnx_hw->mod_params->mutx = false;
+                // Disable the feature in the bitfield so that it won't be displayed
+                sys_feat &= ~BIT(MM_FEAT_MU_MIMO_TX_BIT);
+        }
+    } else {
+#ifdef CONFIG_RWNX_MUMIMO_TX
+        wiphy_err(wiphy,
+                  "MU-MIMO TX disabled in firmware but support compiled in driver\n");
+        res = -1;
+#else
+        rwnx_hw->mod_params->mutx = false;
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+    }
+
+    if (sys_feat & BIT(MM_FEAT_WAPI_BIT)) {
+        rwnx_enable_wapi(rwnx_hw);
+    }
+
+#ifdef CONFIG_RWNX_FULLMAC
+    if (sys_feat & BIT(MM_FEAT_MFP_BIT)) {
+        rwnx_enable_mfp(rwnx_hw);
+    }
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+#define QUEUE_NAME "Broadcast/Multicast queue "
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    if (sys_feat & BIT(MM_FEAT_BCN_BIT)) {
+#if NX_TXQ_CNT == 4
+        wiphy_err(wiphy, QUEUE_NAME
+                  "enabled in firmware but support not compiled in driver\n");
+        res = -1;
+#endif /* NX_TXQ_CNT == 4 */
+    } else {
+#if NX_TXQ_CNT == 5
+        wiphy_err(wiphy, QUEUE_NAME
+                  "disabled in firmware but support compiled in driver\n");
+        res = -1;
+#endif /* NX_TXQ_CNT == 5 */
+    }
+#undef QUEUE_NAME
+
+#ifdef CONFIG_RWNX_RADAR
+    if (sys_feat & BIT(MM_FEAT_RADAR_BIT)) {
+        /* Enable combination with radar detection */
+        wiphy->n_iface_combinations++;
+    }
+#endif /* CONFIG_RWNX_RADAR */
+
+#ifndef CONFIG_RWNX_SDM
+    switch (__MDM_PHYCFG_FROM_VERS(phy_feat)) {
+        case MDM_PHY_CONFIG_TRIDENT:
+        case MDM_PHY_CONFIG_ELMA:
+            rwnx_hw->mod_params->nss = 1;
+            break;
+        case MDM_PHY_CONFIG_KARST:
+            {
+                int nss_supp = (phy_feat & MDM_NSS_MASK) >> MDM_NSS_LSB;
+                if (rwnx_hw->mod_params->nss > nss_supp)
+                    rwnx_hw->mod_params->nss = nss_supp;
+            }
+            break;
+        default:
+            WARN_ON(1);
+            break;
+    }
+#endif /* CONFIG_RWNX_SDM */
+
+    if (rwnx_hw->mod_params->nss < 1 || rwnx_hw->mod_params->nss > 2)
+        rwnx_hw->mod_params->nss = 1;
+
+    if (rwnx_hw->mod_params->phy_cfg < 0 || rwnx_hw->mod_params->phy_cfg > 5)
+        rwnx_hw->mod_params->phy_cfg = 2;
+
+    if (rwnx_hw->mod_params->mcs_map < 0 || rwnx_hw->mod_params->mcs_map > 2)
+        rwnx_hw->mod_params->mcs_map = 0;
+
+    wiphy_info(wiphy, "PHY features: [NSS=%d][CHBW=%d]%s%s\n",
+               rwnx_hw->mod_params->nss,
+               20 * (1 << ((phy_feat & MDM_CHBW_MASK) >> MDM_CHBW_LSB)),
+               rwnx_hw->mod_params->ldpc_on ? "[LDPC]" : "",
+               rwnx_hw->mod_params->he_on ? "[HE]" : "");
+
+#define PRINT_RWNX_FEAT(feat)                                   \
+    (sys_feat & BIT(MM_FEAT_##feat##_BIT) ? "["#feat"]" : "")
+
+    wiphy_info(wiphy, "FW features: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+               PRINT_RWNX_FEAT(BCN),
+               PRINT_RWNX_FEAT(AUTOBCN),
+               PRINT_RWNX_FEAT(HWSCAN),
+               PRINT_RWNX_FEAT(CMON),
+               PRINT_RWNX_FEAT(MROLE),
+               PRINT_RWNX_FEAT(RADAR),
+               PRINT_RWNX_FEAT(PS),
+               PRINT_RWNX_FEAT(UAPSD),
+               PRINT_RWNX_FEAT(DPSM),
+               PRINT_RWNX_FEAT(AMPDU),
+               PRINT_RWNX_FEAT(AMSDU),
+               PRINT_RWNX_FEAT(CHNL_CTXT),
+               PRINT_RWNX_FEAT(REORD),
+               PRINT_RWNX_FEAT(P2P),
+               PRINT_RWNX_FEAT(P2P_GO),
+               PRINT_RWNX_FEAT(UMAC),
+               PRINT_RWNX_FEAT(VHT),
+               PRINT_RWNX_FEAT(HE),
+               PRINT_RWNX_FEAT(BFMEE),
+               PRINT_RWNX_FEAT(BFMER),
+               PRINT_RWNX_FEAT(WAPI),
+               PRINT_RWNX_FEAT(MFP),
+               PRINT_RWNX_FEAT(MU_MIMO_RX),
+               PRINT_RWNX_FEAT(MU_MIMO_TX),
+               PRINT_RWNX_FEAT(MESH),
+               PRINT_RWNX_FEAT(TDLS),
+               PRINT_RWNX_FEAT(ANT_DIV));
+#undef PRINT_RWNX_FEAT
+
+    if(max_sta_nb != NX_REMOTE_STA_MAX)
+    {
+        wiphy_err(wiphy, "Different number of supported stations between driver and FW (%d != %d)\n",
+                  NX_REMOTE_STA_MAX, max_sta_nb);
+        res = -1;
+    }
+
+    if(max_vif_nb != NX_VIRT_DEV_MAX)
+    {
+        wiphy_err(wiphy, "Different number of supported virtual interfaces between driver and FW (%d != %d)\n",
+                  NX_VIRT_DEV_MAX, max_vif_nb);
+        res = -1;
+    }
+
+    return res;
+}
+#endif
+
+
+static void rwnx_set_vht_capa(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+#ifdef CONFIG_VHT_FOR_OLD_KERNEL
+	#ifdef USE_5G
+    struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+    #endif
+    struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+
+    int i;
+    int nss = rwnx_hw->mod_params->nss;
+    int mcs_map;
+    int mcs_map_max;
+    int bw_max;
+
+    if (!rwnx_hw->mod_params->vht_on) {
+        return;
+    }
+
+	rwnx_hw->vht_cap_2G.vht_supported = true;
+		if (rwnx_hw->mod_params->sgi80)
+			rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+		if (rwnx_hw->mod_params->stbc_on)
+			rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+		if (rwnx_hw->mod_params->ldpc_on)
+			rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_RXLDPC;
+		if (rwnx_hw->mod_params->bfmee) {
+			rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+			rwnx_hw->vht_cap_2G.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+        #else
+			rwnx_hw->vht_cap_2G.cap |= 3 << 13;
+        #endif
+		}
+		if (nss > 1)
+			rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_TXSTBC;
+
+		// Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
+		rwnx_hw->vht_cap_2G.cap |= rwnx_hw->mod_params->amsdu_rx_max;
+
+		if (rwnx_hw->mod_params->bfmer) {
+			rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+			/* Set number of sounding dimensions */
+        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+			rwnx_hw->vht_cap_2G.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+        #else
+			rwnx_hw->vht_cap_2G.cap |= (nss - 1) << 16;
+        #endif
+		}
+		if (rwnx_hw->mod_params->murx)
+			rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+		if (rwnx_hw->mod_params->mutx)
+			rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+		/*
+		 * MCS map:
+		 * This capabilities are filled according to the mcs_map module parameter.
+		 * However currently we have some limitations due to FPGA clock constraints
+		 * that prevent always using the range of MCS that is defined by the
+		 * parameter:
+		 *	 - in RX, 2SS, we support up to MCS7
+		 *	 - in TX, 2SS, we support up to MCS8
+		 */
+		// Get max supported BW
+		if (rwnx_hw->mod_params->use_80)
+			bw_max = PHY_CHNL_BW_80;
+		else if (rwnx_hw->mod_params->use_2040)
+			bw_max = PHY_CHNL_BW_40;
+		else
+			bw_max = PHY_CHNL_BW_20;
+
+		// Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
+		// MCS9 is not supported in 1 and 2 SS
+		if (rwnx_hw->mod_params->use_2040)
+			mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
+		else
+			mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
+
+		mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+		rwnx_hw->vht_cap_2G.vht_mcs.rx_mcs_map = cpu_to_le16(0);
+		for (i = 0; i < nss; i++) {
+			rwnx_hw->vht_cap_2G.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+			rwnx_hw->vht_cap_2G.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+			mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
+		}
+		for (; i < 8; i++) {
+			rwnx_hw->vht_cap_2G.vht_mcs.rx_mcs_map |= cpu_to_le16(
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+		}
+
+		mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+		rwnx_hw->vht_cap_2G.vht_mcs.tx_mcs_map = cpu_to_le16(0);
+		for (i = 0; i < nss; i++) {
+			rwnx_hw->vht_cap_2G.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+			rwnx_hw->vht_cap_2G.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+			mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+							IEEE80211_VHT_MCS_SUPPORT_0_8);
+		}
+		for (; i < 8; i++) {
+			rwnx_hw->vht_cap_2G.vht_mcs.tx_mcs_map |= cpu_to_le16(
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+		}
+
+		if (!rwnx_hw->mod_params->use_80) {
+#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
+			rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+			rwnx_hw->vht_cap_2G.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
+		}
+
+			rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+		printk("%s, vht_capa_info=0x%x\n", __func__, rwnx_hw->vht_cap_2G.cap);
+#ifdef USE_5G
+	if (rwnx_hw->band_5g_support) {
+	    rwnx_hw->vht_cap_5G.vht_supported = true;
+	    if (rwnx_hw->mod_params->sgi80)
+	        rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+	    if (rwnx_hw->mod_params->stbc_on)
+	        rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+	    if (rwnx_hw->mod_params->ldpc_on)
+	        rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_RXLDPC;
+	    if (rwnx_hw->mod_params->bfmee) {
+	        rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+	        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+	        rwnx_hw->vht_cap_5G.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+	        #else
+	        rwnx_hw->vht_cap_5G.cap |= 3 << 13;
+	        #endif
+	    }
+	    if (nss > 1)
+	        rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_TXSTBC;
+
+	    // Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
+	    rwnx_hw->vht_cap_5G.cap |= rwnx_hw->mod_params->amsdu_rx_max;
+
+	    if (rwnx_hw->mod_params->bfmer) {
+	        rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+	        /* Set number of sounding dimensions */
+	        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+	        rwnx_hw->vht_cap_5G.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+	        #else
+	        rwnx_hw->vht_cap_5G.cap |= (nss - 1) << 16;
+	        #endif
+	    }
+	    if (rwnx_hw->mod_params->murx)
+	        rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+	    if (rwnx_hw->mod_params->mutx)
+	        rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+	    /*
+	     * MCS map:
+	     * This capabilities are filled according to the mcs_map module parameter.
+	     * However currently we have some limitations due to FPGA clock constraints
+	     * that prevent always using the range of MCS that is defined by the
+	     * parameter:
+	     *   - in RX, 2SS, we support up to MCS7
+	     *   - in TX, 2SS, we support up to MCS8
+	     */
+	    // Get max supported BW
+	    if (rwnx_hw->mod_params->use_80)
+	        bw_max = PHY_CHNL_BW_80;
+	    else if (rwnx_hw->mod_params->use_2040)
+	        bw_max = PHY_CHNL_BW_40;
+	    else
+	        bw_max = PHY_CHNL_BW_20;
+
+	    // Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
+	    // MCS9 is not supported in 1 and 2 SS
+	    if (rwnx_hw->mod_params->use_2040)
+	        mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
+	    else
+	        mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
+
+	    mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+	    rwnx_hw->vht_cap_5G.vht_mcs.rx_mcs_map = cpu_to_le16(0);
+	    for (i = 0; i < nss; i++) {
+	        rwnx_hw->vht_cap_5G.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+	        rwnx_hw->vht_cap_5G.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+	        mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
+	    }
+	    for (; i < 8; i++) {
+	        rwnx_hw->vht_cap_5G.vht_mcs.rx_mcs_map |= cpu_to_le16(
+	            IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+	    }
+
+	    mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+	    rwnx_hw->vht_cap_5G.vht_mcs.tx_mcs_map = cpu_to_le16(0);
+	    for (i = 0; i < nss; i++) {
+	        rwnx_hw->vht_cap_5G.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+	        rwnx_hw->vht_cap_5G.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+	        mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+	                        IEEE80211_VHT_MCS_SUPPORT_0_8);
+	    }
+	    for (; i < 8; i++) {
+	        rwnx_hw->vht_cap_5G.vht_mcs.tx_mcs_map |= cpu_to_le16(
+	            IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+	    }
+
+	    if (!rwnx_hw->mod_params->use_80) {
+#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
+	        rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+	        rwnx_hw->vht_cap_5G.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
+	    }
+
+	} 
+	printk("%s, 5vht_capa_info=0x%x\n", __func__, rwnx_hw->vht_cap_5G.cap);
+#endif
+	printk("%s, 2vht_capa_info=0x%x\n", __func__, rwnx_hw->vht_cap_2G.cap);
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+    #ifdef USE_5G
+    struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+    #endif
+    struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+
+    int i;
+    int nss = rwnx_hw->mod_params->nss;
+    int mcs_map;
+    int mcs_map_max;
+    int bw_max;
+
+    if (!rwnx_hw->mod_params->vht_on) {
+        return;
+    }
+
+	band_2GHz->vht_cap.vht_supported = true;
+		if (rwnx_hw->mod_params->sgi80)
+			band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+		if (rwnx_hw->mod_params->stbc_on)
+			band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+		if (rwnx_hw->mod_params->ldpc_on)
+			band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC;
+		if (rwnx_hw->mod_params->bfmee) {
+			band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+			band_2GHz->vht_cap.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+        #else
+			band_2GHz->vht_cap.cap |= 3 << 13;
+        #endif
+		}
+		if (nss > 1)
+			band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_TXSTBC;
+
+		// Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
+		band_2GHz->vht_cap.cap |= rwnx_hw->mod_params->amsdu_rx_max;
+
+		if (rwnx_hw->mod_params->bfmer) {
+			band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+			/* Set number of sounding dimensions */
+        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+			band_2GHz->vht_cap.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+        #else
+			band_2GHz->vht_cap.cap |= (nss - 1) << 16;
+        #endif
+		}
+		if (rwnx_hw->mod_params->murx)
+			band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+		if (rwnx_hw->mod_params->mutx)
+			band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+		/*
+		 * MCS map:
+		 * This capabilities are filled according to the mcs_map module parameter.
+		 * However currently we have some limitations due to FPGA clock constraints
+		 * that prevent always using the range of MCS that is defined by the
+		 * parameter:
+		 *	 - in RX, 2SS, we support up to MCS7
+		 *	 - in TX, 2SS, we support up to MCS8
+		 */
+		// Get max supported BW
+		if (rwnx_hw->mod_params->use_80)
+			bw_max = PHY_CHNL_BW_80;
+		else if (rwnx_hw->mod_params->use_2040)
+			bw_max = PHY_CHNL_BW_40;
+		else
+			bw_max = PHY_CHNL_BW_20;
+
+		// Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
+		// MCS9 is not supported in 1 and 2 SS
+		if (rwnx_hw->mod_params->use_2040)
+			mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
+		else
+			mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
+
+		mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+		band_2GHz->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0);
+		for (i = 0; i < nss; i++) {
+			band_2GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+			band_2GHz->vht_cap.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+			mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
+		}
+		for (; i < 8; i++) {
+			band_2GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+		}
+
+		mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+		band_2GHz->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0);
+		for (i = 0; i < nss; i++) {
+			band_2GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+			band_2GHz->vht_cap.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+			mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+							IEEE80211_VHT_MCS_SUPPORT_0_8);
+		}
+		for (; i < 8; i++) {
+			band_2GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(
+				IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+		}
+
+		if (!rwnx_hw->mod_params->use_80) {
+#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
+			band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+			band_2GHz->vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
+		}
+
+
+#ifdef USE_5G
+	if (rwnx_hw->band_5g_support) {
+	    band_5GHz->vht_cap.vht_supported = true;
+	    if (rwnx_hw->mod_params->sgi80)
+	        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+	    if (rwnx_hw->mod_params->stbc_on)
+	        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+	    if (rwnx_hw->mod_params->ldpc_on)
+	        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC;
+	    if (rwnx_hw->mod_params->bfmee) {
+	        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+	        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+	        band_5GHz->vht_cap.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+	        #else
+	        band_5GHz->vht_cap.cap |= 3 << 13;
+	        #endif
+	    }
+	    if (nss > 1)
+	        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_TXSTBC;
+
+	    // Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
+	    band_5GHz->vht_cap.cap |= rwnx_hw->mod_params->amsdu_rx_max;
+
+	    if (rwnx_hw->mod_params->bfmer) {
+	        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+	        /* Set number of sounding dimensions */
+	        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+	        band_5GHz->vht_cap.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+	        #else
+	        band_5GHz->vht_cap.cap |= (nss - 1) << 16;
+	        #endif
+	    }
+	    if (rwnx_hw->mod_params->murx)
+	        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+	    if (rwnx_hw->mod_params->mutx)
+	        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+	    /*
+	     * MCS map:
+	     * This capabilities are filled according to the mcs_map module parameter.
+	     * However currently we have some limitations due to FPGA clock constraints
+	     * that prevent always using the range of MCS that is defined by the
+	     * parameter:
+	     *   - in RX, 2SS, we support up to MCS7
+	     *   - in TX, 2SS, we support up to MCS8
+	     */
+	    // Get max supported BW
+	    if (rwnx_hw->mod_params->use_80)
+	        bw_max = PHY_CHNL_BW_80;
+	    else if (rwnx_hw->mod_params->use_2040)
+	        bw_max = PHY_CHNL_BW_40;
+	    else
+	        bw_max = PHY_CHNL_BW_20;
+
+	    // Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
+	    // MCS9 is not supported in 1 and 2 SS
+	    if (rwnx_hw->mod_params->use_2040)
+	        mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
+	    else
+	        mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
+
+	    mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+	    band_5GHz->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0);
+	    for (i = 0; i < nss; i++) {
+	        band_5GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+	        band_5GHz->vht_cap.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+	        mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
+	    }
+	    for (; i < 8; i++) {
+	        band_5GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(
+	            IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+	    }
+
+	    mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+	    band_5GHz->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0);
+	    for (i = 0; i < nss; i++) {
+	        band_5GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+	        band_5GHz->vht_cap.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+	        mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+	                        IEEE80211_VHT_MCS_SUPPORT_0_8);
+	    }
+	    for (; i < 8; i++) {
+	        band_5GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(
+	            IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+	    }
+
+	    if (!rwnx_hw->mod_params->use_80) {
+#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
+	        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+	        band_5GHz->vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
+	    }
+#endif
+	}
+#endif
+}
+
+static void rwnx_set_ht_capa(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+	#ifdef USE_5G
+    struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+	#endif
+
+    struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+    int i;
+    int nss = rwnx_hw->mod_params->nss;
+
+    if (!rwnx_hw->mod_params->ht_on) {
+        band_2GHz->ht_cap.ht_supported = false;
+		#ifdef USE_5G
+        band_5GHz->ht_cap.ht_supported = false;
+		#endif
+        return;
+    }
+
+    if (rwnx_hw->mod_params->stbc_on)
+        band_2GHz->ht_cap.cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
+    if (rwnx_hw->mod_params->ldpc_on)
+        band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
+    if (rwnx_hw->mod_params->use_2040) {
+        band_2GHz->ht_cap.mcs.rx_mask[4] = 0x1; /* MCS32 */
+        band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+        band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(135 * nss);
+    } else {
+        band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(65 * nss);
+    }
+    if (nss > 1)
+        band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
+
+    // Update the AMSDU max RX size
+    if (rwnx_hw->mod_params->amsdu_rx_max)
+        band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
+
+    if (rwnx_hw->mod_params->sgi) {
+        band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+        if (rwnx_hw->mod_params->use_2040) {
+            band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+            band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(150 * nss);
+        } else
+            band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(72 * nss);
+    }
+    if (rwnx_hw->mod_params->gf_rx_on)
+        band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD;
+
+    for (i = 0; i < nss; i++) {
+        band_2GHz->ht_cap.mcs.rx_mask[i] = 0xFF;
+    }
+
+	#ifdef USE_5G
+    band_5GHz->ht_cap = band_2GHz->ht_cap;
+	#endif
+}
+
+#ifdef CONFIG_HE_FOR_OLD_KERNEL
+extern struct ieee80211_sband_iftype_data rwnx_he_capa;
+#endif
+static void rwnx_set_he_capa(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+    #ifdef CONFIG_HE_FOR_OLD_KERNEL
+    struct ieee80211_sta_he_cap *he_cap;
+    int i;
+    int nss = rwnx_hw->mod_params->nss;
+    int mcs_map;
+
+    he_cap = (struct ieee80211_sta_he_cap *) &rwnx_he_capa.he_cap;
+    he_cap->has_he = true;
+    he_cap->he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_ALL_ACK;
+    if (rwnx_hw->mod_params->use_2040) {
+        he_cap->he_cap_elem.phy_cap_info[0] |=
+                        IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+        he_cap->ppe_thres[0] |= 0x10;
+    }
+    //if (rwnx_hw->mod_params->use_80)
+    {
+        he_cap->he_cap_elem.phy_cap_info[0] |=
+                        IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+    }
+    if (rwnx_hw->mod_params->ldpc_on) {
+        he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
+    } else {
+        // If no LDPC is supported, we have to limit to MCS0_9, as LDPC is mandatory
+        // for MCS 10 and 11
+        rwnx_hw->mod_params->he_mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+                                                IEEE80211_HE_MCS_SUPPORT_0_9);
+    }
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+    he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US
+                                            | IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
+
+    he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS |
+                                           IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+                                           IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+    #else
+    he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
+    he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+                                           IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+    #endif
+    if (rwnx_hw->mod_params->stbc_on)
+        he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
+	#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
+    he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+                                           IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
+                                           IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU;
+	#else
+    he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+                                           IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
+                                           IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA;
+	#endif
+    if (rwnx_hw->mod_params->bfmee) {
+        he_cap->he_cap_elem.phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
+        he_cap->he_cap_elem.phy_cap_info[4] |=
+                     IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
+    }
+    he_cap->he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+                                           IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
+    he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+                                           IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+                                           IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
+                                           IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB |
+                                           IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+                                           IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+#else
+    he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+                                           IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+                                           IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB |
+                                           IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB |
+                                           IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+                                           IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+#endif
+    he_cap->he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
+    he_cap->he_cap_elem.phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G;
+    he_cap->he_cap_elem.phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+                                           IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB;
+    mcs_map = rwnx_hw->mod_params->he_mcs_map;
+    //mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_9);
+    memset(&he_cap->he_mcs_nss_supp, 0, sizeof(he_cap->he_mcs_nss_supp));
+    for (i = 0; i < nss; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.rx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+        he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+        mcs_map = IEEE80211_HE_MCS_SUPPORT_0_7;
+        }
+    for (; i < 8; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.rx_mcs_80 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+        }
+    mcs_map = rwnx_hw->mod_params->he_mcs_map;
+    for (i = 0; i < nss; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.tx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+        he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+        mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map,
+                        IEEE80211_HE_MCS_SUPPORT_0_7);
+    }
+    for (; i < 8; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.tx_mcs_80 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+    }
+
+    return ;
+    #endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+	#ifdef USE_5G
+    struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+	#endif
+    struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+    int i;
+    int nss = rwnx_hw->mod_params->nss;
+    struct ieee80211_sta_he_cap *he_cap;
+    int mcs_map;
+    if (!rwnx_hw->mod_params->he_on) {
+        band_2GHz->iftype_data = NULL;
+        band_2GHz->n_iftype_data = 0;
+        #ifdef USE_5G
+        band_5GHz->iftype_data = NULL;
+        band_5GHz->n_iftype_data = 0;
+        #endif
+        return;
+    }
+    he_cap = (struct ieee80211_sta_he_cap *) &band_2GHz->iftype_data->he_cap;
+    he_cap->has_he = true;
+    he_cap->he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_ALL_ACK;
+    if (rwnx_hw->mod_params->use_2040) {
+        he_cap->he_cap_elem.phy_cap_info[0] |=
+                        IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+        he_cap->ppe_thres[0] |= 0x10;
+    }
+    //if (rwnx_hw->mod_params->use_80)
+    {
+        he_cap->he_cap_elem.phy_cap_info[0] |=
+                        IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+    }
+    if (rwnx_hw->mod_params->ldpc_on) {
+        he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
+    } else {
+        // If no LDPC is supported, we have to limit to MCS0_9, as LDPC is mandatory
+        // for MCS 10 and 11
+        rwnx_hw->mod_params->he_mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+                                                IEEE80211_HE_MCS_SUPPORT_0_9);
+    }
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+    he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US
+                                            | IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
+
+    he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS |
+                                           IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+                                           IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+    #else
+    he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
+    he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+                                           IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+    #endif
+    if (rwnx_hw->mod_params->stbc_on)
+        he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
+    he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+                                           IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
+                                           IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU;
+#else
+    he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+                                           IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
+                                           IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA;
+#endif
+    if (rwnx_hw->mod_params->bfmee) {
+        he_cap->he_cap_elem.phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
+        he_cap->he_cap_elem.phy_cap_info[4] |=
+                     IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
+    }
+    he_cap->he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+                                           IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
+	#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
+    he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+                                           IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+                                           IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
+                                           IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB |
+                                           IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+                                           IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+	#else
+    he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+                                           IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+                                           IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB |
+                                           IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB |
+                                           IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+                                           IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+	#endif
+    he_cap->he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
+    he_cap->he_cap_elem.phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G;
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+    he_cap->he_cap_elem.phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+                                           IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB;
+    #endif
+    mcs_map = rwnx_hw->mod_params->he_mcs_map;
+    //mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_9);
+    memset(&he_cap->he_mcs_nss_supp, 0, sizeof(he_cap->he_mcs_nss_supp));
+    for (i = 0; i < nss; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.rx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+        he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+        mcs_map = IEEE80211_HE_MCS_SUPPORT_0_7;
+        }
+    for (; i < 8; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.rx_mcs_80 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+        }
+    mcs_map = rwnx_hw->mod_params->he_mcs_map;
+    for (i = 0; i < nss; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.tx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+        he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+        mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map,
+                        IEEE80211_HE_MCS_SUPPORT_0_7);
+    }
+    for (; i < 8; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.tx_mcs_80 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+    }
+
+#ifdef USE_5G
+    he_cap = (struct ieee80211_sta_he_cap *) &band_5GHz->iftype_data->he_cap;
+    he_cap->has_he = true;
+    he_cap->he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_ALL_ACK;
+    if (rwnx_hw->mod_params->use_2040) {
+        he_cap->he_cap_elem.phy_cap_info[0] |=
+                        IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+        he_cap->ppe_thres[0] |= 0x10;
+    }
+    //if (rwnx_hw->mod_params->use_80)
+    {
+        he_cap->he_cap_elem.phy_cap_info[0] |=
+                        IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+    }
+    if (rwnx_hw->mod_params->ldpc_on) {
+        he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
+    } else {
+        // If no LDPC is supported, we have to limit to MCS0_9, as LDPC is mandatory
+        // for MCS 10 and 11
+        rwnx_hw->mod_params->he_mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+                                                IEEE80211_HE_MCS_SUPPORT_0_9);
+    }
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+    he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US |
+                                           IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
+    he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS |
+                                           IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+                                           IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+    #else
+    he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
+    he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+                                           IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+    #endif
+    if (rwnx_hw->mod_params->stbc_on)
+        he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
+    he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+                                           IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
+	                                           IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU;
+#else
+	    he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+	                                           IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
+	                                           IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA;
+#endif
+    if (rwnx_hw->mod_params->bfmee) {
+        he_cap->he_cap_elem.phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
+        he_cap->he_cap_elem.phy_cap_info[4] |=
+                     IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
+    }
+    he_cap->he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+                                           IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
+    he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+                                           IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+	                                           IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
+	                                           IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB |
+	                                           IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+	                                           IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+#else
+	    he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+	                                           IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+                                           IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB |
+                                           IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB |
+                                           IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+                                           IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+#endif
+    he_cap->he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
+    he_cap->he_cap_elem.phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G;
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+    he_cap->he_cap_elem.phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+                                           IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB;
+    #endif
+    mcs_map = rwnx_hw->mod_params->he_mcs_map;
+    //mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_9);
+    memset(&he_cap->he_mcs_nss_supp, 0, sizeof(he_cap->he_mcs_nss_supp));
+    for (i = 0; i < nss; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.rx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+        he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+        mcs_map = IEEE80211_HE_MCS_SUPPORT_0_7;
+    }
+    for (; i < 8; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.rx_mcs_80 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+    }
+    mcs_map = rwnx_hw->mod_params->he_mcs_map;
+    for (i = 0; i < nss; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.tx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+        he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+        mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map,
+                        IEEE80211_HE_MCS_SUPPORT_0_7);
+    }
+    for (; i < 8; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.tx_mcs_80 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+    }
+#endif
+#endif
+}
+
+static void rwnx_set_wiphy_params(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
+		struct ieee80211_regdomain *regdomain;
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+    /* FULLMAC specific parameters */
+    wiphy->flags |= WIPHY_FLAG_REPORTS_OBSS;
+    wiphy->max_scan_ssids = SCAN_SSID_MAX;
+    wiphy->max_scan_ie_len = SCANU_MAX_IE_LEN;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    if (rwnx_hw->mod_params->tdls) {
+        /* TDLS support */
+        wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+#ifdef CONFIG_RWNX_FULLMAC
+        /* TDLS external setup support */
+        wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
+#endif
+    }
+
+    if (rwnx_hw->mod_params->ap_uapsd_on)
+        wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
+#ifdef CONFIG_RWNX_FULLMAC
+    if (rwnx_hw->mod_params->ps_on)
+        wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+    else
+        wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+#endif
+
+    if (rwnx_hw->mod_params->custregd) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+        // Apply custom regulatory. Note that for recent kernel versions we use instead the
+        // REGULATORY_WIPHY_SELF_MANAGED flag, along with the regulatory_set_wiphy_regd()
+        // function, that needs to be called after wiphy registration
+        memcpy(country_code, default_ccode, sizeof(default_ccode));
+		regdomain = getRegdomainFromRwnxDB(wiphy, default_ccode);
+        printk(KERN_CRIT
+               "\n\n%s: CAUTION: USING PERMISSIVE CUSTOM REGULATORY RULES\n\n",
+               __func__);
+        wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
+        wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
+        wiphy_apply_custom_regulatory(wiphy, regdomain);
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
+        memcpy(country_code, default_ccode, sizeof(default_ccode));
+		regdomain = getRegdomainFromRwnxDB(wiphy, default_ccode);
+		printk(KERN_CRIT"%s: Registering custom regulatory\n", __func__);
+		wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
+		wiphy_apply_custom_regulatory(wiphy, regdomain);
+#endif
+        // Check if custom channel set shall be enabled. In such case only monitor mode is
+        // supported
+        if (rwnx_hw->mod_params->custchan) {
+            wiphy->interface_modes = BIT(NL80211_IFTYPE_MONITOR);
+
+            // Enable "extra" channels
+            wiphy->bands[NL80211_BAND_2GHZ]->n_channels += 13;
+			#ifdef USE_5G
+            wiphy->bands[NL80211_BAND_5GHZ]->n_channels += 59;
+			#endif
+        }
+    }
+
+#if 0
+	if (rwnx_hw->mod_params->custregd) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+		// Apply custom regulatory. Note that for recent kernel versions we use instead the
+		// REGULATORY_WIPHY_SELF_MANAGED flag, along with the regulatory_set_wiphy_regd()
+		// function, that needs to be called after wiphy registration
+		printk(KERN_CRIT
+			   "\n\n%s: CAUTION: USING PERMISSIVE CUSTOM REGULATORY RULES\n\n",
+			   __func__);
+		wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
+		wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
+		wiphy_apply_custom_regulatory(wiphy, &rwnx_regdom);
+#endif
+		// Check if custom channel set shall be enabled. In such case only monitor mode is
+		// supported
+		if (rwnx_hw->mod_params->custchan) {
+			wiphy->interface_modes = BIT(NL80211_IFTYPE_MONITOR);
+
+			// Enable "extra" channels
+			wiphy->bands[NL80211_BAND_2GHZ]->n_channels += 13;
+			if (rwnx_hw->band_5g_support)
+				wiphy->bands[NL80211_BAND_5GHZ]->n_channels += 59;
+		}
+	}
+#endif
+}
+
+#if 0
+static void rwnx_set_rf_params(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+#ifndef CONFIG_RWNX_SDM
+	#ifdef USE_5G
+    struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+	#endif
+    struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+    u32 mdm_phy_cfg = __MDM_PHYCFG_FROM_VERS(rwnx_hw->version_cfm.version_phy_1);
+
+    /*
+     * Get configuration file depending on the RF
+     */
+    if (mdm_phy_cfg == MDM_PHY_CONFIG_TRIDENT) {
+        struct rwnx_phy_conf_file phy_conf;
+        // Retrieve the Trident configuration
+        rwnx_parse_phy_configfile(rwnx_hw, RWNX_PHY_CONFIG_TRD_NAME,
+                                  &phy_conf, rwnx_hw->mod_params->phy_cfg);
+        memcpy(&rwnx_hw->phy.cfg, &phy_conf.trd, sizeof(phy_conf.trd));
+    } else if (mdm_phy_cfg == MDM_PHY_CONFIG_ELMA) {
+    } else if (mdm_phy_cfg == MDM_PHY_CONFIG_KARST) {
+        struct rwnx_phy_conf_file phy_conf;
+        // We use the NSS parameter as is
+        // Retrieve the Karst configuration
+        rwnx_parse_phy_configfile(rwnx_hw, RWNX_PHY_CONFIG_KARST_NAME,
+                                  &phy_conf, rwnx_hw->mod_params->phy_cfg);
+
+        memcpy(&rwnx_hw->phy.cfg, &phy_conf.karst, sizeof(phy_conf.karst));
+    } else {
+        WARN_ON(1);
+    }
+
+    /*
+     * adjust caps depending on the RF
+     */
+    switch (mdm_phy_cfg) {
+        case MDM_PHY_CONFIG_TRIDENT:
+        {
+            wiphy_dbg(wiphy, "found Trident phy .. limit BW to 40MHz\n");
+            rwnx_hw->phy.limit_bw = true;
+            #ifdef USE_5G
+#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
+            band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+            band_5GHz->vht_cap.cap &= ~(IEEE80211_VHT_CAP_SHORT_GI_80 |
+                                        IEEE80211_VHT_CAP_RXSTBC_MASK);
+            #endif
+            break;
+        }
+        case MDM_PHY_CONFIG_ELMA:
+            wiphy_dbg(wiphy, "found ELMA phy .. disabling 2.4GHz and greenfield rx\n");
+            wiphy->bands[NL80211_BAND_2GHZ] = NULL;
+            band_2GHz->ht_cap.cap &= ~IEEE80211_HT_CAP_GRN_FLD;
+            #ifdef USE_5G
+            band_5GHz->ht_cap.cap &= ~IEEE80211_HT_CAP_GRN_FLD;
+            band_5GHz->vht_cap.cap &= ~IEEE80211_VHT_CAP_RXSTBC_MASK;
+            #endif
+            break;
+        case MDM_PHY_CONFIG_KARST:
+        {
+            wiphy_dbg(wiphy, "found KARST phy\n");
+            break;
+        }
+        default:
+            WARN_ON(1);
+            break;
+    }
+#endif /* CONFIG_RWNX_SDM */
+}
+#endif
+
+int rwnx_handle_dynparams(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+#if 0
+    /* Check compatibility between requested parameters and HW/SW features */
+    int ret;
+
+    ret = rwnx_check_fw_hw_feature(rwnx_hw, wiphy);
+    if (ret)
+        return ret;
+
+    /* Allocate the RX buffers according to the maximum AMSDU RX size */
+    ret = rwnx_ipc_rxbuf_init(rwnx_hw,
+                              (4 * (rwnx_hw->mod_params->amsdu_rx_max + 1) + 1) * 1024);
+    if (ret) {
+        wiphy_err(wiphy, "Cannot allocate the RX buffers\n");
+        return ret;
+    }
+#endif
+
+    if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80) {
+        rwnx_hw->mod_params->sgi80 = true;
+        rwnx_hw->mod_params->use_80 = true;
+    }
+
+    /* Set wiphy parameters */
+    rwnx_set_wiphy_params(rwnx_hw, wiphy);
+    /* Set VHT capabilities */
+    rwnx_set_vht_capa(rwnx_hw, wiphy);
+    /* Set HE capabilities */
+    rwnx_set_he_capa(rwnx_hw, wiphy);
+    /* Set HT capabilities */
+    rwnx_set_ht_capa(rwnx_hw, wiphy);
+    /* Set RF specific parameters (shall be done last as it might change some
+       capabilities previously set) */
+#if 0
+    rwnx_set_rf_params(rwnx_hw, wiphy);
+#endif
+    return 0;
+}
+
+void rwnx_custregd(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+// For older kernel version, the custom regulatory is applied before the wiphy
+// registration (in rwnx_set_wiphy_params()), so nothing has to be done here
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+    if (!rwnx_hw->mod_params->custregd)
+        return;
+
+    wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
+    wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
+
+    rtnl_lock();
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
+    if (regulatory_set_wiphy_regd_sync(wiphy, getRegdomainFromRwnxDB(wiphy, default_ccode))){
+        wiphy_err(wiphy, "Failed to set custom regdomain\n");
+    }
+#else
+    if (regulatory_set_wiphy_regd_sync_rtnl(wiphy, getRegdomainFromRwnxDB(wiphy, default_ccode))){
+        wiphy_err(wiphy, "Failed to set custom regdomain\n");
+    }
+#endif
+    else{
+        wiphy_err(wiphy,"\n"
+                  "*******************************************************\n"
+                  "** CAUTION: USING PERMISSIVE CUSTOM REGULATORY RULES **\n"
+                  "*******************************************************\n");
+    }
+     rtnl_unlock();
+#endif
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mod_params.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mod_params.h
new file mode 100755
index 0000000..a033551
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mod_params.h
@@ -0,0 +1,70 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_mod_params.h
+ *
+ * @brief Declaration of module parameters
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_MOD_PARAM_H_
+#define _RWNX_MOD_PARAM_H_
+
+struct rwnx_mod_params {
+	bool ht_on;
+	bool vht_on;
+	bool he_on;
+	int mcs_map;
+	int he_mcs_map;
+	bool he_ul_on;
+	bool ldpc_on;
+	bool stbc_on;
+	bool gf_rx_on;
+	int phy_cfg;
+	int uapsd_timeout;
+	bool ap_uapsd_on;
+	bool sgi;
+	bool sgi80;
+	bool use_2040;
+	bool use_80;
+	bool custregd;
+	bool custchan;
+	int nss;
+	int amsdu_rx_max;
+	bool bfmee;
+	bool bfmer;
+	bool mesh;
+	bool murx;
+	bool mutx;
+	bool mutx_on;
+	unsigned int roc_dur_max;
+	int listen_itv;
+	bool listen_bcmc;
+	int lp_clk_ppm;
+	bool ps_on;
+	int tx_lft;
+	int amsdu_maxnb;
+	int uapsd_queues;
+	bool tdls;
+	bool uf;
+	bool auto_reply;
+	char *ftl;
+	bool dpsm;
+#ifdef CONFIG_RWNX_FULLMAC
+    bool ant_div;
+#endif /* CONFIG_RWNX_FULLMAC */
+};
+
+extern struct rwnx_mod_params rwnx_mod_params;
+
+struct rwnx_hw;
+struct wiphy;
+int rwnx_handle_dynparams(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy);
+void rwnx_custregd(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy);
+void rwnx_enable_wapi(struct rwnx_hw *rwnx_hw);
+void rwnx_enable_mfp(struct rwnx_hw *rwnx_hw);
+
+#endif /* _RWNX_MOD_PARAM_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_msg_rx.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_msg_rx.c
new file mode 100755
index 0000000..ee7b1ee
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_msg_rx.c
@@ -0,0 +1,1393 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_msg_rx.c
+ *
+ * @brief RX function definitions
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+#include "rwnx_defs.h"
+#include "rwnx_prof.h"
+#include "rwnx_tx.h"
+#ifdef CONFIG_RWNX_BFMER
+#include "rwnx_bfmer.h"
+#endif //(CONFIG_RWNX_BFMER)
+#ifdef CONFIG_RWNX_FULLMAC
+#include "rwnx_debugfs.h"
+#include "rwnx_msg_tx.h"
+#include "rwnx_tdls.h"
+#endif /* CONFIG_RWNX_FULLMAC */
+#include "rwnx_events.h"
+#include "rwnx_compat.h"
+#include "aicwf_txrxif.h"
+#include "rwnx_msg_rx.h"
+
+static int rwnx_freq_to_idx(struct rwnx_hw *rwnx_hw, int freq)
+{
+	struct ieee80211_supported_band *sband = NULL;
+	int band, ch, idx = 0;
+
+    for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
+#ifdef CONFIG_RWNX_FULLMAC
+        sband = rwnx_hw->wiphy->bands[band];
+#endif /* CONFIG_RWNX_FULLMAC */
+        if (!sband) {
+            continue;
+        }
+
+        for (ch = 0; ch < sband->n_channels; ch++, idx++) {
+            if (sband->channels[ch].center_freq == freq) {
+                goto exit;
+            }
+        }
+    }
+
+    BUG_ON(1);
+
+exit:
+    // Channel has been found, return the index
+    return idx;
+}
+
+/***************************************************************************
+ * Messages from MM task
+ **************************************************************************/
+static inline int rwnx_rx_chan_pre_switch_ind(struct rwnx_hw *rwnx_hw,
+                                              struct rwnx_cmd *cmd,
+                                              struct ipc_e2a_msg *msg)
+{
+    struct rwnx_vif *rwnx_vif;
+    int chan_idx = ((struct mm_channel_pre_switch_ind *)msg->param)->chan_index;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    REG_SW_SET_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT);
+
+#ifdef CONFIG_RWNX_FULLMAC
+    list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+        if (rwnx_vif->up && rwnx_vif->ch_index == chan_idx) {
+            printk("rwnx_txq_vif_stop\r\n");
+            rwnx_txq_vif_stop(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+        }
+    }
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    REG_SW_CLEAR_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT);
+
+    return 0;
+}
+
+static inline int rwnx_rx_chan_switch_ind(struct rwnx_hw *rwnx_hw,
+                                          struct rwnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    struct rwnx_vif *rwnx_vif;
+    int chan_idx = ((struct mm_channel_switch_ind *)msg->param)->chan_index;
+    bool roc     = ((struct mm_channel_switch_ind *)msg->param)->roc;
+    bool roc_tdls = ((struct mm_channel_switch_ind *)msg->param)->roc_tdls;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    REG_SW_SET_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT);
+
+#ifdef CONFIG_RWNX_FULLMAC
+    if (roc_tdls) {
+        u8 vif_index = ((struct mm_channel_switch_ind *)msg->param)->vif_index;
+        list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+            if (rwnx_vif->vif_index == vif_index) {
+                rwnx_vif->roc_tdls = true;
+                rwnx_txq_tdls_sta_start(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+            }
+        }
+    } else if (!roc) {
+        list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+            if (rwnx_vif->up && rwnx_vif->ch_index == chan_idx) {
+                rwnx_txq_vif_start(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+            }
+        }
+    } else {
+        /* Retrieve the allocated RoC element */
+        struct rwnx_roc_elem *roc_elem = rwnx_hw->roc_elem;
+
+		if(roc_elem) {
+        /* If mgmt_roc is true, remain on channel has been started by ourself */
+        if (!roc_elem->mgmt_roc) {
+            /* Inform the host that we have switch on the indicated off-channel */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+	    cfg80211_ready_on_channel(roc_elem->wdev->netdev, (u64)(rwnx_hw->roc_cookie_cnt),
+                                      roc_elem->chan, NL80211_CHAN_HT20, roc_elem->duration, GFP_ATOMIC);
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+	    cfg80211_ready_on_channel(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt),
+                                      roc_elem->chan, NL80211_CHAN_HT20, roc_elem->duration, GFP_ATOMIC);
+#else
+            cfg80211_ready_on_channel(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt),
+                                      roc_elem->chan, roc_elem->duration, GFP_ATOMIC);
+#endif
+        }
+
+			/* Keep in mind that we have switched on the channel */
+			roc_elem->on_chan = true;
+		} else {
+			printk("roc_elem == null\n");
+		}
+		// Enable traffic on OFF channel queue
+		rwnx_txq_offchan_start(rwnx_hw);
+	}
+
+    tasklet_schedule(&rwnx_hw->task);
+
+    rwnx_hw->cur_chanctx = chan_idx;
+    rwnx_radar_detection_enable_on_cur_channel(rwnx_hw);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    REG_SW_CLEAR_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT);
+
+    return 0;
+}
+
+static inline int rwnx_rx_tdls_chan_switch_cfm(struct rwnx_hw *rwnx_hw,
+                                                struct rwnx_cmd *cmd,
+                                                struct ipc_e2a_msg *msg)
+{
+    return 0;
+}
+
+static inline int rwnx_rx_tdls_chan_switch_ind(struct rwnx_hw *rwnx_hw,
+                                               struct rwnx_cmd *cmd,
+                                               struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_RWNX_FULLMAC
+    // Enable traffic on OFF channel queue
+    rwnx_txq_offchan_start(rwnx_hw);
+
+    return 0;
+#endif
+}
+
+static inline int rwnx_rx_tdls_chan_switch_base_ind(struct rwnx_hw *rwnx_hw,
+                                                    struct rwnx_cmd *cmd,
+                                                    struct ipc_e2a_msg *msg)
+{
+    struct rwnx_vif *rwnx_vif;
+    u8 vif_index = ((struct tdls_chan_switch_base_ind *)msg->param)->vif_index;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_RWNX_FULLMAC
+    list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+        if (rwnx_vif->vif_index == vif_index) {
+            rwnx_vif->roc_tdls = false;
+            rwnx_txq_tdls_sta_stop(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+        }
+    }
+    return 0;
+#endif
+}
+
+static inline int rwnx_rx_tdls_peer_ps_ind(struct rwnx_hw *rwnx_hw,
+                                           struct rwnx_cmd *cmd,
+                                           struct ipc_e2a_msg *msg)
+{
+    struct rwnx_vif *rwnx_vif;
+    u8 vif_index = ((struct tdls_peer_ps_ind *)msg->param)->vif_index;
+    bool ps_on = ((struct tdls_peer_ps_ind *)msg->param)->ps_on;
+
+#ifdef CONFIG_RWNX_FULLMAC
+    list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+        if (rwnx_vif->vif_index == vif_index) {
+            rwnx_vif->sta.tdls_sta->tdls.ps_on = ps_on;
+            // Update PS status for the TDLS station
+            rwnx_ps_bh_enable(rwnx_hw, rwnx_vif->sta.tdls_sta, ps_on);
+        }
+    }
+
+    return 0;
+#endif
+}
+
+static inline int rwnx_rx_remain_on_channel_exp_ind(struct rwnx_hw *rwnx_hw,
+                                                    struct rwnx_cmd *cmd,
+                                                    struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_RWNX_FULLMAC
+	/* Retrieve the allocated RoC element */
+	struct rwnx_roc_elem *roc_elem = rwnx_hw->roc_elem;
+	/* Get VIF on which RoC has been started */
+	struct rwnx_vif *rwnx_vif;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	if (!roc_elem)
+		return 0;
+	rwnx_vif = container_of(roc_elem->wdev, struct rwnx_vif, wdev);
+    /* For debug purpose (use ftrace kernel option) */
+#ifdef CREATE_TRACE_POINTS
+	trace_roc_exp(rwnx_vif->vif_index);
+#endif
+    /* If mgmt_roc is true, remain on channel has been started by ourself */
+    /* If RoC has been cancelled before we switched on channel, do not call cfg80211 */
+    if (!roc_elem->mgmt_roc && roc_elem->on_chan) {
+        /* Inform the host that off-channel period has expired */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+	cfg80211_remain_on_channel_expired(roc_elem->wdev->netdev, (u64)(rwnx_hw->roc_cookie_cnt),
+                                           roc_elem->chan, NL80211_CHAN_HT20, GFP_ATOMIC);
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+	cfg80211_remain_on_channel_expired(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt),
+                                           roc_elem->chan, NL80211_CHAN_HT20, GFP_ATOMIC);
+#else
+        cfg80211_remain_on_channel_expired(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt),
+                                           roc_elem->chan, GFP_ATOMIC);
+#endif
+    }
+
+    /* De-init offchannel TX queue */
+    rwnx_txq_offchan_deinit(rwnx_vif);
+
+    /* Increase the cookie counter cannot be zero */
+    rwnx_hw->roc_cookie_cnt++;
+
+    if (rwnx_hw->roc_cookie_cnt == 0) {
+        rwnx_hw->roc_cookie_cnt = 1;
+    }
+
+    /* Free the allocated RoC element */
+    kfree(roc_elem);
+    rwnx_hw->roc_elem = NULL;
+
+#endif /* CONFIG_RWNX_FULLMAC */
+    return 0;
+}
+
+static inline int rwnx_rx_p2p_vif_ps_change_ind(struct rwnx_hw *rwnx_hw,
+                                                struct rwnx_cmd *cmd,
+                                                struct ipc_e2a_msg *msg)
+{
+    int vif_idx  = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->vif_index;
+    int ps_state = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->ps_state;
+    struct rwnx_vif *vif_entry;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_RWNX_FULLMAC
+    vif_entry = rwnx_hw->vif_table[vif_idx];
+
+    if (vif_entry) {
+        goto found_vif;
+    }
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    goto exit;
+
+found_vif:
+
+#ifdef CONFIG_RWNX_FULLMAC
+    if (ps_state == MM_PS_MODE_OFF) {
+        // Start TX queues for provided VIF
+        rwnx_txq_vif_start(vif_entry, RWNX_TXQ_STOP_VIF_PS, rwnx_hw);
+    }
+    else {
+        // Stop TX queues for provided VIF
+        rwnx_txq_vif_stop(vif_entry, RWNX_TXQ_STOP_VIF_PS, rwnx_hw);
+    }
+#endif /* CONFIG_RWNX_FULLMAC */
+
+exit:
+    return 0;
+}
+
+static inline int rwnx_rx_channel_survey_ind(struct rwnx_hw *rwnx_hw,
+                                             struct rwnx_cmd *cmd,
+                                             struct ipc_e2a_msg *msg)
+{
+    struct mm_channel_survey_ind *ind = (struct mm_channel_survey_ind *)msg->param;
+    // Get the channel index
+    int idx = rwnx_freq_to_idx(rwnx_hw, ind->freq);
+    // Get the survey
+    struct rwnx_survey_info *rwnx_survey;
+
+	if (idx >  ARRAY_SIZE(rwnx_hw->survey))
+		return 0;
+
+    rwnx_survey = &rwnx_hw->survey[idx];
+
+    // Store the received parameters
+    rwnx_survey->chan_time_ms = ind->chan_time_ms;
+    rwnx_survey->chan_time_busy_ms = ind->chan_time_busy_ms;
+    rwnx_survey->noise_dbm = ind->noise_dbm;
+    rwnx_survey->filled = (SURVEY_INFO_TIME |
+                           SURVEY_INFO_TIME_BUSY);
+
+    if (ind->noise_dbm != 0) {
+        rwnx_survey->filled |= SURVEY_INFO_NOISE_DBM;
+    }
+
+    return 0;
+}
+
+static inline int rwnx_rx_p2p_noa_upd_ind(struct rwnx_hw *rwnx_hw,
+                                          struct rwnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    return 0;
+}
+
+static inline int rwnx_rx_rssi_status_ind(struct rwnx_hw *rwnx_hw,
+                                          struct rwnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    struct mm_rssi_status_ind *ind = (struct mm_rssi_status_ind *)msg->param;
+    int vif_idx  = ind->vif_index;
+    bool rssi_status = ind->rssi_status;
+
+    struct rwnx_vif *vif_entry;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_RWNX_FULLMAC
+    vif_entry = rwnx_hw->vif_table[vif_idx];
+    if (vif_entry) {
+        cfg80211_cqm_rssi_notify(vif_entry->ndev,
+                                 rssi_status ? NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+                                               NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+                                 ind->rssi, GFP_ATOMIC);
+    }
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    return 0;
+}
+
+static inline int rwnx_rx_pktloss_notify_ind(struct rwnx_hw *rwnx_hw,
+                                             struct rwnx_cmd *cmd,
+                                             struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_RWNX_FULLMAC
+    struct mm_pktloss_ind *ind = (struct mm_pktloss_ind *)msg->param;
+    struct rwnx_vif *vif_entry;
+    int vif_idx  = ind->vif_index;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    vif_entry = rwnx_hw->vif_table[vif_idx];
+    if (vif_entry) {
+        cfg80211_cqm_pktloss_notify(vif_entry->ndev, (const u8 *)ind->mac_addr.array,
+                                    ind->num_packets, GFP_ATOMIC);
+    }
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    return 0;
+}
+
+static inline int rwnx_apm_staloss_ind(struct rwnx_hw *rwnx_hw,
+                                                struct rwnx_cmd *cmd,
+                                                struct ipc_e2a_msg *msg)
+{
+    struct mm_apm_staloss_ind *ind = (struct mm_apm_staloss_ind *)msg->param;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    memcpy(rwnx_hw->sta_mac_addr, ind->mac_addr, 6);
+    rwnx_hw->apm_vif_idx = ind->vif_idx;
+
+    queue_work(rwnx_hw->apmStaloss_wq, &rwnx_hw->apmStalossWork);
+
+    return 0;
+}
+
+static inline int rwnx_rx_csa_counter_ind(struct rwnx_hw *rwnx_hw,
+                                          struct rwnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    struct mm_csa_counter_ind *ind = (struct mm_csa_counter_ind *)msg->param;
+    struct rwnx_vif *vif;
+    bool found = false;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    // Look for VIF entry
+    list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+        if (vif->vif_index == ind->vif_index) {
+            found=true;
+            break;
+        }
+    }
+
+    if (found) {
+#ifdef CONFIG_RWNX_FULLMAC
+        if (vif->ap.csa)
+            vif->ap.csa->count = ind->csa_count;
+        else
+            netdev_err(vif->ndev, "CSA counter update but no active CSA");
+
+#endif
+    }
+
+    return 0;
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+static inline int rwnx_rx_csa_finish_ind(struct rwnx_hw *rwnx_hw,
+                                         struct rwnx_cmd *cmd,
+                                         struct ipc_e2a_msg *msg)
+{
+    struct mm_csa_finish_ind *ind = (struct mm_csa_finish_ind *)msg->param;
+    struct rwnx_vif *vif;
+    bool found = false;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    // Look for VIF entry
+    list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+        if (vif->vif_index == ind->vif_index) {
+            found=true;
+            break;
+        }
+    }
+
+    if (found) {
+        if (RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP ||
+            RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_GO) {
+            if (vif->ap.csa) {
+                vif->ap.csa->status = ind->status;
+                vif->ap.csa->ch_idx = ind->chan_idx;
+                schedule_work(&vif->ap.csa->work);
+            } else
+                netdev_err(vif->ndev, "CSA finish indication but no active CSA");
+        } else {
+            if (ind->status == 0) {
+                rwnx_chanctx_unlink(vif);
+                rwnx_chanctx_link(vif, ind->chan_idx, NULL);
+                if (rwnx_hw->cur_chanctx == ind->chan_idx) {
+                    rwnx_radar_detection_enable_on_cur_channel(rwnx_hw);
+                    rwnx_txq_vif_start(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+                } else
+                    rwnx_txq_vif_stop(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static inline int rwnx_rx_csa_traffic_ind(struct rwnx_hw *rwnx_hw,
+                                          struct rwnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    struct mm_csa_traffic_ind *ind = (struct mm_csa_traffic_ind *)msg->param;
+    struct rwnx_vif *vif;
+    bool found = false;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    // Look for VIF entry
+    list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+        if (vif->vif_index == ind->vif_index) {
+            found=true;
+            break;
+        }
+    }
+
+    if (found) {
+        if (ind->enable)
+            rwnx_txq_vif_start(vif, RWNX_TXQ_STOP_CSA, rwnx_hw);
+        else
+            rwnx_txq_vif_stop(vif, RWNX_TXQ_STOP_CSA, rwnx_hw);
+    }
+
+    return 0;
+}
+
+static inline int rwnx_rx_ps_change_ind(struct rwnx_hw *rwnx_hw,
+                                        struct rwnx_cmd *cmd,
+                                        struct ipc_e2a_msg *msg)
+{
+    struct mm_ps_change_ind *ind = (struct mm_ps_change_ind *)msg->param;
+    struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->sta_idx];
+
+    //RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (ind->sta_idx >= (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) {
+        wiphy_err(rwnx_hw->wiphy, "Invalid sta index reported by fw %d\n",
+                  ind->sta_idx);
+        return 1;
+    }
+
+//    netdev_dbg(rwnx_hw->vif_table[sta->vif_idx]->ndev,
+//               "Sta %d, change PS mode to %s", sta->sta_idx,
+//               ind->ps_state ? "ON" : "OFF");
+
+    if (sta->valid) {
+        rwnx_ps_bh_enable(rwnx_hw, sta, ind->ps_state);
+    } else if (rwnx_hw->adding_sta) {
+        sta->ps.active = ind->ps_state ? true : false;
+    } else {
+		if (rwnx_hw->vif_table[sta->vif_idx] && rwnx_hw->vif_table[sta->vif_idx]->ndev)
+        netdev_err(rwnx_hw->vif_table[sta->vif_idx]->ndev,
+                   "Ignore PS mode change on invalid sta\n");
+    }
+
+    return 0;
+}
+
+
+static inline int rwnx_rx_traffic_req_ind(struct rwnx_hw *rwnx_hw,
+                                          struct rwnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    struct mm_traffic_req_ind *ind = (struct mm_traffic_req_ind *)msg->param;
+    struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->sta_idx];
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    netdev_dbg(rwnx_hw->vif_table[sta->vif_idx]->ndev,
+               "Sta %d, asked for %d pkt", sta->sta_idx, ind->pkt_cnt);
+
+    rwnx_ps_bh_traffic_req(rwnx_hw, sta, ind->pkt_cnt,
+                           ind->uapsd ? UAPSD_ID : LEGACY_PS_ID);
+
+    return 0;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from SCAN task
+ **************************************************************************/
+#if 0
+static inline int rwnx_rx_scan_done_ind(struct rwnx_hw *rwnx_hw,
+                                        struct rwnx_cmd *cmd,
+                                        struct ipc_e2a_msg *msg)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+    struct cfg80211_scan_info info = {
+        .aborted = false,
+    };
+#endif
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    rwnx_ipc_elem_var_deallocs(rwnx_hw, &rwnx_hw->scan_ie);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+    ieee80211_scan_completed(rwnx_hw->hw, &info);
+#else
+    ieee80211_scan_completed(rwnx_hw->hw, false);
+#endif
+
+    return 0;
+}
+#endif
+
+/***************************************************************************
+ * Messages from SCANU task
+ **************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+extern uint8_t scanning;
+static inline int rwnx_rx_scanu_start_cfm(struct rwnx_hw *rwnx_hw,
+                                          struct rwnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	scanning = 0;
+    //rwnx_ipc_elem_var_deallocs(rwnx_hw, &rwnx_hw->scan_ie);
+    if (rwnx_hw->scan_request) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+        struct cfg80211_scan_info info = {
+            .aborted = false,
+        };
+
+        cfg80211_scan_done(rwnx_hw->scan_request, &info);
+#else
+        cfg80211_scan_done(rwnx_hw->scan_request, false);
+#endif
+    }
+
+    rwnx_hw->scan_request = NULL;
+
+    return 0;
+}
+
+static inline int rwnx_rx_scanu_result_ind(struct rwnx_hw *rwnx_hw,
+                                           struct rwnx_cmd *cmd,
+                                           struct ipc_e2a_msg *msg)
+{
+    struct cfg80211_bss *bss = NULL;
+    struct ieee80211_channel *chan;
+    struct scanu_result_ind *ind = (struct scanu_result_ind *)msg->param;
+    struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)ind->payload;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq);
+
+    if (chan != NULL) {
+        #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+        ktime_t ts;
+        ts = ktime_get_real();
+        mgmt->u.probe_resp.timestamp = ((u64)ts.tv64);//((u64)ts.tv.sec*1000000) + ts.tv.nsec/1000;
+        #else
+        struct timespec64 ts;
+        ktime_get_real_ts64(&ts);
+        mgmt->u.probe_resp.timestamp = ((u64)ts.tv_sec*1000000) + ts.tv_nsec/1000;
+        #endif
+        bss = cfg80211_inform_bss_frame(rwnx_hw->wiphy, chan,
+                                        (struct ieee80211_mgmt *)ind->payload,
+                                        ind->length, ind->rssi * 100, GFP_ATOMIC);
+    }
+
+    if (bss != NULL)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
+	cfg80211_put_bss(bss);
+#else
+        cfg80211_put_bss(rwnx_hw->wiphy, bss);
+#endif
+
+    return 0;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from ME task
+ **************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+static inline int rwnx_rx_me_tkip_mic_failure_ind(struct rwnx_hw *rwnx_hw,
+                                                  struct rwnx_cmd *cmd,
+                                                  struct ipc_e2a_msg *msg)
+{
+    struct me_tkip_mic_failure_ind *ind = (struct me_tkip_mic_failure_ind *)msg->param;
+    struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+    struct net_device *dev = rwnx_vif->ndev;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    cfg80211_michael_mic_failure(dev, (u8 *)&ind->addr, (ind->ga?NL80211_KEYTYPE_GROUP:
+                                 NL80211_KEYTYPE_PAIRWISE), ind->keyid,
+                                 (u8 *)&ind->tsc, GFP_ATOMIC);
+
+    return 0;
+}
+
+static inline int rwnx_rx_me_tx_credits_update_ind(struct rwnx_hw *rwnx_hw,
+                                                   struct rwnx_cmd *cmd,
+                                                   struct ipc_e2a_msg *msg)
+{
+    struct me_tx_credits_update_ind *ind = (struct me_tx_credits_update_ind *)msg->param;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    rwnx_txq_credit_update(rwnx_hw, ind->sta_idx, ind->tid, ind->credits);
+
+    return 0;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from SM task
+ **************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+static inline void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
+                             struct ieee80211_channel *chan,
+                             enum nl80211_channel_type chan_type)
+{
+        if (WARN_ON(!chan))
+                return;
+        chandef->chan = chan;
+        chandef->center_freq2 = 0;
+        switch (chan_type) {
+        case NL80211_CHAN_NO_HT:
+                chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+                chandef->center_freq1 = chan->center_freq;
+                break;
+        case NL80211_CHAN_HT20:
+                chandef->width = NL80211_CHAN_WIDTH_20;
+                chandef->center_freq1 = chan->center_freq;
+                break;
+        case NL80211_CHAN_HT40PLUS:
+                chandef->width = NL80211_CHAN_WIDTH_40;
+                chandef->center_freq1 = chan->center_freq + 10;
+                break;
+        case NL80211_CHAN_HT40MINUS:
+                chandef->width = NL80211_CHAN_WIDTH_40;
+                chandef->center_freq1 = chan->center_freq - 10;
+                break;
+        default:
+                WARN_ON(1);
+        }
+}
+#endif
+static inline int rwnx_rx_sm_connect_ind(struct rwnx_hw *rwnx_hw,
+                                         struct rwnx_cmd *cmd,
+                                         struct ipc_e2a_msg *msg)
+{
+    struct sm_connect_ind *ind = (struct sm_connect_ind *)msg->param;
+    struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+    struct net_device *dev = rwnx_vif->ndev;
+    const u8 *req_ie, *rsp_ie;
+    const u8 *extcap_ie;
+    const struct ieee_types_extcap *extcap;
+	struct ieee80211_channel *chan;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Retrieve IE addresses and lengths */
+    req_ie = (const u8 *)ind->assoc_ie_buf;
+    rsp_ie = req_ie + ind->assoc_req_ie_len;
+
+    if (rwnx_vif->sta.external_auth)
+        rwnx_vif->sta.external_auth = false;
+
+    // Fill-in the AP information
+    if (ind->status_code == 0)
+    {
+        struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->ap_idx];
+        u8 txq_status;
+        struct ieee80211_channel *chan;
+        struct cfg80211_chan_def chandef;
+
+        sta->valid = true;
+        sta->sta_idx = ind->ap_idx;
+        sta->ch_idx = ind->ch_idx;
+        sta->vif_idx = ind->vif_idx;
+        sta->vlan_idx = sta->vif_idx;
+        sta->qos = ind->qos;
+        sta->acm = ind->acm;
+        sta->ps.active = false;
+        sta->aid = ind->aid;
+        sta->band = ind->band;
+        sta->width = ind->width;
+        sta->center_freq = ind->center_freq;
+        sta->center_freq1 = ind->center_freq1;
+        sta->center_freq2 = ind->center_freq2;
+        rwnx_vif->sta.ap = sta;
+        chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq);
+        cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+        if (!rwnx_hw->mod_params->ht_on)
+            chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+        else
+            chandef.width = chnl2bw[ind->width];
+        chandef.center_freq1 = ind->center_freq1;
+        chandef.center_freq2 = ind->center_freq2;
+        rwnx_chanctx_link(rwnx_vif, ind->ch_idx, &chandef);
+        memcpy(sta->mac_addr, ind->bssid.array, ETH_ALEN);
+        if (ind->ch_idx == rwnx_hw->cur_chanctx) {
+            txq_status = 0;
+        } else {
+            txq_status = RWNX_TXQ_STOP_CHAN;
+        }
+        memcpy(sta->ac_param, ind->ac_param, sizeof(sta->ac_param));
+        rwnx_txq_sta_init(rwnx_hw, sta, txq_status);
+        rwnx_txq_tdls_vif_init(rwnx_vif);
+        #ifdef CONFIG_DEBUG_FS_AIC
+        rwnx_dbgfs_register_rc_stat(rwnx_hw, sta);
+        #endif
+        rwnx_mu_group_sta_init(sta, NULL);
+        /* Look for TDLS Channel Switch Prohibited flag in the Extended Capability
+         * Information Element*/
+        extcap_ie = cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, rsp_ie, ind->assoc_rsp_ie_len);
+        if (extcap_ie && extcap_ie[1] >= 5) {
+            extcap = (void *)(extcap_ie);
+            rwnx_vif->tdls_chsw_prohibited = extcap->ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED;
+        }
+		if (rwnx_vif->wep_enabled)
+			rwnx_vif->wep_auth_err = false;
+
+#ifdef CONFIG_RWNX_BFMER
+        /* If Beamformer feature is activated, check if features can be used
+         * with the new peer device
+         */
+        if (rwnx_hw->mod_params->bfmer) {
+            const u8 *vht_capa_ie;
+            const struct ieee80211_vht_cap *vht_cap;
+
+            do {
+                /* Look for VHT Capability Information Element */
+                vht_capa_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, rsp_ie,
+                                               ind->assoc_rsp_ie_len);
+
+                /* Stop here if peer device does not support VHT */
+                if (!vht_capa_ie) {
+                    break;
+                }
+
+                vht_cap = (const struct ieee80211_vht_cap *)(vht_capa_ie + 2);
+
+                /* Send MM_BFMER_ENABLE_REQ message if needed */
+                rwnx_send_bfmer_enable(rwnx_hw, sta, vht_cap);
+            } while (0);
+        }
+#endif //(CONFIG_RWNX_BFMER)
+
+#ifdef CONFIG_RWNX_MON_DATA
+        // If there are 1 sta and 1 monitor interface active at the same time then
+        // monitor interface channel context is always the same as the STA interface.
+        // This doesn't work with 2 STA interfaces but we don't want to support it.
+        if (rwnx_hw->monitor_vif != RWNX_INVALID_VIF) {
+            struct rwnx_vif *rwnx_mon_vif = rwnx_hw->vif_table[rwnx_hw->monitor_vif];
+            rwnx_chanctx_unlink(rwnx_mon_vif);
+            rwnx_chanctx_link(rwnx_mon_vif, ind->ch_idx, NULL);
+        }
+#endif
+    } else if (ind->status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) {
+        if (rwnx_vif->wep_enabled) {
+            rwnx_vif->wep_auth_err = true;
+            printk("con ind wep_auth_err %d\n", rwnx_vif->wep_auth_err);
+        }
+    }
+
+	if (!ind->roamed)
+		cfg80211_connect_result(dev, (const u8 *)ind->bssid.array, req_ie,
+								ind->assoc_req_ie_len, rsp_ie,
+								ind->assoc_rsp_ie_len, ind->status_code,
+								GFP_ATOMIC);
+	else {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+		struct cfg80211_roam_info info;
+		memset(&info, 0, sizeof(info));
+		if (rwnx_vif->ch_index < NX_CHAN_CTXT_CNT)
+			info.channel = rwnx_hw->chanctx_table[rwnx_vif->ch_index].chan_def.chan;
+		info.bssid = (const u8 *)ind->bssid.array;
+		info.req_ie = req_ie;
+		info.req_ie_len = ind->assoc_req_ie_len;
+		info.resp_ie = rsp_ie;
+		info.resp_ie_len = ind->assoc_rsp_ie_len;
+		cfg80211_roamed(dev, &info, GFP_ATOMIC);
+#else
+		chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq);
+		cfg80211_roamed(dev
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) || defined(COMPAT_KERNEL_RELEASE)
+			, chan
+#endif
+			, (const u8 *)ind->bssid.array
+			, req_ie
+			, ind->assoc_req_ie_len
+			, rsp_ie
+			, ind->assoc_rsp_ie_len
+			, GFP_ATOMIC);
+#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)*/
+	}
+
+    netif_tx_start_all_queues(dev);
+    netif_carrier_on(dev);
+
+    return 0;
+}
+
+extern u8 dhcped;
+static inline int rwnx_rx_sm_disconnect_ind(struct rwnx_hw *rwnx_hw,
+                                            struct rwnx_cmd *cmd,
+                                            struct ipc_e2a_msg *msg)
+{
+    struct sm_disconnect_ind *ind = (struct sm_disconnect_ind *)msg->param;
+    struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+    struct net_device *dev = rwnx_vif->ndev;
+#ifdef AICWF_RX_REORDER
+    struct reord_ctrl_info *reord_info, *tmp;
+    u8 *macaddr;
+    struct aicwf_rx_priv *rx_priv;
+#endif
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+    dhcped = 0;
+
+	if(!rwnx_vif)
+		return 0;
+	dev = rwnx_vif->ndev;
+    if(rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
+        rwnx_hw->is_p2p_connected = 0;
+    /* if vif is not up, rwnx_close has already been called */
+    if (rwnx_vif->up) {
+		if (!ind->ft_over_ds && !ind->reassoc) {
+            cfg80211_disconnected(dev, ind->reason_code, NULL, 0,
+                                  (ind->reason_code <= 1), GFP_ATOMIC);
+        }
+        netif_tx_stop_all_queues(dev);
+        netif_carrier_off(dev);
+    }
+
+#ifdef CONFIG_RWNX_BFMER
+    /* Disable Beamformer if supported */
+    rwnx_bfmer_report_del(rwnx_hw, rwnx_vif->sta.ap);
+#endif //(CONFIG_RWNX_BFMER)
+
+#ifdef AICWF_RX_REORDER
+#ifdef AICWF_SDIO_SUPPORT
+    rx_priv = rwnx_hw->sdiodev->rx_priv;
+#else
+    rx_priv = rwnx_hw->usbdev->rx_priv;
+#endif
+    if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) {
+        macaddr = rwnx_vif->ndev->dev_addr;
+        printk("deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0],macaddr[1],macaddr[2], \
+                               macaddr[3],macaddr[4],macaddr[5]);
+
+        spin_lock_bh(&rx_priv->stas_reord_lock);
+        list_for_each_entry_safe(reord_info, tmp, &rx_priv->stas_reord_list, list) {
+            macaddr = rwnx_vif->ndev->dev_addr;
+            printk("reord_mac:%x,%x,%x,%x,%x,%x\r\n", reord_info->mac_addr[0],reord_info->mac_addr[1],reord_info->mac_addr[2], \
+                                   reord_info->mac_addr[3],reord_info->mac_addr[4],reord_info->mac_addr[5]);
+            if (!memcmp(reord_info->mac_addr, macaddr, 6)) {
+                reord_deinit_sta(rx_priv, reord_info);
+                break;
+            }
+        }
+        spin_unlock_bh(&rx_priv->stas_reord_lock);
+    }
+    else if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO))
+        BUG();//should be not here: del_sta function
+#endif
+
+    rwnx_txq_sta_deinit(rwnx_hw, rwnx_vif->sta.ap);
+    rwnx_txq_tdls_vif_deinit(rwnx_vif);
+    #ifdef CONFIG_DEBUG_FS_AIC
+    rwnx_dbgfs_unregister_rc_stat(rwnx_hw, rwnx_vif->sta.ap);
+    #endif
+    rwnx_vif->sta.ap->valid = false;
+    rwnx_vif->sta.ap = NULL;
+    rwnx_external_auth_disable(rwnx_vif);
+    rwnx_chanctx_unlink(rwnx_vif);
+
+    return 0;
+}
+
+static inline int rwnx_rx_sm_external_auth_required_ind(struct rwnx_hw *rwnx_hw,
+                                                        struct rwnx_cmd *cmd,
+                                                        struct ipc_e2a_msg *msg)
+{
+    struct sm_external_auth_required_ind *ind =
+        (struct sm_external_auth_required_ind *)msg->param;
+    struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_WPA3_FOR_OLD_KERNEL)
+    struct net_device *dev = rwnx_vif->ndev;
+    struct cfg80211_external_auth_params params;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    params.action = NL80211_EXTERNAL_AUTH_START;
+    memcpy(params.bssid, ind->bssid.array, ETH_ALEN);
+    params.ssid.ssid_len = ind->ssid.length;
+    memcpy(params.ssid.ssid, ind->ssid.array,
+           min_t(size_t, ind->ssid.length, sizeof(params.ssid.ssid)));
+    params.key_mgmt_suite = ind->akm;
+
+    if ((ind->vif_idx > NX_VIRT_DEV_MAX) || !rwnx_vif->up ||
+        (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_STATION) ||
+        cfg80211_external_auth_request(dev, &params, GFP_ATOMIC)) {
+        wiphy_err(rwnx_hw->wiphy, "Failed to start external auth on vif %d",
+                  ind->vif_idx);
+        rwnx_send_sm_external_auth_required_rsp(rwnx_hw, rwnx_vif,
+                                                WLAN_STATUS_UNSPECIFIED_FAILURE);
+        return 0;
+    }
+
+    rwnx_external_auth_enable(rwnx_vif);
+#else
+    rwnx_send_sm_external_auth_required_rsp(rwnx_hw, rwnx_vif,
+                                            WLAN_STATUS_UNSPECIFIED_FAILURE);
+#endif
+    return 0;
+}
+
+
+static inline int rwnx_rx_mesh_path_create_cfm(struct rwnx_hw *rwnx_hw,
+                                               struct rwnx_cmd *cmd,
+                                               struct ipc_e2a_msg *msg)
+{
+    struct mesh_path_create_cfm *cfm = (struct mesh_path_create_cfm *)msg->param;
+    struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[cfm->vif_idx];
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Check we well have a Mesh Point Interface */
+    if (rwnx_vif && (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MESH_POINT)) {
+        rwnx_vif->ap.create_path = false;
+    }
+
+    return 0;
+}
+
+static inline int rwnx_rx_mesh_peer_update_ind(struct rwnx_hw *rwnx_hw,
+                                               struct rwnx_cmd *cmd,
+                                               struct ipc_e2a_msg *msg)
+{
+    struct mesh_peer_update_ind *ind = (struct mesh_peer_update_ind *)msg->param;
+    struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+    struct rwnx_sta *rwnx_sta = &rwnx_hw->sta_table[ind->sta_idx];
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if ((ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX)) ||
+        (rwnx_vif && (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)) ||
+        (ind->sta_idx >= NX_REMOTE_STA_MAX))
+        return 1;
+
+    /* Check we well have a Mesh Point Interface */
+    if (!rwnx_vif->user_mpm)
+    {
+        /* Check if peer link has been established or lost */
+        if (ind->estab) {
+            if (!rwnx_sta->valid) {
+                u8 txq_status;
+
+                rwnx_sta->valid = true;
+                rwnx_sta->sta_idx = ind->sta_idx;
+                rwnx_sta->ch_idx = rwnx_vif->ch_index;
+                rwnx_sta->vif_idx = ind->vif_idx;
+                rwnx_sta->vlan_idx = rwnx_sta->vif_idx;
+                rwnx_sta->ps.active = false;
+                rwnx_sta->qos = true;
+                rwnx_sta->aid = ind->sta_idx + 1;
+                //rwnx_sta->acm = ind->acm;
+                memcpy(rwnx_sta->mac_addr, ind->peer_addr.array, ETH_ALEN);
+
+                rwnx_chanctx_link(rwnx_vif, rwnx_sta->ch_idx, NULL);
+
+                /* Add the station in the list of VIF's stations */
+                INIT_LIST_HEAD(&rwnx_sta->list);
+                list_add_tail(&rwnx_sta->list, &rwnx_vif->ap.sta_list);
+
+                /* Initialize the TX queues */
+                if (rwnx_sta->ch_idx == rwnx_hw->cur_chanctx) {
+                    txq_status = 0;
+                } else {
+                    txq_status = RWNX_TXQ_STOP_CHAN;
+                }
+
+                rwnx_txq_sta_init(rwnx_hw, rwnx_sta, txq_status);
+		#ifdef CONFIG_DEBUG_FS_AIC
+                rwnx_dbgfs_register_rc_stat(rwnx_hw, rwnx_sta);
+		#endif
+#ifdef CONFIG_RWNX_BFMER
+                // TODO: update indication to contains vht capabilties
+                if (rwnx_hw->mod_params->bfmer)
+                    rwnx_send_bfmer_enable(rwnx_hw, rwnx_sta, NULL);
+
+                rwnx_mu_group_sta_init(rwnx_sta, NULL);
+#endif /* CONFIG_RWNX_BFMER */
+
+            } else {
+                WARN_ON(0);
+            }
+        } else {
+            if (rwnx_sta->valid) {
+                rwnx_sta->ps.active = false;
+                rwnx_sta->valid = false;
+
+                /* Remove the station from the list of VIF's station */
+                list_del_init(&rwnx_sta->list);
+
+                rwnx_txq_sta_deinit(rwnx_hw, rwnx_sta);
+                #ifdef CONFIG_DEBUG_FS_AIC
+                rwnx_dbgfs_unregister_rc_stat(rwnx_hw, rwnx_sta);
+                #endif
+            } else {
+                WARN_ON(0);
+            }
+        }
+    } else {
+        if (!ind->estab && rwnx_sta->valid) {
+            /* There is no way to inform upper layer for lost of peer, still
+               clean everything in the driver */
+            rwnx_sta->ps.active = false;
+            rwnx_sta->valid = false;
+
+            /* Remove the station from the list of VIF's station */
+            list_del_init(&rwnx_sta->list);
+
+            rwnx_txq_sta_deinit(rwnx_hw, rwnx_sta);
+            #ifdef CONFIG_DEBUG_FS_AIC
+            rwnx_dbgfs_unregister_rc_stat(rwnx_hw, rwnx_sta);
+            #endif
+        } else {
+            WARN_ON(0);
+        }
+    }
+
+    return 0;
+}
+
+static inline int rwnx_rx_mesh_path_update_ind(struct rwnx_hw *rwnx_hw,
+                                               struct rwnx_cmd *cmd,
+                                               struct ipc_e2a_msg *msg)
+{
+    struct mesh_path_update_ind *ind = (struct mesh_path_update_ind *)msg->param;
+    struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+    struct rwnx_mesh_path *mesh_path;
+    bool found = false;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX))
+        return 1;
+
+    if (!rwnx_vif || (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT))
+        return 0;
+
+    /* Look for path with provided target address */
+    list_for_each_entry(mesh_path, &rwnx_vif->ap.mpath_list, list) {
+        if (mesh_path->path_idx == ind->path_idx) {
+            found = true;
+            break;
+        }
+    }
+
+    /* Check if element has been deleted */
+    if (ind->delete) {
+        if (found) {
+#ifdef CREATE_TRACE_POINTS
+            trace_mesh_delete_path(mesh_path);
+#endif
+            /* Remove element from list */
+            list_del_init(&mesh_path->list);
+            /* Free the element */
+            kfree(mesh_path);
+        }
+    }
+    else {
+        if (found) {
+            // Update the Next Hop STA
+            mesh_path->p_nhop_sta = &rwnx_hw->sta_table[ind->nhop_sta_idx];
+#ifdef CREATE_TRACE_POINTS
+            trace_mesh_update_path(mesh_path);
+#endif
+        } else {
+            // Allocate a Mesh Path structure
+            mesh_path = (struct rwnx_mesh_path *)kmalloc(sizeof(struct rwnx_mesh_path), GFP_ATOMIC);
+
+            if (mesh_path) {
+                INIT_LIST_HEAD(&mesh_path->list);
+
+                mesh_path->path_idx = ind->path_idx;
+                mesh_path->p_nhop_sta = &rwnx_hw->sta_table[ind->nhop_sta_idx];
+                memcpy(&mesh_path->tgt_mac_addr, &ind->tgt_mac_addr, MAC_ADDR_LEN);
+
+                // Insert the path in the list of path
+                list_add_tail(&mesh_path->list, &rwnx_vif->ap.mpath_list);
+#ifdef CREATE_TRACE_POINTS
+                trace_mesh_create_path(mesh_path);
+#endif
+            }
+        }
+    }
+
+    return 0;
+}
+
+static inline int rwnx_rx_mesh_proxy_update_ind(struct rwnx_hw *rwnx_hw,
+                                               struct rwnx_cmd *cmd,
+                                               struct ipc_e2a_msg *msg)
+{
+    struct mesh_proxy_update_ind *ind = (struct mesh_proxy_update_ind *)msg->param;
+    struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+    struct rwnx_mesh_proxy *mesh_proxy;
+    bool found = false;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX))
+        return 1;
+
+    if (!rwnx_vif || (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT))
+        return 0;
+
+    /* Look for path with provided external STA address */
+    list_for_each_entry(mesh_proxy, &rwnx_vif->ap.proxy_list, list) {
+        if (!memcmp(&ind->ext_sta_addr, &mesh_proxy->ext_sta_addr, ETH_ALEN)) {
+            found = true;
+            break;
+        }
+    }
+
+    if (ind->delete && found) {
+        /* Delete mesh path */
+        list_del_init(&mesh_proxy->list);
+        kfree(mesh_proxy);
+    } else if (!ind->delete && !found) {
+        /* Allocate a Mesh Path structure */
+        mesh_proxy = (struct rwnx_mesh_proxy *)kmalloc(sizeof(*mesh_proxy),
+                                                       GFP_ATOMIC);
+
+        if (mesh_proxy) {
+            INIT_LIST_HEAD(&mesh_proxy->list);
+
+            memcpy(&mesh_proxy->ext_sta_addr, &ind->ext_sta_addr, MAC_ADDR_LEN);
+            mesh_proxy->local = ind->local;
+
+            if (!ind->local) {
+                memcpy(&mesh_proxy->proxy_addr, &ind->proxy_mac_addr, MAC_ADDR_LEN);
+            }
+
+            /* Insert the path in the list of path */
+            list_add_tail(&mesh_proxy->list, &rwnx_vif->ap.proxy_list);
+        }
+    }
+
+    return 0;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from APM task
+ **************************************************************************/
+
+
+/***************************************************************************
+ * Messages from DEBUG task
+ **************************************************************************/
+static inline int rwnx_rx_dbg_error_ind(struct rwnx_hw *rwnx_hw,
+                                        struct rwnx_cmd *cmd,
+                                        struct ipc_e2a_msg *msg)
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    return 0;
+}
+
+#ifdef CONFIG_MCU_MESSAGE
+static inline int rwnx_rx_dbg_custmsg_ind(struct rwnx_hw *rwnx_hw,
+                                          struct rwnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    dbg_custom_msg_ind_t * ind;
+    char str_msg[32 + 1];
+    int str_len;
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    ind = (dbg_custom_msg_ind_t *)msg->param;
+    str_len = (ind->len < 32) ? ind->len : 32;
+    memcpy(str_msg, (char *)ind->buf, str_len);
+    str_msg[str_len] = '\0';
+    printk("CustMsgInd: cmd=0x%x, len=%d, str=%s\r\n", ind->cmd, ind->len, str_msg);
+
+    return 0;
+}
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+static msg_cb_fct mm_hdlrs[MSG_I(MM_MAX)] = {
+    [MSG_I(MM_CHANNEL_SWITCH_IND)]     = rwnx_rx_chan_switch_ind,
+    [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = rwnx_rx_chan_pre_switch_ind,
+    [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = rwnx_rx_remain_on_channel_exp_ind,
+    [MSG_I(MM_PS_CHANGE_IND)]          = rwnx_rx_ps_change_ind,
+    [MSG_I(MM_TRAFFIC_REQ_IND)]        = rwnx_rx_traffic_req_ind,
+    [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)]  = rwnx_rx_p2p_vif_ps_change_ind,
+    [MSG_I(MM_CSA_COUNTER_IND)]        = rwnx_rx_csa_counter_ind,
+    [MSG_I(MM_CSA_FINISH_IND)]         = rwnx_rx_csa_finish_ind,
+    [MSG_I(MM_CSA_TRAFFIC_IND)]        = rwnx_rx_csa_traffic_ind,
+    [MSG_I(MM_CHANNEL_SURVEY_IND)]     = rwnx_rx_channel_survey_ind,
+    [MSG_I(MM_P2P_NOA_UPD_IND)]        = rwnx_rx_p2p_noa_upd_ind,
+    [MSG_I(MM_RSSI_STATUS_IND)]        = rwnx_rx_rssi_status_ind,
+    [MSG_I(MM_PKTLOSS_IND)]            = rwnx_rx_pktloss_notify_ind,
+    [MSG_I(MM_APM_STALOSS_IND)]        = rwnx_apm_staloss_ind,
+};
+
+static msg_cb_fct scan_hdlrs[MSG_I(SCANU_MAX)] = {
+    [MSG_I(SCANU_START_CFM)]           = rwnx_rx_scanu_start_cfm,
+    [MSG_I(SCANU_RESULT_IND)]          = rwnx_rx_scanu_result_ind,
+};
+
+static msg_cb_fct me_hdlrs[MSG_I(ME_MAX)] = {
+    [MSG_I(ME_TKIP_MIC_FAILURE_IND)] = rwnx_rx_me_tkip_mic_failure_ind,
+    [MSG_I(ME_TX_CREDITS_UPDATE_IND)] = rwnx_rx_me_tx_credits_update_ind,
+};
+
+static msg_cb_fct sm_hdlrs[MSG_I(SM_MAX)] = {
+    [MSG_I(SM_CONNECT_IND)]    = rwnx_rx_sm_connect_ind,
+    [MSG_I(SM_DISCONNECT_IND)] = rwnx_rx_sm_disconnect_ind,
+    [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_IND)] = rwnx_rx_sm_external_auth_required_ind,
+};
+
+static msg_cb_fct apm_hdlrs[MSG_I(APM_MAX)] = {
+};
+
+static msg_cb_fct mesh_hdlrs[MSG_I(MESH_MAX)] = {
+    [MSG_I(MESH_PATH_CREATE_CFM)]  = rwnx_rx_mesh_path_create_cfm,
+    [MSG_I(MESH_PEER_UPDATE_IND)]  = rwnx_rx_mesh_peer_update_ind,
+    [MSG_I(MESH_PATH_UPDATE_IND)]  = rwnx_rx_mesh_path_update_ind,
+    [MSG_I(MESH_PROXY_UPDATE_IND)] = rwnx_rx_mesh_proxy_update_ind,
+};
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+static msg_cb_fct dbg_hdlrs[MSG_I(DBG_MAX)] = {
+    [MSG_I(DBG_ERROR_IND)]                = rwnx_rx_dbg_error_ind,
+    #ifdef CONFIG_MCU_MESSAGE
+    [MSG_I(DBG_CUSTOM_MSG_IND)]           = rwnx_rx_dbg_custmsg_ind,
+    #endif
+};
+
+static msg_cb_fct tdls_hdlrs[MSG_I(TDLS_MAX)] = {
+    [MSG_I(TDLS_CHAN_SWITCH_CFM)] = rwnx_rx_tdls_chan_switch_cfm,
+    [MSG_I(TDLS_CHAN_SWITCH_IND)] = rwnx_rx_tdls_chan_switch_ind,
+    [MSG_I(TDLS_CHAN_SWITCH_BASE_IND)] = rwnx_rx_tdls_chan_switch_base_ind,
+    [MSG_I(TDLS_PEER_PS_IND)] = rwnx_rx_tdls_peer_ps_ind,
+};
+
+static msg_cb_fct *msg_hdlrs[] = {
+    [TASK_MM]    = mm_hdlrs,
+    [TASK_DBG]   = dbg_hdlrs,
+#ifdef CONFIG_RWNX_FULLMAC
+    [TASK_TDLS]  = tdls_hdlrs,
+    [TASK_SCANU] = scan_hdlrs,
+    [TASK_ME]    = me_hdlrs,
+    [TASK_SM]    = sm_hdlrs,
+    [TASK_APM]   = apm_hdlrs,
+    [TASK_MESH]  = mesh_hdlrs,
+#endif /* CONFIG_RWNX_FULLMAC */
+};
+
+/**
+ *
+ */
+void rwnx_rx_handle_msg(struct rwnx_hw *rwnx_hw, struct ipc_e2a_msg *msg)
+{
+    rwnx_hw->cmd_mgr->msgind(rwnx_hw->cmd_mgr, msg,
+                            msg_hdlrs[MSG_T(msg->id)][MSG_I(msg->id)]);
+}
+
+void rwnx_rx_handle_print(struct rwnx_hw *rwnx_hw, u8 *msg, u32 len)
+{
+	u8 *data_end = NULL;
+	(void)data_end;
+
+//	if (!rwnx_hw || !rwnx_hw->fwlog_en) {
+    if (!rwnx_hw) {
+		pr_err("FWLOG-OVFL: %s", msg);
+		return;
+	}
+
+	printk("FWLOG: %s", msg);
+
+#ifdef CONFIG_RWNX_DEBUGFS
+	data_end = rwnx_hw->debugfs.fw_log.buf.dataend;
+
+	if (!rwnx_hw->debugfs.fw_log.buf.data)
+		return ;
+
+	//printk("end=%lx, len=%d\n", (unsigned long)rwnx_hw->debugfs.fw_log.buf.end, len);
+
+	spin_lock_bh(&rwnx_hw->debugfs.fw_log.lock);
+
+	if (rwnx_hw->debugfs.fw_log.buf.end + len > data_end) {
+		int rem = data_end - rwnx_hw->debugfs.fw_log.buf.end;
+		memcpy(rwnx_hw->debugfs.fw_log.buf.end, msg, rem);
+		memcpy(rwnx_hw->debugfs.fw_log.buf.data, &msg[rem], len - rem);
+		rwnx_hw->debugfs.fw_log.buf.end = rwnx_hw->debugfs.fw_log.buf.data + (len - rem);
+	} else {
+		memcpy(rwnx_hw->debugfs.fw_log.buf.end, msg, len);
+		rwnx_hw->debugfs.fw_log.buf.end += len;
+	}
+
+	rwnx_hw->debugfs.fw_log.buf.size += len;
+	if (rwnx_hw->debugfs.fw_log.buf.size > FW_LOG_SIZE)
+		rwnx_hw->debugfs.fw_log.buf.size = FW_LOG_SIZE;
+
+	spin_unlock_bh(&rwnx_hw->debugfs.fw_log.lock);
+#endif
+}
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_msg_rx.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_msg_rx.h
new file mode 100755
index 0000000..b5556d1
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_msg_rx.h
@@ -0,0 +1,19 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_msg_rx.h
+ *
+ * @brief RX function declarations
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_MSG_RX_H_
+#define _RWNX_MSG_RX_H_
+
+void rwnx_rx_handle_msg(struct rwnx_hw *rwnx_hw, struct ipc_e2a_msg *msg);
+void rwnx_rx_handle_print(struct rwnx_hw *rwnx_hw, u8 *msg, u32 len);
+
+#endif /* _RWNX_MSG_RX_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_msg_tx.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_msg_tx.c
new file mode 100755
index 0000000..c1796f9
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_msg_tx.c
@@ -0,0 +1,3371 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_msg_tx.c
+ *
+ * @brief TX function definitions
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#include "rwnx_msg_tx.h"
+#include "rwnx_mod_params.h"
+#include "reg_access.h"
+#ifdef CONFIG_RWNX_BFMER
+#include "rwnx_bfmer.h"
+#endif //(CONFIG_RWNX_BFMER)
+#include "rwnx_compat.h"
+#include "rwnx_cmds.h"
+#include "rwnx_main.h"
+#include "aicwf_txrxif.h"
+#include "rwnx_strs.h"
+
+extern u8 chip_sub_id;
+extern u8 chip_mcu_id;
+
+const struct mac_addr mac_addr_bcst = {{0xFFFF, 0xFFFF, 0xFFFF}};
+
+/* Default MAC Rx filters that can be changed by mac80211
+ * (via the configure_filter() callback) */
+#define RWNX_MAC80211_CHANGEABLE        (                                       \
+                                         NXMAC_ACCEPT_BA_BIT                  | \
+                                         NXMAC_ACCEPT_BAR_BIT                 | \
+                                         NXMAC_ACCEPT_OTHER_DATA_FRAMES_BIT   | \
+                                         NXMAC_ACCEPT_PROBE_REQ_BIT           | \
+                                         NXMAC_ACCEPT_PS_POLL_BIT               \
+                                        )
+
+/* Default MAC Rx filters that cannot be changed by mac80211 */
+#define RWNX_MAC80211_NOT_CHANGEABLE    (                                       \
+                                         NXMAC_ACCEPT_QO_S_NULL_BIT           | \
+                                         NXMAC_ACCEPT_Q_DATA_BIT              | \
+                                         NXMAC_ACCEPT_DATA_BIT                | \
+                                         NXMAC_ACCEPT_OTHER_MGMT_FRAMES_BIT   | \
+                                         NXMAC_ACCEPT_MY_UNICAST_BIT          | \
+                                         NXMAC_ACCEPT_BROADCAST_BIT           | \
+                                         NXMAC_ACCEPT_BEACON_BIT              | \
+                                         NXMAC_ACCEPT_PROBE_RESP_BIT            \
+                                        )
+
+/* Default MAC Rx filter */
+#define RWNX_DEFAULT_RX_FILTER  (RWNX_MAC80211_CHANGEABLE | RWNX_MAC80211_NOT_CHANGEABLE)
+
+const int bw2chnl[] = {
+    [NL80211_CHAN_WIDTH_20_NOHT] = PHY_CHNL_BW_20,
+    [NL80211_CHAN_WIDTH_20]      = PHY_CHNL_BW_20,
+    [NL80211_CHAN_WIDTH_40]      = PHY_CHNL_BW_40,
+    [NL80211_CHAN_WIDTH_80]      = PHY_CHNL_BW_80,
+    [NL80211_CHAN_WIDTH_160]     = PHY_CHNL_BW_160,
+    [NL80211_CHAN_WIDTH_80P80]   = PHY_CHNL_BW_80P80,
+};
+
+const int chnl2bw[] = {
+    [PHY_CHNL_BW_20]      = NL80211_CHAN_WIDTH_20,
+    [PHY_CHNL_BW_40]      = NL80211_CHAN_WIDTH_40,
+    [PHY_CHNL_BW_80]      = NL80211_CHAN_WIDTH_80,
+    [PHY_CHNL_BW_160]     = NL80211_CHAN_WIDTH_160,
+    [PHY_CHNL_BW_80P80]   = NL80211_CHAN_WIDTH_80P80,
+};
+
+/*****************************************************************************/
+/*
+ * Parse the ampdu density to retrieve the value in usec, according to the
+ * values defined in ieee80211.h
+ */
+static inline u8 rwnx_ampdudensity2usec(u8 ampdudensity)
+{
+    switch (ampdudensity) {
+    case IEEE80211_HT_MPDU_DENSITY_NONE:
+        return 0;
+        /* 1 microsecond is our granularity */
+    case IEEE80211_HT_MPDU_DENSITY_0_25:
+    case IEEE80211_HT_MPDU_DENSITY_0_5:
+    case IEEE80211_HT_MPDU_DENSITY_1:
+        return 1;
+    case IEEE80211_HT_MPDU_DENSITY_2:
+        return 2;
+    case IEEE80211_HT_MPDU_DENSITY_4:
+        return 4;
+    case IEEE80211_HT_MPDU_DENSITY_8:
+        return 8;
+    case IEEE80211_HT_MPDU_DENSITY_16:
+        return 16;
+    default:
+        return 0;
+    }
+}
+
+static inline bool use_pairwise_key(struct cfg80211_crypto_settings *crypto)
+{
+    if ((crypto->cipher_group ==  WLAN_CIPHER_SUITE_WEP40) ||
+        (crypto->cipher_group ==  WLAN_CIPHER_SUITE_WEP104))
+        return false;
+
+    return true;
+}
+
+static inline bool is_non_blocking_msg(int id)
+{
+    return ((id == MM_TIM_UPDATE_REQ) || (id == ME_RC_SET_RATE_REQ) ||
+            (id == MM_BFMER_ENABLE_REQ) || (id == ME_TRAFFIC_IND_REQ) ||
+            (id == TDLS_PEER_TRAFFIC_IND_REQ) ||
+            (id == MESH_PATH_CREATE_REQ) || (id == MESH_PROXY_ADD_REQ) ||
+            (id == SM_EXTERNAL_AUTH_REQUIRED_RSP));
+}
+
+static inline u8_l get_chan_flags(uint32_t flags)
+{
+    u8_l chan_flags = 0;
+#ifdef RADAR_OR_IR_DETECT
+    #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
+    if (flags & IEEE80211_CHAN_PASSIVE_SCAN)
+    #else
+    if (flags & IEEE80211_CHAN_NO_IR)
+    #endif
+        chan_flags |= CHAN_NO_IR;
+    if (flags & IEEE80211_CHAN_RADAR)
+        chan_flags |= CHAN_RADAR;
+#endif
+    return chan_flags;
+}
+
+static inline s8_l chan_to_fw_pwr(int power)
+{
+    return power>127?127:(s8_l)power;
+}
+
+static inline void limit_chan_bw(u8_l *bw, u16_l primary, u16_l *center1)
+{
+    int oft, new_oft = 10;
+
+    if (*bw <= PHY_CHNL_BW_40)
+        return;
+
+    oft = *center1 - primary;
+    *bw = PHY_CHNL_BW_40;
+
+    if (oft < 0)
+        new_oft = new_oft * -1;
+    if (abs(oft) == 10 || abs(oft) == 50)
+        new_oft = new_oft * -1;
+
+    *center1 = primary + new_oft;
+}
+
+/**
+ ******************************************************************************
+ * @brief Allocate memory for a message
+ *
+ * This primitive allocates memory for a message that has to be sent. The memory
+ * is allocated dynamically on the heap and the length of the variable parameter
+ * structure has to be provided in order to allocate the correct size.
+ *
+ * Several additional parameters are provided which will be preset in the message
+ * and which may be used internally to choose the kind of memory to allocate.
+ *
+ * The memory allocated will be automatically freed by the kernel, after the
+ * pointer has been sent to ke_msg_send(). If the message is not sent, it must
+ * be freed explicitly with ke_msg_free().
+ *
+ * Allocation failure is considered critical and should not happen.
+ *
+ * @param[in] id        Message identifier
+ * @param[in] dest_id   Destination Task Identifier
+ * @param[in] src_id    Source Task Identifier
+ * @param[in] param_len Size of the message parameters to be allocated
+ *
+ * @return Pointer to the parameter member of the ke_msg. If the parameter
+ *         structure is empty, the pointer will point to the end of the message
+ *         and should not be used (except to retrieve the message pointer or to
+ *         send the message)
+ ******************************************************************************
+ */
+static inline void *rwnx_msg_zalloc(lmac_msg_id_t const id,
+                                    lmac_task_id_t const dest_id,
+                                    lmac_task_id_t const src_id,
+                                    uint16_t const param_len)
+{
+    struct lmac_msg *msg;
+    gfp_t flags;
+
+    if (is_non_blocking_msg(id) && in_softirq())
+        flags = GFP_ATOMIC;
+    else
+        flags = GFP_KERNEL;
+
+    msg = (struct lmac_msg *)kzalloc(sizeof(struct lmac_msg) + param_len,
+                                     flags);
+    if (msg == NULL) {
+        printk(KERN_CRIT "%s: msg allocation failed\n", __func__);
+        return NULL;
+    }
+    msg->id = id;
+    msg->dest_id = dest_id;
+    msg->src_id = src_id;
+    msg->param_len = param_len;
+	//printk("rwnx_msg_zalloc size=%d  id=%d\n",msg->param_len,msg->id);
+
+    return msg->param;
+}
+
+static void rwnx_msg_free(struct rwnx_hw *rwnx_hw, const void *msg_params)
+{
+    struct lmac_msg *msg = container_of((void *)msg_params,
+                                        struct lmac_msg, param);
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Free the message */
+    kfree(msg);
+}
+
+static int rwnx_send_msg(struct rwnx_hw *rwnx_hw, const void *msg_params,
+                         int reqcfm, lmac_msg_id_t reqid, void *cfm)
+{
+    struct lmac_msg *msg;
+    struct rwnx_cmd *cmd;
+    bool nonblock;
+    int ret = 0;
+    u8_l empty = 0;
+
+	//RWNX_DBG(RWNX_FN_ENTRY_STR);
+    //printk("%s (%d)%s reqcfm:%d in_irq:%d in_softirq:%d in_atomic:%d\r\n",
+    //__func__, reqid, RWNX_ID2STR(reqid), reqcfm, (int)in_irq(), (int)in_softirq(), (int)in_atomic());
+
+
+#ifdef AICWF_USB_SUPPORT
+    if (rwnx_hw->usbdev->state == USB_DOWN_ST) {
+        rwnx_msg_free(rwnx_hw, msg_params);
+        usb_err("bus is down\n");
+        return 0;
+    }
+#endif
+#ifdef AICWF_SDIO_SUPPORT
+    if(rwnx_hw->sdiodev->bus_if->state == BUS_DOWN_ST) {
+        rwnx_msg_free(rwnx_hw, msg_params);
+        sdio_err("bus is down\n");
+        return 0;
+    }
+#endif
+
+    msg = container_of((void *)msg_params, struct lmac_msg, param);
+
+    #if 0
+    if (!test_bit(RWNX_DEV_STARTED, &rwnx_hw->drv_flags) &&
+        reqid != DBG_MEM_READ_CFM && reqid != DBG_MEM_WRITE_CFM &&
+        reqid != DBG_MEM_BLOCK_WRITE_CFM && reqid != DBG_START_APP_CFM &&
+        reqid != MM_SET_RF_CALIB_CFM && reqid != MM_SET_RF_CONFIG_CFM &&
+        reqid != MM_RESET_CFM && reqid != MM_VERSION_CFM &&
+        reqid != MM_START_CFM && reqid != MM_SET_IDLE_CFM &&
+        reqid != ME_CONFIG_CFM && reqid != MM_SET_PS_MODE_CFM &&
+        reqid != ME_CHAN_CONFIG_CFM) {
+        printk(KERN_CRIT "%s: bypassing (RWNX_DEV_RESTARTING set) 0x%02x\n",
+               __func__, reqid);
+        kfree(msg);
+        return -EBUSY;
+    }
+    #endif
+#if 0
+    else if (!rwnx_hw->ipc_env) {
+        printk(KERN_CRIT "%s: bypassing (restart must have failed)\n", __func__);
+        kfree(msg);
+        return -EBUSY;
+    }
+#endif
+
+    //nonblock = is_non_blocking_msg(msg->id);
+    nonblock = 0;
+    cmd = kzalloc(sizeof(struct rwnx_cmd), nonblock ? GFP_ATOMIC : GFP_KERNEL);
+    cmd->result  = -EINTR;
+    cmd->id      = msg->id;
+    cmd->reqid   = reqid;
+    cmd->a2e_msg = msg;
+    cmd->e2a_msg = cfm;
+    if (nonblock)
+        cmd->flags = RWNX_CMD_FLAG_NONBLOCK;
+    if (reqcfm)
+        cmd->flags |= RWNX_CMD_FLAG_REQ_CFM;
+
+    if(cfm != NULL) {
+        do {
+            if(rwnx_hw->cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED)
+                break;
+            spin_lock_bh(&rwnx_hw->cmd_mgr->lock);
+            empty = list_empty(&rwnx_hw->cmd_mgr->cmds);
+            spin_unlock_bh(&rwnx_hw->cmd_mgr->lock);
+            if(!empty) {
+                if(in_softirq()) {
+                    printk("in_softirq:check cmdqueue empty\n");
+                    mdelay(10);
+                } else {
+                    printk("check cmdqueue empty\n");
+                    msleep(50);
+                }
+            }
+        } while(!empty);//wait for cmd queue empty
+    }
+
+    if(reqcfm) {
+        cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK; // we don't need ack any more
+        ret = rwnx_hw->cmd_mgr->queue(rwnx_hw->cmd_mgr, cmd);
+    } else {
+#ifdef AICWF_SDIO_SUPPORT
+        aicwf_set_cmd_tx((void *)(rwnx_hw->sdiodev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+#else
+        aicwf_set_cmd_tx((void *)(rwnx_hw->usbdev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+#endif
+    }
+
+    if(!reqcfm || ret)
+        kfree(cmd);
+
+    return 0;
+}
+
+
+static int rwnx_send_msg1(struct rwnx_hw *rwnx_hw, const void *msg_params,
+                         int reqcfm, lmac_msg_id_t reqid, void *cfm, bool defer)
+{
+    struct lmac_msg *msg;
+    struct rwnx_cmd *cmd;
+    bool nonblock;
+    int ret = 0;
+
+//	RWNX_DBG(RWNX_FN_ENTRY_STR);
+    printk("%s (%d)%s reqcfm:%d in_irq:%d in_softirq:%d in_atomic:%d\r\n",
+    __func__, reqid, RWNX_ID2STR(reqid), reqcfm, (int)in_irq(), (int)in_softirq(), (int)in_atomic());
+
+    msg = container_of((void *)msg_params, struct lmac_msg, param);
+
+    //nonblock = is_non_blocking_msg(msg->id);
+	nonblock = 0;
+    cmd = kzalloc(sizeof(struct rwnx_cmd), nonblock ? GFP_ATOMIC : GFP_KERNEL);
+    cmd->result  = -EINTR;
+    cmd->id      = msg->id;
+    cmd->reqid   = reqid;
+    cmd->a2e_msg = msg;
+    cmd->e2a_msg = cfm;
+    if (nonblock)
+        cmd->flags = RWNX_CMD_FLAG_NONBLOCK;
+    if (reqcfm)
+        cmd->flags |= RWNX_CMD_FLAG_REQ_CFM;
+
+    if(reqcfm) {
+        cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK; // we don't need ack any more
+        if(!defer)
+            ret = rwnx_hw->cmd_mgr->queue(rwnx_hw->cmd_mgr, cmd);
+        else
+            ret = cmd_mgr_queue_force_defer(rwnx_hw->cmd_mgr, cmd);
+    }
+
+    if (!reqcfm || ret) {
+        kfree(cmd);
+    }
+
+    if (!ret) {
+        ret = cmd->result;
+    }
+
+    //return ret;
+    return 0;
+}
+
+/******************************************************************************
+ *    Control messages handling functions (FULLMAC)
+ *****************************************************************************/
+int rwnx_send_reset(struct rwnx_hw *rwnx_hw)
+{
+    void *void_param;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* RESET REQ has no parameter */
+    void_param = rwnx_msg_zalloc(MM_RESET_REQ, TASK_MM, DRV_TASK_ID, 0);
+    if (!void_param)
+        return -ENOMEM;
+
+    return rwnx_send_msg(rwnx_hw, void_param, 1, MM_RESET_CFM, NULL);
+}
+
+int rwnx_send_start(struct rwnx_hw *rwnx_hw)
+{
+    struct mm_start_req *start_req_param;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the START REQ message */
+    start_req_param = rwnx_msg_zalloc(MM_START_REQ, TASK_MM, DRV_TASK_ID,
+                                      sizeof(struct mm_start_req));
+    if (!start_req_param)
+        return -ENOMEM;
+
+    /* Set parameters for the START message */
+    memcpy(&start_req_param->phy_cfg, &rwnx_hw->phy.cfg, sizeof(rwnx_hw->phy.cfg));
+    start_req_param->uapsd_timeout = (u32_l)rwnx_hw->mod_params->uapsd_timeout;
+    start_req_param->lp_clk_accuracy = (u16_l)rwnx_hw->mod_params->lp_clk_ppm;
+
+    /* Send the START REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, start_req_param, 1, MM_START_CFM, NULL);
+}
+
+rwnx_send_get_temp_req(struct rwnx_hw *rwnx_hw, struct mm_set_vendor_hwconfig_cfm *cfm)
+{
+	int error;
+	struct mm_get_chip_temp_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the CHIP_TEMP_GET_REQ message */
+	req = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, sizeof(struct mm_get_chip_temp_req));
+	if (!req)
+		return -ENOMEM;
+	req->hwconfig_id = 5;
+	/* Send the MM_SET_VENDOR_HWCONFIG_REQ  message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_HWCONFIG_CFM, cfm);
+	if (!error) {
+		printk("get_chip_temp degree=%d\n", cfm->chip_temp_cfm.degree);
+	} else {
+		printk("get_chip_temp err=%d\n", error);
+		return error;
+	}
+	return error;
+}
+
+int rwnx_send_version_req(struct rwnx_hw *rwnx_hw, struct mm_version_cfm *cfm)
+{
+    void *void_param;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* VERSION REQ has no parameter */
+    void_param = rwnx_msg_zalloc(MM_VERSION_REQ, TASK_MM, DRV_TASK_ID, 0);
+    if (!void_param)
+        return -ENOMEM;
+
+    return rwnx_send_msg(rwnx_hw, void_param, 1, MM_VERSION_CFM, cfm);
+}
+
+int rwnx_send_add_if(struct rwnx_hw *rwnx_hw, const unsigned char *mac,
+                     enum nl80211_iftype iftype, bool p2p, struct mm_add_if_cfm *cfm)
+{
+    struct mm_add_if_req *add_if_req_param;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the ADD_IF_REQ message */
+    add_if_req_param = rwnx_msg_zalloc(MM_ADD_IF_REQ, TASK_MM, DRV_TASK_ID,
+                                       sizeof(struct mm_add_if_req));
+    if (!add_if_req_param)
+        return -ENOMEM;
+
+    /* Set parameters for the ADD_IF_REQ message */
+    memcpy(&(add_if_req_param->addr.array[0]), mac, ETH_ALEN);
+    switch (iftype) {
+    #ifdef CONFIG_RWNX_FULLMAC
+    //case NL80211_IFTYPE_P2P_DEVICE:
+    case NL80211_IFTYPE_P2P_CLIENT:
+        add_if_req_param->p2p = true;
+        // no break
+    #endif /* CONFIG_RWNX_FULLMAC */
+    case NL80211_IFTYPE_STATION:
+        add_if_req_param->type = MM_STA;
+        break;
+
+    case NL80211_IFTYPE_ADHOC:
+        add_if_req_param->type = MM_IBSS;
+        break;
+
+    #ifdef CONFIG_RWNX_FULLMAC
+    case NL80211_IFTYPE_P2P_GO:
+        add_if_req_param->p2p = true;
+        // no break
+    #endif /* CONFIG_RWNX_FULLMAC */
+    case NL80211_IFTYPE_AP:
+        add_if_req_param->type = MM_AP;
+        break;
+    case NL80211_IFTYPE_MESH_POINT:
+        add_if_req_param->type = MM_MESH_POINT;
+        break;
+    case NL80211_IFTYPE_AP_VLAN:
+        return -1;
+    case NL80211_IFTYPE_MONITOR:
+        add_if_req_param->type = MM_MONITOR;
+        break;
+    default:
+        add_if_req_param->type = MM_STA;
+        break;
+    }
+
+
+    /* Send the ADD_IF_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, add_if_req_param, 1, MM_ADD_IF_CFM, cfm);
+}
+
+int rwnx_send_remove_if(struct rwnx_hw *rwnx_hw, u8 vif_index, bool defer)
+{
+    struct mm_remove_if_req *remove_if_req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_REMOVE_IF_REQ message */
+    remove_if_req = rwnx_msg_zalloc(MM_REMOVE_IF_REQ, TASK_MM, DRV_TASK_ID,
+                                    sizeof(struct mm_remove_if_req));
+    if (!remove_if_req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_REMOVE_IF_REQ message */
+    remove_if_req->inst_nbr = vif_index;
+
+    /* Send the MM_REMOVE_IF_REQ message to LMAC FW */
+    return rwnx_send_msg1(rwnx_hw, remove_if_req, 1, MM_REMOVE_IF_CFM, NULL, defer);
+}
+
+int rwnx_send_set_channel(struct rwnx_hw *rwnx_hw, int phy_idx,
+                          struct mm_set_channel_cfm *cfm)
+{
+	struct mm_set_channel_req *req;
+	enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT;
+	u16 center_freq = 2412, center_freq1 = 2412, center_freq2 = 2412;
+	s8 tx_power = 0;
+	u8 flags = 0;
+	enum nl80211_band band = NL80211_BAND_2GHZ;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (phy_idx >= rwnx_hw->phy.cnt)
+        return -ENOTSUPP;
+
+    req = rwnx_msg_zalloc(MM_SET_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_set_channel_req));
+    if (!req)
+        return -ENOMEM;
+
+    if (phy_idx == 0) {
+#ifdef CONFIG_RWNX_FULLMAC
+        /* On FULLMAC only setting channel of secondary chain */
+        wiphy_err(rwnx_hw->wiphy, "Trying to set channel of primary chain");
+        return 0;
+#endif /* CONFIG_RWNX_FULLMAC */
+    } else {
+        struct rwnx_sec_phy_chan *chan = &rwnx_hw->phy.sec_chan;
+
+        width = chnl2bw[chan->type];
+        band  = chan->band;
+        center_freq  = chan->prim20_freq;
+        center_freq1 = chan->center_freq1;
+        center_freq2 = chan->center_freq2;
+        flags = 0;
+    }
+
+    req->chan.band = band;
+    req->chan.type = bw2chnl[width];
+    req->chan.prim20_freq  = center_freq;
+    req->chan.center1_freq = center_freq1;
+    req->chan.center2_freq = center_freq2;
+    req->chan.tx_power = tx_power;
+    req->chan.flags = flags;
+    req->index = phy_idx;
+
+    if (rwnx_hw->phy.limit_bw)
+        limit_chan_bw(&req->chan.type, req->chan.prim20_freq, &req->chan.center1_freq);
+
+    RWNX_DBG("mac80211:   freq=%d(c1:%d - c2:%d)/width=%d - band=%d\n"
+             "   hw(%d): prim20=%d(c1:%d - c2:%d)/ type=%d - band=%d\n",
+             center_freq, center_freq1, center_freq2, width, band,
+             phy_idx, req->chan.prim20_freq, req->chan.center1_freq,
+             req->chan.center2_freq, req->chan.type, req->chan.band);
+
+    /* Send the MM_SET_CHANNEL_REQ REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, MM_SET_CHANNEL_CFM, cfm);
+}
+
+int rwnx_send_key_add(struct rwnx_hw *rwnx_hw, u8 vif_idx, u8 sta_idx, bool pairwise,
+                      u8 *key, u8 key_len, u8 key_idx, u8 cipher_suite,
+                      struct mm_key_add_cfm *cfm)
+{
+    struct mm_key_add_req *key_add_req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_KEY_ADD_REQ message */
+    key_add_req = rwnx_msg_zalloc(MM_KEY_ADD_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_key_add_req));
+    if (!key_add_req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_KEY_ADD_REQ message */
+    if (sta_idx != 0xFF) {
+        /* Pairwise key */
+        key_add_req->sta_idx = sta_idx;
+    } else {
+        /* Default key */
+        key_add_req->sta_idx = sta_idx;
+        key_add_req->key_idx = (u8_l)key_idx; /* only useful for default keys */
+    }
+    key_add_req->pairwise = pairwise;
+    key_add_req->inst_nbr = vif_idx;
+    key_add_req->key.length = key_len;
+    memcpy(&(key_add_req->key.array[0]), key, key_len);
+
+    key_add_req->cipher_suite = cipher_suite;
+
+    RWNX_DBG("%s: sta_idx:%d key_idx:%d inst_nbr:%d cipher:%d key_len:%d\n", __func__,
+             key_add_req->sta_idx, key_add_req->key_idx, key_add_req->inst_nbr,
+             key_add_req->cipher_suite, key_add_req->key.length);
+#if defined(CONFIG_RWNX_DBG) || defined(CONFIG_DYNAMIC_DEBUG)
+    print_hex_dump_bytes("key: ", DUMP_PREFIX_OFFSET, key_add_req->key.array, key_add_req->key.length);
+#endif
+
+    /* Send the MM_KEY_ADD_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, key_add_req, 1, MM_KEY_ADD_CFM, cfm);
+}
+
+int rwnx_send_key_del(struct rwnx_hw *rwnx_hw, uint8_t hw_key_idx)
+{
+    struct mm_key_del_req *key_del_req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_KEY_DEL_REQ message */
+    key_del_req = rwnx_msg_zalloc(MM_KEY_DEL_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_key_del_req));
+    if (!key_del_req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_KEY_DEL_REQ message */
+    key_del_req->hw_key_idx = hw_key_idx;
+
+    /* Send the MM_KEY_DEL_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, key_del_req, 1, MM_KEY_DEL_CFM, NULL);
+}
+
+int rwnx_send_bcn(struct rwnx_hw *rwnx_hw,u8 *buf, u8 vif_idx, u16 bcn_len)
+{
+	struct apm_set_bcn_ie_req *bcn_ie_req;
+	bcn_ie_req = rwnx_msg_zalloc(APM_SET_BEACON_IE_REQ, TASK_APM, DRV_TASK_ID,
+							   sizeof(struct apm_set_bcn_ie_req));
+	if (!bcn_ie_req)
+		return -ENOMEM;
+
+	bcn_ie_req->vif_idx = vif_idx;
+	bcn_ie_req->bcn_ie_len = bcn_len;
+	memcpy(bcn_ie_req->bcn_ie, (u8 *)buf, bcn_len);
+    kfree(buf);
+
+	return rwnx_send_msg(rwnx_hw, bcn_ie_req, 1, APM_SET_BEACON_IE_CFM, NULL);
+}
+
+int rwnx_send_bcn_change(struct rwnx_hw *rwnx_hw, u8 vif_idx, u32 bcn_addr,
+                         u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft)
+{
+    struct mm_bcn_change_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_BCN_CHANGE_REQ message */
+    req = rwnx_msg_zalloc(MM_BCN_CHANGE_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_bcn_change_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_BCN_CHANGE_REQ message */
+    req->bcn_ptr = bcn_addr;
+    req->bcn_len = bcn_len;
+    req->tim_oft = tim_oft;
+    req->tim_len = tim_len;
+    req->inst_nbr = vif_idx;
+
+    if (csa_oft) {
+        int i;
+        for (i = 0; i < BCN_MAX_CSA_CPT; i++) {
+            req->csa_oft[i] = csa_oft[i];
+        }
+    }
+
+    /* Send the MM_BCN_CHANGE_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, MM_BCN_CHANGE_CFM, NULL);
+}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+static inline void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
+                             struct ieee80211_channel *chan,
+                             enum nl80211_channel_type chan_type)
+{
+        if (WARN_ON(!chan))
+                return;
+        chandef->chan = chan;
+        chandef->center_freq2 = 0;
+        switch (chan_type) {
+        case NL80211_CHAN_NO_HT:
+                chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+                chandef->center_freq1 = chan->center_freq;
+                break;
+        case NL80211_CHAN_HT20:
+                chandef->width = NL80211_CHAN_WIDTH_20;
+                chandef->center_freq1 = chan->center_freq;
+                break;
+        case NL80211_CHAN_HT40PLUS:
+                chandef->width = NL80211_CHAN_WIDTH_40;
+                chandef->center_freq1 = chan->center_freq + 10;
+                break;
+        case NL80211_CHAN_HT40MINUS:
+                chandef->width = NL80211_CHAN_WIDTH_40;
+                chandef->center_freq1 = chan->center_freq - 10;
+                break;
+        default:
+                WARN_ON(1);
+        }
+}
+#endif
+int rwnx_send_roc(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+				  struct ieee80211_channel *chan, unsigned  int duration,
+				  struct mm_remain_on_channel_cfm *roc_cfm)
+{
+    struct mm_remain_on_channel_req *req;
+    struct cfg80211_chan_def chandef;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Create channel definition structure */
+    cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+
+    /* Build the MM_REMAIN_ON_CHANNEL_REQ message */
+    req = rwnx_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_remain_on_channel_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
+    req->op_code      = MM_ROC_OP_START;
+    req->vif_index    = vif->vif_index;
+    req->duration_ms  = duration;
+    req->band         = chan->band;
+    req->type         = bw2chnl[chandef.width];
+    req->prim20_freq  = chan->center_freq;
+    req->center1_freq = chandef.center_freq1;
+    req->center2_freq = chandef.center_freq2;
+    req->tx_power     = chan_to_fw_pwr(chan->max_power);
+
+    /* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, MM_REMAIN_ON_CHANNEL_CFM, roc_cfm);
+}
+
+int rwnx_send_cancel_roc(struct rwnx_hw *rwnx_hw)
+{
+    struct mm_remain_on_channel_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_REMAIN_ON_CHANNEL_REQ message */
+    req = rwnx_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_remain_on_channel_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
+    req->op_code = MM_ROC_OP_CANCEL;
+
+    /* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, MM_REMAIN_ON_CHANNEL_CFM, NULL);
+}
+
+int rwnx_send_set_power(struct rwnx_hw *rwnx_hw, u8 vif_idx, s8 pwr,
+                        struct mm_set_power_cfm *cfm)
+{
+    struct mm_set_power_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_POWER_REQ message */
+    req = rwnx_msg_zalloc(MM_SET_POWER_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_set_power_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_SET_POWER_REQ message */
+    req->inst_nbr = vif_idx;
+    req->power = pwr;
+
+    /* Send the MM_SET_POWER_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, MM_SET_POWER_CFM, cfm);
+}
+
+int rwnx_send_set_edca(struct rwnx_hw *rwnx_hw, u8 hw_queue, u32 param,
+                       bool uapsd, u8 inst_nbr)
+{
+    struct mm_set_edca_req *set_edca_req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_EDCA_REQ message */
+    set_edca_req = rwnx_msg_zalloc(MM_SET_EDCA_REQ, TASK_MM, DRV_TASK_ID,
+                                   sizeof(struct mm_set_edca_req));
+    if (!set_edca_req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_SET_EDCA_REQ message */
+    set_edca_req->ac_param = param;
+    set_edca_req->uapsd = uapsd;
+    set_edca_req->hw_queue = hw_queue;
+    set_edca_req->inst_nbr = inst_nbr;
+
+    /* Send the MM_SET_EDCA_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, set_edca_req, 1, MM_SET_EDCA_CFM, NULL);
+}
+
+#ifdef CONFIG_RWNX_P2P_DEBUGFS
+int rwnx_send_p2p_oppps_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                            u8 ctw, struct mm_set_p2p_oppps_cfm *cfm)
+{
+    struct mm_set_p2p_oppps_req *p2p_oppps_req;
+    int error;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_P2P_OPPPS_REQ message */
+    p2p_oppps_req = rwnx_msg_zalloc(MM_SET_P2P_OPPPS_REQ, TASK_MM, DRV_TASK_ID,
+                                    sizeof(struct mm_set_p2p_oppps_req));
+
+    if (!p2p_oppps_req) {
+        return -ENOMEM;
+    }
+
+    /* Fill the message parameters */
+    p2p_oppps_req->vif_index = rwnx_vif->vif_index;
+    p2p_oppps_req->ctwindow = ctw;
+
+    /* Send the MM_P2P_OPPPS_REQ message to LMAC FW */
+    error = rwnx_send_msg(rwnx_hw, p2p_oppps_req, 1, MM_SET_P2P_OPPPS_CFM, cfm);
+
+    return (error);
+}
+
+int rwnx_send_p2p_noa_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                          int count, int interval, int duration, bool dyn_noa,
+                          struct mm_set_p2p_noa_cfm *cfm)
+{
+    struct mm_set_p2p_noa_req *p2p_noa_req;
+    int error;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Param check */
+    if (count > 255)
+        count = 255;
+
+    if (duration >= interval) {
+        dev_err(rwnx_hw->dev, "Invalid p2p NOA config: interval=%d <= duration=%d\n",
+                interval, duration);
+        return -EINVAL;
+    }
+
+    /* Build the MM_SET_P2P_NOA_REQ message */
+    p2p_noa_req = rwnx_msg_zalloc(MM_SET_P2P_NOA_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_set_p2p_noa_req));
+
+    if (!p2p_noa_req) {
+        return -ENOMEM;
+    }
+
+    /* Fill the message parameters */
+    p2p_noa_req->vif_index = rwnx_vif->vif_index;
+    p2p_noa_req->noa_inst_nb = 0;
+    p2p_noa_req->count = count;
+
+    if (count) {
+        p2p_noa_req->duration_us = duration * 1024;
+        p2p_noa_req->interval_us = interval * 1024;
+        p2p_noa_req->start_offset = (interval - duration - 10) * 1024;
+        p2p_noa_req->dyn_noa = dyn_noa;
+    }
+
+    /* Send the MM_SET_2P_NOA_REQ message to LMAC FW */
+    error = rwnx_send_msg(rwnx_hw, p2p_noa_req, 1, MM_SET_P2P_NOA_CFM, cfm);
+
+    return (error);
+}
+#endif /* CONFIG_RWNX_P2P_DEBUGFS */
+
+#ifdef AICWF_ARP_OFFLOAD
+int rwnx_send_arpoffload_en_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                          u32_l ipaddr,  u8_l enable)
+{
+    struct mm_set_arpoffload_en_req *arp_offload_req;
+    int error;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_ARPOFFLOAD_REQ message */
+    arp_offload_req = rwnx_msg_zalloc(MM_SET_ARPOFFLOAD_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_set_arpoffload_en_req));
+
+    if (!arp_offload_req) {
+        return -ENOMEM;
+    }
+
+    /* Fill the message parameters */
+	arp_offload_req->enable = enable;
+	arp_offload_req->vif_idx = rwnx_vif->vif_index;
+	arp_offload_req->ipaddr = ipaddr;
+
+    /* Send the MM_ARPOFFLOAD_EN_REQ message to UMAC FW */
+    error = rwnx_send_msg(rwnx_hw, arp_offload_req, 1, MM_SET_ARPOFFLOAD_CFM, NULL);
+
+    return (error);
+}
+#endif
+
+int rwnx_send_coex_req(struct rwnx_hw *rwnx_hw, u8_l disable_coexnull, u8_l enable_nullcts)
+{
+    struct mm_set_coex_req *coex_req;
+    int error;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_COEX_REQ message */
+    coex_req = rwnx_msg_zalloc(MM_SET_COEX_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_set_coex_req));
+
+    if (!coex_req) {
+        return -ENOMEM;
+    }
+
+    coex_req->bt_on = 1;
+    coex_req->disable_coexnull = disable_coexnull;
+    coex_req->enable_nullcts = enable_nullcts;
+    coex_req->enable_periodic_timer = 0;
+    coex_req->coex_timeslot_set = 0;
+
+    /* Send the MM_SET_COEX_REQ message to UMAC FW */
+    error = rwnx_send_msg(rwnx_hw, coex_req, 1, MM_SET_COEX_CFM, NULL);
+
+    return (error);
+}
+
+
+int rwnx_send_rf_config_req(struct rwnx_hw *rwnx_hw, u8_l ofst, u8_l sel, u8_l *tbl, u16_l len)
+{
+    struct mm_set_rf_config_req *rf_config_req;
+    int error;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_RF_CONFIG_REQ message */
+    rf_config_req = rwnx_msg_zalloc(MM_SET_RF_CONFIG_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_set_rf_config_req));
+
+    if (!rf_config_req) {
+        return -ENOMEM;
+    }
+
+    rf_config_req->table_sel = sel;
+    rf_config_req->table_ofst = ofst;
+    rf_config_req->table_num = 16;
+    rf_config_req->deft_page = 0;
+
+	memcpy(rf_config_req->data, tbl, len);  
+
+    /* Send the MM_SET_RF_CONFIG_REQ message to UMAC FW */
+    error = rwnx_send_msg(rwnx_hw, rf_config_req, 1, MM_SET_RF_CONFIG_CFM, NULL);
+
+    return (error);
+}
+
+int rwnx_send_rf_calib_req(struct rwnx_hw *rwnx_hw, struct mm_set_rf_calib_cfm *cfm)
+{
+    struct mm_set_rf_calib_req *rf_calib_req;
+    xtal_cap_conf_t xtal_cap = {0,};
+    int error;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_RF_CALIB_REQ message */
+    rf_calib_req = rwnx_msg_zalloc(MM_SET_RF_CALIB_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_set_rf_calib_req));
+
+    if (!rf_calib_req) {
+        return -ENOMEM;
+    }
+
+	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8801){
+    	rf_calib_req->cal_cfg_24g = 0xbf;
+    	rf_calib_req->cal_cfg_5g = 0x3f;
+    } else if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW) {
+        rf_calib_req->cal_cfg_24g = 0x0f8f;
+        rf_calib_req->cal_cfg_5g = 0;
+    } else if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80) {
+    	rf_calib_req->cal_cfg_24g = 0x0f8f;
+    	rf_calib_req->cal_cfg_5g = 0x0f0f;
+    }
+
+    rf_calib_req->param_alpha = 0x0c34c008;
+    rf_calib_req->bt_calib_en = 0;
+    rf_calib_req->bt_calib_param = 0x264203;
+
+    #ifdef CONFIG_LOAD_USERCONFIG
+    get_userconfig_xtal_cap(&xtal_cap);
+    #endif
+    if (xtal_cap.enable) {
+        printk("user xtal cap: %d, cap_fine: %d\n", xtal_cap.xtal_cap, xtal_cap.xtal_cap_fine);
+        rf_calib_req->xtal_cap = xtal_cap.xtal_cap;
+        rf_calib_req->xtal_cap_fine = xtal_cap.xtal_cap_fine;
+    } else {
+        rf_calib_req->xtal_cap = 0;
+        rf_calib_req->xtal_cap_fine = 0;
+    }
+
+    /* Send the MM_SET_RF_CALIB_REQ message to UMAC FW */
+    error = rwnx_send_msg(rwnx_hw, rf_calib_req, 1, MM_SET_RF_CALIB_CFM, cfm);
+
+    return (error);
+}
+
+int rwnx_send_get_macaddr_req(struct rwnx_hw *rwnx_hw, struct mm_get_mac_addr_cfm *cfm)
+{
+    struct mm_get_mac_addr_req *get_macaddr_req;
+    int error;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_GET_MAC_ADDR_REQ message */
+    get_macaddr_req = rwnx_msg_zalloc(MM_GET_MAC_ADDR_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_get_mac_addr_req));
+
+    if (!get_macaddr_req) {
+        return -ENOMEM;
+    }
+
+    get_macaddr_req->get = 1;
+
+    /* Send the MM_GET_MAC_ADDR_REQ  message to UMAC FW */
+    error = rwnx_send_msg(rwnx_hw, get_macaddr_req, 1, MM_GET_MAC_ADDR_CFM, cfm);
+
+    return (error);
+}
+
+int rwnx_send_get_sta_info_req(struct rwnx_hw *rwnx_hw, u8_l sta_idx, struct mm_get_sta_info_cfm *cfm)
+{
+	struct mm_get_sta_info_req *get_info_req;
+	int error;
+
+
+	/* Build the MM_GET_STA_INFO_REQ message */
+	get_info_req = rwnx_msg_zalloc(MM_GET_STA_INFO_REQ, TASK_MM, DRV_TASK_ID,
+						sizeof(struct mm_get_sta_info_req));
+
+	if (!get_info_req) {
+		return -ENOMEM;
+	}
+
+	get_info_req->sta_idx = sta_idx;
+
+	/* Send the MM_GET_STA_INFO_REQ  message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, get_info_req, 1, MM_GET_STA_INFO_CFM, cfm);
+
+	return error;
+};
+
+int rwnx_send_set_stack_start_req(struct rwnx_hw *rwnx_hw, u8_l on, u8_l efuse_valid, u8_l set_vendor_info,
+					u8_l fwtrace_redir_en, struct mm_set_stack_start_cfm *cfm)
+{
+	struct mm_set_stack_start_req *req;
+	int error;
+
+	/* Build the MM_SET_STACK_START_REQ message */
+	req = rwnx_msg_zalloc(MM_SET_STACK_START_REQ, TASK_MM, DRV_TASK_ID, sizeof(struct mm_set_stack_start_req));
+
+	if (!req) {
+		return -ENOMEM;
+	}
+
+	req->is_stack_start = on;
+	req->efuse_valid = efuse_valid;
+	req->set_vendor_info = set_vendor_info;
+	req->fwtrace_redir = fwtrace_redir_en;
+	/* Send the MM_SET_STACK_START_REQ  message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_STACK_START_CFM, cfm);
+
+	return error;
+}
+
+int rwnx_send_get_fw_version_req(struct rwnx_hw *rwnx_hw, struct mm_get_fw_version_cfm *cfm)
+{
+    void *req;
+    int error;
+
+    /* Build the MM_GET_FW_VERSION_REQ message */
+    req = rwnx_msg_zalloc(MM_GET_FW_VERSION_REQ, TASK_MM, DRV_TASK_ID, sizeof(u8));
+
+    if (!req) {
+            return -ENOMEM;
+    }
+
+    /* Send the MM_GET_FW_VERSION_REQ  message to UMAC FW */
+    error = rwnx_send_msg(rwnx_hw, req, 1, MM_GET_FW_VERSION_CFM, cfm);
+
+    return error;
+}
+
+int rwnx_send_txpwr_lvl_req(struct rwnx_hw *rwnx_hw)
+{
+    struct mm_set_txpwr_lvl_req *txpwr_lvl_req;
+    txpwr_lvl_conf_v2_t txpwr_lvl_v2_tmp;
+    txpwr_lvl_conf_v2_t *txpwr_lvl_v2;
+    txpwr_loss_conf_t txpwr_loss_tmp;
+    txpwr_loss_conf_t *txpwr_loss;
+    int error;
+    int i;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_TXPWR_LVL_REQ message */
+    txpwr_lvl_req = rwnx_msg_zalloc(MM_SET_TXPWR_LVL_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_set_txpwr_lvl_req));
+
+    if (!txpwr_lvl_req) {
+        return -ENOMEM;
+    }
+
+    txpwr_lvl_v2 = &txpwr_lvl_v2_tmp;
+    txpwr_loss = &txpwr_loss_tmp;
+    txpwr_loss->loss_enable = 0;
+
+    #ifdef CONFIG_LOAD_USERCONFIG
+    get_userconfig_txpwr_lvl_v2_in_fdrv(txpwr_lvl_v2);
+    get_userconfig_txpwr_loss(txpwr_loss);
+    #endif
+
+    if (txpwr_lvl_v2->enable == 0) {
+        rwnx_msg_free(rwnx_hw, txpwr_lvl_req);
+        return 0;
+    } else {
+
+	if (txpwr_loss->loss_enable == 1) {
+		printk("%s:loss_value:%d\r\n", __func__, txpwr_loss->loss_value);
+
+        for (i = 0; i <= 11; i++)
+            txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[i] += txpwr_loss->loss_value;
+        for (i = 0; i <= 9; i++)
+            txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[i] += txpwr_loss->loss_value;
+        for (i = 0; i <= 11; i++)
+            txpwr_lvl_v2->pwrlvl_11ax_2g4[i] += txpwr_loss->loss_value;
+	}
+
+		printk("%s:enable:%d\r\n", __func__, txpwr_lvl_v2->enable);
+		printk("%s:lvl_11b_11ag_1m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[0]);
+#if 0
+		printk("%s:lvl_11b_11ag_2m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[1]);
+		printk("%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[2]);
+		printk("%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]);
+		printk("%s:lvl_11b_11ag_6m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[4]);
+		printk("%s:lvl_11b_11ag_9m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[5]);
+		printk("%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[6]);
+		printk("%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[7]);
+		printk("%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[8]);
+		printk("%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[9]);
+		printk("%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[10]);
+		printk("%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[11]);
+		printk("%s:lvl_11n_11ac_mcs0_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[0]);
+		printk("%s:lvl_11n_11ac_mcs1_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[1]);
+		printk("%s:lvl_11n_11ac_mcs2_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[2]);
+		printk("%s:lvl_11n_11ac_mcs3_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[3]);
+		printk("%s:lvl_11n_11ac_mcs4_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[4]);
+		printk("%s:lvl_11n_11ac_mcs5_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[5]);
+		printk("%s:lvl_11n_11ac_mcs6_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[6]);
+		printk("%s:lvl_11n_11ac_mcs7_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[7]);
+		printk("%s:lvl_11n_11ac_mcs8_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[8]);
+		printk("%s:lvl_11n_11ac_mcs9_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[9]);
+		printk("%s:lvl_11ax_mcs0_2g4:%d\r\n",	 __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[0]);
+		printk("%s:lvl_11ax_mcs1_2g4:%d\r\n",	 __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[1]);
+		printk("%s:lvl_11ax_mcs2_2g4:%d\r\n",	 __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[2]);
+		printk("%s:lvl_11ax_mcs3_2g4:%d\r\n",	 __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[3]);
+		printk("%s:lvl_11ax_mcs4_2g4:%d\r\n",	 __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[4]);
+		printk("%s:lvl_11ax_mcs5_2g4:%d\r\n",	 __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[5]);
+		printk("%s:lvl_11ax_mcs6_2g4:%d\r\n",	 __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[6]);
+		printk("%s:lvl_11ax_mcs7_2g4:%d\r\n",	 __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[7]);
+		printk("%s:lvl_11ax_mcs8_2g4:%d\r\n",	 __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[8]);
+		printk("%s:lvl_11ax_mcs9_2g4:%d\r\n",	 __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[9]);
+		printk("%s:lvl_11ax_mcs10_2g4:%d\r\n",	 __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[10]);
+		printk("%s:lvl_11ax_mcs11_2g4:%d\r\n",	 __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[11]);
+#endif
+
+        if ((testmode == 0) && (chip_sub_id == 0)) {
+            txpwr_lvl_req->txpwr_lvl.enable         = txpwr_lvl_v2->enable;
+            txpwr_lvl_req->txpwr_lvl.dsss           = txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]; // 11M
+            txpwr_lvl_req->txpwr_lvl.ofdmlowrate_2g4= txpwr_lvl_v2->pwrlvl_11ax_2g4[4]; // MCS4
+            txpwr_lvl_req->txpwr_lvl.ofdm64qam_2g4  = txpwr_lvl_v2->pwrlvl_11ax_2g4[7]; // MCS7
+            txpwr_lvl_req->txpwr_lvl.ofdm256qam_2g4 = txpwr_lvl_v2->pwrlvl_11ax_2g4[9]; // MCS9
+            txpwr_lvl_req->txpwr_lvl.ofdm1024qam_2g4= txpwr_lvl_v2->pwrlvl_11ax_2g4[11]; // MCS11
+            txpwr_lvl_req->txpwr_lvl.ofdmlowrate_5g = 13; // unused
+            txpwr_lvl_req->txpwr_lvl.ofdm64qam_5g   = 13; // unused
+            txpwr_lvl_req->txpwr_lvl.ofdm256qam_5g  = 13; // unused
+            txpwr_lvl_req->txpwr_lvl.ofdm1024qam_5g = 13; // unused
+         } else {
+            txpwr_lvl_req->txpwr_lvl_v2  = *txpwr_lvl_v2;
+        }
+
+        /* Send the MM_SET_TXPWR_LVL_REQ message to UMAC FW */
+        error = rwnx_send_msg(rwnx_hw, txpwr_lvl_req, 1, MM_SET_TXPWR_LVL_CFM, NULL);
+
+        return (error);
+    }
+}
+
+int rwnx_send_txpwr_lvl_v3_req(struct rwnx_hw *rwnx_hw)
+{
+    struct mm_set_txpwr_lvl_req *txpwr_lvl_req;
+    txpwr_lvl_conf_v3_t txpwr_lvl_v3_tmp;
+    txpwr_lvl_conf_v3_t *txpwr_lvl_v3;
+	txpwr_loss_conf_t txpwr_loss_tmp;
+    txpwr_loss_conf_t *txpwr_loss;
+    int error;
+	int i;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_TXPWR_LVL_REQ message */
+    txpwr_lvl_req = rwnx_msg_zalloc(MM_SET_TXPWR_LVL_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_set_txpwr_lvl_req));
+
+    if (!txpwr_lvl_req) {
+        return -ENOMEM;
+    }
+
+    txpwr_lvl_v3 = &txpwr_lvl_v3_tmp;
+    txpwr_loss = &txpwr_loss_tmp;
+    txpwr_loss->loss_enable = 0;
+
+	#ifdef CONFIG_LOAD_USERCONFIG
+    get_userconfig_txpwr_lvl_v3_in_fdrv(txpwr_lvl_v3);
+    get_userconfig_txpwr_loss(txpwr_loss);
+	#endif
+
+    if (txpwr_loss->loss_enable == 1) {
+        printk("%s:loss_value:%d\r\n", __func__, txpwr_loss->loss_value);
+
+        for (i = 0; i <= 11; i++)
+            txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[i] += txpwr_loss->loss_value;
+        for (i = 0; i <= 9; i++)
+            txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[i] += txpwr_loss->loss_value;
+        for (i = 0; i <= 11; i++)
+            txpwr_lvl_v3->pwrlvl_11ax_2g4[i] += txpwr_loss->loss_value;
+
+		for (i = 0; i <= 11; i++)
+            txpwr_lvl_v3->pwrlvl_11a_5g[i] += txpwr_loss->loss_value;
+        for (i = 0; i <= 9; i++)
+            txpwr_lvl_v3->pwrlvl_11n_11ac_5g[i] += txpwr_loss->loss_value;
+        for (i = 0; i <= 11; i++)
+            txpwr_lvl_v3->pwrlvl_11ax_5g[i] += txpwr_loss->loss_value;
+    }
+
+    if (txpwr_lvl_v3->enable == 0) {
+        rwnx_msg_free(rwnx_hw, txpwr_lvl_req);
+        return 0;
+    } else {
+        printk("%s:enable:%d\r\n",               __func__, txpwr_lvl_v3->enable);
+        printk("%s:lvl_11b_11ag_1m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[0]);
+        printk("%s:lvl_11b_11ag_2m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[1]);
+        printk("%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[2]);
+        printk("%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[3]);
+        printk("%s:lvl_11b_11ag_6m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[4]);
+        printk("%s:lvl_11b_11ag_9m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[5]);
+        printk("%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[6]);
+        printk("%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[7]);
+        printk("%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[8]);
+        printk("%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[9]);
+        printk("%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[10]);
+        printk("%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[11]);
+        printk("%s:lvl_11n_11ac_mcs0_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[0]);
+        printk("%s:lvl_11n_11ac_mcs1_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[1]);
+        printk("%s:lvl_11n_11ac_mcs2_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[2]);
+        printk("%s:lvl_11n_11ac_mcs3_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[3]);
+        printk("%s:lvl_11n_11ac_mcs4_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[4]);
+        printk("%s:lvl_11n_11ac_mcs5_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[5]);
+        printk("%s:lvl_11n_11ac_mcs6_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[6]);
+        printk("%s:lvl_11n_11ac_mcs7_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[7]);
+        printk("%s:lvl_11n_11ac_mcs8_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[8]);
+        printk("%s:lvl_11n_11ac_mcs9_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[9]);
+        printk("%s:lvl_11ax_mcs0_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[0]);
+        printk("%s:lvl_11ax_mcs1_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[1]);
+        printk("%s:lvl_11ax_mcs2_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[2]);
+        printk("%s:lvl_11ax_mcs3_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[3]);
+        printk("%s:lvl_11ax_mcs4_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[4]);
+        printk("%s:lvl_11ax_mcs5_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[5]);
+        printk("%s:lvl_11ax_mcs6_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[6]);
+        printk("%s:lvl_11ax_mcs7_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[7]);
+        printk("%s:lvl_11ax_mcs8_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[8]);
+        printk("%s:lvl_11ax_mcs9_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[9]);
+        printk("%s:lvl_11ax_mcs10_2g4:%d\r\n",   __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[10]);
+        printk("%s:lvl_11ax_mcs11_2g4:%d\r\n",   __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[11]);
+
+        printk("%s:lvl_11a_1m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[0]);
+        printk("%s:lvl_11a_2m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[1]);
+        printk("%s:lvl_11a_5m5_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[2]);
+        printk("%s:lvl_11a_11m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[3]);
+        printk("%s:lvl_11a_6m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[4]);
+        printk("%s:lvl_11a_9m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[5]);
+        printk("%s:lvl_11a_12m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[6]);
+        printk("%s:lvl_11a_18m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[7]);
+        printk("%s:lvl_11a_24m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[8]);
+        printk("%s:lvl_11a_36m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[9]);
+        printk("%s:lvl_11a_48m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[10]);
+        printk("%s:lvl_11a_54m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[11]);
+        printk("%s:lvl_11n_11ac_mcs0_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[0]);
+        printk("%s:lvl_11n_11ac_mcs1_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[1]);
+        printk("%s:lvl_11n_11ac_mcs2_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[2]);
+        printk("%s:lvl_11n_11ac_mcs3_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[3]);
+        printk("%s:lvl_11n_11ac_mcs4_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[4]);
+        printk("%s:lvl_11n_11ac_mcs5_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[5]);
+        printk("%s:lvl_11n_11ac_mcs6_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[6]);
+        printk("%s:lvl_11n_11ac_mcs7_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[7]);
+        printk("%s:lvl_11n_11ac_mcs8_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[8]);
+        printk("%s:lvl_11n_11ac_mcs9_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[9]);
+        printk("%s:lvl_11ax_mcs0_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[0]);
+        printk("%s:lvl_11ax_mcs1_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[1]);
+        printk("%s:lvl_11ax_mcs2_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[2]);
+        printk("%s:lvl_11ax_mcs3_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[3]);
+        printk("%s:lvl_11ax_mcs4_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[4]);
+        printk("%s:lvl_11ax_mcs5_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[5]);
+        printk("%s:lvl_11ax_mcs6_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[6]);
+        printk("%s:lvl_11ax_mcs7_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[7]);
+        printk("%s:lvl_11ax_mcs8_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[8]);
+        printk("%s:lvl_11ax_mcs9_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[9]);
+        printk("%s:lvl_11ax_mcs10_5g:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[10]);
+        printk("%s:lvl_11ax_mcs11_5g:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[11]);
+
+        txpwr_lvl_req->txpwr_lvl_v3 = *txpwr_lvl_v3;
+
+        /* Send the MM_SET_TXPWR_LVL_REQ message to UMAC FW */
+        error = rwnx_send_msg(rwnx_hw, txpwr_lvl_req, 1, MM_SET_TXPWR_LVL_CFM, NULL);
+
+        return (error);
+    }
+}
+
+int rwnx_send_txpwr_lvl_adj_req(struct rwnx_hw *rwnx_hw)
+{
+    struct mm_set_txpwr_lvl_adj_req *txpwr_lvl_adj_req;
+    txpwr_lvl_adj_conf_t txpwr_lvl_adj_tmp;
+    txpwr_lvl_adj_conf_t *txpwr_lvl_adj;
+    int error;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_TXPWR_LVL_REQ message */
+    txpwr_lvl_adj_req = rwnx_msg_zalloc(MM_SET_TXPWR_LVL_ADJ_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_set_txpwr_lvl_adj_req));
+
+    if (!txpwr_lvl_adj_req) {
+        return -ENOMEM;
+    }
+
+    txpwr_lvl_adj = &txpwr_lvl_adj_tmp;
+
+	#ifdef CONFIG_LOAD_USERCONFIG
+    get_userconfig_txpwr_lvl_adj_in_fdrv(txpwr_lvl_adj);
+	#endif
+
+    if (txpwr_lvl_adj->enable == 0) {
+        rwnx_msg_free(rwnx_hw, txpwr_lvl_adj_req);
+        return 0;
+    } else {
+        printk("%s:enable:%d\r\n",                   __func__, txpwr_lvl_adj->enable);
+        printk( "%s:lvl_adj_2g4_chan_1_4:%d\r\n",     __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[0]);
+        printk("%s:lvl_adj_2g4_chan_5_9:%d\r\n",     __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[1]);
+        printk( "%s:lvl_adj_2g4_chan_10_13:%d\r\n",   __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[2]);
+
+        printk("%s:lvl_adj_5g_chan_42:%d\r\n",       __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[0]);
+        printk("%s:lvl_adj_5g_chan_58:%d\r\n",       __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[1]);
+        printk("%s:lvl_adj_5g_chan_106:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[2]);
+        printk("%s:lvl_adj_5g_chan_122:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[3]);
+        printk("%s:lvl_adj_5g_chan_138:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[4]);
+        printk("%s:lvl_adj_5g_chan_155:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[5]);
+
+        txpwr_lvl_adj_req->txpwr_lvl_adj  = *txpwr_lvl_adj;
+
+        /* Send the MM_SET_TXPWR_LVL_REQ message to UMAC FW */
+        error = rwnx_send_msg(rwnx_hw, txpwr_lvl_adj_req, 1, MM_SET_TXPWR_LVL_ADJ_CFM, NULL);
+
+        return (error);
+    }
+}
+
+int rwnx_send_txpwr_ofst_req(struct rwnx_hw *rwnx_hw)
+{
+    struct mm_set_txpwr_ofst_req *txpwr_ofst_req;
+    txpwr_ofst_conf_t *txpwr_ofst;
+    int error;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_TXPWR_OFST_REQ message */
+    txpwr_ofst_req = rwnx_msg_zalloc(MM_SET_TXPWR_OFST_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_set_txpwr_ofst_req));
+
+    if (!txpwr_ofst_req) {
+        return -ENOMEM;
+    }
+
+    txpwr_ofst = &txpwr_ofst_req->txpwr_ofst;
+    txpwr_ofst->enable       = 0;
+    txpwr_ofst->chan_1_4     = 0;
+    txpwr_ofst->chan_5_9     = 0;
+    txpwr_ofst->chan_10_13   = 0;
+    txpwr_ofst->chan_36_64   = 0;
+    txpwr_ofst->chan_100_120 = 0;
+    txpwr_ofst->chan_122_140 = 0;
+    txpwr_ofst->chan_142_165 = 0;
+
+    #ifdef CONFIG_LOAD_USERCONFIG
+    get_userconfig_txpwr_ofst(txpwr_ofst);
+    #endif
+
+    if (txpwr_ofst->enable == 0) {
+        rwnx_msg_free(rwnx_hw, txpwr_ofst_req);
+        return 0;
+    } else {
+        printk("%s:enable      :%d\r\n", __func__, txpwr_ofst->enable);
+        printk("%s:chan_1_4    :%d\r\n", __func__, txpwr_ofst->chan_1_4);
+        printk("%s:chan_5_9    :%d\r\n", __func__, txpwr_ofst->chan_5_9);
+        printk("%s:chan_10_13  :%d\r\n", __func__, txpwr_ofst->chan_10_13);
+        printk("%s:chan_36_64  :%d\r\n", __func__, txpwr_ofst->chan_36_64);
+        printk("%s:chan_100_120:%d\r\n", __func__, txpwr_ofst->chan_100_120);
+        printk("%s:chan_122_140:%d\r\n", __func__, txpwr_ofst->chan_122_140);
+        printk("%s:chan_142_165:%d\r\n", __func__, txpwr_ofst->chan_142_165);
+
+        /* Send the MM_SET_TXPWR_OFST_REQ message to UMAC FW */
+        error = rwnx_send_msg(rwnx_hw, txpwr_ofst_req, 1, MM_SET_TXPWR_OFST_CFM, NULL);
+
+        return (error);
+    }
+}
+
+int rwnx_send_txpwr_ofst2x_req(struct rwnx_hw *rwnx_hw)
+{
+    struct mm_set_txpwr_ofst_req *txpwr_ofst_req;
+    txpwr_ofst2x_conf_t *txpwr_ofst2x;
+    int error = 0;
+    int type, ch_grp;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_TXPWR_OFST_REQ message */
+    txpwr_ofst_req = rwnx_msg_zalloc(MM_SET_TXPWR_OFST_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_set_txpwr_ofst_req));
+
+    if (!txpwr_ofst_req) {
+        return -ENOMEM;
+    }
+
+    txpwr_ofst2x = &txpwr_ofst_req->txpwr_ofst2x;
+    txpwr_ofst2x->enable = 0;
+    for (type = 0; type < 3; type++) {
+        for (ch_grp = 0; ch_grp < 6; ch_grp++) {
+            if (ch_grp < 3) {
+                txpwr_ofst2x->pwrofst2x_tbl_2g4[type][ch_grp] = 0;
+            }
+            txpwr_ofst2x->pwrofst2x_tbl_5g[type][ch_grp] = 0;
+        }
+    }
+	#ifdef CONFIG_LOAD_USERCONFIG
+    if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+        get_userconfig_txpwr_ofst2x_in_fdrv(txpwr_ofst2x);
+    }
+	#endif
+    if (txpwr_ofst2x->enable){
+        printk("%s:enable:%d\r\n", __func__, txpwr_ofst2x->enable);
+        printk("pwrofst2x 2.4g: [0]:11b, [1]:ofdm_highrate, [2]:ofdm_lowrate\n"
+            "  chan=" "\t1-4" "\t5-9" "\t10-13");
+        for (type = 0; type < 3; type++) {
+            printk("\n  [%d] =", type);
+            for (ch_grp = 0; ch_grp < 3; ch_grp++) {
+                printk("\t%d", txpwr_ofst2x->pwrofst2x_tbl_2g4[type][ch_grp]);
+            }
+        }
+        printk("\npwrofst2x 5g: [0]:ofdm_lowrate, [1]:ofdm_highrate, [2]:ofdm_midrate\n"
+            "  chan=" "\t36-50" "\t51-64" "\t98-114" "\t115-130" "\t131-146" "\t147-166");
+        for (type = 0; type < 3; type++) {
+            printk("\n  [%d] =", type);
+            for (ch_grp = 0; ch_grp < 6; ch_grp++) {
+                printk("\t%d", txpwr_ofst2x->pwrofst2x_tbl_5g[type][ch_grp]);
+            }
+        }
+        printk("\n");
+
+        /* Send the MM_SET_TXPWR_OFST_REQ message to UMAC FW */
+        error = rwnx_send_msg(rwnx_hw, txpwr_ofst_req, 1, MM_SET_TXPWR_OFST_CFM, NULL);
+    }else{
+        printk("%s:Do not use txpwr_ofst2x\r\n", __func__);
+        rwnx_msg_free(rwnx_hw, txpwr_ofst_req);
+    }
+
+    return (error);
+}
+
+/******************************************************************************
+ *    Control messages handling functions (FULLMAC only)
+ *****************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+#ifdef CONFIG_HE_FOR_OLD_KERNEL
+extern struct ieee80211_sband_iftype_data rwnx_he_capa;
+#endif
+#ifdef CONFIG_VHT_FOR_OLD_KERNEL
+static struct ieee80211_sta_vht_cap* rwnx_vht_capa;
+#endif
+int rwnx_send_me_config_req(struct rwnx_hw *rwnx_hw)
+{
+    struct me_config_req *req;
+    struct wiphy *wiphy = rwnx_hw->wiphy;
+#if 0
+    #ifdef USE_5G
+    struct ieee80211_sta_ht_cap *ht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->ht_cap;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+    struct ieee80211_sta_vht_cap *vht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->vht_cap;
+#endif
+
+    #else
+    struct ieee80211_sta_ht_cap *ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+    struct ieee80211_sta_vht_cap *vht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->vht_cap;
+#endif
+    #endif
+#endif
+
+    struct ieee80211_sta_ht_cap *ht_cap;
+
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+        struct ieee80211_sta_vht_cap *vht_cap;
+    #endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+    struct ieee80211_sta_he_cap const *he_cap;
+#else
+    #ifdef CONFIG_HE_FOR_OLD_KERNEL
+    struct ieee80211_sta_he_cap const *he_cap;
+    #endif
+#endif
+    //uint8_t *ht_mcs = (uint8_t *)&ht_cap->mcs;
+    uint8_t *ht_mcs;
+    int i;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80) {
+        rwnx_hw->mod_params->use_80 = true;
+    }
+
+#ifdef USE_5G
+	if (rwnx_hw->band_5g_support) {
+		ht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->ht_cap;
+	#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+		vht_cap = &rwnx_hw->vht_cap_5G;
+	#endif
+	} else {
+		ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap;
+	#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+		vht_cap = &rwnx_hw->vht_cap_2G;
+	#endif
+	}
+#else
+    ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap;
+	#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+	vht_cap = &rwnx_hw->vht_cap_2G;
+	printk("%s, cap=0x%x\n", __func__, vht_cap->cap);
+	#endif
+#endif
+
+    #ifdef CONFIG_VHT_FOR_OLD_KERNEL
+    rwnx_vht_capa = vht_cap;
+    #endif
+
+    ht_mcs = (uint8_t *)&ht_cap->mcs;
+
+    /* Build the ME_CONFIG_REQ message */
+    req = rwnx_msg_zalloc(ME_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
+                                   sizeof(struct me_config_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the ME_CONFIG_REQ message */
+    req->ht_supp = ht_cap->ht_supported;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+    req->vht_supp = vht_cap->vht_supported;
+#endif
+    req->ht_cap.ht_capa_info = cpu_to_le16(ht_cap->cap | IEEE80211_HT_CAP_LDPC_CODING);
+    req->ht_cap.a_mpdu_param = ht_cap->ampdu_factor |
+                                     (ht_cap->ampdu_density <<
+                                         IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+    for (i = 0; i < sizeof(ht_cap->mcs); i++)
+        req->ht_cap.mcs_rate[i] = ht_mcs[i];
+    req->ht_cap.ht_extended_capa = 0;
+    req->ht_cap.tx_beamforming_capa = 0;
+    req->ht_cap.asel_capa = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+    if(req->vht_supp) {
+    	req->vht_cap.vht_capa_info = cpu_to_le32(vht_cap->cap);
+    	req->vht_cap.rx_highest = cpu_to_le16(vht_cap->vht_mcs.rx_highest);
+    	req->vht_cap.rx_mcs_map = cpu_to_le16(vht_cap->vht_mcs.rx_mcs_map);
+    	req->vht_cap.tx_highest = cpu_to_le16(vht_cap->vht_mcs.tx_highest);
+    	req->vht_cap.tx_mcs_map = cpu_to_le16(vht_cap->vht_mcs.tx_mcs_map);
+    }
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) || defined(CONFIG_HE_FOR_OLD_KERNEL)
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+    if (wiphy->bands[NL80211_BAND_2GHZ]->iftype_data != NULL) {
+        he_cap = &wiphy->bands[NL80211_BAND_2GHZ]->iftype_data->he_cap;
+    #endif
+    #if defined(CONFIG_HE_FOR_OLD_KERNEL)
+    if (1) {
+        he_cap = &rwnx_he_capa.he_cap;
+    #endif
+        req->he_supp = he_cap->has_he;
+        for (i = 0; i < ARRAY_SIZE(he_cap->he_cap_elem.mac_cap_info); i++) {
+            req->he_cap.mac_cap_info[i] = he_cap->he_cap_elem.mac_cap_info[i];
+        }
+        for (i = 0; i < ARRAY_SIZE(he_cap->he_cap_elem.phy_cap_info); i++) {
+            req->he_cap.phy_cap_info[i] = he_cap->he_cap_elem.phy_cap_info[i];
+        }
+        req->he_cap.mcs_supp.rx_mcs_80 = cpu_to_le16(he_cap->he_mcs_nss_supp.rx_mcs_80);
+        req->he_cap.mcs_supp.tx_mcs_80 = cpu_to_le16(he_cap->he_mcs_nss_supp.tx_mcs_80);
+        req->he_cap.mcs_supp.rx_mcs_160 = cpu_to_le16(he_cap->he_mcs_nss_supp.rx_mcs_160);
+        req->he_cap.mcs_supp.tx_mcs_160 = cpu_to_le16(he_cap->he_mcs_nss_supp.tx_mcs_160);
+        req->he_cap.mcs_supp.rx_mcs_80p80 = cpu_to_le16(he_cap->he_mcs_nss_supp.rx_mcs_80p80);
+        req->he_cap.mcs_supp.tx_mcs_80p80 = cpu_to_le16(he_cap->he_mcs_nss_supp.tx_mcs_80p80);
+        for (i = 0; i < MAC_HE_PPE_THRES_MAX_LEN; i++) {
+            req->he_cap.ppe_thres[i] = he_cap->ppe_thres[i];
+        }
+        req->he_ul_on = rwnx_hw->mod_params->he_ul_on;
+    }
+#else
+    req->he_supp = false;
+    req->he_ul_on = false;
+#endif
+    req->ps_on = rwnx_hw->mod_params->ps_on;
+    req->dpsm = rwnx_hw->mod_params->dpsm;
+    req->tx_lft = rwnx_hw->mod_params->tx_lft;
+    req->ant_div_on = rwnx_hw->mod_params->ant_div;
+    if (rwnx_hw->mod_params->use_80)
+        req->phy_bw_max = PHY_CHNL_BW_80;
+    else if (rwnx_hw->mod_params->use_2040)
+        req->phy_bw_max = PHY_CHNL_BW_40;
+    else
+        req->phy_bw_max = PHY_CHNL_BW_20;
+
+    wiphy_info(wiphy, "HT supp %d, VHT supp %d, HE supp %d\n", req->ht_supp,
+                                                               req->vht_supp,
+                                                               req->he_supp);
+    /* Send the ME_CONFIG_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, ME_CONFIG_CFM, NULL);
+}
+
+int rwnx_send_me_chan_config_req(struct rwnx_hw *rwnx_hw)
+{
+    struct me_chan_config_req *req;
+    struct wiphy *wiphy = rwnx_hw->wiphy;
+    int i;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the ME_CHAN_CONFIG_REQ message */
+    req = rwnx_msg_zalloc(ME_CHAN_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
+                                            sizeof(struct me_chan_config_req));
+    if (!req)
+        return -ENOMEM;
+
+    req->chan2G4_cnt=  0;
+    if (wiphy->bands[NL80211_BAND_2GHZ] != NULL) {
+        struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_2GHZ];
+        for (i = 0; i < b->n_channels; i++) {
+            req->chan2G4[req->chan2G4_cnt].flags = 0;
+            if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
+                req->chan2G4[req->chan2G4_cnt].flags |= CHAN_DISABLED;
+            req->chan2G4[req->chan2G4_cnt].flags |= get_chan_flags(b->channels[i].flags);
+            req->chan2G4[req->chan2G4_cnt].band = NL80211_BAND_2GHZ;
+            req->chan2G4[req->chan2G4_cnt].freq = b->channels[i].center_freq;
+            req->chan2G4[req->chan2G4_cnt].tx_power = chan_to_fw_pwr(b->channels[i].max_power);
+            req->chan2G4_cnt++;
+            if (req->chan2G4_cnt == MAC_DOMAINCHANNEL_24G_MAX)
+                break;
+        }
+    }
+
+    req->chan5G_cnt = 0;
+    if (wiphy->bands[NL80211_BAND_5GHZ] != NULL) {
+        struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_5GHZ];
+        for (i = 0; i < b->n_channels; i++) {
+            req->chan5G[req->chan5G_cnt].flags = 0;
+            if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
+                req->chan5G[req->chan5G_cnt].flags |= CHAN_DISABLED;
+            req->chan5G[req->chan5G_cnt].flags |= get_chan_flags(b->channels[i].flags);
+            req->chan5G[req->chan5G_cnt].band = NL80211_BAND_5GHZ;
+            req->chan5G[req->chan5G_cnt].freq = b->channels[i].center_freq;
+            req->chan5G[req->chan5G_cnt].tx_power = chan_to_fw_pwr(b->channels[i].max_power);
+            req->chan5G_cnt++;
+            if (req->chan5G_cnt == MAC_DOMAINCHANNEL_5G_MAX)
+                break;
+        }
+    }
+
+    /* Send the ME_CHAN_CONFIG_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, ME_CHAN_CONFIG_CFM, NULL);
+}
+
+int rwnx_send_me_set_control_port_req(struct rwnx_hw *rwnx_hw, bool opened, u8 sta_idx)
+{
+    struct me_set_control_port_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the ME_SET_CONTROL_PORT_REQ message */
+    req = rwnx_msg_zalloc(ME_SET_CONTROL_PORT_REQ, TASK_ME, DRV_TASK_ID,
+                                   sizeof(struct me_set_control_port_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the ME_SET_CONTROL_PORT_REQ message */
+    req->sta_idx = sta_idx;
+    req->control_port_open = opened;
+
+    /* Send the ME_SET_CONTROL_PORT_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, ME_SET_CONTROL_PORT_CFM, NULL);
+}
+
+int rwnx_send_me_sta_add(struct rwnx_hw *rwnx_hw, struct station_parameters *params,
+                         const u8 *mac, u8 inst_nbr, struct me_sta_add_cfm *cfm)
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    struct me_sta_add_req *req;
+    u8 *ht_mcs = (u8 *)&params->ht_capa->mcs;
+    int i;
+    struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[inst_nbr];
+    #if (defined CONFIG_HE_FOR_OLD_KERNEL) || (defined CONFIG_VHT_FOR_OLD_KERNEL)
+    struct aic_sta *sta = &rwnx_hw->aic_table[rwnx_vif->ap.aic_index];
+    printk("assoc_req idx %d, he: %d, vht: %d\n ", rwnx_vif->ap.aic_index, sta->he, sta->vht);
+    if (rwnx_vif->ap.aic_index < NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)
+        rwnx_vif->ap.aic_index++;
+    else
+        rwnx_vif->ap.aic_index = 0;
+    #endif
+
+    /* Build the MM_STA_ADD_REQ message */
+    req = rwnx_msg_zalloc(ME_STA_ADD_REQ, TASK_ME, DRV_TASK_ID,
+                                  sizeof(struct me_sta_add_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_STA_ADD_REQ message */
+    memcpy(&(req->mac_addr.array[0]), mac, ETH_ALEN);
+
+    req->rate_set.length = params->supported_rates_len;
+    for (i = 0; i < params->supported_rates_len; i++)
+        req->rate_set.array[i] = params->supported_rates[i];
+
+    req->flags = 0;
+    if (params->ht_capa) {
+        const struct ieee80211_ht_cap *ht_capa = params->ht_capa;
+
+        req->flags |= STA_HT_CAPA;
+        req->ht_cap.ht_capa_info = cpu_to_le16(ht_capa->cap_info);
+        req->ht_cap.a_mpdu_param = ht_capa->ampdu_params_info;
+        for (i = 0; i < sizeof(ht_capa->mcs); i++)
+            req->ht_cap.mcs_rate[i] = ht_mcs[i];
+        req->ht_cap.ht_extended_capa = cpu_to_le16(ht_capa->extended_ht_cap_info);
+        req->ht_cap.tx_beamforming_capa = cpu_to_le32(ht_capa->tx_BF_cap_info);
+        req->ht_cap.asel_capa = ht_capa->antenna_selection_info;
+    }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+    if (params->vht_capa) {
+        const struct ieee80211_vht_cap *vht_capa = params->vht_capa;
+
+        req->flags |= STA_VHT_CAPA;
+        req->vht_cap.vht_capa_info = cpu_to_le32(vht_capa->vht_cap_info);
+        req->vht_cap.rx_highest = cpu_to_le16(vht_capa->supp_mcs.rx_highest);
+        req->vht_cap.rx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.rx_mcs_map);
+        req->vht_cap.tx_highest = cpu_to_le16(vht_capa->supp_mcs.tx_highest);
+        req->vht_cap.tx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.tx_mcs_map);
+    }
+#elif defined(CONFIG_VHT_FOR_OLD_KERNEL)
+    if (sta->vht) {
+        const struct ieee80211_vht_cap *vht_capa = rwnx_vht_capa;
+
+        req->flags |= STA_VHT_CAPA;
+        req->vht_cap.vht_capa_info = cpu_to_le32(vht_capa->vht_cap_info);
+        req->vht_cap.rx_highest = cpu_to_le16(vht_capa->supp_mcs.rx_highest);
+        req->vht_cap.rx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.rx_mcs_map);
+        req->vht_cap.tx_highest = cpu_to_le16(vht_capa->supp_mcs.tx_highest);
+        req->vht_cap.tx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.tx_mcs_map);
+    }
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+    if (params->he_capa) {
+        const struct ieee80211_he_cap_elem *he_capa = params->he_capa;
+        struct ieee80211_he_mcs_nss_supp *mcs_nss_supp =
+                                (struct ieee80211_he_mcs_nss_supp *)(he_capa + 1);
+
+        req->flags |= STA_HE_CAPA;
+        for (i = 0; i < ARRAY_SIZE(he_capa->mac_cap_info); i++) {
+            req->he_cap.mac_cap_info[i] = he_capa->mac_cap_info[i];
+        }
+        for (i = 0; i < ARRAY_SIZE(he_capa->phy_cap_info); i++) {
+            req->he_cap.phy_cap_info[i] = he_capa->phy_cap_info[i];
+        }
+        req->he_cap.mcs_supp.rx_mcs_80 = mcs_nss_supp->rx_mcs_80;
+        req->he_cap.mcs_supp.tx_mcs_80 = mcs_nss_supp->tx_mcs_80;
+        req->he_cap.mcs_supp.rx_mcs_160 = mcs_nss_supp->rx_mcs_160;
+        req->he_cap.mcs_supp.tx_mcs_160 = mcs_nss_supp->tx_mcs_160;
+        req->he_cap.mcs_supp.rx_mcs_80p80 = mcs_nss_supp->rx_mcs_80p80;
+        req->he_cap.mcs_supp.tx_mcs_80p80 = mcs_nss_supp->tx_mcs_80p80;
+    }
+#else
+    #ifdef CONFIG_HE_FOR_OLD_KERNEL
+	if (sta->he) {
+		const struct ieee80211_he_cap_elem *he_capa = &rwnx_he_capa.he_cap.he_cap_elem;
+		struct ieee80211_he_mcs_nss_supp *mcs_nss_supp =
+								(struct ieee80211_he_mcs_nss_supp *)(he_capa + 1);
+		req->flags |= STA_HE_CAPA;
+		for (i = 0; i < ARRAY_SIZE(he_capa->mac_cap_info); i++) {
+			req->he_cap.mac_cap_info[i] = he_capa->mac_cap_info[i];
+		}
+		for (i = 0; i < ARRAY_SIZE(he_capa->phy_cap_info); i++) {
+			req->he_cap.phy_cap_info[i] = he_capa->phy_cap_info[i];
+		}
+		req->he_cap.mcs_supp.rx_mcs_80 = mcs_nss_supp->rx_mcs_80;
+		req->he_cap.mcs_supp.tx_mcs_80 = mcs_nss_supp->tx_mcs_80;
+		req->he_cap.mcs_supp.rx_mcs_160 = mcs_nss_supp->rx_mcs_160;
+		req->he_cap.mcs_supp.tx_mcs_160 = mcs_nss_supp->tx_mcs_160;
+		req->he_cap.mcs_supp.rx_mcs_80p80 = mcs_nss_supp->rx_mcs_80p80;
+		req->he_cap.mcs_supp.tx_mcs_80p80 = mcs_nss_supp->tx_mcs_80p80;
+    }
+    #endif
+#endif
+
+    if (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME))
+        req->flags |= STA_QOS_CAPA;
+
+    if (params->sta_flags_set & BIT(NL80211_STA_FLAG_MFP))
+        req->flags |= STA_MFP_CAPA;
+
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+    if (params->opmode_notif_used) {
+        req->flags |= STA_OPMOD_NOTIF;
+        req->opmode = params->opmode_notif;
+    }
+    #endif
+
+    req->aid = cpu_to_le16(params->aid);
+    req->uapsd_queues = params->uapsd_queues;
+    req->max_sp_len = params->max_sp * 2;
+    req->vif_idx = inst_nbr;
+
+    if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
+        //struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[inst_nbr];
+        req->tdls_sta = true;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+        if ((params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) &&
+            !rwnx_vif->tdls_chsw_prohibited)
+            req->tdls_chsw_allowed = true;
+#endif
+        if (rwnx_vif->tdls_status == TDLS_SETUP_RSP_TX)
+            req->tdls_sta_initiator = true;
+    }
+
+    /* Send the ME_STA_ADD_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, ME_STA_ADD_CFM, cfm);
+}
+
+int rwnx_send_me_sta_del(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool tdls_sta)
+{
+    struct me_sta_del_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_STA_DEL_REQ message */
+    req = rwnx_msg_zalloc(ME_STA_DEL_REQ, TASK_ME, DRV_TASK_ID,
+                          sizeof(struct me_sta_del_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_STA_DEL_REQ message */
+    req->sta_idx = sta_idx;
+    req->tdls_sta = tdls_sta;
+
+    /* Send the ME_STA_DEL_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, ME_STA_DEL_CFM, NULL);
+}
+
+int rwnx_send_me_traffic_ind(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool uapsd, u8 tx_status)
+{
+    struct me_traffic_ind_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the ME_UTRAFFIC_IND_REQ message */
+    req = rwnx_msg_zalloc(ME_TRAFFIC_IND_REQ, TASK_ME, DRV_TASK_ID,
+                          sizeof(struct me_traffic_ind_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the ME_TRAFFIC_IND_REQ message */
+    req->sta_idx = sta_idx;
+    req->tx_avail = tx_status;
+    req->uapsd = uapsd;
+
+    /* Send the ME_TRAFFIC_IND_REQ to UMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, ME_TRAFFIC_IND_CFM, NULL);
+}
+
+int rwnx_send_me_rc_stats(struct rwnx_hw *rwnx_hw,
+                          u8 sta_idx,
+                          struct me_rc_stats_cfm *cfm)
+{
+    struct me_rc_stats_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the ME_RC_STATS_REQ message */
+    req = rwnx_msg_zalloc(ME_RC_STATS_REQ, TASK_ME, DRV_TASK_ID,
+                                  sizeof(struct me_rc_stats_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the ME_RC_STATS_REQ message */
+    req->sta_idx = sta_idx;
+
+    /* Send the ME_RC_STATS_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, ME_RC_STATS_CFM, cfm);
+}
+
+int rwnx_send_me_rc_set_rate(struct rwnx_hw *rwnx_hw,
+                             u8 sta_idx,
+                             u16 rate_cfg)
+{
+    struct me_rc_set_rate_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the ME_RC_SET_RATE_REQ message */
+    req = rwnx_msg_zalloc(ME_RC_SET_RATE_REQ, TASK_ME, DRV_TASK_ID,
+                          sizeof(struct me_rc_set_rate_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the ME_RC_SET_RATE_REQ message */
+    req->sta_idx = sta_idx;
+    req->fixed_rate_cfg = rate_cfg;
+
+    /* Send the ME_RC_SET_RATE_REQ message to FW */
+    return rwnx_send_msg(rwnx_hw, req, 0, 0, NULL);
+}
+
+int rwnx_send_me_set_ps_mode(struct rwnx_hw *rwnx_hw, u8 ps_mode)
+{
+    struct me_set_ps_mode_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the ME_SET_PS_MODE_REQ message */
+    req = rwnx_msg_zalloc(ME_SET_PS_MODE_REQ, TASK_ME, DRV_TASK_ID,
+                          sizeof(struct me_set_ps_mode_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the ME_SET_PS_MODE_REQ message */
+    req->ps_state = ps_mode;
+
+    /* Send the ME_SET_PS_MODE_REQ message to FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, ME_SET_PS_MODE_CFM, NULL);
+}
+
+int rwnx_send_me_set_lp_level(struct rwnx_hw *rwnx_hw, u8 lp_level)
+{
+    struct me_set_lp_level_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the ME_SET_LP_LEVEL_REQ message */
+    req = rwnx_msg_zalloc(ME_SET_LP_LEVEL_REQ, TASK_ME, DRV_TASK_ID,
+                          sizeof(struct me_set_lp_level_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the ME_SET_LP_LEVEL_REQ message */
+    req->lp_level = lp_level;
+
+    /* Send the ME_SET_LP_LEVEL_REQ message to FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, ME_SET_LP_LEVEL_CFM, NULL);
+}
+
+int rwnx_send_sm_connect_req(struct rwnx_hw *rwnx_hw,
+                             struct rwnx_vif *rwnx_vif,
+                             struct cfg80211_connect_params *sme,
+                             struct sm_connect_cfm *cfm)
+{
+    struct sm_connect_req *req;
+    int i;
+    u32_l flags = 0;
+    bool gval = false;
+    bool pval = false;
+    rwnx_vif->wep_enabled = false;
+    rwnx_vif->wep_auth_err = false;
+    rwnx_vif->last_auth_type = 0;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the SM_CONNECT_REQ message */
+    req = rwnx_msg_zalloc(SM_CONNECT_REQ, TASK_SM, DRV_TASK_ID,
+                                   sizeof(struct sm_connect_req));
+    if (!req)
+        return -ENOMEM;
+
+    if ((sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40) ||
+        (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104)) {
+         gval = true;
+    }
+
+    if (sme->crypto.n_ciphers_pairwise &&
+        ((sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP40) ||
+         (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP104))) {
+        pval = true;
+    }
+
+    /* Set parameters for the SM_CONNECT_REQ message */
+    if (sme->crypto.n_ciphers_pairwise &&
+        ((sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP40) ||
+         (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_TKIP) ||
+         (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP104)))
+        flags |= DISABLE_HT;
+
+    if (sme->crypto.control_port)
+        flags |= CONTROL_PORT_HOST;
+
+    if (sme->crypto.control_port_no_encrypt)
+        flags |= CONTROL_PORT_NO_ENC;
+
+    if (use_pairwise_key(&sme->crypto))
+        flags |= WPA_WPA2_IN_USE;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) || defined (CONFIG_WPA3_FOR_OLD_KERNEL)
+    if (sme->mfp == NL80211_MFP_REQUIRED)
+        flags |= MFP_IN_USE;
+#endif
+	if (rwnx_vif->sta.ap)
+		flags |= REASSOCIATION;
+
+    req->ctrl_port_ethertype = sme->crypto.control_port_ethertype;
+
+    if (sme->bssid)
+        memcpy(&req->bssid, sme->bssid, ETH_ALEN);
+    else
+        req->bssid = mac_addr_bcst;
+    req->vif_idx = rwnx_vif->vif_index;
+    if (sme->channel) {
+        req->chan.band = sme->channel->band;
+        req->chan.freq = sme->channel->center_freq;
+        req->chan.flags = get_chan_flags(sme->channel->flags);
+    } else {
+        req->chan.freq = (u16_l)-1;
+    }
+    for (i = 0; i < sme->ssid_len; i++)
+        req->ssid.array[i] = sme->ssid[i];
+    req->ssid.length = sme->ssid_len;
+    req->flags = flags;
+    if (WARN_ON(sme->ie_len > sizeof(req->ie_buf)))
+        goto invalid_param;
+    if (sme->ie_len)
+        memcpy(req->ie_buf, sme->ie, sme->ie_len);
+    req->ie_len = sme->ie_len;
+    req->listen_interval = rwnx_mod_params.listen_itv;
+    req->dont_wait_bcmc = !rwnx_mod_params.listen_bcmc;
+
+    /* Set auth_type */
+    if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC)
+        req->auth_type = WLAN_AUTH_OPEN;
+    else if (sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM)
+        req->auth_type = WLAN_AUTH_OPEN;
+    else if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY)
+        req->auth_type = WLAN_AUTH_SHARED_KEY;
+    else if (sme->auth_type == NL80211_AUTHTYPE_FT)
+        req->auth_type = WLAN_AUTH_FT;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) || defined (CONFIG_WPA3_FOR_OLD_KERNEL)
+    else if (sme->auth_type == NL80211_AUTHTYPE_SAE)
+        req->auth_type = WLAN_AUTH_SAE;
+#endif
+    else
+        goto invalid_param;
+
+    /* Set UAPSD queues */
+    req->uapsd_queues = rwnx_mod_params.uapsd_queues;
+
+    rwnx_vif->wep_enabled = pval & gval;
+
+    if (rwnx_vif->wep_enabled) {
+        rwnx_vif->last_auth_type = sme->auth_type;
+    }
+
+    /* Send the SM_CONNECT_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, SM_CONNECT_CFM, cfm);
+
+invalid_param:
+    rwnx_msg_free(rwnx_hw, req);
+    return -EINVAL;
+}
+
+int rwnx_send_sm_disconnect_req(struct rwnx_hw *rwnx_hw,
+                                struct rwnx_vif *rwnx_vif,
+                                u16 reason)
+{
+    struct sm_disconnect_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the SM_DISCONNECT_REQ message */
+    req = rwnx_msg_zalloc(SM_DISCONNECT_REQ, TASK_SM, DRV_TASK_ID,
+                                   sizeof(struct sm_disconnect_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the SM_DISCONNECT_REQ message */
+    req->reason_code = reason;
+    req->vif_idx = rwnx_vif->vif_index;
+
+    /* Send the SM_DISCONNECT_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, SM_DISCONNECT_CFM, NULL);
+}
+
+int rwnx_send_sm_external_auth_required_rsp(struct rwnx_hw *rwnx_hw,
+                                            struct rwnx_vif *rwnx_vif,
+                                            u16 status)
+{
+    struct sm_external_auth_required_rsp *rsp;
+
+    /* Build the SM_EXTERNAL_AUTH_CFM message */
+    rsp = rwnx_msg_zalloc(SM_EXTERNAL_AUTH_REQUIRED_RSP, TASK_SM, DRV_TASK_ID,
+                          sizeof(struct sm_external_auth_required_rsp));
+    if (!rsp)
+        return -ENOMEM;
+
+    rsp->status = status;
+    rsp->vif_idx = rwnx_vif->vif_index;
+
+    /* send the SM_EXTERNAL_AUTH_REQUIRED_RSP message UMAC FW */
+    return rwnx_send_msg(rwnx_hw, rsp, 0, 0, NULL);
+}
+
+int rwnx_send_apm_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                            struct cfg80211_ap_settings *settings,
+                            struct apm_start_cfm *cfm,
+                            struct rwnx_ipc_elem_var *elem)
+{
+    struct apm_start_req *req;
+    struct rwnx_bcn *bcn = &vif->ap.bcn;
+    u8 *buf;
+    u32 flags = 0;
+    const u8 *rate_ie;
+    u8 rate_len = 0;
+    int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+    const u8 *var_pos;
+    int len, i;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the APM_START_REQ message */
+    req = rwnx_msg_zalloc(APM_START_REQ, TASK_APM, DRV_TASK_ID,
+                                   sizeof(struct apm_start_req));
+    if (!req)
+        return -ENOMEM;
+
+    // Build the beacon
+    bcn->dtim = (u8)settings->dtim_period;
+    buf = rwnx_build_bcn(bcn, &settings->beacon);
+    if (!buf) {
+        rwnx_msg_free(rwnx_hw, req);
+        return -ENOMEM;
+    }
+
+    // Retrieve the basic rate set from the beacon buffer
+    len = bcn->len - var_offset;
+    var_pos = buf + var_offset;
+
+// Assume that rate higher that 54 Mbps are BSS membership
+#define IS_BASIC_RATE(r) ((r & 0x80) && ((r & ~0x80) <= (54 * 2)))
+
+    rate_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
+    if (rate_ie) {
+        const u8 *rates = rate_ie + 2;
+        for (i = 0; (i < rate_ie[1]) && (rate_len < MAC_RATESET_LEN); i++) {
+            if (IS_BASIC_RATE(rates[i]))
+                req->basic_rates.array[rate_len++] = rates[i];
+        }
+    }
+    rate_ie = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, var_pos, len);
+    if (rate_ie) {
+        const u8 *rates = rate_ie + 2;
+        for (i = 0; (i < rate_ie[1]) && (rate_len < MAC_RATESET_LEN); i++) {
+            if (IS_BASIC_RATE(rates[i]))
+                req->basic_rates.array[rate_len++] = rates[i];
+        }
+    }
+    req->basic_rates.length = rate_len;
+#undef IS_BASIC_RATE
+
+    #if 0
+    // Sync buffer for FW
+    if ((error = rwnx_ipc_elem_var_allocs(rwnx_hw, elem, bcn->len,
+                                          DMA_TO_DEVICE, buf, NULL, NULL))) {
+        return error;
+    }
+    #else
+    rwnx_send_bcn(rwnx_hw, buf, vif->vif_index, bcn->len);
+    #endif
+
+    /* Set parameters for the APM_START_REQ message */
+    req->vif_idx = vif->vif_index;
+    req->bcn_addr = elem->dma_addr;
+    req->bcn_len = bcn->len;
+    req->tim_oft = bcn->head_len;
+    req->tim_len = bcn->tim_len;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+    req->chan.band = settings->chandef.chan->band;
+    req->chan.freq = settings->chandef.chan->center_freq;
+#endif
+    req->chan.flags = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+    req->chan.tx_power = chan_to_fw_pwr(settings->chandef.chan->max_power);
+    req->center_freq1 = settings->chandef.center_freq1;
+    req->center_freq2 = settings->chandef.center_freq2;
+    req->ch_width = bw2chnl[settings->chandef.width];
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+    req->chan.band = rwnx_hw->ap_chan.band;
+    req->chan.freq = rwnx_hw->ap_chan.prim20_freq;
+    req->center_freq1 = rwnx_hw->ap_chan.center1_freq;
+    req->center_freq2 = rwnx_hw->ap_chan.center2_freq;
+    req->chan.tx_power = rwnx_hw->ap_chan.tx_power;
+#endif
+    req->bcn_int = settings->beacon_interval;
+    if (settings->crypto.control_port)
+        flags |= CONTROL_PORT_HOST;
+
+    if (settings->crypto.control_port_no_encrypt)
+        flags |= CONTROL_PORT_NO_ENC;
+
+    if (use_pairwise_key(&settings->crypto))
+        flags |= WPA_WPA2_IN_USE;
+
+    if (settings->crypto.control_port_ethertype)
+        req->ctrl_port_ethertype = settings->crypto.control_port_ethertype;
+    else
+        req->ctrl_port_ethertype = ETH_P_PAE;
+    req->flags = flags;
+
+    /* Send the APM_START_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, APM_START_CFM, cfm);
+}
+
+int rwnx_send_apm_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif)
+{
+    struct apm_stop_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the APM_STOP_REQ message */
+    req = rwnx_msg_zalloc(APM_STOP_REQ, TASK_APM, DRV_TASK_ID,
+                                   sizeof(struct apm_stop_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the APM_STOP_REQ message */
+    req->vif_idx = vif->vif_index;
+
+    /* Send the APM_STOP_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, APM_STOP_CFM, NULL);
+}
+
+uint8_t scanning;// = 0;
+
+#define P2P_WILDCARD_SSID                       "DIRECT-"
+#define P2P_WILDCARD_SSID_LEN                   (sizeof(P2P_WILDCARD_SSID) - 1)
+
+#ifdef CONFIG_SET_VENDOR_EXTENSION_IE
+u8_l vendor_extension_data[256];
+u8_l vendor_extension_len = 0;
+#if 0
+u8_l vendor_extension_data[]={
+	0x10,0x49,0x00,0x17,0x00,0x01,0x37,0x10,
+	0x06,0x00,0x10,0xc5,0xc9,0x91,0xeb,0x1f,
+	0xce,0x4d,0x00,0xa1,0x2a,0xdf,0xa1,0xe9,
+	0xc3,0x44,0xe6,0x10,0x49,0x00,0x21,0x00,
+	0x01,0x37,0x20,0x01,0x00,0x01,0x05,0x20,
+	0x02,0x00,0x04,0x43,0x56,0x54,0x45,0x20,
+	0x05,0x00,0x0d,0x31,0x39,0x32,0x2e,0x31,
+	0x36,0x38,0x2e,0x31,0x35,0x34,0x2e,0x31};
+#endif
+
+void rwnx_insert_vendor_extension_data(struct scanu_vendor_ie_req *ie_req){
+	u8_l temp_ie[256];
+	u8_l vendor_extension_subelement[3] = {0x00,0x37,0x2A};
+	u8_l vendor_extension_id[2] = {0x10,0x49};
+	int index = 0;
+	int vendor_extension_subelement_len = 0;
+
+	memset(temp_ie, 0, 256);
+
+	//find vendor_extension_subelement
+	for(index = 0; index < ie_req->add_ie_len; index++){
+		if(ie_req->ie[index] == vendor_extension_id[0]){
+			index++;
+			if(index == ie_req->add_ie_len){
+				return;
+			}
+			if(ie_req->ie[index] == vendor_extension_id[1] &&
+				ie_req->ie[index + 3] == vendor_extension_subelement[0]&&
+				ie_req->ie[index + 4] == vendor_extension_subelement[1]&&
+				ie_req->ie[index + 5] == vendor_extension_subelement[2]){
+				index = index + 2;
+				vendor_extension_subelement_len = ie_req->ie[index];
+				printk("%s find vendor_extension_subelement,index:%d len:%d\r\n", __func__, index, ie_req->ie[index]);
+				break;
+			}
+		}
+	}
+	index = index + vendor_extension_subelement_len;
+
+	//insert vendor extension
+	memcpy(&temp_ie[0], ie_req->ie, index + 1);
+	memcpy(&temp_ie[index + 1], vendor_extension_data, vendor_extension_len/*sizeof(vendor_extension_data)*/);//insert vendor extension data
+	memcpy(&temp_ie[index + 1 + vendor_extension_len/*sizeof(vendor_extension_data)*/], &ie_req->ie[index + 1], ie_req->add_ie_len - index);
+
+	memcpy(ie_req->ie, temp_ie, ie_req->add_ie_len + vendor_extension_len/*sizeof(vendor_extension_data)*/);
+	ie_req->add_ie_len = ie_req->add_ie_len + vendor_extension_len/*sizeof(vendor_extension_data)*/;
+	ie_req->ie[1] = ie_req->ie[1] + vendor_extension_len/*sizeof(vendor_extension_data)*/;
+
+	//rwnx_data_dump((char*)__func__, (void*)ie_req->ie, ie_req->add_ie_len);
+}
+#endif//CONFIG_SET_VENDOR_EXTENSION_IE
+
+
+int rwnx_send_scanu_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                        struct cfg80211_scan_request *param)
+{
+    struct scanu_start_req *req = NULL;
+    struct scanu_vendor_ie_req *ie_req = NULL;
+    struct mm_add_if_cfm add_if_cfm;
+    int i;
+    uint8_t chan_flags = 0;
+    int err;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the SCANU_START_REQ message */
+    req = rwnx_msg_zalloc(SCANU_START_REQ, TASK_SCANU, DRV_TASK_ID,
+                          sizeof(struct scanu_start_req));
+    if (!req)
+        return -ENOMEM;
+
+    scanning = 1;
+    /* Set parameters */
+    req->vif_idx = rwnx_vif->vif_index;
+    req->chan_cnt = (u8)min_t(int, SCAN_CHANNEL_MAX, param->n_channels);
+    req->ssid_cnt = (u8)min_t(int, SCAN_SSID_MAX, param->n_ssids);
+    req->bssid = mac_addr_bcst;
+    req->no_cck = param->no_cck;
+
+#ifdef RADAR_OR_IR_DETECT
+    if (req->ssid_cnt == 0)
+        chan_flags |= CHAN_NO_IR;
+#endif
+    for (i = 0; i < req->ssid_cnt; i++) {
+        int j;
+        for (j = 0; j < param->ssids[i].ssid_len; j++)
+            req->ssid[i].array[j] = param->ssids[i].ssid[j];
+        req->ssid[i].length = param->ssids[i].ssid_len;
+
+        if (!memcmp(P2P_WILDCARD_SSID, param->ssids[i].ssid,
+            P2P_WILDCARD_SSID_LEN)) {
+            printk("p2p scanu:%d,%d,%d\n",rwnx_vif->vif_index, rwnx_vif->is_p2p_vif, rwnx_hw->is_p2p_alive);
+            if(rwnx_vif == rwnx_hw->p2p_dev_vif && !rwnx_vif->up) {
+                if ((err = rwnx_send_add_if(rwnx_hw, rwnx_vif->address,
+                                              RWNX_VIF_TYPE(rwnx_vif), false, &add_if_cfm)))
+                   goto error;
+
+                if (add_if_cfm.status != 0) {
+                    return -EIO;
+                }
+
+                /* Save the index retrieved from LMAC */
+                spin_lock_bh(&rwnx_hw->cb_lock);
+                rwnx_vif->vif_index = add_if_cfm.inst_nbr;
+                rwnx_vif->up = true;
+                rwnx_hw->vif_started++;
+                rwnx_hw->vif_table[add_if_cfm.inst_nbr] = rwnx_vif;
+                spin_unlock_bh(&rwnx_hw->cb_lock);
+            }
+            rwnx_hw->is_p2p_alive = 1;
+            mod_timer(&rwnx_hw->p2p_alive_timer, jiffies + msecs_to_jiffies(1000));
+            atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+            printk("p2p scan start\n");
+        }
+    }
+
+#if 1
+    if (param->ie) {
+        #if 0
+        if (rwnx_ipc_elem_var_allocs(rwnx_hw, &rwnx_hw->scan_ie,
+                                     param->ie_len, DMA_TO_DEVICE,
+                                     NULL, param->ie, NULL))
+            goto error;
+
+        req->add_ie_len = param->ie_len;
+        req->add_ies = rwnx_hw->scan_ie.dma_addr;
+        #else
+        ie_req = rwnx_msg_zalloc(SCANU_VENDOR_IE_REQ, TASK_SCANU, DRV_TASK_ID,
+                              sizeof(struct scanu_vendor_ie_req));
+        if (!ie_req)
+            return -ENOMEM;
+
+		ie_req->add_ie_len = param->ie_len;
+		ie_req->vif_idx = rwnx_vif->vif_index;
+		memcpy(ie_req->ie, param->ie, param->ie_len);
+#ifdef CONFIG_SET_VENDOR_EXTENSION_IE
+		rwnx_insert_vendor_extension_data(ie_req);
+#endif //CONFIG_SET_VENDOR_EXTENSION_IE
+		req->add_ie_len = 0;
+		req->add_ies = 0;
+
+        if ((err = rwnx_send_msg(rwnx_hw, ie_req, 1, SCANU_VENDOR_IE_CFM, NULL)))
+            goto error;
+        #endif
+        }
+    else {
+        req->add_ie_len = 0;
+        req->add_ies = 0;
+    }
+#else
+        req->add_ie_len = 0;
+        req->add_ies = 0;
+#endif
+
+    for (i = 0; i < req->chan_cnt; i++) {
+        struct ieee80211_channel *chan = param->channels[i];
+
+        req->chan[i].band = chan->band;
+        req->chan[i].freq = chan->center_freq;
+        req->chan[i].flags = chan_flags | get_chan_flags(chan->flags);
+        req->chan[i].tx_power = chan_to_fw_pwr(chan->max_reg_power);
+    }
+
+    /* Send the SCANU_START_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1,  SCANU_START_CFM_ADDTIONAL, NULL);
+error:
+    if (req != NULL)
+        rwnx_msg_free(rwnx_hw, req);
+    if (ie_req != NULL)
+        rwnx_msg_free(rwnx_hw, ie_req);
+    return -ENOMEM;
+}
+
+int rwnx_send_scanu_cancel_req(struct rwnx_hw *rwnx_hw, struct scan_cancel_cfm *cfm)
+{
+    struct scan_cancel_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the SCAN_CANCEL_REQ message */
+    req = rwnx_msg_zalloc(SCANU_CANCEL_REQ, TASK_SCANU, DRV_TASK_ID,
+                          sizeof(struct scan_cancel_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Send the SCAN_CANCEL_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, SCANU_CANCEL_CFM, cfm);
+}
+
+int rwnx_send_apm_start_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                                struct cfg80211_chan_def *chandef,
+                                struct apm_start_cac_cfm *cfm)
+{
+    struct apm_start_cac_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the APM_START_CAC_REQ message */
+    req = rwnx_msg_zalloc(APM_START_CAC_REQ, TASK_APM, DRV_TASK_ID,
+                          sizeof(struct apm_start_cac_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the APM_START_CAC_REQ message */
+    req->vif_idx = vif->vif_index;
+    req->chan.band = chandef->chan->band;
+    req->chan.freq = chandef->chan->center_freq;
+    req->chan.flags = 0;
+    req->center_freq1 = chandef->center_freq1;
+    req->center_freq2 = chandef->center_freq2;
+    req->ch_width = bw2chnl[chandef->width];
+
+    /* Send the APM_START_CAC_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, APM_START_CAC_CFM, cfm);
+}
+
+int rwnx_send_apm_stop_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif)
+{
+    struct apm_stop_cac_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the APM_STOP_CAC_REQ message */
+    req = rwnx_msg_zalloc(APM_STOP_CAC_REQ, TASK_APM, DRV_TASK_ID,
+                          sizeof(struct apm_stop_cac_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the APM_STOP_CAC_REQ message */
+    req->vif_idx = vif->vif_index;
+
+    /* Send the APM_STOP_CAC_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, APM_STOP_CAC_CFM, NULL);
+}
+
+int rwnx_send_mesh_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                             const struct mesh_config *conf, const struct mesh_setup *setup,
+                             struct mesh_start_cfm *cfm)
+{
+    // Message to send
+    struct mesh_start_req *req;
+    // Supported basic rates
+    struct ieee80211_supported_band *band_2GHz = rwnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+    /* Counter */
+    int i;
+    /* Return status */
+    int status;
+    /* DMA Address to be unmapped after confirmation reception */
+    u32 dma_addr = 0;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MESH_START_REQ message */
+    req = rwnx_msg_zalloc(MESH_START_REQ, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_start_req));
+    if (!req) {
+        return -ENOMEM;
+    }
+
+    req->vif_index = vif->vif_index;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+    req->bcn_int = setup->beacon_interval;
+    req->dtim_period = setup->dtim_period;
+#endif
+    req->mesh_id_len = setup->mesh_id_len;
+
+    for (i = 0; i < setup->mesh_id_len; i++) {
+        req->mesh_id[i] = *(setup->mesh_id + i);
+    }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    req->user_mpm = setup->user_mpm;
+#endif
+    req->is_auth = setup->is_authenticated;
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+    req->auth_id = setup->auth_id;
+    #endif
+    req->ie_len = setup->ie_len;
+
+    if (setup->ie_len) {
+        /*
+         * Need to provide a Virtual Address to the MAC so that it can download the
+         * additional information elements.
+         */
+        req->ie_addr = dma_map_single(rwnx_hw->dev, (void *)setup->ie,
+                                      setup->ie_len, DMA_FROM_DEVICE);
+
+        /* Check DMA mapping result */
+        if (dma_mapping_error(rwnx_hw->dev, req->ie_addr)) {
+            printk(KERN_CRIT "%s - DMA Mapping error on additional IEs\n", __func__);
+
+            /* Consider there is no Additional IEs */
+            req->ie_len = 0;
+        } else {
+            /* Store DMA Address so that we can unmap the memory section once MESH_START_CFM is received */
+            dma_addr = req->ie_addr;
+        }
+    }
+
+    /* Provide rate information */
+    req->basic_rates.length = 0;
+    for (i = 0; i < band_2GHz->n_bitrates; i++) {
+        u16 rate = band_2GHz->bitrates[i].bitrate;
+
+        /* Read value is in in units of 100 Kbps, provided value is in units
+         * of 1Mbps, and multiplied by 2 so that 5.5 becomes 11 */
+        rate = (rate << 1) / 10;
+
+        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) // TODO: check basic rates
+        if (setup->basic_rates & CO_BIT(i)) {
+            rate |= 0x80;
+        }
+        #endif
+
+        req->basic_rates.array[i] = (u8)rate;
+        req->basic_rates.length++;
+    }
+
+    /* Provide channel information */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+    req->chan.band = setup->chandef.chan->band;
+    req->chan.freq = setup->chandef.chan->center_freq;
+#endif
+
+    req->chan.flags = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+    req->chan.tx_power = chan_to_fw_pwr(setup->chandef.chan->max_power);
+    req->center_freq1 = setup->chandef.center_freq1;
+    req->center_freq2 = setup->chandef.center_freq2;
+    req->ch_width = bw2chnl[setup->chandef.width];
+#endif
+
+    /* Send the MESH_START_REQ message to UMAC FW */
+    status = rwnx_send_msg(rwnx_hw, req, 1, MESH_START_CFM, cfm);
+
+    /* Unmap DMA area */
+    if (setup->ie_len) {
+        dma_unmap_single(rwnx_hw->dev, dma_addr, setup->ie_len, DMA_TO_DEVICE);
+    }
+
+    /* Return the status */
+    return (status);
+}
+
+int rwnx_send_mesh_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                            struct mesh_stop_cfm *cfm)
+{
+    // Message to send
+    struct mesh_stop_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MESH_STOP_REQ message */
+    req = rwnx_msg_zalloc(MESH_STOP_REQ, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_stop_req));
+    if (!req) {
+        return -ENOMEM;
+    }
+
+    req->vif_idx = vif->vif_index;
+
+    /* Send the MESH_STOP_REQ message to UMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, MESH_STOP_CFM, cfm);
+}
+
+int rwnx_send_mesh_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                              u32 mask, const struct mesh_config *p_mconf, struct mesh_update_cfm *cfm)
+{
+    // Message to send
+    struct mesh_update_req *req;
+    // Keep only bit for fields which can be updated
+    u32 supp_mask = (mask << 1) & (CO_BIT(NL80211_MESHCONF_GATE_ANNOUNCEMENTS)
+                                   | CO_BIT(NL80211_MESHCONF_HWMP_ROOTMODE)
+                                   | CO_BIT(NL80211_MESHCONF_FORWARDING)
+                                   | CO_BIT(NL80211_MESHCONF_POWER_MODE));
+
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (!supp_mask) {
+        return -ENOENT;
+    }
+
+    /* Build the MESH_UPDATE_REQ message */
+    req = rwnx_msg_zalloc(MESH_UPDATE_REQ, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_update_req));
+
+    if (!req) {
+        return -ENOMEM;
+    }
+
+    req->vif_idx = vif->vif_index;
+
+    if (supp_mask & CO_BIT(NL80211_MESHCONF_GATE_ANNOUNCEMENTS))
+    {
+        req->flags |= CO_BIT(MESH_UPDATE_FLAGS_GATE_MODE_BIT);
+        req->gate_announ = p_mconf->dot11MeshGateAnnouncementProtocol;
+    }
+
+    if (supp_mask & CO_BIT(NL80211_MESHCONF_HWMP_ROOTMODE))
+    {
+        req->flags |= CO_BIT(MESH_UPDATE_FLAGS_ROOT_MODE_BIT);
+        req->root_mode = p_mconf->dot11MeshHWMPRootMode;
+    }
+
+    if (supp_mask & CO_BIT(NL80211_MESHCONF_FORWARDING))
+    {
+        req->flags |= CO_BIT(MESH_UPDATE_FLAGS_MESH_FWD_BIT);
+        req->mesh_forward = p_mconf->dot11MeshForwarding;
+    }
+
+    if (supp_mask & CO_BIT(NL80211_MESHCONF_POWER_MODE))
+    {
+        req->flags |= CO_BIT(MESH_UPDATE_FLAGS_LOCAL_PSM_BIT);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+        req->local_ps_mode = p_mconf->power_mode;
+#endif
+    }
+
+    /* Send the MESH_UPDATE_REQ message to UMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, MESH_UPDATE_CFM, cfm);
+}
+
+int rwnx_send_mesh_peer_info_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                                 u8 sta_idx, struct mesh_peer_info_cfm *cfm)
+{
+    // Message to send
+    struct mesh_peer_info_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MESH_PEER_INFO_REQ message */
+    req = rwnx_msg_zalloc(MESH_PEER_INFO_REQ, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_peer_info_req));
+    if (!req) {
+        return -ENOMEM;
+    }
+
+    req->sta_idx = sta_idx;
+
+    /* Send the MESH_PEER_INFO_REQ message to UMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, MESH_PEER_INFO_CFM, cfm);
+}
+
+void rwnx_send_mesh_peer_update_ntf(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                                    u8 sta_idx, u8 mlink_state)
+{
+    // Message to send
+    struct mesh_peer_update_ntf *ntf;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MESH_PEER_UPDATE_NTF message */
+    ntf = rwnx_msg_zalloc(MESH_PEER_UPDATE_NTF, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_peer_update_ntf));
+
+    if (ntf) {
+        ntf->vif_idx = vif->vif_index;
+        ntf->sta_idx = sta_idx;
+        ntf->state = mlink_state;
+
+        /* Send the MESH_PEER_INFO_REQ message to UMAC FW */
+        rwnx_send_msg(rwnx_hw, ntf, 0, 0, NULL);
+    }
+}
+
+void rwnx_send_mesh_path_create_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, u8 *tgt_addr)
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Check if we are already waiting for a confirmation */
+    if (!vif->ap.create_path) {
+        // Message to send
+        struct mesh_path_create_req *req;
+
+        /* Build the MESH_PATH_CREATE_REQ message */
+        req = rwnx_msg_zalloc(MESH_PATH_CREATE_REQ, TASK_MESH, DRV_TASK_ID,
+                              sizeof(struct mesh_path_create_req));
+
+        if (req) {
+            req->vif_idx = vif->vif_index;
+            memcpy(&req->tgt_mac_addr, tgt_addr, ETH_ALEN);
+
+            vif->ap.create_path = true;
+
+            /* Send the MESH_PATH_CREATE_REQ message to UMAC FW */
+            rwnx_send_msg(rwnx_hw, req, 0, 0, NULL);
+        }
+    }
+}
+
+int rwnx_send_mesh_path_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, const u8 *tgt_addr,
+                                   const u8 *p_nhop_addr, struct mesh_path_update_cfm *cfm)
+{
+    // Message to send
+    struct mesh_path_update_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MESH_PATH_UPDATE_REQ message */
+    req = rwnx_msg_zalloc(MESH_PATH_UPDATE_REQ, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_path_update_req));
+    if (!req) {
+        return -ENOMEM;
+    }
+
+    req->delete = (p_nhop_addr == NULL);
+    req->vif_idx = vif->vif_index;
+    memcpy(&req->tgt_mac_addr, tgt_addr, ETH_ALEN);
+
+    if (p_nhop_addr) {
+        memcpy(&req->nhop_mac_addr, p_nhop_addr, ETH_ALEN);
+    }
+
+    /* Send the MESH_PATH_UPDATE_REQ message to UMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, MESH_PATH_UPDATE_CFM, cfm);
+}
+
+void rwnx_send_mesh_proxy_add_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, u8 *ext_addr)
+{
+    // Message to send
+    struct mesh_proxy_add_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MESH_PROXY_ADD_REQ message */
+    req = rwnx_msg_zalloc(MESH_PROXY_ADD_REQ, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_proxy_add_req));
+
+    if (req) {
+        req->vif_idx = vif->vif_index;
+        memcpy(&req->ext_sta_addr, ext_addr, ETH_ALEN);
+
+        /* Send the MESH_PROXY_ADD_REQ message to UMAC FW */
+        rwnx_send_msg(rwnx_hw, req, 0, 0, NULL);
+    }
+}
+
+int rwnx_send_tdls_peer_traffic_ind_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif)
+{
+    struct tdls_peer_traffic_ind_req *tdls_peer_traffic_ind_req;
+
+    if (!rwnx_vif->sta.tdls_sta)
+        return -ENOLINK;
+
+    /* Build the TDLS_PEER_TRAFFIC_IND_REQ message */
+    tdls_peer_traffic_ind_req = rwnx_msg_zalloc(TDLS_PEER_TRAFFIC_IND_REQ, TASK_TDLS, DRV_TASK_ID,
+                                           sizeof(struct tdls_peer_traffic_ind_req));
+
+    if (!tdls_peer_traffic_ind_req)
+        return -ENOMEM;
+
+    /* Set parameters for the TDLS_PEER_TRAFFIC_IND_REQ message */
+    tdls_peer_traffic_ind_req->vif_index = rwnx_vif->vif_index;
+    tdls_peer_traffic_ind_req->sta_idx = rwnx_vif->sta.tdls_sta->sta_idx;
+    memcpy(&(tdls_peer_traffic_ind_req->peer_mac_addr.array[0]),
+           rwnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN);
+    tdls_peer_traffic_ind_req->dialog_token = 0; // check dialog token value
+    tdls_peer_traffic_ind_req->last_tid = rwnx_vif->sta.tdls_sta->tdls.last_tid;
+    tdls_peer_traffic_ind_req->last_sn = rwnx_vif->sta.tdls_sta->tdls.last_sn;
+
+    /* Send the TDLS_PEER_TRAFFIC_IND_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, tdls_peer_traffic_ind_req, 0, 0, NULL);
+}
+
+int rwnx_send_config_monitor_req(struct rwnx_hw *rwnx_hw,
+                                 struct cfg80211_chan_def *chandef,
+                                 struct me_config_monitor_cfm *cfm)
+{
+    struct me_config_monitor_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the ME_CONFIG_MONITOR_REQ message */
+    req = rwnx_msg_zalloc(ME_CONFIG_MONITOR_REQ, TASK_ME, DRV_TASK_ID,
+                                   sizeof(struct me_config_monitor_req));
+    if (!req)
+        return -ENOMEM;
+
+    if (chandef) {
+        req->chan_set = true;
+
+        req->chan.band = chandef->chan->band;
+        req->chan.type = bw2chnl[chandef->width];
+        req->chan.prim20_freq = chandef->chan->center_freq;
+        req->chan.center1_freq = chandef->center_freq1;
+        req->chan.center2_freq = chandef->center_freq2;
+        req->chan.tx_power = chan_to_fw_pwr(chandef->chan->max_power);
+
+        if (rwnx_hw->phy.limit_bw)
+            limit_chan_bw(&req->chan.type, req->chan.prim20_freq, &req->chan.center1_freq);
+    } else {
+         req->chan_set = false;
+    }
+
+	req->uf = rwnx_hw->mod_params->uf;
+	req->auto_reply = rwnx_hw->mod_params->auto_reply;
+
+    /* Send the ME_CONFIG_MONITOR_REQ message to FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, ME_CONFIG_MONITOR_CFM, cfm);
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+int rwnx_send_tdls_chan_switch_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                                   struct rwnx_sta *rwnx_sta, bool sta_initiator,
+                                   u8 oper_class, struct cfg80211_chan_def *chandef,
+                                   struct tdls_chan_switch_cfm *cfm)
+{
+    struct tdls_chan_switch_req *tdls_chan_switch_req;
+
+
+    /* Build the TDLS_CHAN_SWITCH_REQ message */
+    tdls_chan_switch_req = rwnx_msg_zalloc(TDLS_CHAN_SWITCH_REQ, TASK_TDLS, DRV_TASK_ID,
+                                           sizeof(struct tdls_chan_switch_req));
+
+    if (!tdls_chan_switch_req)
+        return -ENOMEM;
+
+    /* Set parameters for the TDLS_CHAN_SWITCH_REQ message */
+    tdls_chan_switch_req->vif_index = rwnx_vif->vif_index;
+    tdls_chan_switch_req->sta_idx = rwnx_sta->sta_idx;
+    memcpy(&(tdls_chan_switch_req->peer_mac_addr.array[0]),
+           rwnx_sta_addr(rwnx_sta), ETH_ALEN);
+    tdls_chan_switch_req->initiator = sta_initiator;
+    tdls_chan_switch_req->band = chandef->chan->band;
+    tdls_chan_switch_req->type = bw2chnl[chandef->width];
+    tdls_chan_switch_req->prim20_freq = chandef->chan->center_freq;
+    tdls_chan_switch_req->center1_freq = chandef->center_freq1;
+    tdls_chan_switch_req->center2_freq = chandef->center_freq2;
+    tdls_chan_switch_req->tx_power = chan_to_fw_pwr(chandef->chan->max_power);
+    tdls_chan_switch_req->op_class = oper_class;
+
+    /* Send the TDLS_CHAN_SWITCH_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, tdls_chan_switch_req, 1, TDLS_CHAN_SWITCH_CFM, cfm);
+}
+
+int rwnx_send_tdls_cancel_chan_switch_req(struct rwnx_hw *rwnx_hw,
+                                          struct rwnx_vif *rwnx_vif,
+                                          struct rwnx_sta *rwnx_sta,
+                                          struct tdls_cancel_chan_switch_cfm *cfm)
+{
+    struct tdls_cancel_chan_switch_req *tdls_cancel_chan_switch_req;
+
+    /* Build the TDLS_CHAN_SWITCH_REQ message */
+    tdls_cancel_chan_switch_req = rwnx_msg_zalloc(TDLS_CANCEL_CHAN_SWITCH_REQ, TASK_TDLS, DRV_TASK_ID,
+                                           sizeof(struct tdls_cancel_chan_switch_req));
+    if (!tdls_cancel_chan_switch_req)
+        return -ENOMEM;
+
+    /* Set parameters for the TDLS_CHAN_SWITCH_REQ message */
+    tdls_cancel_chan_switch_req->vif_index = rwnx_vif->vif_index;
+    tdls_cancel_chan_switch_req->sta_idx = rwnx_sta->sta_idx;
+    memcpy(&(tdls_cancel_chan_switch_req->peer_mac_addr.array[0]),
+           rwnx_sta_addr(rwnx_sta), ETH_ALEN);
+
+    /* Send the TDLS_CHAN_SWITCH_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, tdls_cancel_chan_switch_req, 1, TDLS_CANCEL_CHAN_SWITCH_CFM, cfm);
+}
+
+#ifdef CONFIG_RWNX_BFMER
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_send_bfmer_enable(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta,
+                            const struct ieee80211_vht_cap *vht_cap)
+#endif /* CONFIG_RWNX_FULLMAC*/
+{
+    struct mm_bfmer_enable_req *bfmer_en_req;
+#ifdef CONFIG_RWNX_FULLMAC
+    __le32 vht_capability;
+    u8 rx_nss = 0;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_RWNX_FULLMAC
+    if (!vht_cap) {
+#endif /* CONFIG_RWNX_FULLMAC */
+        goto end;
+    }
+
+#ifdef CONFIG_RWNX_FULLMAC
+    vht_capability = vht_cap->vht_cap_info;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    if (!(vht_capability & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) {
+        goto end;
+    }
+
+#ifdef CONFIG_RWNX_FULLMAC
+    rx_nss = rwnx_bfmer_get_rx_nss(vht_cap);
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    /* Allocate a structure that will contain the beamforming report */
+    if (rwnx_bfmer_report_add(rwnx_hw, rwnx_sta, RWNX_BFMER_REPORT_SPACE_SIZE))
+    {
+        goto end;
+    }
+
+    /* Build the MM_BFMER_ENABLE_REQ message */
+    bfmer_en_req = rwnx_msg_zalloc(MM_BFMER_ENABLE_REQ, TASK_MM, DRV_TASK_ID,
+                                   sizeof(struct mm_bfmer_enable_req));
+
+    /* Check message allocation */
+    if (!bfmer_en_req) {
+        /* Free memory allocated for the report */
+        rwnx_bfmer_report_del(rwnx_hw, rwnx_sta);
+
+        /* Do not use beamforming */
+        goto end;
+    }
+
+    /* Provide DMA address to the MAC */
+    bfmer_en_req->host_bfr_addr = rwnx_sta->bfm_report->dma_addr;
+    bfmer_en_req->host_bfr_size = RWNX_BFMER_REPORT_SPACE_SIZE;
+    bfmer_en_req->sta_idx = rwnx_sta->sta_idx;
+#ifdef CONFIG_RWNX_FULLMAC
+    bfmer_en_req->aid = rwnx_sta->aid;
+    bfmer_en_req->rx_nss = rx_nss;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    if (vht_capability & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) {
+        bfmer_en_req->vht_mu_bfmee = true;
+    } else {
+        bfmer_en_req->vht_mu_bfmee = false;
+    }
+
+    /* Send the MM_BFMER_EN_REQ message to LMAC FW */
+    rwnx_send_msg(rwnx_hw, bfmer_en_req, 0, 0, NULL);
+
+end:
+    return;
+}
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+int rwnx_send_mu_group_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta)
+{
+    struct mm_mu_group_update_req *req;
+    int group_id, i = 0;
+    u64 map;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_MU_GROUP_UPDATE_REQ message */
+    req = rwnx_msg_zalloc(MM_MU_GROUP_UPDATE_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_mu_group_update_req) +
+                          rwnx_sta->group_info.cnt * sizeof(req->groups[0]));
+
+    /* Check message allocation */
+    if (!req)
+        return -ENOMEM;
+
+    /* Go through the groups the STA belongs to */
+    group_sta_for_each(rwnx_sta, group_id, map) {
+        int user_pos = rwnx_mu_group_sta_get_pos(rwnx_hw, rwnx_sta, group_id);
+
+        if (WARN((i >= rwnx_sta->group_info.cnt),
+                 "STA%d: Too much group (%d)\n",
+                 rwnx_sta->sta_idx, i + 1))
+            break;
+
+        req->groups[i].group_id = group_id;
+        req->groups[i].user_pos = user_pos;
+
+        i++;
+    }
+
+    req->group_cnt = rwnx_sta->group_info.cnt;
+    req->sta_idx = rwnx_sta->sta_idx;
+
+    /* Send the MM_MU_GROUP_UPDATE_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, MM_MU_GROUP_UPDATE_CFM, NULL);
+}
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+#endif /* CONFIG_RWNX_BFMER */
+
+/**********************************************************************
+ *    Debug Messages
+ *********************************************************************/
+int rwnx_send_dbg_trigger_req(struct rwnx_hw *rwnx_hw, char *msg)
+{
+    struct mm_dbg_trigger_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_DBG_TRIGGER_REQ message */
+    req = rwnx_msg_zalloc(MM_DBG_TRIGGER_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_dbg_trigger_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_DBG_TRIGGER_REQ message */
+    strncpy(req->error, msg, sizeof(req->error));
+
+    /* Send the MM_DBG_TRIGGER_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 0, -1, NULL);
+}
+
+int rwnx_send_dbg_mem_read_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+                               struct dbg_mem_read_cfm *cfm)
+{
+    struct dbg_mem_read_req *mem_read_req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the DBG_MEM_READ_REQ message */
+    mem_read_req = rwnx_msg_zalloc(DBG_MEM_READ_REQ, TASK_DBG, DRV_TASK_ID,
+                                   sizeof(struct dbg_mem_read_req));
+    if (!mem_read_req)
+        return -ENOMEM;
+
+    /* Set parameters for the DBG_MEM_READ_REQ message */
+    mem_read_req->memaddr = mem_addr;
+
+    /* Send the DBG_MEM_READ_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, mem_read_req, 1, DBG_MEM_READ_CFM, cfm);
+}
+
+int rwnx_send_dbg_mem_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+                                u32 mem_data)
+{
+    struct dbg_mem_write_req *mem_write_req;
+
+    //RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the DBG_MEM_WRITE_REQ message */
+    mem_write_req = rwnx_msg_zalloc(DBG_MEM_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+                                    sizeof(struct dbg_mem_write_req));
+    if (!mem_write_req)
+        return -ENOMEM;
+
+    /* Set parameters for the DBG_MEM_WRITE_REQ message */
+    mem_write_req->memaddr = mem_addr;
+    mem_write_req->memdata = mem_data;
+
+    /* Send the DBG_MEM_WRITE_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, mem_write_req, 1, DBG_MEM_WRITE_CFM, NULL);
+}
+
+int rwnx_send_dbg_mem_mask_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+                                     u32 mem_mask, u32 mem_data)
+{
+    struct dbg_mem_mask_write_req *mem_mask_write_req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the DBG_MEM_MASK_WRITE_REQ message */
+    mem_mask_write_req = rwnx_msg_zalloc(DBG_MEM_MASK_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+                                         sizeof(struct dbg_mem_mask_write_req));
+    if (!mem_mask_write_req)
+        return -ENOMEM;
+
+    /* Set parameters for the DBG_MEM_MASK_WRITE_REQ message */
+    mem_mask_write_req->memaddr = mem_addr;
+    mem_mask_write_req->memmask = mem_mask;
+    mem_mask_write_req->memdata = mem_data;
+
+    /* Send the DBG_MEM_MASK_WRITE_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, mem_mask_write_req, 1, DBG_MEM_MASK_WRITE_CFM, NULL);
+}
+
+#ifdef CONFIG_RFTEST
+int rwnx_send_rftest_req(struct rwnx_hw *rwnx_hw, u32_l cmd, u32_l argc, u8_l *argv, struct dbg_rftest_cmd_cfm *cfm)
+{
+    struct dbg_rftest_cmd_req *mem_rftest_cmd_req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the DBG_RFTEST_CMD_REQ message */
+    mem_rftest_cmd_req = rwnx_msg_zalloc(DBG_RFTEST_CMD_REQ, TASK_DBG, DRV_TASK_ID,
+                                         sizeof(struct dbg_rftest_cmd_req));
+    if (!mem_rftest_cmd_req)
+        return -ENOMEM;
+
+    if(argc > 30)
+        return -ENOMEM;
+
+    /* Set parameters for the DBG_MEM_MASK_WRITE_REQ message */
+    mem_rftest_cmd_req->cmd = cmd;
+    mem_rftest_cmd_req->argc = argc;
+    if(argc != 0)
+        memcpy(mem_rftest_cmd_req->argv, argv, argc);
+
+    /* Send the DBG_RFTEST_CMD_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, mem_rftest_cmd_req, 1, DBG_RFTEST_CMD_CFM, cfm);
+}
+#endif
+
+#ifdef CONFIG_MCU_MESSAGE
+int rwnx_send_dbg_custom_msg_req(struct rwnx_hw *rwnx_hw,
+                                 u32 cmd, void *buf, u32 len, u32 action,
+                                 struct dbg_custom_msg_cfm *cfm)
+{
+    struct dbg_custom_msg_req *cust_msg_req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the DBG_CUSTOM_MSG_REQ message */
+    cust_msg_req =
+        rwnx_msg_zalloc(DBG_CUSTOM_MSG_REQ, TASK_DBG, DRV_TASK_ID,
+                        offsetof(struct dbg_custom_msg_req, buf) + len);
+    if (!cust_msg_req)
+        return -ENOMEM;
+
+    /* Set parameters for the DBG_CUSTOM_MSG_REQ message */
+    cust_msg_req->cmd = cmd;
+    cust_msg_req->len = len;
+    cust_msg_req->flags = action;
+    if (buf) {
+        memcpy(cust_msg_req->buf, buf, len);
+    }
+
+    /* Send the DBG_CUSTOM_MSG_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, cust_msg_req, 1, DBG_CUSTOM_MSG_CFM, cfm);
+}
+#endif
+
+int rwnx_send_dbg_set_mod_filter_req(struct rwnx_hw *rwnx_hw, u32 filter)
+{
+    struct dbg_set_mod_filter_req *set_mod_filter_req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the DBG_SET_MOD_FILTER_REQ message */
+    set_mod_filter_req =
+        rwnx_msg_zalloc(DBG_SET_MOD_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
+                        sizeof(struct dbg_set_mod_filter_req));
+    if (!set_mod_filter_req)
+        return -ENOMEM;
+
+    /* Set parameters for the DBG_SET_MOD_FILTER_REQ message */
+    set_mod_filter_req->mod_filter = filter;
+
+    /* Send the DBG_SET_MOD_FILTER_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, set_mod_filter_req, 1, DBG_SET_MOD_FILTER_CFM, NULL);
+}
+
+int rwnx_send_dbg_set_sev_filter_req(struct rwnx_hw *rwnx_hw, u32 filter)
+{
+    struct dbg_set_sev_filter_req *set_sev_filter_req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the DBG_SET_SEV_FILTER_REQ message */
+    set_sev_filter_req =
+        rwnx_msg_zalloc(DBG_SET_SEV_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
+                        sizeof(struct dbg_set_sev_filter_req));
+    if (!set_sev_filter_req)
+        return -ENOMEM;
+
+    /* Set parameters for the DBG_SET_SEV_FILTER_REQ message */
+    set_sev_filter_req->sev_filter = filter;
+
+    /* Send the DBG_SET_SEV_FILTER_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, set_sev_filter_req, 1, DBG_SET_SEV_FILTER_CFM, NULL);
+}
+
+int rwnx_send_dbg_get_sys_stat_req(struct rwnx_hw *rwnx_hw,
+                                   struct dbg_get_sys_stat_cfm *cfm)
+{
+    void *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Allocate the message */
+    req = rwnx_msg_zalloc(DBG_GET_SYS_STAT_REQ, TASK_DBG, DRV_TASK_ID, 0);
+    if (!req)
+        return -ENOMEM;
+
+    /* Send the DBG_MEM_READ_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, DBG_GET_SYS_STAT_CFM, cfm);
+}
+
+int rwnx_send_dbg_mem_block_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+                                      u32 mem_size, u32 *mem_data)
+{
+    struct dbg_mem_block_write_req *mem_blk_write_req;
+
+    //RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the DBG_MEM_BLOCK_WRITE_REQ message */
+    mem_blk_write_req = rwnx_msg_zalloc(DBG_MEM_BLOCK_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+                                        sizeof(struct dbg_mem_block_write_req));
+    if (!mem_blk_write_req)
+        return -ENOMEM;
+
+    /* Set parameters for the DBG_MEM_BLOCK_WRITE_REQ message */
+    mem_blk_write_req->memaddr = mem_addr;
+    mem_blk_write_req->memsize = mem_size;
+    memcpy(mem_blk_write_req->memdata, mem_data, mem_size);
+
+    /* Send the DBG_MEM_BLOCK_WRITE_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, mem_blk_write_req, 1, DBG_MEM_BLOCK_WRITE_CFM, NULL);
+}
+
+int rwnx_send_dbg_start_app_req(struct rwnx_hw *rwnx_hw, u32 boot_addr,
+                                u32 boot_type)
+{
+    struct dbg_start_app_req *start_app_req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the DBG_START_APP_REQ message */
+    start_app_req = rwnx_msg_zalloc(DBG_START_APP_REQ, TASK_DBG, DRV_TASK_ID,
+                                    sizeof(struct dbg_start_app_req));
+    if (!start_app_req)
+        return -ENOMEM;
+
+    /* Set parameters for the DBG_START_APP_REQ message */
+    start_app_req->bootaddr = boot_addr;
+    start_app_req->boottype = boot_type;
+
+    /* Send the DBG_START_APP_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, start_app_req, 1, DBG_START_APP_CFM, NULL);
+}
+
+int rwnx_send_cfg_rssi_req(struct rwnx_hw *rwnx_hw, u8 vif_index, int rssi_thold, u32 rssi_hyst)
+{
+    struct mm_cfg_rssi_req *req;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* Build the MM_CFG_RSSI_REQ message */
+    req = rwnx_msg_zalloc(MM_CFG_RSSI_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_cfg_rssi_req));
+    if (!req)
+        return -ENOMEM;
+
+    if(rwnx_hw->vif_table[vif_index]==NULL)
+	return 0;
+
+    /* Set parameters for the MM_CFG_RSSI_REQ message */
+    req->vif_index = vif_index;
+    req->rssi_thold = (s8)rssi_thold;
+    req->rssi_hyst = (u8)rssi_hyst;
+
+    /* Send the MM_CFG_RSSI_REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 0, 0, NULL);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+int rwnx_send_set_channel_new(struct rwnx_hw *rwnx_hw, struct mac_chan_op *chan,
+                          struct mm_set_channel_cfm *cfm)
+{
+    struct mm_set_channel_req *req;
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+    req = rwnx_msg_zalloc(MM_SET_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_set_channel_req));
+    if (!req)
+        return -ENOMEM;
+
+    req->chan.band = chan->band;
+    req->chan.type = chan->type;
+    req->chan.prim20_freq  = chan->prim20_freq;
+    req->chan.center1_freq = chan->center1_freq;
+    req->chan.tx_power = chan->tx_power;
+    req->chan.flags = chan->flags;
+    req->index = 0;
+    /* Send the MM_SET_CHANNEL_REQ REQ message to LMAC FW */
+    return rwnx_send_msg(rwnx_hw, req, 1, MM_SET_CHANNEL_CFM, cfm);
+}
+#endif
+
+int rwnx_send_reboot(struct rwnx_hw *rwnx_hw)
+{
+    int ret = 0;
+    u32 delay = 2 *1000; //1s
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    ret = rwnx_send_dbg_start_app_req(rwnx_hw, delay, HOST_START_APP_REBOOT);
+    return ret;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_msg_tx.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_msg_tx.h
new file mode 100755
index 0000000..ffaca24
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_msg_tx.h
@@ -0,0 +1,182 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_msg_tx.h
+ *
+ * @brief TX function declarations
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_MSG_TX_H_
+#define _RWNX_MSG_TX_H_
+
+#include "rwnx_defs.h"
+
+int rwnx_send_reset(struct rwnx_hw *rwnx_hw);
+int rwnx_send_start(struct rwnx_hw *rwnx_hw);
+int rwnx_send_get_temp_req(struct rwnx_hw *rwnx_hw, struct mm_set_vendor_hwconfig_cfm *cfm);
+int rwnx_send_version_req(struct rwnx_hw *rwnx_hw, struct mm_version_cfm *cfm);
+int rwnx_send_add_if(struct rwnx_hw *rwnx_hw, const unsigned char *mac,
+                     enum nl80211_iftype iftype, bool p2p, struct mm_add_if_cfm *cfm);
+int rwnx_send_remove_if(struct rwnx_hw *rwnx_hw, u8 vif_index, bool defer);
+int rwnx_send_set_channel(struct rwnx_hw *rwnx_hw, int phy_idx,
+                          struct mm_set_channel_cfm *cfm);
+int rwnx_send_key_add(struct rwnx_hw *rwnx_hw, u8 vif_idx, u8 sta_idx, bool pairwise,
+                      u8 *key, u8 key_len, u8 key_idx, u8 cipher_suite,
+                      struct mm_key_add_cfm *cfm);
+int rwnx_send_key_del(struct rwnx_hw *rwnx_hw, uint8_t hw_key_idx);
+int rwnx_send_bcn(struct rwnx_hw *rwnx_hw,u8 *buf, u8 vif_idx, u16 bcn_len);
+
+int rwnx_send_bcn_change(struct rwnx_hw *rwnx_hw, u8 vif_idx, u32 bcn_addr,
+                         u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft);
+int rwnx_send_tim_update(struct rwnx_hw *rwnx_hw, u8 vif_idx, u16 aid,
+                         u8 tx_status);
+int rwnx_send_roc(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+				  struct ieee80211_channel *chan, unsigned int duration, struct mm_remain_on_channel_cfm *roc_cfm);
+int rwnx_send_cancel_roc(struct rwnx_hw *rwnx_hw);
+int rwnx_send_set_power(struct rwnx_hw *rwnx_hw,  u8 vif_idx, s8 pwr,
+                        struct mm_set_power_cfm *cfm);
+int rwnx_send_set_edca(struct rwnx_hw *rwnx_hw, u8 hw_queue, u32 param,
+                       bool uapsd, u8 inst_nbr);
+int rwnx_send_tdls_chan_switch_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                                   struct rwnx_sta *rwnx_sta, bool sta_initiator,
+                                   u8 oper_class, struct cfg80211_chan_def *chandef,
+                                   struct tdls_chan_switch_cfm *cfm);
+int rwnx_send_tdls_cancel_chan_switch_req(struct rwnx_hw *rwnx_hw,
+                                          struct rwnx_vif *rwnx_vif,
+                                          struct rwnx_sta *rwnx_sta,
+                                          struct tdls_cancel_chan_switch_cfm *cfm);
+
+#ifdef CONFIG_RWNX_P2P_DEBUGFS
+int rwnx_send_p2p_oppps_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                            u8 ctw, struct mm_set_p2p_oppps_cfm *cfm);
+int rwnx_send_p2p_noa_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                          int count, int interval, int duration,
+                          bool dyn_noa, struct mm_set_p2p_noa_cfm *cfm);
+#endif /* CONFIG_RWNX_P2P_DEBUGFS */
+
+#ifdef AICWF_ARP_OFFLOAD
+int rwnx_send_arpoffload_en_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                          u32_l ipaddr,  u8_l enable);
+#endif
+int rwnx_send_rf_config_req(struct rwnx_hw *rwnx_hw, u8_l ofst, u8_l sel, u8_l *tbl, u16_l len);
+int rwnx_send_rf_calib_req(struct rwnx_hw *rwnx_hw, struct mm_set_rf_calib_cfm *cfm);
+int rwnx_send_get_macaddr_req(struct rwnx_hw *rwnx_hw, struct mm_get_mac_addr_cfm *cfm);
+
+#ifdef CONFIG_RWNX_FULLMAC
+int rwnx_send_me_config_req(struct rwnx_hw *rwnx_hw);
+int rwnx_send_me_chan_config_req(struct rwnx_hw *rwnx_hw);
+int rwnx_send_me_set_control_port_req(struct rwnx_hw *rwnx_hw, bool opened,
+                                      u8 sta_idx);
+int rwnx_send_me_sta_add(struct rwnx_hw *rwnx_hw, struct station_parameters *params,
+                         const u8 *mac, u8 inst_nbr, struct me_sta_add_cfm *cfm);
+int rwnx_send_me_sta_del(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool tdls_sta);
+int rwnx_send_me_traffic_ind(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool uapsd, u8 tx_status);
+int rwnx_send_me_rc_stats(struct rwnx_hw *rwnx_hw, u8 sta_idx,
+                          struct me_rc_stats_cfm *cfm);
+int rwnx_send_me_rc_set_rate(struct rwnx_hw *rwnx_hw,
+                             u8 sta_idx,
+                             u16 rate_idx);
+int rwnx_send_me_set_ps_mode(struct rwnx_hw *rwnx_hw, u8 ps_mode);
+int rwnx_send_me_set_lp_level(struct rwnx_hw *rwnx_hw, u8 lp_level);
+int rwnx_send_sm_connect_req(struct rwnx_hw *rwnx_hw,
+                             struct rwnx_vif *rwnx_vif,
+                             struct cfg80211_connect_params *sme,
+                             struct sm_connect_cfm *cfm);
+int rwnx_send_sm_disconnect_req(struct rwnx_hw *rwnx_hw,
+                                struct rwnx_vif *rwnx_vif,
+                                u16 reason);
+int rwnx_send_sm_external_auth_required_rsp(struct rwnx_hw *rwnx_hw,
+                                            struct rwnx_vif *rwnx_vif,
+                                            u16 status);
+int rwnx_send_apm_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                            struct cfg80211_ap_settings *settings,
+                            struct apm_start_cfm *cfm,
+                            struct rwnx_ipc_elem_var *elem);
+int rwnx_send_apm_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif);
+int rwnx_send_scanu_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                        struct cfg80211_scan_request *param);
+int rwnx_send_scanu_cancel_req(struct rwnx_hw *rwnx_hw,
+                              struct scan_cancel_cfm *cfm);
+
+int rwnx_send_apm_start_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                                struct cfg80211_chan_def *chandef,
+                                struct apm_start_cac_cfm *cfm);
+int rwnx_send_apm_stop_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif);
+int rwnx_send_tdls_peer_traffic_ind_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif);
+int rwnx_send_config_monitor_req(struct rwnx_hw *rwnx_hw,
+                                 struct cfg80211_chan_def *chandef,
+                                 struct me_config_monitor_cfm *cfm);
+int rwnx_send_mesh_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                             const struct mesh_config *conf, const struct mesh_setup *setup,
+                             struct mesh_start_cfm *cfm);
+int rwnx_send_mesh_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                            struct mesh_stop_cfm *cfm);
+int rwnx_send_mesh_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                              u32 mask, const struct mesh_config *p_mconf, struct mesh_update_cfm *cfm);
+int rwnx_send_mesh_peer_info_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                                 u8 sta_idx, struct mesh_peer_info_cfm *cfm);
+void rwnx_send_mesh_peer_update_ntf(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+                                    u8 sta_idx, u8 mlink_state);
+void rwnx_send_mesh_path_create_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, u8 *tgt_addr);
+int rwnx_send_mesh_path_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, const u8 *tgt_addr,
+                                   const u8 *p_nhop_addr, struct mesh_path_update_cfm *cfm);
+void rwnx_send_mesh_proxy_add_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, u8 *ext_addr);
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#ifdef CONFIG_RWNX_BFMER
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_send_bfmer_enable(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta,
+                            const struct ieee80211_vht_cap *vht_cap);
+#endif /* CONFIG_RWNX_FULLMAC */
+#ifdef CONFIG_RWNX_MUMIMO_TX
+int rwnx_send_mu_group_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta);
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+#endif /* CONFIG_RWNX_BFMER */
+
+/* Debug messages */
+int rwnx_send_dbg_trigger_req(struct rwnx_hw *rwnx_hw, char *msg);
+int rwnx_send_dbg_mem_read_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+                               struct dbg_mem_read_cfm *cfm);
+int rwnx_send_dbg_mem_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+                                u32 mem_data);
+int rwnx_send_dbg_mem_mask_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+                                     u32 mem_mask, u32 mem_data);
+int rwnx_send_dbg_set_mod_filter_req(struct rwnx_hw *rwnx_hw, u32 filter);
+#ifdef CONFIG_RFTEST
+int rwnx_send_rftest_req(struct rwnx_hw *rwnx_hw, u32_l cmd, u32_l argc, u8_l *argv, struct dbg_rftest_cmd_cfm *cfm);
+#endif
+#ifdef CONFIG_MCU_MESSAGE
+int rwnx_send_dbg_custom_msg_req(struct rwnx_hw *rwnx_hw,
+                                 u32 cmd, void *buf, u32 len, u32 action,
+                                 struct dbg_custom_msg_cfm *cfm);
+#endif
+int rwnx_send_dbg_set_sev_filter_req(struct rwnx_hw *rwnx_hw, u32 filter);
+int rwnx_send_dbg_get_sys_stat_req(struct rwnx_hw *rwnx_hw,
+                                   struct dbg_get_sys_stat_cfm *cfm);
+int rwnx_send_dbg_mem_block_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+                                      u32 mem_size, u32 *mem_data);
+int rwnx_send_dbg_start_app_req(struct rwnx_hw *rwnx_hw, u32 boot_addr,
+                                u32 boot_type);
+int rwnx_send_cfg_rssi_req(struct rwnx_hw *rwnx_hw, u8 vif_index, int rssi_thold, u32 rssi_hyst);
+int rwnx_send_coex_req(struct rwnx_hw *rwnx_hw, u8_l disable_coexnull, u8_l enable_nullcts);
+int rwnx_send_get_sta_info_req(struct rwnx_hw *rwnx_hw, u8_l sta_idx, struct mm_get_sta_info_cfm *cfm);
+int rwnx_send_set_stack_start_req(struct rwnx_hw *rwnx_hw, u8_l on, u8_l efuse_valid, u8_l set_vendor_info,
+					u8_l fwtrace_redir_en, struct mm_set_stack_start_cfm *cfm);
+int rwnx_send_txop_req(struct rwnx_hw *rwnx_hw, uint16_t *txop, u8_l long_nav_en, u8_l cfe_en);
+int rwnx_send_get_fw_version_req(struct rwnx_hw *rwnx_hw, struct mm_get_fw_version_cfm *cfm);
+int rwnx_send_txpwr_lvl_req(struct rwnx_hw *rwnx_hw);
+int rwnx_send_txpwr_lvl_v3_req(struct rwnx_hw *rwnx_hw);
+int rwnx_send_txpwr_lvl_adj_req(struct rwnx_hw *rwnx_hw);
+int rwnx_send_txpwr_ofst_req(struct rwnx_hw *rwnx_hw);
+int rwnx_send_txpwr_ofst2x_req(struct rwnx_hw *rwnx_hw);
+int rwnx_send_reboot(struct rwnx_hw *rwnx_hw);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)
+int rwnx_send_set_channel_new(struct rwnx_hw *rwnx_hw, struct mac_chan_op *chan,
+                          struct mm_set_channel_cfm *cfm);
+#endif
+
+#endif /* _RWNX_MSG_TX_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mu_group.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mu_group.c
new file mode 100755
index 0000000..4ac2c8e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mu_group.c
@@ -0,0 +1,670 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_mu_group.c
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ******************************************************************************
+ */
+
+#include "rwnx_defs.h"
+#include "rwnx_msg_tx.h"
+#include "rwnx_events.h"
+
+
+/**
+ * rwnx_mu_group_sta_init - Initialize group information for a STA
+ *
+ * @sta: Sta to initialize
+ */
+void rwnx_mu_group_sta_init(struct rwnx_sta *sta,
+                            const struct ieee80211_vht_cap *vht_cap)
+{
+    sta->group_info.map = 0;
+    sta->group_info.cnt = 0;
+    sta->group_info.active.next = LIST_POISON1;
+    sta->group_info.update.next = LIST_POISON1;
+    sta->group_info.last_update = 0;
+    sta->group_info.traffic = 0;
+    sta->group_info.group = 0;
+
+    if (!vht_cap ||
+        !(vht_cap->vht_cap_info & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) {
+            sta->group_info.map = RWNX_SU_GROUP;
+    }
+}
+
+/**
+ * rwnx_mu_group_sta_del - Remove a sta from all MU group
+ *
+ * @rwnx_hw: main driver data
+ * @sta: STA to remove
+ *
+ * Remove one sta from all the MU groups it belongs to.
+ */
+void rwnx_mu_group_sta_del(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta)
+{
+    struct rwnx_mu_info *mu = &rwnx_hw->mu;
+    int i, j, group_id;
+    bool lock_taken;
+    u64 map;
+
+    lock_taken = (down_interruptible(&mu->lock) == 0);
+
+    group_sta_for_each(sta, group_id, map) {
+        struct rwnx_mu_group *group = rwnx_mu_group_from_id(mu, group_id);
+
+        for (i = 0; i < CONFIG_USER_MAX; i++) {
+            if (group->users[i] == sta) {
+                group->users[i] = NULL;
+                group->user_cnt --;
+                /* Don't keep group with only one user */
+                if (group->user_cnt == 1) {
+                    for (j = 0; j < CONFIG_USER_MAX; j++) {
+                        if (group->users[j]) {
+                            group->users[j]->group_info.cnt--;
+                            group->users[j]->group_info.map &= ~BIT_ULL(group->group_id);
+                            if (group->users[j]->group_info.group == group_id)
+                                group->users[j]->group_info.group = 0;
+                            group->user_cnt --;
+                            break;
+                        }
+                    }
+                    mu->group_cnt--;
+#ifdef CREATE_TRACE_POINTS
+                    trace_mu_group_delete(group->group_id);
+#endif
+                } else {
+#ifdef CREATE_TRACE_POINTS
+                    trace_mu_group_update(group);
+#endif
+                }
+                break;
+            }
+        }
+
+        WARN((i == CONFIG_USER_MAX), "sta %d doesn't belongs to group %d",
+            sta->sta_idx, group_id);
+    }
+
+    sta->group_info.map = 0;
+    sta->group_info.cnt = 0;
+    sta->group_info.traffic = 0;
+
+    if (sta->group_info.active.next != LIST_POISON1)
+        list_del(&sta->group_info.active);
+
+    if (sta->group_info.update.next != LIST_POISON1)
+        list_del(&sta->group_info.update);
+
+    if (lock_taken)
+        up(&mu->lock);
+}
+
+/**
+ * rwnx_mu_group_sta_get_map - Get the list of group a STA belongs to
+ *
+ * @sta: pointer to the sta
+ *
+ * @return the list of group a STA belongs to as a bitfield
+ */
+u64 rwnx_mu_group_sta_get_map(struct rwnx_sta *sta)
+{
+    if (sta)
+        return sta->group_info.map;
+    return 0;
+}
+
+/**
+ * rwnx_mu_group_sta_get_pos - Get sta position in a group
+ *
+ * @rwnx_hw: main driver data
+ * @sta: pointer to the sta
+ * @group_id: Group id
+ *
+ * @return the positon of @sta in group @group_id or -1 if the sta
+ * doesn't belongs to the group (or group id is invalid)
+ */
+int rwnx_mu_group_sta_get_pos(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+                              int group_id)
+{
+    struct rwnx_mu_group *group;
+    int i;
+
+    group = rwnx_mu_group_from_id(&rwnx_hw->mu, group_id);
+    if (!group)
+        return -1;
+
+    for (i = 0; i < CONFIG_USER_MAX; i++) {
+        if (group->users[i] == sta)
+            return i;
+    }
+
+    WARN(1, "sta %d doesn't belongs to group %d",
+         sta->sta_idx, group_id);
+    return -1;
+}
+
+/**
+ * rwnx_mu_group_move_head - Move (or add) one element at the top of a list
+ *
+ * @list: list pointer
+ * @elem: element to move (or add) at the top of @list
+ *
+ */
+static inline
+void rwnx_mu_group_move_head(struct list_head *list, struct list_head *elem)
+{
+    if (elem->next != LIST_POISON1) {
+        __list_del_entry(elem);
+    }
+    list_add(elem, list);
+}
+
+/**
+ * rwnx_mu_group_remove_users - Remove all the users of a group
+ *
+ * @mu: pointer on MU info
+ * @group: pointer on group to remove users from
+ *
+ * Loop over all users one one group and remove this group from their
+ * map (and count).
+ * Each users is also added to the update_sta list, so that group info
+ * will be resent to fw for this user.
+ */
+static inline
+void rwnx_mu_group_remove_users(struct rwnx_mu_info *mu,
+                                struct rwnx_mu_group *group)
+{
+    struct rwnx_sta *sta;
+    int i, group_id = group->group_id;
+
+    for (i = 0; i < CONFIG_USER_MAX; i++) {
+        if (group->users[i]) {
+            sta = group->users[i];
+            group->users[i] = NULL;
+            sta->group_info.cnt--;
+            sta->group_info.map &= ~BIT_ULL(group_id);
+            rwnx_mu_group_move_head(&mu->update_sta,
+                                    &sta->group_info.update);
+        }
+    }
+
+    if (group->user_cnt)
+        mu->group_cnt--;
+    group->user_cnt = 0;
+#ifdef CREATE_TRACE_POINTS
+    trace_mu_group_delete(group_id);
+#endif
+}
+
+/**
+ * rwnx_mu_group_add_users - Add users to a group
+ *
+ * @mu: pointer on MU info
+ * @group: pointer on group to add users in
+ * @nb_user: number of users to ad
+ * @users: table of user to add
+ *
+ * Add @nb_users to @group (which may already have users)
+ * Each new users is added to the first free position.
+ * It is assume that @group has at least @nb_user free position. If it is not
+ * case it only add the number of users needed to complete the group.
+ * Each users (effectively added to @group) is also added to the update_sta
+ * list, so that group info will be resent to fw for this user.
+ */
+static inline
+void rwnx_mu_group_add_users(struct rwnx_mu_info *mu,
+                             struct rwnx_mu_group *group,
+                             int nb_user, struct rwnx_sta **users)
+{
+    int i, j, group_id = group->group_id;
+
+    if (!group->user_cnt)
+        mu->group_cnt++;
+
+    j = 0;
+    for (i = 0; i < nb_user ; i++) {
+        for (; j < CONFIG_USER_MAX ; j++) {
+            if (group->users[j] == NULL) {
+                group->users[j] = users[i];
+                users[i]->group_info.cnt ++;
+                users[i]->group_info.map |= BIT_ULL(group_id);
+
+                rwnx_mu_group_move_head(&(mu->update_sta),
+                                        &(users[i]->group_info.update));
+                group->user_cnt ++;
+                j ++;
+                break;
+            }
+
+            WARN(j == (CONFIG_USER_MAX - 1),
+                 "Too many user for group %d (nb_user=%d)",
+                 group_id, group->user_cnt + nb_user - i);
+        }
+    }
+#ifdef CREATE_TRACE_POINTS
+    trace_mu_group_update(group);
+#endif
+}
+
+
+/**
+ * rwnx_mu_group_create_one - create on group with a specific group of user
+ *
+ * @mu: pointer on MU info
+ * @nb_user: number of user to include in the group (<= CONFIG_USER_MAX)
+ * @users: table of users
+ *
+ * Try to create a new group with a specific group of users.
+ * 1- First it checks if a group containing all this users already exists.
+ *
+ * 2- Then it checks if it is possible to complete a group which already
+ *    contains at least one user.
+ *
+ * 3- Finally it create a new group. To do so, it take take the last group of
+ *    the active_groups list, remove all its current users and add the new ones
+ *
+ * In all cases, the group selected is moved at the top of the active_groups
+ * list
+ *
+ * @return 1 if a new group has been created and 0 otherwise
+ */
+static
+int rwnx_mu_group_create_one(struct rwnx_mu_info *mu, int nb_user,
+                             struct rwnx_sta **users, int *nb_group_left)
+{
+    int i, group_id;
+    struct rwnx_mu_group *group;
+    u64 group_match;
+    u64 group_avail;
+
+    group_match = users[0]->group_info.map;
+    group_avail = users[0]->group_info.map;
+    for (i = 1; i < nb_user ; i++) {
+        group_match &= users[i]->group_info.map;
+        group_avail |= users[i]->group_info.map;
+
+    }
+
+    if (group_match) {
+        /* a group (or more) with all the users already exist */
+        group_id = RWNX_GET_FIRST_GROUP_ID(group_match);
+        group = rwnx_mu_group_from_id(mu, group_id);
+        rwnx_mu_group_move_head(&mu->active_groups, &group->list);
+        return 0;
+    }
+
+#if CONFIG_USER_MAX > 2
+    if (group_avail) {
+        /* check if we can complete a group */
+        struct rwnx_sta *users2[CONFIG_USER_MAX];
+        int nb_user2;
+
+        group_for_each(group_id, group_avail) {
+            group = rwnx_mu_group_from_id(mu, group_id);
+            if (group->user_cnt == CONFIG_USER_MAX)
+                continue;
+
+            nb_user2 = 0;
+            for (i = 0; i < nb_user ; i++) {
+                if (!(users[i]->group_info.map & BIT_ULL(group_id))) {
+                    users2[nb_user2] = users[i];
+                    nb_user2++;
+                }
+            }
+
+            if ((group->user_cnt + nb_user2) <= CONFIG_USER_MAX) {
+                rwnx_mu_group_add_users(mu, group, nb_user2, users2);
+                rwnx_mu_group_move_head(&mu->active_groups, &group->list);
+                return 0;
+            }
+        }
+    }
+#endif /* CONFIG_USER_MAX > 2*/
+
+    /* create a new group */
+    group = list_last_entry(&mu->active_groups, struct rwnx_mu_group, list);
+    rwnx_mu_group_remove_users(mu, group);
+    rwnx_mu_group_add_users(mu, group, nb_user, users);
+    rwnx_mu_group_move_head(&mu->active_groups, &group->list);
+    (*nb_group_left)--;
+
+    return 1;
+}
+
+/**
+ * rwnx_mu_group_create - Create new groups containing one specific sta
+ *
+ * @mu: pointer on MU info
+ * @sta: sta to add in each group
+ * @nb_group_left: maximum number to new group allowed. (updated on exit)
+ *
+ * This will try to create "all the possible" group with a specific sta being
+ * a member of all these group.
+ * The function simply loops over the @active_sta list (starting from @sta).
+ * When it has (CONFIG_USER_MAX - 1) users it try to create a new group with
+ * these users (plus @sta).
+ * Loops end when there is no more users, or no more new group is allowed
+ *
+ */
+static
+void rwnx_mu_group_create(struct rwnx_mu_info *mu, struct rwnx_sta *sta,
+                          int *nb_group_left)
+{
+    struct rwnx_sta *user_sta = sta;
+    struct rwnx_sta *users[CONFIG_USER_MAX];
+    int nb_user = 1;
+
+    users[0] = sta;
+    while (*nb_group_left) {
+
+        list_for_each_entry_continue(user_sta, &mu->active_sta, group_info.active) {
+            users[nb_user] = user_sta;
+            if (++nb_user == CONFIG_USER_MAX) {
+                break;
+            }
+        }
+
+        if (nb_user > 1) {
+            if (rwnx_mu_group_create_one(mu, nb_user, users, nb_group_left))
+                (*nb_group_left)--;
+
+            if (nb_user < CONFIG_USER_MAX)
+                break;
+            else
+                nb_user = 1;
+        } else
+            break;
+    }
+}
+
+/**
+ * rwnx_mu_group_work - process function of the "group_work"
+ *
+ * The work is scheduled when several sta (MU beamformee capable) are active.
+ * When called, the @active_sta contains the list of the active sta (starting
+ * from the most recent one), and @active_groups is the list of all possible
+ * groups ordered so that the first one is the most recently used.
+ *
+ * This function will create new groups, starting from group containing the
+ * most "active" sta.
+ * For example if the list of sta is :
+ * sta8 -> sta3 -> sta4 -> sta7 -> sta1
+ * and the number of user per group is 3, it will create grooups :
+ * - sta8 / sta3 / sta4
+ * - sta8 / sta7 / sta1
+ * - sta3 / sta4 / sta7
+ * - sta3 / sta1
+ * - sta4 / sta7 / sta1
+ * - sta7 / sta1
+ *
+ * To create new group, the least used group are first selected.
+ * It is only allowed to create NX_MU_GROUP_MAX per iteration.
+ *
+ * Once groups have been updated, mu group information is update to the fw.
+ * To do so it use the @update_sta list to know which sta has been affected.
+ * As it is necessary to wait for fw confirmation before using this new group
+ * MU is temporarily disabled during group update
+ *
+ * Work is then rescheduled.
+ *
+ * At the end of the function, both @active_sta and @update_sta list are empty.
+ *
+ * Note:
+ * - This is still a WIP, and will require more tuning
+ * - not all combinations are created, to avoid to much processing.
+ * - reschedule delay should be adaptative
+ */
+void rwnx_mu_group_work(struct work_struct *ws)
+{
+    struct delayed_work *dw = container_of(ws, struct delayed_work, work);
+    struct rwnx_mu_info *mu = container_of(dw, struct rwnx_mu_info, group_work);
+    struct rwnx_hw *rwnx_hw = container_of(mu, struct rwnx_hw, mu);
+    struct rwnx_sta *sta, *next;
+    int nb_group_left = NX_MU_GROUP_MAX;
+
+    if (WARN(!rwnx_hw->mod_params->mutx,
+             "In group formation work, but mutx disabled"))
+        return;
+
+    if (down_interruptible(&mu->lock) != 0)
+        return;
+
+    mu->update_count++;
+    if (!mu->update_count)
+        mu->update_count++;
+
+    list_for_each_entry_safe(sta, next, &mu->active_sta, group_info.active) {
+        if (nb_group_left)
+            rwnx_mu_group_create(mu, sta, &nb_group_left);
+
+        sta->group_info.last_update = mu->update_count;
+        list_del(&sta->group_info.active);
+    }
+
+    if (! list_empty(&mu->update_sta)) {
+        list_for_each_entry_safe(sta, next, &mu->update_sta, group_info.update) {
+            rwnx_send_mu_group_update_req(rwnx_hw, sta);
+            list_del(&sta->group_info.update);
+        }
+    }
+
+    mu->next_group_select = jiffies;
+    rwnx_mu_group_sta_select(rwnx_hw);
+    up(&mu->lock);
+
+    return;
+}
+
+/**
+ * rwnx_mu_group_init - Initialize MU groups
+ *
+ * @rwnx_hw: main driver data
+ *
+ * Initialize all MU group
+ */
+void rwnx_mu_group_init(struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_mu_info *mu = &rwnx_hw->mu;
+    int i;
+
+    INIT_LIST_HEAD(&mu->active_groups);
+    INIT_LIST_HEAD(&mu->active_sta);
+    INIT_LIST_HEAD(&mu->update_sta);
+
+    for (i = 0; i < NX_MU_GROUP_MAX; i++) {
+        int j;
+        mu->groups[i].user_cnt = 0;
+        mu->groups[i].group_id = i + 1;
+        for (j = 0; j < CONFIG_USER_MAX; j++) {
+            mu->groups[i].users[j] = NULL;
+        }
+        list_add(&mu->groups[i].list, &mu->active_groups);
+    }
+
+    mu->update_count = 1;
+    mu->group_cnt = 0;
+    mu->next_group_select = jiffies;
+    INIT_DELAYED_WORK(&mu->group_work, rwnx_mu_group_work);
+    sema_init(&mu->lock, 1);
+}
+
+/**
+ * rwnx_mu_set_active_sta - mark a STA as active
+ *
+ * @rwnx_hw: main driver data
+ * @sta: pointer to the sta
+ * @traffic: Number of buffers to add in the sta's traffic counter
+ *
+ * If @sta is MU beamformee capable (and MU-MIMO tx is enabled) move the
+ * sta at the top of the @active_sta list.
+ * It also schedule the group_work if not already scheduled and the list
+ * contains more than one sta.
+ *
+ * If a STA was already in the list during the last group update
+ * (i.e. sta->group_info.last_update == mu->update_count) it is not added
+ * back to the list until a sta that wasn't active during the last update is
+ * added. This is to avoid scheduling group update with a list of sta that
+ * were all already in the list during previous update.
+ *
+ * It is called with mu->lock taken.
+ */
+void rwnx_mu_set_active_sta(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+                            int traffic)
+{
+    struct rwnx_mu_info *mu = &rwnx_hw->mu;
+
+    if (!sta || (sta->group_info.map & RWNX_SU_GROUP))
+        return;
+
+    sta->group_info.traffic += traffic;
+
+    if ((sta->group_info.last_update != mu->update_count) ||
+        !list_empty(&mu->active_sta)) {
+
+        rwnx_mu_group_move_head(&mu->active_sta, &sta->group_info.active);
+
+        if (!delayed_work_pending(&mu->group_work) &&
+            !list_is_singular(&mu->active_sta)) {
+            schedule_delayed_work(&mu->group_work,
+                                  msecs_to_jiffies(RWNX_MU_GROUP_INTERVAL));
+        }
+    }
+}
+
+/**
+ * rwnx_mu_set_active_group - mark a MU group as active
+ *
+ * @rwnx_hw: main driver data
+ * @group_id: Group id
+ *
+ * move a group at the top of the @active_groups list
+ */
+void rwnx_mu_set_active_group(struct rwnx_hw *rwnx_hw, int group_id)
+{
+    struct rwnx_mu_info *mu = &rwnx_hw->mu;
+    struct rwnx_mu_group *group = rwnx_mu_group_from_id(mu, group_id);
+
+    rwnx_mu_group_move_head(&mu->active_groups, &group->list);
+}
+
+
+/**
+ * rwnx_mu_group_sta_select - Select the best group for MU stas
+ *
+ * @rwnx_hw: main driver data
+ *
+ * For each MU capable client of AP interfaces this function tries to select
+ * the best group to use.
+ *
+ * In first pass, gather information from all stations to form statistics
+ * for each group for the previous @RWNX_MU_GROUP_SELECT_INTERVAL interval:
+ * - number of buffers transmitted
+ * - number of user
+ *
+ * Then groups with more than 2 active users, are assigned after being ordered
+ * by traffic :
+ * - group with highest traffic is selected: set this group for all its users
+ * - update nb_users for all others group (as one sta may be in several groups)
+ * - select the next group that have still mor than 2 users and assign it.
+ * - continue until all group are processed
+ *
+ */
+void rwnx_mu_group_sta_select(struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_mu_info *mu = &rwnx_hw->mu;
+    int nb_users[NX_MU_GROUP_MAX + 1];
+    int traffic[NX_MU_GROUP_MAX + 1];
+    int order[NX_MU_GROUP_MAX + 1];
+    struct rwnx_sta *sta;
+    struct rwnx_vif *vif;
+    struct list_head *head;
+    u64 map;
+    int i, j, update, group_id, tmp, cnt = 0;
+
+    if (!mu->group_cnt || time_before(jiffies, mu->next_group_select))
+        return;
+
+    list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+
+        if (RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_AP)
+            continue;
+
+#ifdef CONFIG_RWNX_FULLMAC
+        head = &vif->ap.sta_list;
+#else
+        head = &vif->stations;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+        memset(nb_users, 0, sizeof(nb_users));
+        memset(traffic, 0, sizeof(traffic));
+        list_for_each_entry(sta, head, list) {
+            int sta_traffic = sta->group_info.traffic;
+
+            /* reset statistics for next selection */
+            sta->group_info.traffic = 0;
+#ifdef CREATE_TRACE_POINTS
+            if (sta->group_info.group)
+                trace_mu_group_selection(sta, 0);
+#endif
+            sta->group_info.group = 0;
+
+            if (sta->group_info.cnt == 0 ||
+                sta_traffic < RWNX_MU_GROUP_MIN_TRAFFIC)
+                continue;
+
+            group_sta_for_each(sta, group_id, map) {
+                nb_users[group_id]++;
+                traffic[group_id] += sta_traffic;
+
+                /* list group with 2 users or more */
+                if (nb_users[group_id] == 2)
+                    order[cnt++] = group_id;
+            }
+        }
+
+        /* reorder list of group with more that 2 users */
+        update = 1;
+        while(update) {
+            update = 0;
+            for (i = 0; i < cnt - 1; i++) {
+                if (traffic[order[i]] < traffic[order[i + 1]]) {
+                    tmp = order[i];
+                    order[i] = order[i + 1];
+                    order[i + 1] = tmp;
+                    update = 1;
+                }
+            }
+        }
+
+        /* now assign group in traffic order */
+        for (i = 0; i < cnt ; i ++) {
+            struct rwnx_mu_group *group;
+            group_id = order[i];
+
+            if (nb_users[group_id] < 2)
+                continue;
+
+            group = rwnx_mu_group_from_id(mu, group_id);
+            for (j = 0; j < CONFIG_USER_MAX ; j++) {
+                if (group->users[j]) {
+#ifdef CREATE_TRACE_POINTS
+                    trace_mu_group_selection(group->users[j], group_id);
+#endif
+                    group->users[j]->group_info.group = group_id;
+
+                    group_sta_for_each(group->users[j], tmp, map) {
+                        if (group_id != tmp)
+                            nb_users[tmp]--;
+                    }
+                }
+            }
+        }
+    }
+
+    mu->next_group_select = jiffies +
+        msecs_to_jiffies(RWNX_MU_GROUP_SELECT_INTERVAL);
+    mu->next_group_select |= 1;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mu_group.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mu_group.h
new file mode 100755
index 0000000..64563bd
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_mu_group.h
@@ -0,0 +1,181 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_mu_group.h
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ******************************************************************************
+ */
+#ifndef _RWNX_MU_GROUP_H_
+#define _RWNX_MU_GROUP_H_
+
+#include <linux/workqueue.h>
+#include <linux/semaphore.h>
+
+struct rwnx_hw;
+struct rwnx_sta;
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+
+/**
+ * struct rwnx_sta_group_info - Group Information for a STA
+ *
+ * @active: node for @mu->active_sta list
+ * @update: node for @mu->update_sta list
+ * @cnt: Number of groups the STA belongs to
+ * @map: Bitfield of groups the sta belongs to
+ * @traffic: Number of buffers sent since previous group selection
+ * @group: Id of the group selected by previous group selection
+ *         (cf @rwnx_mu_group_sta_select)
+ */
+struct rwnx_sta_group_info {
+    struct list_head active;
+    struct list_head update;
+    u16 last_update;
+    int cnt;
+    u64 map;
+    int traffic;
+    u8  group;
+};
+
+/**
+ * struct mu_group_info - Information about the users of a group
+ *
+ * @list: node for mu->active_groups
+ * @group_id: Group identifier
+ * @user_cnt: Number of the users in the group
+ * @users: Pointer to the sta, ordered by user position
+ */
+struct rwnx_mu_group {
+    struct list_head list;
+    int group_id;
+    int user_cnt;
+    struct rwnx_sta *users[CONFIG_USER_MAX];
+};
+
+/**
+ * struct rwnx_mu_info - Information about all MU group
+ *
+ * @active_groups: List of all possible groups. Ordered from the most recently
+ *                 used one to the least one (and possibly never used)
+ * @active_sta: List of MU beamformee sta that have been active (since previous
+ *              group update). Ordered from the most recently active.
+ * @update_sta: List of sta whose group information has changed and need to be
+ *              updated at fw level
+ * @groups: Table of all groups
+ * @group_work: Work item used to schedule group update
+ * @update_count: Counter used to identify the last group formation update.
+ *                (cf rwnx_sta_group_info.last_update)
+ * @lock: Lock taken during group update. If tx happens lock is taken, then tx
+ *        will not used MU.
+ * @next_group_assign: Next time the group selection should be run
+ *                     (ref @rwnx_mu_group_sta_select)
+ * @group_cnt: Number of group created
+ */
+struct rwnx_mu_info {
+    struct list_head active_groups;
+    struct list_head active_sta;
+    struct list_head update_sta;
+    struct rwnx_mu_group groups[NX_MU_GROUP_MAX];
+    struct delayed_work group_work;
+    u16 update_count;
+    struct semaphore lock;
+    unsigned long next_group_select;
+    u8 group_cnt;
+};
+
+#define RWNX_SU_GROUP BIT_ULL(0)
+#define RWNX_MU_GROUP_MASK 0x7ffffffffffffffeULL
+#define RWNX_MU_GROUP_INTERVAL 200 /* in ms */
+#define RWNX_MU_GROUP_SELECT_INTERVAL 100 /* in ms */
+// minimum traffic in a RWNX_MU_GROUP_SELECT_INTERVAL to consider the sta
+#define RWNX_MU_GROUP_MIN_TRAFFIC 50 /* in number of packet */
+
+
+#define RWNX_GET_FIRST_GROUP_ID(map) (fls64(map) - 1)
+
+#define group_sta_for_each(sta, id, map)                                \
+	do {								\
+	map = sta->group_info.map & RWNX_MU_GROUP_MASK;                     \
+	for (id = (fls64(map) - 1) ; id > 0 ;                               \
+		 map &= ~(u64)BIT_ULL(id), id = (fls64(map) - 1))	\
+	} while (0)
+
+#define group_for_each(id, map)                                         \
+    for (id = (fls64(map) - 1) ; id > 0 ;                               \
+         map &= ~(u64)BIT_ULL(id), id = (fls64(map) - 1))
+
+#define RWNX_MUMIMO_INFO_POS_ID(info) (((info) >> 6) & 0x3)
+#define RWNX_MUMIMO_INFO_GROUP_ID(info) ((info) & 0x3f)
+
+static inline
+struct rwnx_mu_group *rwnx_mu_group_from_id(struct rwnx_mu_info *mu, int id)
+{
+    if (id > NX_MU_GROUP_MAX)
+        return NULL;
+
+    return &mu->groups[id - 1];
+}
+
+
+void rwnx_mu_group_sta_init(struct rwnx_sta *sta,
+                            const struct ieee80211_vht_cap *vht_cap);
+void rwnx_mu_group_sta_del(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta);
+u64 rwnx_mu_group_sta_get_map(struct rwnx_sta *sta);
+int rwnx_mu_group_sta_get_pos(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+                              int group_id);
+
+void rwnx_mu_group_init(struct rwnx_hw *rwnx_hw);
+
+void rwnx_mu_set_active_sta(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+                            int traffic);
+void rwnx_mu_set_active_group(struct rwnx_hw *rwnx_hw, int group_id);
+void rwnx_mu_group_sta_select(struct rwnx_hw *rwnx_hw);
+
+
+#else /* ! CONFIG_RWNX_MUMIMO_TX */
+
+static inline
+void rwnx_mu_group_sta_init(struct rwnx_sta *sta,
+                            const struct ieee80211_vht_cap *vht_cap)
+{}
+
+static inline
+void rwnx_mu_group_sta_del(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta)
+{}
+
+static inline
+u64 rwnx_mu_group_sta_get_map(struct rwnx_sta *sta)
+{
+    return 0;
+}
+
+static inline
+int rwnx_mu_group_sta_get_pos(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+                              int group_id)
+{
+    return 0;
+}
+
+static inline
+void rwnx_mu_group_init(struct rwnx_hw *rwnx_hw)
+{}
+
+static inline
+void rwnx_mu_set_active_sta(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+                            int traffic)
+{}
+
+static inline
+void rwnx_mu_set_active_group(struct rwnx_hw *rwnx_hw, int group_id)
+{}
+
+static inline
+void rwnx_mu_group_sta_select(struct rwnx_hw *rwnx_hw)
+{}
+
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+#endif /* _RWNX_MU_GROUP_H_ */
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_pci.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_pci.c
new file mode 100755
index 0000000..cd207c8
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_pci.c
@@ -0,0 +1,94 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_pci.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#include <linux/pci.h>
+#include <linux/module.h>
+
+#include "rwnx_defs.h"
+#include "rwnx_dini.h"
+#include "rwnx_v7.h"
+
+#define PCI_VENDOR_ID_DINIGROUP              0x17DF
+#define PCI_DEVICE_ID_DINIGROUP_DNV6_F2PCIE  0x1907
+
+#define PCI_DEVICE_ID_XILINX_CEVA_VIRTEX7    0x7011
+
+static const struct pci_device_id rwnx_pci_ids[] = {
+    {PCI_DEVICE(PCI_VENDOR_ID_DINIGROUP, PCI_DEVICE_ID_DINIGROUP_DNV6_F2PCIE)},
+    {PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILINX_CEVA_VIRTEX7)},
+    {0,}
+};
+
+
+/* Uncomment this for depmod to create module alias */
+/* We don't want this on development platform */
+//MODULE_DEVICE_TABLE(pci, rwnx_pci_ids);
+
+static int rwnx_pci_probe(struct pci_dev *pci_dev,
+                          const struct pci_device_id *pci_id)
+{
+    struct rwnx_plat *rwnx_plat = NULL;
+    void *drvdata;
+    int ret = -ENODEV;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (pci_id->vendor == PCI_VENDOR_ID_DINIGROUP) {
+        ret = rwnx_dini_platform_init(pci_dev, &rwnx_plat);
+    } else if (pci_id->vendor == PCI_VENDOR_ID_XILINX) {
+        ret = rwnx_v7_platform_init(pci_dev, &rwnx_plat);
+    }
+
+    if (ret)
+        return ret;
+
+    rwnx_plat->pci_dev = pci_dev;
+
+    ret = rwnx_platform_init(rwnx_plat, &drvdata);
+    pci_set_drvdata(pci_dev, drvdata);
+
+    if (ret)
+        rwnx_plat->deinit(rwnx_plat);
+
+    return ret;
+}
+
+static void rwnx_pci_remove(struct pci_dev *pci_dev)
+{
+    struct rwnx_hw *rwnx_hw;
+    struct rwnx_plat *rwnx_plat;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    rwnx_hw = pci_get_drvdata(pci_dev);
+    rwnx_plat = rwnx_hw->plat;
+
+    rwnx_platform_deinit(rwnx_hw);
+    rwnx_plat->deinit(rwnx_plat);
+
+    pci_set_drvdata(pci_dev, NULL);
+}
+
+static struct pci_driver rwnx_pci_drv = {
+    .name     = KBUILD_MODNAME,
+    .id_table = rwnx_pci_ids,
+    .probe    = rwnx_pci_probe,
+    .remove   = rwnx_pci_remove
+};
+
+int rwnx_pci_register_drv(void)
+{
+    return pci_register_driver(&rwnx_pci_drv);
+}
+
+void rwnx_pci_unregister_drv(void)
+{
+    pci_unregister_driver(&rwnx_pci_drv);
+}
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_pci.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_pci.h
new file mode 100755
index 0000000..d81578c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_pci.h
@@ -0,0 +1,17 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_pci.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_PCI_H_
+#define _RWNX_PCI_H_
+
+int rwnx_pci_register_drv(void);
+void rwnx_pci_unregister_drv(void);
+
+#endif /* _RWNX_PCI_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_platform.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_platform.c
new file mode 100755
index 0000000..47ef9be
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_platform.c
@@ -0,0 +1,3682 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_platform.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+
+#include "rwnx_platform.h"
+#include "reg_access.h"
+#include "hal_desc.h"
+#include "rwnx_main.h"
+#include "rwnx_pci.h"
+#ifndef CONFIG_RWNX_FHOST
+#include "ipc_host.h"
+#endif /* !CONFIG_RWNX_FHOST */
+#include "rwnx_msg_tx.h"
+
+#ifdef AICWF_SDIO_SUPPORT
+#include "aicwf_sdio.h"
+#endif
+
+#ifdef AICWF_USB_SUPPORT
+#include "aicwf_usb.h"
+#endif
+
+struct rwnx_plat *g_rwnx_plat = NULL;
+extern int testmode;
+
+extern u8 chip_sub_id;
+extern u8 chip_mcu_id;
+extern u8 btenable;
+
+static struct aicbt_info_t aicbt_info[]={
+    {
+        .btmode        = AICBT_BTMODE_DEFAULT,
+        .btport        = AICBT_BTPORT_DEFAULT,
+        .uart_baud     = AICBT_UART_BAUD_DEFAULT,
+        .uart_flowctrl = AICBT_UART_FC_DEFAULT,
+        .lpm_enable    = AICBT_LPM_ENABLE_DEFAULT,
+        .txpwr_lvl     = AICBT_TXPWR_LVL_DEFAULT,
+    },//PRODUCT_ID_AIC8801
+    {
+        .btmode        = AICBT_BTMODE_BT_WIFI_COMBO,
+        .btport        = AICBT_BTPORT_DEFAULT,
+        .uart_baud     = AICBT_UART_BAUD_DEFAULT,
+        .uart_flowctrl = AICBT_UART_FC_DEFAULT,
+        .lpm_enable    = AICBT_LPM_ENABLE_DEFAULT,
+        .txpwr_lvl     = AICBT_TXPWR_LVL_DEFAULT_8800dc,
+    },//PRODUCT_ID_AIC8800DC
+    {
+        .btmode        = AICBT_BTMODE_BT_WIFI_COMBO,
+        .btport        = AICBT_BTPORT_DEFAULT,
+        .uart_baud     = AICBT_UART_BAUD_DEFAULT,
+        .uart_flowctrl = AICBT_UART_FC_DEFAULT,
+        .lpm_enable    = AICBT_LPM_ENABLE_DEFAULT,
+        .txpwr_lvl     = AICBT_TXPWR_LVL_DEFAULT_8800dc,
+    },//PRODUCT_ID_AIC8800DW
+    {
+        .btmode        = AICBT_BTMODE_DEFAULT_8800d80,
+        .btport        = AICBT_BTPORT_DEFAULT,
+        .uart_baud     = AICBT_UART_BAUD_DEFAULT,
+        .uart_flowctrl = AICBT_UART_FC_DEFAULT,
+        .lpm_enable    = AICBT_LPM_ENABLE_DEFAULT,
+        .txpwr_lvl     = AICBT_TXPWR_LVL_DEFAULT_8800d80,
+    }//PRODUCT_ID_AIC8800D80
+};
+
+const struct aicbsp_firmware fw_8800dc_u01[] = {
+	[AICBSP_CPMODE_WORK] = {
+		.desc          = "normal work mode(sdio u01)",
+		.bt_adid       = "fw_adid_8800dc.bin",
+		.bt_patch      = "fw_patch_8800dc.bin",
+		.bt_table      = "fw_patch_table_8800dc.bin",
+		.wl_fw         = "fmacfw_8800dc.bin"
+	},
+
+	[AICBSP_CPMODE_TEST] = {
+		.desc          = "rf test mode(sdio u01)",
+		.bt_adid       = "fw_adid_8800dc.bin",
+		.bt_patch      = "fw_patch_8800dc.bin",
+		.bt_table      = "fw_patch_table_8800dc.bin",
+		.wl_fw         = "fmacfw_rf_8800dc.bin"
+	},
+};
+
+const struct aicbsp_firmware fw_8800dc_u02[] = {
+	[AICBSP_CPMODE_WORK] = {
+		.desc          = "normal work mode(8800dc sdio u02)",
+		.bt_adid       = "fw_adid_8800dc_u02.bin",
+		.bt_patch      = "fw_patch_8800dc_u02.bin",
+		.bt_table      = "fw_patch_table_8800dc_u02.bin",
+		.wl_fw         = "fmacfw_patch_8800dc_u02.bin"
+	},
+
+	[AICBSP_CPMODE_TEST] = {
+		.desc          = "rf test mode(8800dc sdio u02)",
+		.bt_adid       = "fw_adid_8800dc_u02.bin",
+		.bt_patch      = "fw_patch_8800dc_u02.bin",
+		.bt_table      = "fw_patch_table_8800dc_u02.bin",
+		.wl_fw         = "lmacfw_rf_8800dc.bin" //u01,u02 lmacfw load same bin
+	},
+};
+
+const struct aicbsp_firmware fw_8800dc_h_u02[] = {
+	[AICBSP_CPMODE_WORK] = {
+		.desc          = "normal work mode(8800dc_h sdio u02)",
+		.bt_adid       = "fw_adid_8800dc_u02h.bin",
+		.bt_patch      = "fw_patch_8800dc_u02h.bin",
+		.bt_table      = "fw_patch_table_8800dc_u02h.bin",
+		.wl_fw         = "fmacfw_patch_8800dc_h_u02.bin"
+	},
+
+	[AICBSP_CPMODE_TEST] = {
+		.desc          = "rf test mode(8800dc_h sdio u02)",
+		.bt_adid       = "fw_adid_8800dc_u02h.bin",
+		.bt_patch      = "fw_patch_8800dc_u02h.bin",
+		.bt_table      = "fw_patch_table_8800dc_u02h.bin",
+		.wl_fw         = "lmacfw_rf_8800dc.bin" //u01,u02 lmacfw load same bin
+	},
+};
+
+const struct aicbsp_firmware fw_8800d80_u01[] = {
+	[AICBSP_CPMODE_WORK] = {
+		.desc          = "normal work mode(8800d80 sdio u01)",
+		.bt_adid       = "fw_adid_8800d80.bin",
+		.bt_patch      = "fw_patch_8800d80.bin",
+		.bt_table      = "fw_patch_table_8800d80.bin",
+		.wl_fw         = "fmacfw_8800d80.bin"
+	},
+
+	[AICBSP_CPMODE_TEST] = {
+		.desc          = "rf test mode(8800d80 sdio u01)",
+		.bt_adid       = "fw_adid_8800d80.bin",
+		.bt_patch      = "fw_patch_8800d80.bin",
+		.bt_table      = "fw_patch_table_8800d80.bin",
+		.wl_fw         = "lmacfw_rf_8800d80.bin"
+	},
+};
+
+const struct aicbsp_firmware fw_8800d80_u02[] = {
+	[AICBSP_CPMODE_WORK] = {
+		.desc          = "normal work mode(8800d80 sdio u02)",
+		.bt_adid       = "fw_adid_8800d80_u02.bin",
+		.bt_patch      = "fw_patch_8800d80_u02.bin",
+		.bt_table      = "fw_patch_table_8800d80_u02.bin",
+	#ifdef CONFIG_SDIO_BT
+		.wl_fw         = "fmacfwbt_8800d80_u02.bin"
+	#else
+		.wl_fw         = "fmacfw_8800d80_u02.bin"
+	#endif
+	},
+
+	[AICBSP_CPMODE_TEST] = {
+		.desc          = "rf test mode(8800d80 sdio u02)",
+		.bt_adid       = "fw_adid_8800d80_u02.bin",
+		.bt_patch      = "fw_patch_8800d80_u02.bin",
+		.bt_table      = "fw_patch_table_8800d80_u02.bin",
+		.wl_fw         = "lmacfw_rf_8800d80_u02.bin"
+	},
+};
+
+
+const struct aicbsp_firmware *aicbsp_firmware_list = fw_8800dc_u02;
+
+struct aicbsp_info_t aicbsp_info = {
+	.hwinfo_r = AICBSP_HWINFO_DEFAULT,
+	.hwinfo   = AICBSP_HWINFO_DEFAULT,
+	.cpmode   = AICBSP_CPMODE_DEFAULT,
+	.fwlog_en = AICBSP_FWLOG_EN_DEFAULT,
+#ifdef CONFIG_IRQ_FALL
+	.irqf     = 1,
+#else
+	.irqf     = 0,
+#endif
+};
+
+//#ifndef CONFIG_ROM_PATCH_EN
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0))
+static inline struct inode *file_inode(const struct file *f)
+{
+        return f->f_dentry->d_inode;
+}
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) */
+#ifdef CONFIG_NANOPI_M4
+static const char* aic_fw_path = "/vendor/firmware";
+#endif
+#ifdef CONFIG_PLATFORM_ALLWINNER
+static const char* aic_fw_path = "/vendor/etc/firmware";
+#endif
+//#endif/* !CONFIG_ROM_PATCH_EN */
+#define FW_PATH_MAX_LEN 200
+
+//Parser state
+#define INIT 0
+#define CMD 1
+#define PRINT 2
+#define GET_VALUE 3
+
+typedef struct
+{
+    txpwr_lvl_conf_t txpwr_lvl;
+    txpwr_lvl_conf_v2_t txpwr_lvl_v2;
+	txpwr_lvl_conf_v3_t txpwr_lvl_v3;
+	txpwr_lvl_adj_conf_t txpwr_lvl_adj;
+    txpwr_loss_conf_t txpwr_loss;
+    txpwr_ofst_conf_t txpwr_ofst;
+	txpwr_ofst2x_conf_t txpwr_ofst2x;
+    xtal_cap_conf_t xtal_cap;
+} nvram_info_t;
+
+nvram_info_t nvram_info = {
+    .txpwr_lvl = {
+        .enable           = 1,
+        .dsss             = 17,
+        .ofdmlowrate_2g4  = 15,
+        .ofdm64qam_2g4    = 14,
+        .ofdm256qam_2g4   = 13,
+        .ofdm1024qam_2g4  = 13,
+        .ofdmlowrate_5g   = 15,
+        .ofdm64qam_5g     = 14,
+        .ofdm256qam_5g    = 13,
+        .ofdm1024qam_5g   = 13
+    },
+    .txpwr_lvl_v2 = {
+        .enable             = 1,
+        .pwrlvl_11b_11ag_2g4 =
+            //1M,   2M,   5M5,  11M,  6M,   9M,   12M,  18M,  24M,  36M,  48M,  54M
+            { 20,   20,   20,   20,   20,   20,   20,   20,   18,   18,   16,   16},
+        .pwrlvl_11n_11ac_2g4 =
+            //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
+            { 20,   20,   20,   20,   18,   18,   16,   16,   16,   16},
+        .pwrlvl_11ax_2g4 =
+            //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, MCS10,MCS11
+            { 20,   20,   20,   20,   18,   18,   16,   16,   16,   16,   15,   15},
+    },
+    .txpwr_lvl_v3 = {
+        .enable             = 1,
+        .pwrlvl_11b_11ag_2g4 =
+            //1M,   2M,   5M5,  11M,  6M,   9M,   12M,  18M,  24M,  36M,  48M,  54M
+            { 20,   20,   20,   20,   20,   20,   20,   20,   18,   18,   16,   16},
+        .pwrlvl_11n_11ac_2g4 =
+            //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
+            { 20,   20,   20,   20,   18,   18,   16,   16,   16,   16},
+        .pwrlvl_11ax_2g4 =
+            //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, MCS10,MCS11
+            { 20,   20,   20,   20,   18,   18,   16,   16,   16,   16,   15,   15},
+         .pwrlvl_11a_5g =
+            //NA,   NA,   NA,   NA,   6M,   9M,   12M,  18M,  24M,  36M,  48M,  54M
+            { 0x80, 0x80, 0x80, 0x80, 20,   20,   20,   20,   18,   18,   16,   16},
+        .pwrlvl_11n_11ac_5g =
+            //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
+            { 20,   20,   20,   20,   18,   18,   16,   16,   16,   15},
+        .pwrlvl_11ax_5g =
+            //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, MCS10,MCS11
+            { 20,   20,   20,   20,   18,   18,   16,   16,   16,   15,   14,   14},
+    },
+    .txpwr_loss = {
+        .loss_enable      = 1,
+        .loss_value       = 0,
+    },
+    .txpwr_ofst = {
+        .enable       = 1,
+        .chan_1_4     = 0,
+        .chan_5_9     = 0,
+        .chan_10_13   = 0,
+        .chan_36_64   = 0,
+        .chan_100_120 = 0,
+        .chan_122_140 = 0,
+        .chan_142_165 = 0,
+    },
+    .txpwr_ofst2x = {
+        .enable       = 0,
+        .pwrofst2x_tbl_2g4 =
+        { // ch1-4, ch5-9, ch10-13
+            {   0,    0,    0   }, // 11b
+            {   0,    0,    0   }, // ofdm_highrate
+            {   0,    0,    0   }, // ofdm_lowrate
+        },
+        .pwrofst2x_tbl_5g =
+        { // ch42,  ch58, ch106,ch122,ch138,ch155
+            {   0,    0,    0,    0,    0,    0   }, // ofdm_lowrate
+            {   0,    0,    0,    0,    0,    0   }, // ofdm_highrate
+            {   0,    0,    0,    0,    0,    0   }, // ofdm_midrate
+        },
+    },
+    .xtal_cap = {
+        .enable        = 0,
+        .xtal_cap      = 24,
+        .xtal_cap_fine = 31,
+    },
+};
+
+//add d80
+extern int adap_test;
+
+typedef u32 (*array2_tbl_t)[2];
+
+#define AIC_PATCH_MAGIG_NUM     0x48435450 // "PTCH"
+#define AIC_PATCH_MAGIG_NUM_2   0x50544348 // "HCTP"
+#define AIC_PATCH_BLOCK_MAX     4
+
+typedef struct {
+    uint32_t magic_num;
+    uint32_t pair_start;
+    uint32_t magic_num_2;
+    uint32_t pair_count;
+    uint32_t block_dst[AIC_PATCH_BLOCK_MAX];
+    uint32_t block_src[AIC_PATCH_BLOCK_MAX];
+    uint32_t block_size[AIC_PATCH_BLOCK_MAX]; // word count
+} aic_patch_t;
+
+#define AIC_PATCH_OFST(mem) ((size_t) &((aic_patch_t *)0)->mem)
+#define AIC_PATCH_ADDR(mem) ((u32)(aic_patch_str_base + AIC_PATCH_OFST(mem)))
+
+u32 adaptivity_patch_tbl_8800d80[][2] = {
+	{0x000C, 0x0000320A}, //linkloss_thd
+	{0x009C, 0x00000000}, //ac_param_conf
+	{0x0168, 0x00010000}, //tx_adaptivity_en
+};
+
+#define USER_CHAN_MAX_TXPWR_EN_FLAG     (0x01U << 1)
+#define USER_TX_USE_ANA_F_FLAG          (0x01U << 2)
+
+#define CFG_USER_CHAN_MAX_TXPWR_EN  0
+#define CFG_USER_TX_USE_ANA_F       0
+
+#define CFG_USER_EXT_FLAGS_EN   (CFG_USER_CHAN_MAX_TXPWR_EN || CFG_USER_TX_USE_ANA_F)
+
+u32 patch_tbl_8800d80[][2] = {
+	#ifdef USE_5G
+	{0x00b4, 0xf3010001},
+	#else
+	{0x00b4, 0xf3010000},
+	#endif
+#if defined(CONFIG_AMSDU_RX)
+        {0x170, 0x0100000a}
+#endif
+#ifdef CONFIG_IRQ_FALL
+	{0x00000170, 0x0000010a}, //irqf
+#endif
+
+    #if CFG_USER_EXT_FLAGS_EN
+    {0x0188, 0x00000001
+        #if CFG_USER_CHAN_MAX_TXPWR_EN
+        | USER_CHAN_MAX_TXPWR_EN_FLAG
+        #endif
+        #if CFG_USER_TX_USE_ANA_F
+        | USER_TX_USE_ANA_F_FLAG
+        #endif
+    }, // user_ext_flags
+    #endif
+};
+
+#ifdef CONFIG_OOB
+// for 8800d40/d80     map data1 isr to gpiob1
+u32 gpio_cfg_tbl_8800d40d80[][2] = {
+    {0x40504084, 0x00000006},
+    {0x40500040, 0x00000000},
+    {0x40100030, 0x00000001},
+    {0x40241020, 0x00000001},
+    {0x40240030, 0x00000004},
+    {0x40240020, 0x03020700},
+};
+#endif
+
+int aicwifi_sys_config_8800d80(struct rwnx_hw *rwnx_hw)
+{
+#ifdef CONFIG_OOB
+    int ret, cnt;
+	int gpiocfg_num = sizeof(gpio_cfg_tbl_8800d40d80) / sizeof(u32) / 2;
+	for (cnt = 0; cnt < gpiocfg_num; cnt++) {
+		ret = rwnx_send_dbg_mem_write_req(rwnx_hw, gpio_cfg_tbl_8800d40d80[cnt][0], gpio_cfg_tbl_8800d40d80[cnt][1]);
+		if (ret) {
+			printk("%x write fail: %d\n", gpio_cfg_tbl_8800d40d80[cnt][0], ret);
+			return ret;
+		}
+	}
+#endif
+
+	return 0;
+}
+
+#define NEW_PATCH_BUFFER_MAP    1
+
+int aicwifi_patch_config_8800d80(struct rwnx_hw *rwnx_hw)
+{
+	const u32 rd_patch_addr = RAM_FMAC_FW_ADDR + 0x0198;
+	u32 aic_patch_addr;
+	u32 config_base, aic_patch_str_base;
+	#if (NEW_PATCH_BUFFER_MAP)
+	u32 patch_buff_addr, patch_buff_base, rd_version_addr, rd_version_val;
+	#endif
+	uint32_t start_addr = 0x0016F800;
+	u32 patch_addr = start_addr;
+	u32 patch_cnt = sizeof(patch_tbl_8800d80)/sizeof(u32)/2;
+	struct dbg_mem_read_cfm rd_patch_addr_cfm;
+	int ret = 0;
+	int cnt = 0;
+	//adap test
+	int adap_patch_cnt = 0;
+
+	if (adap_test) {
+        printk("%s for adaptivity test \r\n", __func__);
+		adap_patch_cnt = sizeof(adaptivity_patch_tbl_8800d80)/sizeof(u32)/2;
+	}
+
+	aic_patch_addr = rd_patch_addr + 8;
+
+	ret = rwnx_send_dbg_mem_read_req(rwnx_hw, rd_patch_addr, &rd_patch_addr_cfm);
+	if (ret) {
+		printk("patch rd fail\n");
+		return ret;
+	}
+
+	config_base = rd_patch_addr_cfm.memdata;
+
+	ret = rwnx_send_dbg_mem_read_req(rwnx_hw, aic_patch_addr, &rd_patch_addr_cfm);
+	if (ret) {
+		printk("patch str rd fail\n");
+		return ret;
+	}
+	aic_patch_str_base = rd_patch_addr_cfm.memdata;
+
+	#if (NEW_PATCH_BUFFER_MAP)
+	rd_version_addr = RAM_FMAC_FW_ADDR + 0x01C;
+	if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, rd_version_addr, &rd_patch_addr_cfm))) {
+		printk("version val[0x%x] rd fail: %d\n", rd_version_addr, ret);
+		return ret;
+	}
+	rd_version_val = rd_patch_addr_cfm.memdata;
+	printk("rd_version_val=%08X\n", rd_version_val);
+	//sdiodev->fw_version_uint = rd_version_val;
+	if (rd_version_val > 0x06090100) {
+		patch_buff_addr = rd_patch_addr + 12;
+		ret = rwnx_send_dbg_mem_read_req(rwnx_hw, patch_buff_addr, &rd_patch_addr_cfm);
+		if (ret) {
+			printk("patch buf rd fail\n");
+			return ret;
+		}
+		patch_buff_base = rd_patch_addr_cfm.memdata;
+		patch_addr = start_addr = patch_buff_base;
+	}
+	#endif
+
+	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(magic_num), AIC_PATCH_MAGIG_NUM);
+	if (ret) {
+		printk("0x%x write fail\n", AIC_PATCH_ADDR(magic_num));
+		return ret;
+	}
+
+	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(magic_num_2), AIC_PATCH_MAGIG_NUM_2);
+	if (ret) {
+		printk("0x%x write fail\n", AIC_PATCH_ADDR(magic_num_2));
+		return ret;
+	}
+
+	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(pair_start), patch_addr);
+	if (ret) {
+		printk("0x%x write fail\n", AIC_PATCH_ADDR(pair_start));
+		return ret;
+	}
+
+	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(pair_count), patch_cnt + adap_patch_cnt);
+	if (ret) {
+		printk("0x%x write fail\n", AIC_PATCH_ADDR(pair_count));
+		return ret;
+	}
+
+	for (cnt = 0; cnt < patch_cnt; cnt++) {
+		ret = rwnx_send_dbg_mem_write_req(rwnx_hw, start_addr+8*cnt, patch_tbl_8800d80[cnt][0]+config_base);
+		if (ret) {
+			printk("%x write fail\n", start_addr+8*cnt);
+			return ret;
+		}
+		ret = rwnx_send_dbg_mem_write_req(rwnx_hw, start_addr+8*cnt+4, patch_tbl_8800d80[cnt][1]);
+		if (ret) {
+			printk("%x write fail\n", start_addr+8*cnt+4);
+			return ret;
+		}
+	}
+
+	if (adap_test){
+		int tmp_cnt = patch_cnt + adap_patch_cnt;
+		for (cnt = patch_cnt; cnt < tmp_cnt; cnt++) {
+			int tbl_idx = cnt - patch_cnt;
+			ret = rwnx_send_dbg_mem_write_req(rwnx_hw, start_addr+8*cnt, adaptivity_patch_tbl_8800d80[tbl_idx][0]+config_base);
+			if(ret) {
+				printk("%x write fail\n", start_addr+8*cnt);
+				return ret;
+			}
+			ret = rwnx_send_dbg_mem_write_req(rwnx_hw, start_addr+8*cnt+4, adaptivity_patch_tbl_8800d80[tbl_idx][1]);
+			if(ret) {
+				printk("%x write fail\n", start_addr+8*cnt+4);
+				return ret;
+			}
+		}
+	}
+
+	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(block_size[0]), 0);
+	if (ret) {
+		printk("block_size[0x%x] write fail: %d\n", AIC_PATCH_ADDR(block_size[0]), ret);
+		return ret;
+	}
+	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(block_size[1]), 0);
+	if (ret) {
+		printk("block_size[0x%x] write fail: %d\n", AIC_PATCH_ADDR(block_size[1]), ret);
+		return ret;
+	}
+	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(block_size[2]), 0);
+	if (ret) {
+		printk("block_size[0x%x] write fail: %d\n", AIC_PATCH_ADDR(block_size[2]), ret);
+		return ret;
+	}
+	ret = rwnx_send_dbg_mem_write_req(rwnx_hw, AIC_PATCH_ADDR(block_size[3]), 0);
+	if (ret) {
+		printk("block_size[0x%x] write fail: %d\n", AIC_PATCH_ADDR(block_size[3]), ret);
+		return ret;
+	}
+
+	return 0;
+}
+// end d80
+
+#ifdef CONFIG_TEMP_PW
+void set_txpwr_ctrl(struct aic_sdio_dev *sdiodev, s8_l value)
+{
+	nvram_info.txpwr_loss.loss_enable = 1;
+	if (value > TEMP_THD_1 && value <= TEMP_THD_2) {
+		if (sdiodev->range == 0) {
+			nvram_info.txpwr_loss.loss_value += -TEMP_STEP_1;
+			sdiodev->range = 1;
+			rwnx_send_txpwr_lvl_req(sdiodev->rwnx_hw);
+		} else if (sdiodev->range == 1) {
+			return;
+		} else if (sdiodev->range == 2) {
+			nvram_info.txpwr_loss.loss_value += TEMP_STEP_1;
+			sdiodev->range = 1;
+			rwnx_send_txpwr_lvl_req(sdiodev->rwnx_hw);
+		}
+	} else if (value > TEMP_THD_2) {
+		if (sdiodev->range == 0) {
+			nvram_info.txpwr_loss.loss_value += -TEMP_STEP_2;
+			sdiodev->range = 2;
+			rwnx_send_txpwr_lvl_req(sdiodev->rwnx_hw);
+		} else if (sdiodev->range == 1) {
+			nvram_info.txpwr_loss.loss_value += -TEMP_STEP_1;
+			sdiodev->range = 2;
+			rwnx_send_txpwr_lvl_req(sdiodev->rwnx_hw);
+		} else {
+			return;
+		}
+	} else {
+		if (sdiodev->range == 0) {
+			return;
+		} else if (sdiodev->range == 1) {
+			nvram_info.txpwr_loss.loss_value += TEMP_STEP_1;
+			sdiodev->range = 0;
+			rwnx_send_txpwr_lvl_req(sdiodev->rwnx_hw);
+		} else if (sdiodev->range == 2) {
+			nvram_info.txpwr_loss.loss_value += TEMP_STEP_2;
+			sdiodev->range = 0;
+			rwnx_send_txpwr_lvl_req(sdiodev->rwnx_hw);
+		}
+	}
+	return;
+}
+#endif
+
+#ifdef VENDOR_SPECIFIED_FW_PATH
+int rwnx_load_firmware(u32 **fw_buf, const char *name, struct device *device)
+{
+    void *buffer = NULL;
+    char *path = NULL;
+    struct file *fp = NULL;
+    int size = 0, len = 0;//, i = 0;
+    ssize_t rdlen = 0;
+    //u32 *src = NULL, *dst = NULL;
+
+    /* get the firmware path */
+    path = __getname();
+    if (!path) {
+        *fw_buf = NULL;
+        return -1;
+    }
+#if (defined(CONFIG_DPD) && !defined(CONFIG_FORCE_DPD_CALIB))
+	if(strcmp(name, FW_DPDRESULT_NAME_8800DC) == 0) {
+#else
+	if(0) {
+#endif
+		len = snprintf(path, FW_PATH_MAX_LEN, "%s/%s", VENDOR_SPECIFIED_DPD_PATH, name);
+	} else {
+    	len = snprintf(path, FW_PATH_MAX_LEN, "%s/%s", VENDOR_SPECIFIED_FW_PATH, name);
+	}
+    if (len >= FW_PATH_MAX_LEN) {
+        printk("%s: %s file's path too long\n", __func__, name);
+        *fw_buf = NULL;
+        __putname(path);
+        return -1;
+    }
+
+    printk("%s :firmware path = %s  \n", __func__, path);
+
+    /* open the firmware file */
+    fp = filp_open(path, O_RDONLY, 0);
+    if (IS_ERR_OR_NULL(fp)) {
+        printk("%s: %s file failed to open\n", __func__, name);
+        *fw_buf = NULL;
+        __putname(path);
+        fp = NULL;
+        return -1;
+    }
+
+    size = i_size_read(file_inode(fp));
+    if (size <= 0) {
+        printk("%s: %s file size invalid %d\n", __func__, name, size);
+        *fw_buf = NULL;
+        __putname(path);
+        filp_close(fp, NULL);
+        fp = NULL;
+        return -1;
+    }
+
+    /* start to read from firmware file */
+    buffer = kzalloc(size, GFP_KERNEL);
+    if (!buffer) {
+        *fw_buf = NULL;
+        __putname(path);
+        filp_close(fp, NULL);
+        fp = NULL;
+        return -1;
+    }
+
+    #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 13, 16)
+    rdlen = kernel_read(fp, buffer, size, &fp->f_pos);
+    #else
+    rdlen = kernel_read(fp, fp->f_pos, buffer, size);
+    #endif
+
+    if (size != rdlen) {
+        printk("%s: %s file rdlen invalid %ld\n", __func__, name, rdlen);
+        *fw_buf = NULL;
+        __putname(path);
+        filp_close(fp, NULL);
+        fp = NULL;
+        kfree(buffer);
+        buffer = NULL;
+        return -1;
+    }
+    if (rdlen > 0) {
+        fp->f_pos += rdlen;
+    }
+
+#if 0
+    /*start to transform the data format*/
+    src = (u32 *)buffer;
+    dst = (u32 *)kzalloc(size, GFP_KERNEL);
+
+    if (!dst) {
+        *fw_buf = NULL;
+        __putname(path);
+        filp_close(fp, NULL);
+        fp = NULL;
+        kfree(buffer);
+        buffer = NULL;
+        return -1;
+    }
+
+    for (i = 0; i < (size/4); i++) {
+        dst[i] = src[i];
+    }
+#endif
+
+    __putname(path);
+    filp_close(fp, NULL);
+    fp = NULL;
+    //kfree(buffer);
+    //buffer = NULL;
+    *fw_buf = (u32*)buffer;
+
+    return size;
+}
+
+
+static void rwnx_restore_firmware(u32 **fw_buf)
+{
+    kfree(*fw_buf);
+    *fw_buf = NULL;
+}
+
+#else
+static int kernel_load_firmware(u32 **fw_buf, const char *name, struct device *device)
+{
+    const struct firmware *fw;
+    char *filename = name;
+    struct device *dev = device;
+    int err = 0;
+    unsigned int i, size;
+    u32 *src, *dst;
+    err = request_firmware(&fw, filename, dev);
+    if (err) {
+        printk("request_firmware fail: %d\r\n",err);
+        return err;
+    }
+    /* Copy the file on the Embedded side */
+    size = (unsigned int)fw->size;
+    src = (u32 *)fw->data;
+    dst = (u32 *)kzalloc(size, GFP_KERNEL);
+    if (dst == NULL) {
+        printk(KERN_CRIT "%s: data allocation failed\n", __func__);
+        return -2;
+    }
+    //printk("src:%p,dst:%p,size:%d\r\n",src,dst,size);
+    for (i = 0; i < (size / 4); i++) {
+        dst[i] = src[i];
+    }
+    release_firmware(fw);
+    *fw_buf = dst;
+    return size;
+}
+
+static void kernel_restore_firmware(u32 **fw_buf)
+{
+    kfree(*fw_buf);
+    *fw_buf = NULL;
+}
+#endif
+
+/* buffer is allocated by kzalloc */
+static int rwnx_request_firmware_common(struct rwnx_hw *rwnx_hw, u32** buffer, const char *filename)
+{
+    int size;
+    char fw_path[FW_PATH_MAX_LEN] = {0,};
+    int len;
+
+    #ifdef CONFIG_VENDOR_SUBDIR_NAME
+    len = snprintf(fw_path, FW_PATH_MAX_LEN, "%s/%s", VENDOR_SUBDIR_NAME, filename);
+    #else
+    len = snprintf(fw_path, FW_PATH_MAX_LEN, "%s", filename);
+    #endif
+    if (len >= FW_PATH_MAX_LEN) {
+        printk("filename is too long: %s\r\n", filename);
+        return -1;
+    }
+    printk("\n### Load file %s\n", fw_path);
+
+    #ifdef VENDOR_SPECIFIED_FW_PATH
+    size = rwnx_load_firmware(buffer, fw_path, NULL);
+    #else
+    {
+        struct device *dev = rwnx_platform_get_dev(rwnx_hw->plat);
+        size = kernel_load_firmware(buffer, fw_path, dev);
+    }
+    #endif
+
+    return size;
+}
+
+static void rwnx_release_firmware_common(u32** buffer)
+{
+    #ifdef CONFIG_VENDOR_SPECIFIED_FW_PATH
+    rwnx_restore_firmware(buffer);
+    #else
+    kernel_restore_firmware(buffer);
+    #endif
+}
+
+#ifdef CONFIG_RWNX_TL4
+/**
+ * rwnx_plat_tl4_fw_upload() - Load the requested FW into embedded side.
+ *
+ * @rwnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a hex file, into the specified address
+ */
+static int rwnx_plat_tl4_fw_upload(struct rwnx_plat *rwnx_plat, u8* fw_addr,
+                                   char *filename)
+{
+    struct device *dev = rwnx_platform_get_dev(rwnx_plat);
+    const struct firmware *fw;
+    int err = 0;
+    u32 *dst;
+    u8 const *file_data;
+    char typ0, typ1;
+    u32 addr0, addr1;
+    u32 dat0, dat1;
+    int remain;
+
+    err = request_firmware(&fw, filename, dev);
+    if (err) {
+        return err;
+    }
+    file_data = fw->data;
+    remain = fw->size;
+
+    /* Copy the file on the Embedded side */
+    dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+    /* Walk through all the lines of the configuration file */
+    while (remain >= 16) {
+        u32 data, offset;
+
+        if (sscanf(file_data, "%c:%08X %04X", &typ0, &addr0, &dat0) != 3)
+            break;
+        if ((addr0 & 0x01) != 0) {
+            addr0 = addr0 - 1;
+            dat0 = 0;
+        } else {
+            file_data += 16;
+            remain -= 16;
+        }
+        if ((remain < 16) ||
+            (sscanf(file_data, "%c:%08X %04X", &typ1, &addr1, &dat1) != 3) ||
+            (typ1 != typ0) || (addr1 != (addr0 + 1))) {
+            typ1 = typ0;
+            addr1 = addr0 + 1;
+            dat1 = 0;
+        } else {
+            file_data += 16;
+            remain -= 16;
+        }
+
+        if (typ0 == 'C') {
+            offset = 0x00200000;
+            if ((addr1 % 4) == 3)
+                offset += 2*(addr1 - 3);
+            else
+                offset += 2*(addr1 + 1);
+
+            data = dat1 | (dat0 << 16);
+        } else {
+            offset = 2*(addr1 - 1);
+            data = dat0 | (dat1 << 16);
+        }
+        dst = (u32 *)(fw_addr + offset);
+        *dst = data;
+    }
+
+    release_firmware(fw);
+
+    return err;
+}
+#endif
+
+#if 0
+/**
+ * rwnx_plat_bin_fw_upload() - Load the requested binary FW into embedded side.
+ *
+ * @rwnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a binary file, into the specified address
+ */
+static int rwnx_plat_bin_fw_upload(struct rwnx_plat *rwnx_plat, u8* fw_addr,
+                               char *filename)
+{
+    const struct firmware *fw;
+    struct device *dev = rwnx_platform_get_dev(rwnx_plat);
+    int err = 0;
+    unsigned int i, size;
+    u32 *src, *dst;
+
+    err = request_firmware(&fw, filename, dev);
+    if (err) {
+        return err;
+    }
+
+    /* Copy the file on the Embedded side */
+    dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+    src = (u32 *)fw->data;
+    dst = (u32 *)fw_addr;
+    size = (unsigned int)fw->size;
+
+    /* check potential platform bug on multiple stores vs memcpy */
+    for (i = 0; i < size; i += 4) {
+        *dst++ = *src++;
+    }
+
+    release_firmware(fw);
+
+    return err;
+}
+#endif
+
+#ifdef CONFIG_DPD
+rf_misc_ram_lite_t dpd_res = {0,};
+#endif
+
+int aicwf_patch_table_load(struct rwnx_hw *rwnx_hw, char *filename)
+{
+    int err = 0;
+    unsigned int i = 0, size;
+	u32 FMACFW_PATCH_TBL_8800DC_U02_DESCRIBE_BASE;
+	u32 FMACFW_PATCH_TBL_8800DC_U02_DESCRIBE_SIZE = 124;
+    u32 *dst;
+    u8 *describle;
+
+    /* Copy the file on the Embedded side */
+    printk("### Upload %s \n", filename);
+
+    size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
+    if (!dst) {
+	    printk("No such file or directory\n");
+	    return -1;
+    }
+    if (size <= 0) {
+            printk("wrong size of firmware file\n");
+            dst = NULL;
+            err = -1;
+    }
+
+	FMACFW_PATCH_TBL_8800DC_U02_DESCRIBE_BASE = dst[0];
+	printk("tbl size = %d patch_tbl_base = %x\n", size, FMACFW_PATCH_TBL_8800DC_U02_DESCRIBE_BASE);
+
+	if (!err && (i < size)) {
+	err=rwnx_send_dbg_mem_block_write_req(rwnx_hw, FMACFW_PATCH_TBL_8800DC_U02_DESCRIBE_BASE, 
+						FMACFW_PATCH_TBL_8800DC_U02_DESCRIBE_SIZE + 4, dst);
+		if(err){
+			printk("write describe information fail \n");
+		}
+
+		describle=kzalloc(FMACFW_PATCH_TBL_8800DC_U02_DESCRIBE_SIZE,GFP_KERNEL);
+		memcpy(describle, &dst[1], FMACFW_PATCH_TBL_8800DC_U02_DESCRIBE_SIZE);
+		printk("WIFI Patch Version: %s\n",describle);
+		kfree(describle);
+		describle=NULL;
+	}
+
+    if (!err && (i < size)) {
+        for (i =(128/ 4); i < (size/4); i += 2) {
+            printk("patch_tbl:  %x  %x\n", dst[i], dst[i+1]);
+            err = rwnx_send_dbg_mem_write_req(rwnx_hw, dst[i], dst[i+1]);
+        }
+        if (err) {
+            printk("bin upload fail: %x, err:%d\r\n", dst[i], err);
+        }
+    }
+
+    if (dst) {
+        rwnx_release_firmware_common(&dst);
+    }
+
+	return err;
+
+}
+
+/**
+ * rwnx_plat_bin_fw_upload_2() - Load the requested binary FW into embedded side.
+ *
+ * @rwnx_hw: Main driver data
+ * @fw_addr: Address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a binary file, into the specified address
+ */
+static int rwnx_plat_bin_fw_upload_2(struct rwnx_hw *rwnx_hw, u32 fw_addr,
+                               char *filename)
+{
+    int err = 0;
+    unsigned int i = 0, size;
+    u32 *src, *dst;
+
+    /* Copy the file on the Embedded side */
+    printk("\n### Upload %s firmware, @ = %x\n", filename, fw_addr);
+
+    size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
+    if (!dst) {
+	    printk("No such file or directory\n");
+	    return -1;
+    }
+    if (size <= 0) {
+            printk("wrong size of firmware file\n");
+            dst = NULL;
+            err = -1;
+    }
+
+	printk("size=%d, dst[0]=%x\n", size, dst[0]);
+    if (size > 512) {
+        for (; i < (size - 512); i += 512) {
+            //printk("wr blk 0: %p -> %x\r\n", dst + i / 4, fw_addr + i);
+            err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, fw_addr + i, 512, dst + i / 4);
+            if (err) {
+                printk("bin upload fail: %x, err:%d\r\n", fw_addr + i, err);
+                break;
+            }
+        }
+    }
+    if (!err && (i < size)) {
+        //printk("wr blk 1: %p -> %x size %d \r\n", dst + i / 4, fw_addr + i, size - i);
+        err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, fw_addr + i, size - i, dst + i / 4);
+        if (err) {
+            printk("bin upload fail: %x, err:%d\r\n", fw_addr + i, err);
+        }
+    }
+
+
+    if (dst) {
+        rwnx_release_firmware_common(&dst);
+    }
+
+    return err;
+}
+
+#ifndef CONFIG_ROM_PATCH_EN
+#if defined(CONFIG_PLATFORM_ALLWINNER) || defined(CONFIG_NANOPI_M4)
+static int aic_load_firmware(u32 ** fw_buf, const char *name,
+                 struct device *device)
+{
+        void *buffer=NULL;
+        char *path=NULL;
+        struct file *fp=NULL;
+        int size = 0, len=0, i=0;
+        ssize_t rdlen=0;
+        u32 *src=NULL, *dst = NULL;
+        RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+        /* get the firmware path */
+        path = __getname();
+        if (!path){
+                *fw_buf=NULL;
+                return -1;
+        }
+
+        len = snprintf(path, FW_PATH_MAX_LEN, "%s/%s",aic_fw_path, name);
+        if (len >= FW_PATH_MAX_LEN) {
+                printk("%s: %s file's path too long\n", __func__, name);
+                *fw_buf=NULL;
+                __putname(path);
+                return -1;
+        }
+
+        printk("%s :firmware path = %s  \n", __func__ ,path);
+
+
+        /* open the firmware file */
+        fp=filp_open(path, O_RDONLY, 0);
+        if(IS_ERR(fp) || (!fp)){
+	        printk("%s: %s file failed to open\n", __func__, name);
+                if(IS_ERR(fp))
+			printk("is_Err\n");
+		*fw_buf=NULL;
+                __putname(path);
+                fp=NULL;
+                return -1;
+        }
+
+        size = i_size_read(file_inode(fp));
+        if(size<=0){
+                printk("%s: %s file size invalid %d\n", __func__, name, size);
+                *fw_buf=NULL;
+                __putname(path);
+                filp_close(fp,NULL);
+                fp=NULL;
+                return -1;
+	}
+
+        /* start to read from firmware file */
+        buffer = kzalloc(size, GFP_KERNEL);
+        if(!buffer){
+                *fw_buf=NULL;
+                __putname(path);
+                filp_close(fp,NULL);
+                fp=NULL;
+                return -1;
+        }
+
+
+        #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 13, 16)
+        rdlen = kernel_read(fp, buffer, size, &fp->f_pos);
+        #else
+        rdlen = kernel_read(fp, fp->f_pos, buffer, size);
+        #endif
+
+        if(size != rdlen){
+                printk("%s: %s file rdlen invalid %ld\n", __func__, name, rdlen);
+                *fw_buf=NULL;
+                __putname(path);
+                filp_close(fp,NULL);
+                fp=NULL;
+                kfree(buffer);
+                buffer=NULL;
+                return -1;
+        }
+        if(rdlen > 0){
+                fp->f_pos += rdlen;
+        }
+
+
+       /*start to transform the data format*/
+        src = (u32*)buffer;
+        printk("malloc dst\n");
+        dst = (u32*)kzalloc(size,GFP_KERNEL);
+
+        if(!dst){
+                *fw_buf=NULL;
+                __putname(path);
+                filp_close(fp,NULL);
+                fp=NULL;
+                kfree(buffer);
+                buffer=NULL;
+                return -1;
+        }
+
+        for(i=0;i<(size/4);i++){
+                dst[i] = src[i];
+        }
+
+        __putname(path);
+        filp_close(fp,NULL);
+        fp=NULL;
+        kfree(buffer);
+        buffer=NULL;
+        *fw_buf = dst;
+
+        return size;
+
+}
+#endif
+#endif
+
+#ifdef CONFIG_LOAD_USERCONFIG
+int rwnx_atoi(char *value)
+{
+    int len = 0;
+    int i = 0;
+    int result = 0;
+    int flag = 1;
+
+    if (value[0] == '-') {
+        flag = -1;
+        value++;
+    }
+    len = strlen(value);
+
+    for (i = 0;i < len ;i++) {
+        result = result * 10;
+        if (value[i] >= 48 && value[i] <= 57) {
+            result += value[i] - 48;
+        } else {
+            result = 0;
+            break;
+        }
+    }
+
+    return result * flag;
+}
+
+void get_userconfig_txpwr_lvl(txpwr_lvl_conf_t *txpwr_lvl)
+{
+    txpwr_lvl->enable           = nvram_info.txpwr_lvl.enable;
+    txpwr_lvl->dsss             = nvram_info.txpwr_lvl.dsss;
+    txpwr_lvl->ofdmlowrate_2g4  = nvram_info.txpwr_lvl.ofdmlowrate_2g4;
+    txpwr_lvl->ofdm64qam_2g4    = nvram_info.txpwr_lvl.ofdm64qam_2g4;
+    txpwr_lvl->ofdm256qam_2g4   = nvram_info.txpwr_lvl.ofdm256qam_2g4;
+    txpwr_lvl->ofdm1024qam_2g4  = nvram_info.txpwr_lvl.ofdm1024qam_2g4;
+    txpwr_lvl->ofdmlowrate_5g   = nvram_info.txpwr_lvl.ofdmlowrate_5g;
+    txpwr_lvl->ofdm64qam_5g     = nvram_info.txpwr_lvl.ofdm64qam_5g;
+    txpwr_lvl->ofdm256qam_5g    = nvram_info.txpwr_lvl.ofdm256qam_5g;
+    txpwr_lvl->ofdm1024qam_5g   = nvram_info.txpwr_lvl.ofdm1024qam_5g;
+
+    printk("%s:enable:%d\r\n",          __func__, txpwr_lvl->enable);
+    printk("%s:dsss:%d\r\n",            __func__, txpwr_lvl->dsss);
+    printk("%s:ofdmlowrate_2g4:%d\r\n", __func__, txpwr_lvl->ofdmlowrate_2g4);
+    printk("%s:ofdm64qam_2g4:%d\r\n",   __func__, txpwr_lvl->ofdm64qam_2g4);
+    printk("%s:ofdm256qam_2g4:%d\r\n",  __func__, txpwr_lvl->ofdm256qam_2g4);
+    printk("%s:ofdm1024qam_2g4:%d\r\n", __func__, txpwr_lvl->ofdm1024qam_2g4);
+    printk("%s:ofdmlowrate_5g:%d\r\n",  __func__, txpwr_lvl->ofdmlowrate_5g);
+    printk("%s:ofdm64qam_5g:%d\r\n",    __func__, txpwr_lvl->ofdm64qam_5g);
+    printk("%s:ofdm256qam_5g:%d\r\n",   __func__, txpwr_lvl->ofdm256qam_5g);
+    printk("%s:ofdm1024qam_5g:%d\r\n",  __func__, txpwr_lvl->ofdm1024qam_5g);
+}
+
+void get_userconfig_txpwr_lvl_v2_in_fdrv(txpwr_lvl_conf_v2_t *txpwr_lvl_v2)
+{
+    *txpwr_lvl_v2 = nvram_info.txpwr_lvl_v2;
+
+    printk("%s:enable:%d\r\n",               __func__, txpwr_lvl_v2->enable);
+    printk("%s:lvl_11b_11ag_1m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[0]);
+#if 0
+    printk("%s:lvl_11b_11ag_2m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[1]);
+    printk("%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[2]);
+    printk("%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]);
+    printk("%s:lvl_11b_11ag_6m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[4]);
+    printk("%s:lvl_11b_11ag_9m_2g4:%d\r\n",  __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[5]);
+    printk("%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[6]);
+    printk("%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[7]);
+    printk("%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[8]);
+    printk("%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[9]);
+    printk("%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[10]);
+    printk("%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[11]);
+    printk("%s:lvl_11n_11ac_mcs0_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[0]);
+    printk("%s:lvl_11n_11ac_mcs1_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[1]);
+    printk("%s:lvl_11n_11ac_mcs2_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[2]);
+    printk("%s:lvl_11n_11ac_mcs3_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[3]);
+    printk("%s:lvl_11n_11ac_mcs4_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[4]);
+    printk("%s:lvl_11n_11ac_mcs5_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[5]);
+    printk("%s:lvl_11n_11ac_mcs6_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[6]);
+    printk("%s:lvl_11n_11ac_mcs7_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[7]);
+    printk("%s:lvl_11n_11ac_mcs8_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[8]);
+    printk("%s:lvl_11n_11ac_mcs9_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[9]);
+    printk("%s:lvl_11ax_mcs0_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[0]);
+    printk("%s:lvl_11ax_mcs1_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[1]);
+    printk("%s:lvl_11ax_mcs2_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[2]);
+    printk("%s:lvl_11ax_mcs3_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[3]);
+    printk("%s:lvl_11ax_mcs4_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[4]);
+    printk("%s:lvl_11ax_mcs5_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[5]);
+    printk("%s:lvl_11ax_mcs6_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[6]);
+    printk("%s:lvl_11ax_mcs7_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[7]);
+    printk("%s:lvl_11ax_mcs8_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[8]);
+    printk("%s:lvl_11ax_mcs9_2g4:%d\r\n",    __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[9]);
+    printk("%s:lvl_11ax_mcs10_2g4:%d\r\n",   __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[10]);
+    printk("%s:lvl_11ax_mcs11_2g4:%d\r\n",   __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[11]);
+#endif
+}
+
+void get_userconfig_txpwr_lvl_v3_in_fdrv(txpwr_lvl_conf_v3_t *txpwr_lvl_v3)
+{
+    *txpwr_lvl_v3 = nvram_info.txpwr_lvl_v3;
+
+    printk("%s:enable:%d\r\n",               __func__, txpwr_lvl_v3->enable);
+    printk("%s:lvl_11b_11ag_1m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[0]);
+    printk("%s:lvl_11b_11ag_2m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[1]);
+    printk("%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[2]);
+    printk("%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[3]);
+    printk("%s:lvl_11b_11ag_6m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[4]);
+    printk("%s:lvl_11b_11ag_9m_2g4:%d\r\n",  __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[5]);
+    printk("%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[6]);
+    printk("%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[7]);
+    printk("%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[8]);
+    printk("%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[9]);
+    printk("%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[10]);
+    printk("%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[11]);
+    printk("%s:lvl_11n_11ac_mcs0_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[0]);
+    printk("%s:lvl_11n_11ac_mcs1_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[1]);
+    printk("%s:lvl_11n_11ac_mcs2_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[2]);
+    printk("%s:lvl_11n_11ac_mcs3_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[3]);
+    printk("%s:lvl_11n_11ac_mcs4_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[4]);
+    printk("%s:lvl_11n_11ac_mcs5_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[5]);
+    printk("%s:lvl_11n_11ac_mcs6_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[6]);
+    printk("%s:lvl_11n_11ac_mcs7_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[7]);
+    printk("%s:lvl_11n_11ac_mcs8_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[8]);
+    printk("%s:lvl_11n_11ac_mcs9_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[9]);
+    printk("%s:lvl_11ax_mcs0_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[0]);
+    printk("%s:lvl_11ax_mcs1_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[1]);
+    printk("%s:lvl_11ax_mcs2_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[2]);
+    printk("%s:lvl_11ax_mcs3_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[3]);
+    printk("%s:lvl_11ax_mcs4_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[4]);
+    printk("%s:lvl_11ax_mcs5_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[5]);
+    printk("%s:lvl_11ax_mcs6_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[6]);
+    printk("%s:lvl_11ax_mcs7_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[7]);
+    printk("%s:lvl_11ax_mcs8_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[8]);
+    printk("%s:lvl_11ax_mcs9_2g4:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[9]);
+    printk("%s:lvl_11ax_mcs10_2g4:%d\r\n",   __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[10]);
+    printk("%s:lvl_11ax_mcs11_2g4:%d\r\n",   __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[11]);
+
+    printk("%s:lvl_11a_1m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[0]);
+    printk("%s:lvl_11a_2m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[1]);
+    printk("%s:lvl_11a_5m5_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[2]);
+    printk("%s:lvl_11a_11m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[3]);
+    printk("%s:lvl_11a_6m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[4]);
+    printk("%s:lvl_11a_9m_5g:%d\r\n",        __func__, txpwr_lvl_v3->pwrlvl_11a_5g[5]);
+    printk("%s:lvl_11a_12m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[6]);
+    printk("%s:lvl_11a_18m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[7]);
+    printk("%s:lvl_11a_24m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[8]);
+    printk("%s:lvl_11a_36m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[9]);
+    printk("%s:lvl_11a_48m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[10]);
+    printk("%s:lvl_11a_54m_5g:%d\r\n",       __func__, txpwr_lvl_v3->pwrlvl_11a_5g[11]);
+    printk("%s:lvl_11n_11ac_mcs0_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[0]);
+    printk("%s:lvl_11n_11ac_mcs1_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[1]);
+    printk("%s:lvl_11n_11ac_mcs2_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[2]);
+    printk("%s:lvl_11n_11ac_mcs3_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[3]);
+    printk("%s:lvl_11n_11ac_mcs4_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[4]);
+    printk("%s:lvl_11n_11ac_mcs5_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[5]);
+    printk("%s:lvl_11n_11ac_mcs6_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[6]);
+    printk("%s:lvl_11n_11ac_mcs7_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[7]);
+    printk("%s:lvl_11n_11ac_mcs8_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[8]);
+    printk("%s:lvl_11n_11ac_mcs9_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[9]);
+    printk("%s:lvl_11ax_mcs0_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[0]);
+    printk("%s:lvl_11ax_mcs1_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[1]);
+    printk("%s:lvl_11ax_mcs2_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[2]);
+    printk("%s:lvl_11ax_mcs3_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[3]);
+    printk("%s:lvl_11ax_mcs4_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[4]);
+    printk("%s:lvl_11ax_mcs5_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[5]);
+    printk("%s:lvl_11ax_mcs6_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[6]);
+    printk("%s:lvl_11ax_mcs7_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[7]);
+    printk("%s:lvl_11ax_mcs8_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[8]);
+    printk("%s:lvl_11ax_mcs9_5g:%d\r\n",     __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[9]);
+    printk("%s:lvl_11ax_mcs10_5g:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[10]);
+    printk("%s:lvl_11ax_mcs11_5g:%d\r\n",    __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[11]);
+}
+
+void get_userconfig_txpwr_lvl_adj_in_fdrv(txpwr_lvl_adj_conf_t *txpwr_lvl_adj)
+{
+    *txpwr_lvl_adj = nvram_info.txpwr_lvl_adj;
+
+    printk("%s:enable:%d\r\n",                   __func__, txpwr_lvl_adj->enable);
+    printk("%s:lvl_adj_2g4_chan_1_4:%d\r\n",     __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[0]);
+    printk("%s:lvl_adj_2g4_chan_5_9:%d\r\n",     __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[1]);
+    printk("%s:lvl_adj_2g4_chan_10_13:%d\r\n",   __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[2]);
+
+    printk("%s:lvl_adj_5g_chan_42:%d\r\n",       __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[0]);
+    printk("%s:lvl_adj_5g_chan_58:%d\r\n",       __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[1]);
+    printk("%s:lvl_adj_5g_chan_106:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[2]);
+    printk("%s:lvl_adj_5g_chan_122:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[3]);
+    printk("%s:lvl_adj_5g_chan_138:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[4]);
+    printk("%s:lvl_adj_5g_chan_155:%d\r\n",      __func__, txpwr_lvl_adj->pwrlvl_adj_tbl_5g[5]);
+}
+
+void get_userconfig_txpwr_loss(txpwr_loss_conf_t *txpwr_loss)
+{
+    txpwr_loss->loss_enable      = nvram_info.txpwr_loss.loss_enable;
+    txpwr_loss->loss_value       = nvram_info.txpwr_loss.loss_value;
+
+    printk("%s:loss_enable:%d\r\n",     __func__, txpwr_loss->loss_enable);
+    printk("%s:loss_value:%d\r\n",      __func__, txpwr_loss->loss_value);
+}
+
+void set_txpwr_loss_ofst(s8_l value)
+{
+    nvram_info.txpwr_loss.loss_enable = 1;
+    nvram_info.txpwr_loss.loss_value += value;
+
+    printk("%s:value:%d\r\n",      __func__, value);
+}
+
+void get_userconfig_txpwr_ofst(txpwr_ofst_conf_t *txpwr_ofst)
+{
+    txpwr_ofst->enable       = nvram_info.txpwr_ofst.enable;
+    txpwr_ofst->chan_1_4     = nvram_info.txpwr_ofst.chan_1_4;
+    txpwr_ofst->chan_5_9     = nvram_info.txpwr_ofst.chan_5_9;
+    txpwr_ofst->chan_10_13   = nvram_info.txpwr_ofst.chan_10_13;
+    txpwr_ofst->chan_36_64   = nvram_info.txpwr_ofst.chan_36_64;
+    txpwr_ofst->chan_100_120 = nvram_info.txpwr_ofst.chan_100_120;
+    txpwr_ofst->chan_122_140 = nvram_info.txpwr_ofst.chan_122_140;
+    txpwr_ofst->chan_142_165 = nvram_info.txpwr_ofst.chan_142_165;
+
+    printk("%s:enable      :%d\r\n", __func__, txpwr_ofst->enable);
+    printk("%s:chan_1_4    :%d\r\n", __func__, txpwr_ofst->chan_1_4);
+    printk("%s:chan_5_9    :%d\r\n", __func__, txpwr_ofst->chan_5_9);
+    printk("%s:chan_10_13  :%d\r\n", __func__, txpwr_ofst->chan_10_13);
+    printk("%s:chan_36_64  :%d\r\n", __func__, txpwr_ofst->chan_36_64);
+    printk("%s:chan_100_120:%d\r\n", __func__, txpwr_ofst->chan_100_120);
+    printk("%s:chan_122_140:%d\r\n", __func__, txpwr_ofst->chan_122_140);
+    printk("%s:chan_142_165:%d\r\n", __func__, txpwr_ofst->chan_142_165);
+}
+
+void get_userconfig_txpwr_ofst2x_in_fdrv(txpwr_ofst2x_conf_t *txpwr_ofst2x)
+{
+    int type, ch_grp;
+    *txpwr_ofst2x = nvram_info.txpwr_ofst2x;
+    printk("%s:enable      :%d\r\n", __func__, txpwr_ofst2x->enable);
+    printk("pwrofst2x 2.4g: [0]:11b, [1]:ofdm_highrate, [2]:ofdm_lowrate\n"
+        "  chan=" "\t1-4" "\t5-9" "\t10-13");
+    for (type = 0; type < 3; type++) {
+        printk("\n  [%d] =", type);
+        for (ch_grp = 0; ch_grp < 3; ch_grp++) {
+            printk("\t%d", txpwr_ofst2x->pwrofst2x_tbl_2g4[type][ch_grp]);
+        }
+    }
+    printk("\npwrofst2x 5g: [0]:ofdm_lowrate, [1]:ofdm_highrate, [2]:ofdm_midrate\n"
+        "  chan=" "\t36-50" "\t51-64" "\t98-114" "\t115-130" "\t131-146" "\t147-166");
+    for (type = 0; type < 3; type++) {
+        printk("\n  [%d] =", type);
+        for (ch_grp = 0; ch_grp < 6; ch_grp++) {
+            printk("\t%d", txpwr_ofst2x->pwrofst2x_tbl_5g[type][ch_grp]);
+        }
+    }
+    printk("\n");
+}
+
+
+void get_userconfig_xtal_cap(xtal_cap_conf_t *xtal_cap)
+{
+    *xtal_cap = nvram_info.xtal_cap;
+
+    printk("%s:enable       :%d\r\n", __func__, xtal_cap->enable);
+    printk("%s:xtal_cap     :%d\r\n", __func__, xtal_cap->xtal_cap);
+    printk("%s:xtal_cap_fine:%d\r\n", __func__, xtal_cap->xtal_cap_fine);
+}
+
+void rwnx_plat_nvram_set_value(char *command, char *value)
+{
+    //TODO send command
+    printk("%s:command=%s value=%s\n", __func__, command, value);
+    if (!strcmp(command, "enable")) {
+        nvram_info.txpwr_lvl.enable = rwnx_atoi(value);
+        nvram_info.txpwr_lvl_v2.enable = rwnx_atoi(value);
+    } else if (!strcmp(command, "dsss")) {
+        nvram_info.txpwr_lvl.dsss = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdmlowrate_2g4")) {
+        nvram_info.txpwr_lvl.ofdmlowrate_2g4 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdm64qam_2g4")) {
+        nvram_info.txpwr_lvl.ofdm64qam_2g4 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdm256qam_2g4")) {
+        nvram_info.txpwr_lvl.ofdm256qam_2g4 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdm1024qam_2g4")) {
+        nvram_info.txpwr_lvl.ofdm1024qam_2g4 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdmlowrate_5g")) {
+        nvram_info.txpwr_lvl.ofdmlowrate_5g = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdm64qam_5g")) {
+        nvram_info.txpwr_lvl.ofdm64qam_5g = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdm256qam_5g")) {
+        nvram_info.txpwr_lvl.ofdm256qam_5g = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdm1024qam_5g")) {
+        nvram_info.txpwr_lvl.ofdm1024qam_5g = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_1m_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[0] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_2m_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[1] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_5m5_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[2] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_11m_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[3] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_6m_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[4] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_9m_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[5] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_12m_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[6] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_18m_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[7] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_24m_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[8] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_36m_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[9] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_48m_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[10] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_54m_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[11] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs0_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[0] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs1_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[1] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs2_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[2] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs3_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[3] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs4_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[4] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs5_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[5] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs6_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[6] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs7_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[7] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs8_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[8] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs9_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[9] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs0_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[0] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs1_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[1] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs2_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[2] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs3_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[3] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs4_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[4] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs5_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[5] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs6_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[6] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs7_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[7] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs8_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[8] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs9_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[9] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs10_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[10] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs11_2g4")) {
+        nvram_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[11] = rwnx_atoi(value);
+    } else if (!strcmp(command, "loss_enable")) {
+        nvram_info.txpwr_loss.loss_enable = rwnx_atoi(value);
+    } else if (!strcmp(command, "loss_value")) {
+        nvram_info.txpwr_loss.loss_value = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_enable")) {
+        nvram_info.txpwr_ofst.enable = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_1_4")) {
+        nvram_info.txpwr_ofst.chan_1_4 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_5_9")) {
+        nvram_info.txpwr_ofst.chan_5_9 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_10_13")) {
+        nvram_info.txpwr_ofst.chan_10_13 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_36_64")) {
+        nvram_info.txpwr_ofst.chan_36_64 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_100_120")) {
+        nvram_info.txpwr_ofst.chan_100_120 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_122_140")) {
+        nvram_info.txpwr_ofst.chan_122_140 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_142_165")) {
+        nvram_info.txpwr_ofst.chan_142_165 = rwnx_atoi(value);
+    } else if (!strcmp(command, "xtal_enable")) {
+        nvram_info.xtal_cap.enable = rwnx_atoi(value);
+    } else if (!strcmp(command, "xtal_cap")) {
+        nvram_info.xtal_cap.xtal_cap = rwnx_atoi(value);
+    } else if (!strcmp(command, "xtal_cap_fine")) {
+        nvram_info.xtal_cap.xtal_cap_fine = rwnx_atoi(value);
+    } else {
+        printk("invalid cmd: %s\n", command);
+    }
+}
+
+void rwnx_plat_nvram_set_value_v3(char *command, char *value)
+{
+    //TODO send command
+    printk("%s:command=%s value=%s\n", __func__, command, value);
+    if (!strcmp(command, "enable")) {
+        nvram_info.txpwr_lvl.enable = rwnx_atoi(value);
+        nvram_info.txpwr_lvl_v3.enable = rwnx_atoi(value);
+    } else if (!strcmp(command, "dsss")) {
+        nvram_info.txpwr_lvl.dsss = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdmlowrate_2g4")) {
+        nvram_info.txpwr_lvl.ofdmlowrate_2g4 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdm64qam_2g4")) {
+        nvram_info.txpwr_lvl.ofdm64qam_2g4 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdm256qam_2g4")) {
+        nvram_info.txpwr_lvl.ofdm256qam_2g4 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdm1024qam_2g4")) {
+        nvram_info.txpwr_lvl.ofdm1024qam_2g4 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdmlowrate_5g")) {
+        nvram_info.txpwr_lvl.ofdmlowrate_5g = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdm64qam_5g")) {
+        nvram_info.txpwr_lvl.ofdm64qam_5g = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdm256qam_5g")) {
+        nvram_info.txpwr_lvl.ofdm256qam_5g = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofdm1024qam_5g")) {
+        nvram_info.txpwr_lvl.ofdm1024qam_5g = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_1m_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[0] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_2m_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[1] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_5m5_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[2] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_11m_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[3] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_6m_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[4] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_9m_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[5] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_12m_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[6] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_18m_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[7] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_24m_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[8] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_36m_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[9] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_48m_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[10] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11b_11ag_54m_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[11] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs0_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[0] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs1_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[1] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs2_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[2] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs3_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[3] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs4_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[4] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs5_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[5] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs6_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[6] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs7_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[7] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs8_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[8] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs9_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[9] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs0_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[0] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs1_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[1] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs2_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[2] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs3_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[3] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs4_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[4] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs5_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[5] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs6_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[6] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs7_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[7] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs8_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[8] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs9_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[9] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs10_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[10] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs11_2g4")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[11] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11a_1m_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[0] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11a_2m_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[1] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11a_5m5_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[2] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11a_11m_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[3] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11a_6m_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[4] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11a_9m_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[5] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11a_12m_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[6] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11a_18m_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[7] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11a_24m_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[8] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11a_36m_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[9] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11a_48m_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[10] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11a_54m_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11a_5g[11] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs0_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[0] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs1_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[1] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs2_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[2] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs3_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[3] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs4_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[4] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs5_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[5] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs6_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[6] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs7_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[7] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs8_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[8] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11n_11ac_mcs9_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[9] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs0_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[0] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs1_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[1] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs2_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[2] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs3_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[3] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs4_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[4] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs5_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[5] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs6_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[6] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs7_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[7] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs8_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[8] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs9_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[9] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs10_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[10] = rwnx_atoi(value);
+    } else if (!strcmp(command,     "lvl_11ax_mcs11_5g")) {
+        nvram_info.txpwr_lvl_v3.pwrlvl_11ax_5g[11] = rwnx_atoi(value);
+    } else if (!strcmp(command, "lvl_adj_enable")) {
+        nvram_info.txpwr_lvl_adj.enable = rwnx_atoi(value);
+    } else if (!strcmp(command, "lvl_adj_2g4_chan_1_4")) {
+        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[0] = rwnx_atoi(value);
+    } else if (!strcmp(command, "lvl_adj_2g4_chan_5_9")) {
+        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[1] = rwnx_atoi(value);
+    } else if (!strcmp(command, "lvl_adj_2g4_chan_10_13")) {
+        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[2] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_42")) {
+        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[0] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_58")) {
+        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[1] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_106")) {
+        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[2] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_122")) {
+        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[3] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_138")) {
+        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[4] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_155")) {
+        nvram_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[5] = rwnx_atoi(value);
+    } else if (!strcmp(command, "loss_enable")) {
+        nvram_info.txpwr_loss.loss_enable = rwnx_atoi(value);
+    } else if (!strcmp(command, "loss_value")) {
+        nvram_info.txpwr_loss.loss_value = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_enable")) {
+        nvram_info.txpwr_ofst.enable = rwnx_atoi(value);
+		nvram_info.txpwr_ofst2x.enable = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_1_4")) {
+        nvram_info.txpwr_ofst.chan_1_4 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_5_9")) {
+        nvram_info.txpwr_ofst.chan_5_9 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_10_13")) {
+        nvram_info.txpwr_ofst.chan_10_13 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_36_64")) {
+        nvram_info.txpwr_ofst.chan_36_64 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_100_120")) {
+        nvram_info.txpwr_ofst.chan_100_120 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_122_140")) {
+        nvram_info.txpwr_ofst.chan_122_140 = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_chan_142_165")) {
+        nvram_info.txpwr_ofst.chan_142_165 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_11b_chan_1_4")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][0] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_2g4_11b_chan_5_9")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][1] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_2g4_11b_chan_10_13")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][2] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_1_4")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][0] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_5_9")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][1] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_10_13")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][2] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_1_4")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][0] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_5_9")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_10_13")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][0] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_42")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][0] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_58")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][1] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_106")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][2] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_122")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][3] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_138")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][4] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_155")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][5] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_42")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][0] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_58")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][1] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_106")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][2] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_122")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][3] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_138")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][4] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_155")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][5] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_42")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][0] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_58")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][1] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_106")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][2] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_122")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][3] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_138")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][4] = rwnx_atoi(value);
+    } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_155")) {
+        nvram_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][5] = rwnx_atoi(value);
+    } else if (!strcmp(command, "xtal_enable")) {
+        nvram_info.xtal_cap.enable = rwnx_atoi(value);
+    } else if (!strcmp(command, "xtal_cap")) {
+        nvram_info.xtal_cap.xtal_cap = rwnx_atoi(value);
+    } else if (!strcmp(command, "xtal_cap_fine")) {
+        nvram_info.xtal_cap.xtal_cap_fine = rwnx_atoi(value);
+    } else {
+        printk("invalid cmd: %s\n", command);
+    }
+}
+
+void rwnx_plat_userconfig_parsing(char *buffer, int size)
+{
+    int i = 0;
+    int parse_state = 0;
+    char command[30];
+    char value[100];
+    int char_counter = 0;
+
+    memset(command, 0, 30);
+    memset(value, 0, 100);
+
+    for (i = 0; i < size; i++) {
+        //Send command or print nvram log when char is \r or \n
+        if (buffer[i] == 0x0a || buffer[i] == 0x0d) {
+            if (command[0] != 0 && value[0] != 0) {
+                if (parse_state == PRINT) {
+                    printk("%s:%s\r\n", __func__, value);
+                } else if (parse_state == GET_VALUE) {
+                    rwnx_plat_nvram_set_value(command, value);
+                }
+            }
+            //Reset command value and char_counter
+            memset(command, 0, 30);
+            memset(value, 0, 100);
+            char_counter = 0;
+            parse_state = INIT;
+            continue;
+        }
+
+        //Switch parser state
+        if (parse_state == INIT) {
+            if (buffer[i] == '#') {
+                parse_state = PRINT;
+                continue;
+            } else if (buffer[i] == 0x0a || buffer[i] == 0x0d) {
+                parse_state = INIT;
+                continue;
+            } else {
+                parse_state = CMD;
+            }
+        }
+
+        //Fill data to command and value
+        if (parse_state == PRINT) {
+            command[0] = 0x01;
+            value[char_counter] = buffer[i];
+            char_counter++;
+        } else if (parse_state == CMD) {
+            if (command[0] != 0 && buffer[i] == '=') {
+                parse_state = GET_VALUE;
+                char_counter = 0;
+                continue;
+            }
+            command[char_counter] = buffer[i];
+            char_counter++;
+        } else if (parse_state == GET_VALUE) {
+            value[char_counter] = buffer[i];
+            char_counter++;
+        }
+    }
+}
+
+void rwnx_plat_userconfig_parsing3(char *buffer, int size)
+{
+    int i = 0;
+    int parse_state = 0;
+    char command[64];
+    char value[100];
+    int char_counter = 0;
+
+    memset(command, 0, 64);
+    memset(value, 0, 100);
+
+    for (i = 0; i < size; i++) {
+        //Send command or print nvram log when char is \r or \n
+        if (buffer[i] == 0x0a || buffer[i] == 0x0d) {
+            if (command[0] != 0 && value[0] != 0) {
+                if (parse_state == PRINT) {
+                    printk("%s:%s\r\n", __func__, value);
+                } else if (parse_state == GET_VALUE) {
+                    rwnx_plat_nvram_set_value_v3(command, value);
+                }
+            }
+            //Reset command value and char_counter
+            memset(command, 0, 64);
+            memset(value, 0, 100);
+            char_counter = 0;
+            parse_state = INIT;
+            continue;
+        }
+
+        //Switch parser state
+        if (parse_state == INIT) {
+            if (buffer[i] == '#') {
+                parse_state = PRINT;
+                continue;
+            } else if (buffer[i] == 0x0a || buffer[i] == 0x0d) {
+                parse_state = INIT;
+                continue;
+            } else {
+                parse_state = CMD;
+            }
+        }
+
+        //Fill data to command and value
+        if (parse_state == PRINT) {
+            command[0] = 0x01;
+            value[char_counter] = buffer[i];
+            char_counter++;
+        } else if (parse_state == CMD) {
+            if (command[0] != 0 && buffer[i] == '=') {
+                parse_state = GET_VALUE;
+                char_counter = 0;
+                continue;
+            }
+            command[char_counter] = buffer[i];
+            char_counter++;
+        } else if (parse_state == GET_VALUE) {
+            if(buffer[i] != 0x2D && (buffer[i] < 0x30 || buffer[i] > 0x39)) {
+                continue;
+            }
+            value[char_counter] = buffer[i];
+            char_counter++;
+        }
+    }
+}
+
+/**
+ * rwnx_plat_userconfig_load  ---Load aic_userconfig.txt
+ *@filename name of config
+*/
+static int rwnx_plat_userconfig_load(struct rwnx_hw *rwnx_hw) {
+    int size;
+    u32 *dst=NULL;
+    char *filename = FW_USERCONFIG_NAME;
+
+    printk("userconfig file path:%s \r\n", filename);
+
+    /* load file */
+    size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
+    if (size <= 0) {
+            printk("wrong size of firmware file\n");
+            dst = NULL;
+            return 0;
+    }
+
+	/* Copy the file on the Embedded side */
+    printk("### Load file done: %s, size=%d\n", filename, size);
+
+	rwnx_plat_userconfig_parsing((char *)dst, size);
+
+    rwnx_release_firmware_common(&dst);
+
+    printk("userconfig download complete\n\n");
+    return 0;
+}
+
+int	rwnx_plat_userconfig_load_8800d80(struct rwnx_hw *rwnx_hw)
+{
+    int size;
+    u32 *dst=NULL;
+    char *filename = FW_USERCONFIG_NAME_8800D80;
+
+    printk("userconfig file path:%s \r\n", filename);
+
+    /* load file */
+    size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
+    if (size <= 0) {
+            printk("wrong size of firmware file\n");
+            dst = NULL;
+            return 0;
+    }
+
+	/* Copy the file on the Embedded side */
+    printk("### Load file done: %s, size=%d\n", filename, size);
+
+	rwnx_plat_userconfig_parsing3((char *)dst, size);
+
+    rwnx_release_firmware_common(&dst);
+
+    printk("userconfig download complete\n\n");
+    return 0;
+
+}
+
+#endif
+
+#ifndef CONFIG_ROM_PATCH_EN
+#if defined(CONFIG_PLATFORM_ALLWINNER) || defined(CONFIG_NANOPI_M4)
+static int rwnx_plat_bin_fw_upload_android(struct rwnx_hw *rwnx_hw, u32 fw_addr,
+                               char *filename)
+{
+    struct device *dev = rwnx_platform_get_dev(rwnx_hw->plat);
+    unsigned int i = 0;
+    int size;
+    u32 *dst=NULL;
+    int err=0;
+
+
+        /* load aic firmware */
+        size = aic_load_firmware(&dst, filename, dev);
+        if(size<=0){
+                printk("wrong size of firmware file\n");
+                kfree(dst);
+                dst = NULL;
+                return -1;
+        }
+
+
+    /* Copy the file on the Embedded side */
+    printk("\n### Upload %s firmware, @ = %x  size=%d\n", filename, fw_addr, size);
+
+    if (size > 1024) {// > 1KB data
+        for (; i < (size - 1024); i += 1024) {//each time write 1KB
+            err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, fw_addr + i, 1024, dst + i / 4);
+                        if (err) {
+                printk("bin upload fail: %x, err:%d\r\n", fw_addr + i, err);
+                break;
+            }
+        }
+    }
+
+    if (!err && (i < size)) {// <1KB data
+        err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, fw_addr + i, size - i, dst + i / 4);
+        if (err) {
+            printk("bin upload fail: %x, err:%d\r\n", fw_addr + i, err);
+        }
+    }
+
+    if (dst) {
+        kfree(dst);
+        dst = NULL;
+    }
+
+    return err;
+}
+#endif
+#endif
+
+
+
+#if 0
+#ifndef CONFIG_RWNX_TL4
+#define IHEX_REC_DATA           0
+#define IHEX_REC_EOF            1
+#define IHEX_REC_EXT_SEG_ADD    2
+#define IHEX_REC_START_SEG_ADD  3
+#define IHEX_REC_EXT_LIN_ADD    4
+#define IHEX_REC_START_LIN_ADD  5
+
+/**
+ * rwnx_plat_ihex_fw_upload() - Load the requested intel hex 8 FW into embedded side.
+ *
+ * @rwnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a ihex file, into the specified address.
+ */
+static int rwnx_plat_ihex_fw_upload(struct rwnx_plat *rwnx_plat, u8* fw_addr,
+                                    char *filename)
+{
+    const struct firmware *fw;
+    struct device *dev = rwnx_platform_get_dev(rwnx_plat);
+    u8 const *src, *end;
+    u32 *dst;
+    u16 haddr, segaddr, addr;
+    u32 hwaddr;
+    u8 load_fw, byte_count, checksum, csum, rec_type;
+    int err, rec_idx;
+    char hex_buff[9];
+
+    err = request_firmware(&fw, filename, dev);
+    if (err) {
+        return err;
+    }
+
+    /* Copy the file on the Embedded side */
+    dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+    src = fw->data;
+    end = src + (unsigned int)fw->size;
+    haddr = 0;
+    segaddr = 0;
+    load_fw = 1;
+    err = -EINVAL;
+    rec_idx = 0;
+    hwaddr = 0;
+
+#define IHEX_READ8(_val, _cs) {                  \
+        hex_buff[2] = 0;                         \
+        strncpy(hex_buff, src, 2);               \
+        if (kstrtou8(hex_buff, 16, &_val))       \
+            goto end;                            \
+        src += 2;                                \
+        if (_cs)                                 \
+            csum += _val;                        \
+    }
+
+#define IHEX_READ16(_val) {                        \
+        hex_buff[4] = 0;                           \
+        strncpy(hex_buff, src, 4);                 \
+        if (kstrtou16(hex_buff, 16, &_val))        \
+            goto end;                              \
+        src += 4;                                  \
+        csum += (_val & 0xff) + (_val >> 8);       \
+    }
+
+#define IHEX_READ32(_val) {                              \
+        hex_buff[8] = 0;                                 \
+        strncpy(hex_buff, src, 8);                       \
+        if (kstrtouint(hex_buff, 16, &_val))             \
+            goto end;                                    \
+        src += 8;                                        \
+        csum += (_val & 0xff) + ((_val >> 8) & 0xff) +   \
+            ((_val >> 16) & 0xff) + (_val >> 24);        \
+    }
+
+#define IHEX_READ32_PAD(_val, _nb) {                    \
+        memset(hex_buff, '0', 8);                       \
+        hex_buff[8] = 0;                                \
+        strncpy(hex_buff, src, (2 * _nb));              \
+        if (kstrtouint(hex_buff, 16, &_val))            \
+            goto end;                                   \
+        src += (2 * _nb);                               \
+        csum += (_val & 0xff) + ((_val >> 8) & 0xff) +  \
+            ((_val >> 16) & 0xff) + (_val >> 24);       \
+}
+
+    /* loop until end of file is read*/
+    while (load_fw) {
+        rec_idx++;
+        csum = 0;
+
+        /* Find next colon start code */
+        while (*src != ':') {
+            src++;
+            if ((src + 3) >= end) /* 3 = : + rec_len */
+                goto end;
+        }
+        src++;
+
+        /* Read record len */
+        IHEX_READ8(byte_count, 1);
+        if ((src + (byte_count * 2) + 8) >= end) /* 8 = rec_addr + rec_type + chksum */
+            goto end;
+
+        /* Read record addr */
+        IHEX_READ16(addr);
+
+        /* Read record type */
+        IHEX_READ8(rec_type, 1);
+
+        switch(rec_type) {
+            case IHEX_REC_DATA:
+            {
+                /* Update destination address */
+                dst = (u32 *) (fw_addr + hwaddr + addr);
+
+                while (byte_count) {
+                    u32 val;
+                    if (byte_count >= 4) {
+                        IHEX_READ32(val);
+                        byte_count -= 4;
+                    } else {
+                        IHEX_READ32_PAD(val, byte_count);
+                        byte_count = 0;
+                    }
+                    *dst++ = __swab32(val);
+                }
+                break;
+            }
+            case IHEX_REC_EOF:
+            {
+                load_fw = 0;
+                err = 0;
+                break;
+            }
+            case IHEX_REC_EXT_SEG_ADD: /* Extended Segment Address */
+            {
+                IHEX_READ16(segaddr);
+                hwaddr = (haddr << 16) + (segaddr << 4);
+                break;
+            }
+            case IHEX_REC_EXT_LIN_ADD: /* Extended Linear Address */
+            {
+                IHEX_READ16(haddr);
+                hwaddr = (haddr << 16) + (segaddr << 4);
+                break;
+            }
+            case IHEX_REC_START_LIN_ADD: /* Start Linear Address */
+            {
+                u32 val;
+                IHEX_READ32(val); /* need to read for checksum */
+                break;
+            }
+            case IHEX_REC_START_SEG_ADD:
+            default:
+            {
+                dev_err(dev, "ihex: record type %d not supported\n", rec_type);
+                load_fw = 0;
+            }
+        }
+
+        /* Read and compare checksum */
+        IHEX_READ8(checksum, 0);
+        if (checksum != (u8)(~csum + 1))
+            goto end;
+    }
+
+#undef IHEX_READ8
+#undef IHEX_READ16
+#undef IHEX_READ32
+#undef IHEX_READ32_PAD
+
+  end:
+    release_firmware(fw);
+
+    if (err)
+        dev_err(dev, "%s: Invalid ihex record around line %d\n", filename, rec_idx);
+
+    return err;
+}
+#endif /* CONFIG_RWNX_TL4 */
+
+#ifndef CONFIG_RWNX_SDM
+/**
+ * rwnx_plat_get_rf() - Retrun the RF used in the platform
+ *
+ * @rwnx_plat: pointer to platform structure
+ */
+static u32 rwnx_plat_get_rf(struct rwnx_plat *rwnx_plat)
+{
+    u32 ver;
+    ver = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, MDM_HDMCONFIG_ADDR);
+
+    ver = __MDM_PHYCFG_FROM_VERS(ver);
+    WARN(((ver != MDM_PHY_CONFIG_TRIDENT) &&
+          (ver != MDM_PHY_CONFIG_ELMA) &&
+          (ver != MDM_PHY_CONFIG_KARST)),
+         "bad phy version 0x%08x\n", ver);
+
+    return ver;
+}
+
+/**
+ * rwnx_plat_stop_agcfsm() - Stop a AGC state machine
+ *
+ * @rwnx_plat: pointer to platform structure
+ * @agg_reg: Address of the agccntl register (within RWNX_ADDR_SYSTEM)
+ * @agcctl: Updated with value of the agccntl rgister before stop
+ * @memclk: Updated with value of the clock register before stop
+ * @agc_ver: Version of the AGC load procedure
+ * @clkctrladdr: Indicates which AGC clock register should be accessed
+ */
+static void rwnx_plat_stop_agcfsm(struct rwnx_plat *rwnx_plat, int agc_reg,
+                                  u32 *agcctl, u32 *memclk, u8 agc_ver,
+                                  u32 clkctrladdr)
+{
+    /* First read agcctnl and clock registers */
+    *memclk = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, clkctrladdr);
+
+    /* Stop state machine : xxAGCCNTL0[AGCFSMRESET]=1 */
+    *agcctl = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, agc_reg);
+    RWNX_REG_WRITE((*agcctl) | BIT(12), rwnx_plat, RWNX_ADDR_SYSTEM, agc_reg);
+
+    /* Force clock */
+    if (agc_ver > 0) {
+        /* CLKGATEFCTRL0[AGCCLKFORCE]=1 */
+        RWNX_REG_WRITE((*memclk) | BIT(29), rwnx_plat, RWNX_ADDR_SYSTEM,
+                       clkctrladdr);
+    } else {
+        /* MEMCLKCTRL0[AGCMEMCLKCTRL]=0 */
+        RWNX_REG_WRITE((*memclk) & ~BIT(3), rwnx_plat, RWNX_ADDR_SYSTEM,
+                       clkctrladdr);
+    }
+}
+
+
+/**
+ * rwnx_plat_start_agcfsm() - Restart a AGC state machine
+ *
+ * @rwnx_plat: pointer to platform structure
+ * @agg_reg: Address of the agccntl register (within RWNX_ADDR_SYSTEM)
+ * @agcctl: value of the agccntl register to restore
+ * @memclk: value of the clock register to restore
+ * @agc_ver: Version of the AGC load procedure
+ * @clkctrladdr: Indicates which AGC clock register should be accessed
+ */
+static void rwnx_plat_start_agcfsm(struct rwnx_plat *rwnx_plat, int agc_reg,
+                                   u32 agcctl, u32 memclk, u8 agc_ver,
+                                   u32 clkctrladdr)
+{
+
+    /* Release clock */
+    if (agc_ver > 0)
+        /* CLKGATEFCTRL0[AGCCLKFORCE]=0 */
+        RWNX_REG_WRITE(memclk & ~BIT(29), rwnx_plat, RWNX_ADDR_SYSTEM,
+                       clkctrladdr);
+    else
+        /* MEMCLKCTRL0[AGCMEMCLKCTRL]=1 */
+        RWNX_REG_WRITE(memclk | BIT(3), rwnx_plat, RWNX_ADDR_SYSTEM,
+                       clkctrladdr);
+
+    /* Restart state machine: xxAGCCNTL0[AGCFSMRESET]=0 */
+    RWNX_REG_WRITE(agcctl & ~BIT(12), rwnx_plat, RWNX_ADDR_SYSTEM, agc_reg);
+}
+#endif
+
+/**
+ * rwnx_plat_fcu_load() - Load FCU (Fith Chain Unit) ucode
+ *
+ * @rwnx_hw: main driver data
+ *
+ * c.f Modem UM (AGC/CCA initialization)
+ */
+static int rwnx_plat_fcu_load(struct rwnx_hw *rwnx_hw)
+{
+    int ret=0;
+#ifndef CONFIG_RWNX_SDM
+    struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+    u32 agcctl, memclk;
+
+#ifndef CONFIG_RWNX_FHOST
+    /* By default, we consider that there is only one RF in the system */
+    rwnx_hw->phy.cnt = 1;
+#endif // CONFIG_RWNX_FHOST
+
+    if (rwnx_plat_get_rf(rwnx_plat) != MDM_PHY_CONFIG_ELMA)
+        /* No FCU for PHYs other than Elma */
+        return 0;
+
+    agcctl = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, RIU_RWNXAGCCNTL_ADDR);
+    if (!__RIU_FCU_PRESENT(agcctl))
+        /* No FCU present in this version */
+        return 0;
+
+#ifndef CONFIG_RWNX_FHOST
+    /* FCU is present */
+	#ifdef USE_5G
+    rwnx_hw->phy.cnt = 2;
+    rwnx_hw->phy.sec_chan.band = NL80211_BAND_5GHZ;
+    rwnx_hw->phy.sec_chan.type = PHY_CHNL_BW_20;
+    rwnx_hw->phy.sec_chan.prim20_freq = 5500;
+    rwnx_hw->phy.sec_chan.center_freq1 = 5500;
+    rwnx_hw->phy.sec_chan.center_freq2 = 0;
+	#endif
+#endif // CONFIG_RWNX_FHOST
+
+    rwnx_plat_stop_agcfsm(rwnx_plat, FCU_RWNXFCAGCCNTL_ADDR, &agcctl, &memclk, 0,
+                          MDM_MEMCLKCTRL0_ADDR);
+
+    ret = rwnx_plat_bin_fw_upload(rwnx_plat,
+                              RWNX_ADDR(rwnx_plat, RWNX_ADDR_SYSTEM, PHY_FCU_UCODE_ADDR),
+                              RWNX_FCU_FW_NAME);
+
+    rwnx_plat_start_agcfsm(rwnx_plat, FCU_RWNXFCAGCCNTL_ADDR, agcctl, memclk, 0,
+                           MDM_MEMCLKCTRL0_ADDR);
+#endif
+
+    return ret;
+}
+
+/**
+ * rwnx_is_new_agc_load() - Return is new agc clock register should be used
+ *
+ * @rwnx_plat: platform data
+ * @rf: rf in used
+ *
+ * c.f Modem UM (AGC/CCA initialization)
+ */
+#ifndef CONFIG_RWNX_SDM
+static u8 rwnx_get_agc_load_version(struct rwnx_plat *rwnx_plat, u32 rf, u32 *clkctrladdr)
+{
+    u8 agc_load_ver = 0;
+    u32 agc_ver;
+    u32 regval;
+
+    /* Trident and Elma PHY use old method */
+    if (rf !=  MDM_PHY_CONFIG_KARST) {
+        *clkctrladdr = MDM_MEMCLKCTRL0_ADDR;
+        return 0;
+    }
+
+    /* Get the FPGA signature */
+    regval = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+
+    if (__FPGA_TYPE(regval) == 0xC0CA)
+        *clkctrladdr = CRM_CLKGATEFCTRL0_ADDR;
+    else
+        *clkctrladdr = MDM_CLKGATEFCTRL0_ADDR;
+
+    /* Read RIU version register */
+    agc_ver = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, RIU_RWNXVERSION_ADDR);
+    agc_load_ver = __RIU_AGCLOAD_FROM_VERS(agc_ver);
+
+    return agc_load_ver;
+}
+#endif /* CONFIG_RWNX_SDM */
+
+/**
+ * rwnx_plat_agc_load() - Load AGC ucode
+ *
+ * @rwnx_plat: platform data
+ * c.f Modem UM (AGC/CCA initialization)
+ */
+static int rwnx_plat_agc_load(struct rwnx_plat *rwnx_plat)
+{
+    int ret = 0;
+#ifndef CONFIG_RWNX_SDM
+    u32 agc = 0, agcctl, memclk;
+    u32 clkctrladdr;
+    u32 rf = rwnx_plat_get_rf(rwnx_plat);
+    u8 agc_ver;
+
+    switch (rf) {
+        case MDM_PHY_CONFIG_TRIDENT:
+            agc = AGC_RWNXAGCCNTL_ADDR;
+            break;
+        case MDM_PHY_CONFIG_ELMA:
+        case MDM_PHY_CONFIG_KARST:
+            agc = RIU_RWNXAGCCNTL_ADDR;
+            break;
+        default:
+            return -1;
+    }
+
+    agc_ver = rwnx_get_agc_load_version(rwnx_plat, rf, &clkctrladdr);
+
+    rwnx_plat_stop_agcfsm(rwnx_plat, agc, &agcctl, &memclk, agc_ver, clkctrladdr);
+
+    ret = rwnx_plat_bin_fw_upload(rwnx_plat,
+                              RWNX_ADDR(rwnx_plat, RWNX_ADDR_SYSTEM, PHY_AGC_UCODE_ADDR),
+                              RWNX_AGC_FW_NAME);
+
+    if (!ret && (agc_ver == 1)) {
+        /* Run BIST to ensure that the AGC RAM was correctly loaded */
+        RWNX_REG_WRITE(BIT(28), rwnx_plat, RWNX_ADDR_SYSTEM,
+                       RIU_RWNXDYNAMICCONFIG_ADDR);
+        while (RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM,
+                             RIU_RWNXDYNAMICCONFIG_ADDR) & BIT(28));
+
+        if (!(RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM,
+                            RIU_AGCMEMBISTSTAT_ADDR) & BIT(0))) {
+            dev_err(rwnx_platform_get_dev(rwnx_plat),
+                    "AGC RAM not loaded correctly 0x%08x\n",
+                    RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM,
+                                  RIU_AGCMEMSIGNATURESTAT_ADDR));
+            ret = -EIO;
+        }
+    }
+
+    rwnx_plat_start_agcfsm(rwnx_plat, agc, agcctl, memclk, agc_ver, clkctrladdr);
+
+#endif
+    return ret;
+}
+
+/**
+ * rwnx_ldpc_load() - Load LDPC RAM
+ *
+ * @rwnx_hw: Main driver data
+ * c.f Modem UM (LDPC initialization)
+ */
+static int rwnx_ldpc_load(struct rwnx_hw *rwnx_hw)
+{
+#ifndef CONFIG_RWNX_SDM
+    struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+    u32 rf = rwnx_plat_get_rf(rwnx_plat);
+    u32 phy_feat = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, MDM_HDMCONFIG_ADDR);
+
+    if ((rf !=  MDM_PHY_CONFIG_KARST) ||
+        (phy_feat & (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT)) !=
+        (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT)) {
+        goto disable_ldpc;
+    }
+
+    if (rwnx_plat_bin_fw_upload(rwnx_plat,
+                            RWNX_ADDR(rwnx_plat, RWNX_ADDR_SYSTEM, PHY_LDPC_RAM_ADDR),
+                            RWNX_LDPC_RAM_NAME)) {
+        goto disable_ldpc;
+    }
+
+    return 0;
+
+  disable_ldpc:
+    rwnx_hw->mod_params->ldpc_on = false;
+
+#endif /* CONFIG_RWNX_SDM */
+    return 0;
+}
+
+/**
+ * rwnx_plat_lmac_load() - Load FW code
+ *
+ * @rwnx_plat: platform data
+ */
+static int rwnx_plat_lmac_load(struct rwnx_plat *rwnx_plat)
+{
+    int ret;
+
+    #ifdef CONFIG_RWNX_TL4
+    ret = rwnx_plat_tl4_fw_upload(rwnx_plat,
+                                  RWNX_ADDR(rwnx_plat, RWNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+                                  RWNX_MAC_FW_NAME);
+    #else
+    ret = rwnx_plat_ihex_fw_upload(rwnx_plat,
+                                   RWNX_ADDR(rwnx_plat, RWNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+                                   RWNX_MAC_FW_NAME);
+    if (ret == -ENOENT)
+    {
+        ret = rwnx_plat_bin_fw_upload(rwnx_plat,
+                                      RWNX_ADDR(rwnx_plat, RWNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+                                      RWNX_MAC_FW_NAME2);
+    }
+    #endif
+
+    return ret;
+}
+#endif
+
+#ifdef CONFIG_BT_SUPPORT
+int aicbt_patch_table_free(struct aicbt_patch_table **head)
+{
+	struct aicbt_patch_table *p = *head, *n = NULL;
+	while (p) {
+		n = p->next;
+		kfree(p->name);
+		kfree(p->data);
+		kfree(p);
+		p = n;
+	}
+	*head = NULL;
+	return 0;
+}
+
+struct aicbt_patch_table *aicbt_patch_table_alloc(const char *filename)
+{
+	uint8_t *rawdata = NULL, *p;
+	int size;
+	struct aicbt_patch_table *head = NULL, *new = NULL, *cur = NULL;
+
+	/* load aic firmware */
+	size = rwnx_load_firmware((u32 **)&rawdata, filename, NULL);
+	if (size <= 0) {
+		printk("wrong size of firmware file\n");
+		goto err;
+	}
+
+	p = rawdata;
+	if (memcmp(p, AICBT_PT_TAG, sizeof(AICBT_PT_TAG) < 16 ? sizeof(AICBT_PT_TAG) : 16)) {
+		printk("TAG err\n");
+		goto err;
+	}
+	p += 16;
+
+	while (p - rawdata < size) {
+		new = (struct aicbt_patch_table *)kmalloc(sizeof(struct aicbt_patch_table),GFP_KERNEL);
+		memset(new, 0, sizeof(struct aicbt_patch_table));
+		if (head == NULL) {
+			head = new;
+			cur  = new;
+		} else {
+			cur->next = new;
+			cur = cur->next;
+		}
+
+		cur->name = (char *)kmalloc(sizeof(char) * 16, GFP_KERNEL);
+		memset(cur->name, 0, sizeof(char) * 16);
+		memcpy(cur->name, p, 16);
+		p += 16;
+
+		cur->type = *(uint32_t *)p;
+		p += 4;
+
+		cur->len = *(uint32_t *)p;
+		p += 4;
+
+		if((cur->type )  >= 1000 ) {//Temp Workaround
+			cur->len = 0;
+		}else{
+			if(cur->len > 0){
+				cur->data = (uint32_t *)kmalloc(sizeof(uint8_t) * cur->len * 8, GFP_KERNEL);
+				memset(cur->data, 0, sizeof(uint8_t) * cur->len * 8);
+				memcpy(cur->data, p, cur->len * 8);
+				p += cur->len * 8;
+			}
+		}
+	}
+#ifndef CONFIG_FIRMWARE_ARRAY
+	kfree(rawdata);
+#endif
+	return head;
+
+err:
+	aicbt_patch_table_free(&head);
+	if (rawdata)
+		kfree(rawdata);
+	return NULL;
+}
+
+int aicbt_patch_info_unpack(struct aicbt_patch_info_t *patch_info, struct aicbt_patch_table *head_t)
+{
+    if (AICBT_PT_INF == head_t->type) {
+        patch_info->info_len = head_t->len;
+        if(patch_info->info_len == 0)
+            return 0;
+        memcpy(&patch_info->adid_addrinf, head_t->data, patch_info->info_len * sizeof(uint32_t) * 2);
+		printk("aicbt_patch_info_unpack memcpy \n");
+    }
+    return 0;
+}
+
+int aicbt_patch_trap_data_load(struct aic_sdio_dev *sdiodev, struct aicbt_patch_table *head)
+{
+	struct aicbt_patch_info_t patch_info = {
+		.info_len          = 0,
+		.adid_addrinf      = 0,
+		.addr_adid         = 0,
+		.patch_addrinf     = 0,
+		.addr_patch        = 0,
+		.reset_addr        = 0,
+        .reset_val         = 0,
+        .adid_flag_addr    = 0,
+        .adid_flag         = 0,
+	};
+    if(head == NULL){
+        return -1;
+    }
+
+	/*if(sdiodev->chipid == PRODUCT_ID_AIC8801){
+		patch_info.addr_adid  = FW_RAM_ADID_BASE_ADDR;
+		patch_info.addr_patch = FW_RAM_PATCH_BASE_ADDR;
+	}
+	else if(sdiodev->chipid == PRODUCT_ID_AIC8800DC){*/
+		if(aicbsp_info.chip_rev == CHIP_REV_U01){
+			patch_info.addr_adid = RAM_8800DC_U01_ADID_ADDR;
+		}else if(aicbsp_info.chip_rev == CHIP_REV_U02){
+			patch_info.addr_adid = RAM_8800DC_U02_ADID_ADDR;
+		}
+		patch_info.addr_patch = RAM_8800DC_FW_PATCH_ADDR;
+        aicbt_patch_info_unpack(&patch_info, head);
+        if(patch_info.reset_addr == 0) {
+            patch_info.reset_addr        = FW_RESET_START_ADDR;
+            patch_info.reset_val         = FW_RESET_START_VAL;
+            patch_info.adid_flag_addr    = FW_ADID_FLAG_ADDR;
+            patch_info.adid_flag         = FW_ADID_FLAG_VAL;
+            if (rwnx_send_dbg_mem_write_req(sdiodev->rwnx_hw, patch_info.reset_addr, patch_info.reset_val))
+                return -1;
+            if (rwnx_send_dbg_mem_write_req(sdiodev->rwnx_hw, patch_info.adid_flag_addr, patch_info.adid_flag))
+                return -1;
+        }
+	/*} else if(sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+        if (aicbsp_info.chip_rev == CHIP_REV_U01) {
+		    patch_info.addr_adid = FW_RAM_ADID_BASE_ADDR_8800D80;
+		    patch_info.addr_patch = FW_RAM_PATCH_BASE_ADDR_8800D80;
+        } else if (aicbsp_info.chip_rev == CHIP_REV_U02 || aicbsp_info.chip_rev == CHIP_REV_U03) {
+            patch_info.addr_adid = FW_RAM_ADID_BASE_ADDR_8800D80_U02;
+		    patch_info.addr_patch = FW_RAM_PATCH_BASE_ADDR_8800D80_U02;
+        }
+        aicbt_patch_info_unpack(&patch_info, head);
+        if(patch_info.info_len == 0) {
+            printk("%s, aicbt_patch_info_unpack fail\n", __func__);
+            return -1;
+        }
+	}*/
+
+	printk("addr_adid %x addr_patch %x \n",patch_info.addr_adid, patch_info.addr_patch);
+
+	if (rwnx_plat_bin_fw_upload_2(sdiodev->rwnx_hw, patch_info.addr_adid, aicbsp_firmware_list[aicbsp_info.cpmode].bt_adid))
+		return -1;
+	if (rwnx_plat_bin_fw_upload_2(sdiodev->rwnx_hw, patch_info.addr_patch, aicbsp_firmware_list[aicbsp_info.cpmode].bt_patch))
+		return -1;
+	return 0;
+
+}
+
+int aicbt_patch_table_load(struct aic_sdio_dev *sdiodev, struct aicbt_patch_table *head)
+{
+	struct aicbt_patch_table *p;
+	int ret = 0, i;
+	uint32_t *data = NULL;
+    if(head == NULL){
+        return -1;
+    }
+
+	printk("aicbt_patch_table_load begin \n");
+
+    for (p = head; p != NULL; p = p->next) {
+    	data = p->data;
+    	if (AICBT_PT_BTMODE == p->type) {
+    		*(data + 1)  = aicbsp_info.hwinfo < 0;
+    		*(data + 3)  = aicbsp_info.hwinfo;
+    		*(data + 5)  = (sdiodev->chipid == PRODUCT_ID_AIC8800DC?aicbsp_info.cpmode:0);//0;//aicbsp_info.cpmode;
+
+    		*(data + 7)  = aicbt_info[sdiodev->chipid].btmode;
+    		*(data + 9)  = aicbt_info[sdiodev->chipid].btport;
+    		*(data + 11) = aicbt_info[sdiodev->chipid].uart_baud;
+    		*(data + 13) = aicbt_info[sdiodev->chipid].uart_flowctrl;
+    		*(data + 15) = aicbt_info[sdiodev->chipid].lpm_enable;
+    		*(data + 17) = aicbt_info[sdiodev->chipid].txpwr_lvl;
+
+            printk("%s bt btmode[%d]:%d \r\n", __func__, sdiodev->chipid, aicbt_info[sdiodev->chipid].btmode);
+    		printk("%s bt uart_baud[%d]:%d \r\n", __func__, sdiodev->chipid, aicbt_info[sdiodev->chipid].uart_baud);
+    		printk("%s bt uart_flowctrl[%d]:%d \r\n", __func__, sdiodev->chipid, aicbt_info[sdiodev->chipid].uart_flowctrl);
+    		printk("%s bt lpm_enable[%d]:%d \r\n", __func__, sdiodev->chipid, aicbt_info[sdiodev->chipid].lpm_enable);
+    		printk("%s bt tx_pwr[%d]:%d \r\n", __func__, sdiodev->chipid, aicbt_info[sdiodev->chipid].txpwr_lvl);
+    	}
+
+    	if (AICBT_PT_VER == p->type) {
+    		printk("aicbsp: bt patch version: %s\n", (char *)p->data);
+    		continue;
+    	}
+
+    	for (i = 0; i < p->len; i++) {
+    		ret = rwnx_send_dbg_mem_write_req(sdiodev->rwnx_hw, *data, *(data + 1));
+    		if (ret != 0)
+    			return ret;
+    		data += 2;
+    	}
+    	if (p->type == AICBT_PT_PWRON)
+    		udelay(500);
+    }
+
+	printk("aicbt_patch_table_load end \n");
+	///aicbt_patch_table_free(&head);
+	return 0;
+}
+
+int aicbt_init(struct aic_sdio_dev *sdiodev)
+{
+    int ret = 0;
+    struct aicbt_patch_table *head = aicbt_patch_table_alloc(aicbsp_firmware_list[aicbsp_info.cpmode].bt_table);
+	if (head == NULL){
+        printk("aicbt_patch_table_alloc fail\n");
+        return -1;
+    }
+
+    if (aicbt_patch_trap_data_load(sdiodev, head)) {
+		printk("aicbt_patch_trap_data_load fail\n");
+        ret = -1;
+		goto err;
+	}
+
+	if (aicbt_patch_table_load(sdiodev, head)) {
+		 printk("aicbt_patch_table_load fail\n");
+        ret = -1;
+		goto err;
+	}
+
+err:
+	aicbt_patch_table_free(&head);
+	return ret;
+}
+
+#endif
+
+
+/**
+ * rwnx_plat_fmac_load() - Load FW code
+ *
+ * @rwnx_hw: Main driver data
+ */
+static int rwnx_plat_fmac_load(struct rwnx_hw *rwnx_hw)
+{
+    int ret;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+    #if defined(CONFIG_NANOPI_M4) || defined(CONFIG_PLATFORM_ALLWINNER)
+    if (testmode) {
+        ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, RAM_FMAC_FW_ADDR, RWNX_MAC_FW_RF_BASE_NAME);
+    } else {
+        ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, RAM_FMAC_FW_ADDR, RWNX_MAC_FW_NAME2);
+    }
+    #else
+    if (testmode) {
+        ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, RAM_FMAC_FW_ADDR, RWNX_MAC_FW_RF_BASE_NAME);
+    } else {
+        ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, RAM_FMAC_FW_ADDR, RWNX_MAC_FW_NAME2);
+    }
+    #endif
+    return ret;
+}
+
+int aicwf_plat_patch_load_8800dc(struct rwnx_hw *rwnx_hw)
+{
+    int ret = 0;
+#if !defined(CONFIG_FPGA_VERIFICATION)
+    if (chip_sub_id == 0) {
+        ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, ROM_FMAC_PATCH_ADDR, RWNX_MAC_PATCH_NAME2);
+    } else if (chip_sub_id == 1) {
+        ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, ROM_FMAC_PATCH_ADDR, RWNX_MAC_PATCH_NAME2_U02);
+    } else if (chip_sub_id == 2) {
+        ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, ROM_FMAC_PATCH_ADDR, RWNX_MAC_PATCH_NAME2_8800DC_H_U02);
+    } else {
+        printk("%s: unsupported id: %d\n", __func__, chip_sub_id);
+    }
+#endif
+    return ret;
+}
+
+/**
+ * rwnx_plat_patch_load() - Load patch code
+ *
+ * @rwnx_hw: Main driver data
+ */
+#ifdef CONFIG_DPD
+extern int aicwf_dpd_calib_8800dc(struct rwnx_hw *rwnx_hw,  rf_misc_ram_lite_t *dpd_res);
+extern int aicwf_dpd_result_load_8800dc(struct rwnx_hw *rwnx_hw, rf_misc_ram_lite_t *dpd_res);
+extern int aicwf_dpd_result_apply_8800dc(struct rwnx_hw *rwnx_hw, rf_misc_ram_lite_t *dpd_res);
+#endif
+static int rwnx_plat_patch_load(struct rwnx_hw *rwnx_hw)
+{
+    int ret = 0;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+    if (testmode == 0) {
+        #if !defined(CONFIG_FPGA_VERIFICATION)
+        if (chip_sub_id == 0) {
+            aicwf_plat_patch_load_8800dc(rwnx_hw);
+        } else if (chip_sub_id >= 1) {
+            aicwf_plat_patch_load_8800dc(rwnx_hw);
+			if (ret) {
+				printk("patch load fail: %d\n", ret);
+				return ret;
+			}
+#ifdef CONFIG_DPD
+#ifdef CONFIG_FORCE_DPD_CALIB
+		if (1) {
+			printk("dpd calib & write\n");
+			ret = aicwf_dpd_calib_8800dc(rwnx_hw, &dpd_res);
+			if (ret) {
+				printk("dpd calib fail: %d\n", ret);
+				return ret;
+			}
+		}
+#else
+		if (is_file_exist(FW_DPDRESULT_NAME_8800DC) == 1) {
+			printk("dpd bin load\n");
+			ret = aicwf_dpd_result_load_8800dc(rwnx_hw, &dpd_res);;
+			if (ret) {
+				printk("load dpd bin fail: %d\n", ret);
+				return ret;
+			}
+			ret = aicwf_dpd_result_apply_8800dc(rwnx_hw, &dpd_res);
+			if (ret) {
+				printk("apply dpd bin fail: %d\n", ret);
+				return ret;
+			}
+
+		}
+#endif
+		else
+#endif
+		{
+			ret = aicwf_misc_ram_init_8800dc(rwnx_hw);
+			if (ret) {
+				printk("misc ram init fail: %d\n", ret);
+				return ret;
+			}
+		}
+        } else {
+            printk("unsupported id: %d\n", chip_sub_id);
+        }
+        #endif
+    } else if (testmode == 1) {
+    	printk("patch load\n");
+		aicwf_plat_patch_load_8800dc(rwnx_hw);
+		if (ret) {
+			printk("patch load fail: %d\n", ret);
+			return ret;
+		}
+#ifdef CONFIG_DPD
+#ifdef CONFIG_FORCE_DPD_CALIB
+			if (1) {
+				printk("dpd calib & write\n");
+				ret = aicwf_dpd_calib_8800dc(rwnx_hw, &dpd_res);
+				if (ret) {
+					printk("dpd calib fail: %d\n", ret);
+					return ret;
+				}
+			}
+#endif
+#endif
+		printk("%s load rftest bin\n", __func__);
+        if (chip_sub_id == 0) {
+            ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, ROM_FMAC_PATCH_ADDR_U01, RWNX_MAC_RF_PATCH_NAME);
+        }
+        if (!ret) {
+			ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, RAM_LMAC_FW_ADDR, RWNX_MAC_FW_RF_BASE_NAME);
+        	}
+		if (ret) {
+			printk("rftest bin load fail: %d\n", ret);
+			return ret;
+		}
+		/* Note: apply dpd_res after rftest running */
+        }
+	else if (testmode == 4) {
+                #if (defined(CONFIG_DPD) && !defined(CONFIG_FORCE_DPD_CALIB))
+					if (is_file_exist(FW_DPDRESULT_NAME_8800DC) == 0) {
+						printk("patch load\n");
+						aicwf_plat_patch_load_8800dc(rwnx_hw);
+						if (ret) {
+							printk("load patch bin fail: %d\n", ret);
+							return ret;
+						}
+						printk("dpd calib & write\n");
+						ret = aicwf_dpd_calib_8800dc(rwnx_hw, &dpd_res);
+						if (ret) {
+							printk("dpd calib fail: %d\n", ret);
+							return ret;
+						}
+						ret = aicwf_dpd_result_write_8800dc((void *)dpd_res, DPD_RESULT_SIZE_8800DC);
+						if (ret) {
+							printk("file write fail: %d\n", ret);
+							return ret;
+						}
+					}
+                #endif
+					return 1; // exit calib mode
+				}
+
+    return ret;
+}
+
+static int rwnx_plat_patch_load_d80(struct rwnx_hw *rwnx_hw){
+	int ret = 0;
+	
+	if (rwnx_plat_bin_fw_upload_2(rwnx_hw, RAM_FMAC_FW_ADDR, aicbsp_firmware_list[aicbsp_info.cpmode].wl_fw)) {
+		printk("8800d80 download wifi fw fail\n");
+		return -1;
+	}
+
+	if (aicwifi_patch_config_8800d80(rwnx_hw)) {
+		printk("aicwifi_patch_config_8800d80 fail\n");
+		return -1;
+	}
+
+	if (aicwifi_sys_config_8800d80(rwnx_hw)) {
+		printk("aicwifi_patch_config_8800d80 fail\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_DPD
+#define RAM_LMAC_FW_ADDR               0x00150000
+int aicwf_plat_calib_load_8800dc(struct rwnx_hw *rwnx_hw)
+{
+    int ret = 0;
+    if (chip_sub_id == 1) {
+        ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, ROM_FMAC_CALIB_ADDR, RWNX_MAC_CALIB_NAME_8800DC_U02);
+        if (ret) {
+            printk("load rftest bin fail: %d\n", ret);
+            return ret;
+        }
+    } else if (chip_sub_id == 2) {
+        ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, ROM_FMAC_CALIB_ADDR, RWNX_MAC_CALIB_NAME_8800DC_H_U02);
+        if (ret) {
+            printk("load calib bin fail: %d\n", ret);
+            return ret;
+        }
+    }
+    return ret;
+}
+
+int is_file_exist(char* name)
+{
+    char *path = NULL;
+    struct file *fp = NULL;
+    int len;
+
+    path = __getname();
+    if (!path) {
+        printk("%s getname fail\n", __func__);
+        return -1;
+    }
+
+    len = snprintf(path, FW_PATH_MAX_LEN, "%s/%s", VENDOR_SPECIFIED_DPD_PATH, name);
+
+    fp = filp_open(path, O_RDONLY, 0);
+    if (IS_ERR(fp)) {
+        __putname(path);
+        fp = NULL;
+        return 0;
+    } else {
+        __putname(path);
+        filp_close(fp, NULL);
+		fp = NULL;
+        return 1;
+    }
+}
+
+#endif
+
+int aicwf_misc_ram_init_8800dc(struct rwnx_hw *rwnx_hw)
+{
+    int ret = 0;
+    uint32_t cfg_base = 0x10164;
+    struct dbg_mem_read_cfm cfm;
+    uint32_t misc_ram_addr;
+    uint32_t misc_ram_size = 12;
+    int i;
+    if (testmode == 1) {
+        cfg_base = RAM_LMAC_FW_ADDR + 0x0164;
+    }
+    // init misc ram
+    ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0x14, &cfm);
+    if (ret) {
+        printk("rf misc ram[0x%x] rd fail: %d\n", cfg_base + 0x14, ret);
+        return ret;
+    }
+    misc_ram_addr = cfm.memdata;
+    printk("misc_ram_addr=%x\n", misc_ram_addr);
+    for (i = 0; i < (misc_ram_size / 4); i++) {
+        ret = rwnx_send_dbg_mem_write_req(rwnx_hw, misc_ram_addr + i * 4, 0);
+        if (ret) {
+            printk("rf misc ram[0x%x] wr fail: %d\n",  misc_ram_addr + i * 4, ret);
+            return ret;
+        }
+    }
+    return ret;
+}
+
+#ifdef CONFIG_DPD
+int aicwf_dpd_calib_8800dc(struct rwnx_hw *rwnx_hw, rf_misc_ram_lite_t *dpd_res)
+{
+    int ret = 0;
+    uint32_t fw_addr, boot_type;
+
+	printk("%s\n", __func__);
+
+    ret = aicwf_plat_calib_load_8800dc(rwnx_hw);
+    if (ret) {
+        printk("load calib bin fail: %d\n", ret);
+        return ret;
+    }
+    /* fw start */
+    fw_addr = 0x00130009;
+    boot_type = 4;//HOST_START_APP_FNCALL;
+    printk("Start app: %08x, %d\n", fw_addr, boot_type);
+    ret = rwnx_send_dbg_start_app_req(rwnx_hw, fw_addr, boot_type);
+    if (ret) {
+        printk("start app fail: %d\n", ret);
+        return ret;
+    }
+    { // read dpd res
+        const uint32_t cfg_base = 0x10164;
+        struct dbg_mem_read_cfm cfm;
+        uint32_t misc_ram_addr;
+        uint32_t ram_base_addr, ram_word_cnt;
+        int i;
+        ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0x14, &cfm);
+        if (ret) {
+            printk("rf misc ram[0x%x] rd fail: %d\n", cfg_base + 0x14, ret);
+            return ret;
+        }
+        misc_ram_addr = cfm.memdata;
+        // bit_mask
+        ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, bit_mask);
+        ram_word_cnt = (MEMBER_SIZE(rf_misc_ram_t, bit_mask) + MEMBER_SIZE(rf_misc_ram_t, reserved)) / 4;
+        for (i = 0; i < ram_word_cnt; i++) {
+            ret = rwnx_send_dbg_mem_read_req(rwnx_hw, ram_base_addr + i * 4, &cfm);
+            if (ret) {
+                printk("bit_mask[0x%x] rd fail: %d\n",  ram_base_addr + i * 4, ret);
+                return ret;
+            }
+            dpd_res->bit_mask[i] = cfm.memdata;
+        }
+        // dpd_high
+        ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, dpd_high);
+        ram_word_cnt = MEMBER_SIZE(rf_misc_ram_t, dpd_high) / 4;
+        for (i = 0; i < ram_word_cnt; i++) {
+            ret = rwnx_send_dbg_mem_read_req(rwnx_hw, ram_base_addr + i * 4, &cfm);
+            if (ret) {
+                printk("bit_mask[0x%x] rd fail: %d\n",  ram_base_addr + i * 4, ret);
+                return ret;
+            }
+            dpd_res->dpd_high[i] = cfm.memdata;
+        }
+        // loft_res
+        ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, loft_res);
+        ram_word_cnt = MEMBER_SIZE(rf_misc_ram_t, loft_res) / 4;
+        for (i = 0; i < ram_word_cnt; i++) {
+            ret = rwnx_send_dbg_mem_read_req(rwnx_hw, ram_base_addr + i * 4, &cfm);
+            if (ret) {
+                printk("bit_mask[0x%x] rd fail: %d\n",  ram_base_addr + i * 4, ret);
+                return ret;
+            }
+            dpd_res->loft_res[i] = cfm.memdata;
+        }
+    }
+    return ret;
+}
+
+int aicwf_dpd_result_apply_8800dc(struct rwnx_hw *rwnx_hw, rf_misc_ram_lite_t *dpd_res)
+{
+    int ret = 0;
+    uint32_t cfg_base = 0x10164;
+    struct dbg_mem_read_cfm cfm;
+    uint32_t misc_ram_addr;
+    uint32_t ram_base_addr, ram_byte_cnt;
+    printk("bit_mask[1]=%x\n", dpd_res->bit_mask[1]);
+    if (dpd_res->bit_mask[1] == 0) {
+        printk("void dpd_res, bypass it.\n");
+        return 0;
+    }
+    if (testmode == 1) {
+        cfg_base = RAM_LMAC_FW_ADDR + 0x0164;
+    }
+    if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0x14, &cfm))) {
+        printk("rf misc ram[0x%x] rd fail: %d\n", cfg_base + 0x14, ret);
+        return ret;
+    }
+    misc_ram_addr = cfm.memdata;
+    printk("misc_ram_addr: %x\n", misc_ram_addr);
+    /* Copy dpd_res on the Embedded side */
+    // bit_mask
+    printk("bit_mask[0]=%x\n", dpd_res->bit_mask[0]);
+    ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, bit_mask);
+    ram_byte_cnt = MEMBER_SIZE(rf_misc_ram_t, bit_mask) + MEMBER_SIZE(rf_misc_ram_t, reserved);
+    ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, ram_base_addr, ram_byte_cnt, (u32 *)&dpd_res->bit_mask[0]);
+    if (ret) {
+        printk("bit_mask wr fail: %x, ret:%d\r\n", ram_base_addr, ret);
+        return ret;
+    }
+    // dpd_high
+    printk("dpd_high[0]=%x\n", dpd_res->dpd_high[0]);
+    ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, dpd_high);
+    ram_byte_cnt = MEMBER_SIZE(rf_misc_ram_t, dpd_high);
+    ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, ram_base_addr, ram_byte_cnt, (u32 *)&dpd_res->dpd_high[0]);
+    if (ret) {
+        printk("dpd_high wr fail: %x, ret:%d\r\n", ram_base_addr, ret);
+        return ret;
+    }
+    // loft_res
+    printk("loft_res[0]=%x\n", dpd_res->loft_res[0]);
+    ram_base_addr = misc_ram_addr + offsetof(rf_misc_ram_t, loft_res);
+    ram_byte_cnt = MEMBER_SIZE(rf_misc_ram_t, loft_res);
+    ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, ram_base_addr, ram_byte_cnt, (u32 *)&dpd_res->loft_res[0]);
+    if (ret) {
+        printk("loft_res wr fail: %x, ret:%d\r\n", ram_base_addr, ret);
+        return ret;
+    }
+    return ret;
+}
+
+#ifndef CONFIG_FORCE_DPD_CALIB
+int aicwf_dpd_result_load_8800dc(struct rwnx_hw *rwnx_hw, rf_misc_ram_lite_t *dpd_res)
+{
+    int ret = 0;
+    int size;
+    u32 *dst=NULL;
+    char *filename = FW_DPDRESULT_NAME_8800DC;
+    printk("dpd_res file path:%s \r\n", filename);
+    /* load file */
+    size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
+    if (size <= 0) {
+        printk("wrong size of dpd_res file\n");
+        dst = NULL;
+        return -1;
+    }
+    printk("### Load file done: %s, size=%d, dst[0]=%x\n", filename, size, dst[0]);
+    memcpy((u8 *)dpd_res, (u8 *)dst, sizeof(rf_misc_ram_lite_t));
+    if (dst) {
+        rwnx_release_firmware_common(&dst);
+    }
+    return ret;
+}
+
+
+int aicwf_dpd_result_write_8800dc(void *buf, int buf_len)
+{
+	printk("%s\n", __func__);
+    int sum = 0, len = 0;
+    char *path = NULL;
+    struct file *fp = NULL;
+    loff_t pos = 0;
+    mm_segment_t fs;
+
+    path = __getname();
+    if (!path) {
+        printk("get path fail\n");
+        return -1;
+    }
+
+    len = snprintf(path, FW_PATH_MAX_LEN, "%s/%s", VENDOR_SPECIFIED_DPD_PATH, FW_DPDRESULT_NAME_8800DC);
+    printk("%s\n", path);
+
+    fp = filp_open(path, O_RDWR | O_CREAT, 0644);
+    if (IS_ERR(fp)) {
+        printk("fp open fial\n");
+		__putname(path);
+        fp = NULL;
+        return -1;
+    }
+
+    fs = get_fs();
+    set_fs(KERNEL_DS);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
+    sum = kernel_write(fp, buf, buf_len, &pos);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+    sum = kernel_write(fp, (char *)buf, buf_len, pos);
+#else
+    sum = vfs_write(fp, (char *)buf, buf_len, &pos);
+#endif
+
+    set_fs(fs);
+    __putname(path);
+    filp_close(fp, NULL);
+	fp = NULL;
+
+    return 0;
+}
+#endif /* !CONFIG_FORCE_DPD_CALIB */
+#endif
+
+#if 0
+/**
+ * rwnx_plat_mpif_sel() - Select the MPIF according to the FPGA signature
+ *
+ * @rwnx_plat: platform data
+ */
+static void rwnx_plat_mpif_sel(struct rwnx_plat *rwnx_plat)
+{
+#ifndef CONFIG_RWNX_SDM
+    u32 regval;
+    u32 type;
+
+    /* Get the FPGA signature */
+    regval = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+    type = __FPGA_TYPE(regval);
+
+    /* Check if we need to switch to the old MPIF or not */
+    if ((type != 0xCAFE) && (type != 0XC0CA) && (regval & 0xF) < 0x3)
+    {
+        /* A old FPGA A is used, so configure the FPGA B to use the old MPIF */
+        RWNX_REG_WRITE(0x3, rwnx_plat, RWNX_ADDR_SYSTEM, FPGAB_MPIF_SEL_ADDR);
+    }
+#endif
+}
+#endif
+
+
+/**
+ * rwnx_platform_reset() - Reset the platform
+ *
+ * @rwnx_plat: platform data
+ */
+static int rwnx_platform_reset(struct rwnx_plat *rwnx_plat)
+{
+    u32 regval;
+
+#if defined(AICWF_USB_SUPPORT) || defined(AICWF_SDIO_SUPPORT)
+    return 0;
+#endif
+
+    /* the doc states that SOFT implies FPGA_B_RESET
+     * adding FPGA_B_RESET is clearer */
+    RWNX_REG_WRITE(SOFT_RESET | FPGA_B_RESET, rwnx_plat,
+                   RWNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+    msleep(100);
+
+    regval = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+
+    if (regval & SOFT_RESET) {
+        dev_err(rwnx_platform_get_dev(rwnx_plat), "reset: failed\n");
+        return -EIO;
+    }
+
+    RWNX_REG_WRITE(regval & ~FPGA_B_RESET, rwnx_plat,
+                   RWNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+    msleep(100);
+    return 0;
+}
+
+/**
+ * rwmx_platform_save_config() - Save hardware config before reload
+ *
+ * @rwnx_plat: Pointer to platform data
+ *
+ * Return configuration registers values.
+ */
+static void* rwnx_term_save_config(struct rwnx_plat *rwnx_plat)
+{
+    const u32 *reg_list;
+    u32 *reg_value, *res;
+    int i, size = 0;
+
+    if (rwnx_plat->get_config_reg) {
+        size = rwnx_plat->get_config_reg(rwnx_plat, &reg_list);
+    }
+
+    if (size <= 0)
+        return NULL;
+
+    res = kmalloc(sizeof(u32) * size, GFP_KERNEL);
+    if (!res)
+        return NULL;
+
+    reg_value = res;
+    for (i = 0; i < size; i++) {
+        *reg_value++ = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM,
+                                     *reg_list++);
+    }
+
+    return res;
+}
+
+#if 0
+/**
+ * rwmx_platform_restore_config() - Restore hardware config after reload
+ *
+ * @rwnx_plat: Pointer to platform data
+ * @reg_value: Pointer of value to restore
+ * (obtained with rwmx_platform_save_config())
+ *
+ * Restore configuration registers value.
+ */
+static void rwnx_term_restore_config(struct rwnx_plat *rwnx_plat,
+                                     u32 *reg_value)
+{
+    const u32 *reg_list;
+    int i, size = 0;
+
+    if (!reg_value || !rwnx_plat->get_config_reg)
+        return;
+
+    size = rwnx_plat->get_config_reg(rwnx_plat, &reg_list);
+
+    for (i = 0; i < size; i++) {
+        RWNX_REG_WRITE(*reg_value++, rwnx_plat, RWNX_ADDR_SYSTEM,
+                       *reg_list++);
+    }
+}
+#endif
+
+#ifndef CONFIG_RWNX_FHOST
+#if 0
+static int rwnx_check_fw_compatibility(struct rwnx_hw *rwnx_hw)
+{
+    struct ipc_shared_env_tag *shared = rwnx_hw->ipc_env->shared;
+    #ifdef CONFIG_RWNX_FULLMAC
+    struct wiphy *wiphy = rwnx_hw->wiphy;
+    #endif //CONFIG_RWNX_FULLMAC
+    #ifdef CONFIG_RWNX_OLD_IPC
+    int ipc_shared_version = 10;
+    #else //CONFIG_RWNX_OLD_IPC
+    int ipc_shared_version = 11;
+    #endif //CONFIG_RWNX_OLD_IPC
+    int res = 0;
+
+    if(shared->comp_info.ipc_shared_version != ipc_shared_version)
+    {
+        wiphy_err(wiphy, "Different versions of IPC shared version between driver and FW (%d != %d)\n ",
+                  ipc_shared_version, shared->comp_info.ipc_shared_version);
+        res = -1;
+    }
+
+    if(shared->comp_info.radarbuf_cnt != IPC_RADARBUF_CNT)
+    {
+        wiphy_err(wiphy, "Different number of host buffers available for Radar events handling "\
+                  "between driver and FW (%d != %d)\n", IPC_RADARBUF_CNT,
+                  shared->comp_info.radarbuf_cnt);
+        res = -1;
+    }
+
+    if(shared->comp_info.unsuprxvecbuf_cnt != IPC_UNSUPRXVECBUF_CNT)
+    {
+        wiphy_err(wiphy, "Different number of host buffers available for unsupported Rx vectors "\
+                  "handling between driver and FW (%d != %d)\n", IPC_UNSUPRXVECBUF_CNT,
+                  shared->comp_info.unsuprxvecbuf_cnt);
+        res = -1;
+    }
+
+    #ifdef CONFIG_RWNX_FULLMAC
+    if(shared->comp_info.rxdesc_cnt != IPC_RXDESC_CNT)
+    {
+        wiphy_err(wiphy, "Different number of shared descriptors available for Data RX handling "\
+                  "between driver and FW (%d != %d)\n", IPC_RXDESC_CNT,
+                  shared->comp_info.rxdesc_cnt);
+        res = -1;
+    }
+    #endif /* CONFIG_RWNX_FULLMAC */
+
+    if(shared->comp_info.rxbuf_cnt != IPC_RXBUF_CNT)
+    {
+        wiphy_err(wiphy, "Different number of host buffers available for Data Rx handling "\
+                  "between driver and FW (%d != %d)\n", IPC_RXBUF_CNT,
+                  shared->comp_info.rxbuf_cnt);
+        res = -1;
+    }
+
+    if(shared->comp_info.msge2a_buf_cnt != IPC_MSGE2A_BUF_CNT)
+    {
+        wiphy_err(wiphy, "Different number of host buffers available for Emb->App MSGs "\
+                  "sending between driver and FW (%d != %d)\n", IPC_MSGE2A_BUF_CNT,
+                  shared->comp_info.msge2a_buf_cnt);
+        res = -1;
+    }
+
+    if(shared->comp_info.dbgbuf_cnt != IPC_DBGBUF_CNT)
+    {
+        wiphy_err(wiphy, "Different number of host buffers available for debug messages "\
+                  "sending between driver and FW (%d != %d)\n", IPC_DBGBUF_CNT,
+                  shared->comp_info.dbgbuf_cnt);
+        res = -1;
+    }
+
+    if(shared->comp_info.bk_txq != NX_TXDESC_CNT0)
+    {
+        wiphy_err(wiphy, "Driver and FW have different sizes of BK TX queue (%d != %d)\n",
+                  NX_TXDESC_CNT0, shared->comp_info.bk_txq);
+        res = -1;
+    }
+
+    if(shared->comp_info.be_txq != NX_TXDESC_CNT1)
+    {
+        wiphy_err(wiphy, "Driver and FW have different sizes of BE TX queue (%d != %d)\n",
+                  NX_TXDESC_CNT1, shared->comp_info.be_txq);
+        res = -1;
+    }
+
+    if(shared->comp_info.vi_txq != NX_TXDESC_CNT2)
+    {
+        wiphy_err(wiphy, "Driver and FW have different sizes of VI TX queue (%d != %d)\n",
+                  NX_TXDESC_CNT2, shared->comp_info.vi_txq);
+        res = -1;
+    }
+
+    if(shared->comp_info.vo_txq != NX_TXDESC_CNT3)
+    {
+        wiphy_err(wiphy, "Driver and FW have different sizes of VO TX queue (%d != %d)\n",
+                  NX_TXDESC_CNT3, shared->comp_info.vo_txq);
+        res = -1;
+    }
+
+    #if NX_TXQ_CNT == 5
+    if(shared->comp_info.bcn_txq != NX_TXDESC_CNT4)
+    {
+        wiphy_err(wiphy, "Driver and FW have different sizes of BCN TX queue (%d != %d)\n",
+                NX_TXDESC_CNT4, shared->comp_info.bcn_txq);
+        res = -1;
+    }
+    #else
+    if (shared->comp_info.bcn_txq > 0)
+    {
+        wiphy_err(wiphy, "BCMC enabled in firmware but disabled in driver\n");
+        res = -1;
+    }
+    #endif /* NX_TXQ_CNT == 5 */
+
+    if(shared->comp_info.ipc_shared_size != sizeof(ipc_shared_env))
+    {
+        wiphy_err(wiphy, "Different sizes of IPC shared between driver and FW (%zd != %d)\n",
+                  sizeof(ipc_shared_env), shared->comp_info.ipc_shared_size);
+        res = -1;
+    }
+
+    if(shared->comp_info.msg_api != MSG_API_VER)
+    {
+        wiphy_warn(wiphy, "WARNING: Different supported message API versions between "\
+                   "driver and FW (%d != %d)\n", MSG_API_VER, shared->comp_info.msg_api);
+    }
+
+    return res;
+}
+#endif
+#endif /* !CONFIG_RWNX_FHOST */
+
+/**
+ * rwnx_platform_on() - Start the platform
+ *
+ * @rwnx_hw: Main driver data
+ * @config: Config to restore (NULL if nothing to restore)
+ *
+ * It starts the platform :
+ * - load fw and ucodes
+ * - initialize IPC
+ * - boot the fw
+ * - enable link communication/IRQ
+ *
+ * Called by 802.11 part
+ */
+int rwnx_platform_on(struct rwnx_hw *rwnx_hw, void *config)
+{
+    #if 0
+    u8 *shared_ram;
+    #endif
+    int ret;
+    struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (rwnx_plat->enabled)
+        return 0;
+
+
+	#ifdef CONFIG_BT_SUPPORT
+		ret = aicbt_init(rwnx_hw->sdiodev);
+	#endif
+
+    #if 0
+    if (rwnx_platform_reset(rwnx_plat))
+        return -1;
+
+    rwnx_plat_mpif_sel(rwnx_plat);
+
+    if ((ret = rwnx_plat_fcu_load(rwnx_hw)))
+        return ret;
+    if ((ret = rwnx_plat_agc_load(rwnx_plat)))
+        return ret;
+    if ((ret = rwnx_ldpc_load(rwnx_hw)))
+        return ret;
+    if ((ret = rwnx_plat_lmac_load(rwnx_plat)))
+        return ret;
+
+    shared_ram = RWNX_ADDR(rwnx_plat, RWNX_ADDR_SYSTEM, SHARED_RAM_START_ADDR);
+    if ((ret = rwnx_ipc_init(rwnx_hw, shared_ram)))
+        return ret;
+
+    if ((ret = rwnx_plat->enable(rwnx_hw)))
+        return ret;
+    RWNX_REG_WRITE(BOOTROM_ENABLE, rwnx_plat,
+                   RWNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+
+	#if 0
+    if ((ret = rwnx_fw_trace_config_filters(rwnx_get_shared_trace_buf(rwnx_hw),
+                                            rwnx_ipc_fw_trace_desc_get(rwnx_hw),
+                                            rwnx_hw->mod_params->ftl)))
+	#endif
+
+    #ifndef CONFIG_RWNX_FHOST
+    if ((ret = rwnx_check_fw_compatibility(rwnx_hw)))
+    {
+        rwnx_hw->plat->disable(rwnx_hw);
+        tasklet_kill(&rwnx_hw->task);
+        rwnx_ipc_deinit(rwnx_hw);
+        return ret;
+    }
+    #endif /* !CONFIG_RWNX_FHOST */
+
+    if (config)
+        rwnx_term_restore_config(rwnx_plat, config);
+
+    rwnx_ipc_start(rwnx_hw);
+    #else
+    #ifndef CONFIG_ROM_PATCH_EN
+    #ifdef CONFIG_DOWNLOAD_FW
+    if ((ret = rwnx_plat_fmac_load(rwnx_hw)))
+        return ret;
+    #endif /* !CONFIG_ROM_PATCH_EN */
+    #endif
+    #endif
+    #ifdef CONFIG_ROM_PATCH_EN
+	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
+	    ret = rwnx_plat_patch_load(rwnx_hw);
+	    if (ret) {
+			printk("DCDW patch load return %d\n", ret);
+	        return ret;
+	    }
+	}else if(rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+		ret = rwnx_plat_patch_load_d80(rwnx_hw);
+		if (ret) {
+			printk("D80 patch load return %d\n", ret);
+	        return ret;
+	    }
+	}
+    #endif
+
+    #ifdef CONFIG_LOAD_USERCONFIG
+	if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
+    	rwnx_plat_userconfig_load(rwnx_hw);
+	}else if(rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
+		rwnx_plat_userconfig_load_8800d80(rwnx_hw);
+	}
+    #endif
+
+    rwnx_plat->enabled = true;
+
+    return 0;
+}
+
+/**
+ * rwnx_platform_off() - Stop the platform
+ *
+ * @rwnx_hw: Main driver data
+ * @config: Updated with pointer to config, to be able to restore it with
+ * rwnx_platform_on(). It's up to the caller to free the config. Set to NULL
+ * if configuration is not needed.
+ *
+ * Called by 802.11 part
+ */
+void rwnx_platform_off(struct rwnx_hw *rwnx_hw, void **config)
+{
+#if defined(AICWF_USB_SUPPORT) || defined(AICWF_SDIO_SUPPORT)
+	tasklet_kill(&rwnx_hw->task);
+	rwnx_hw->plat->enabled = false;
+	return ;
+#endif
+
+    if (!rwnx_hw->plat->enabled) {
+        if (config)
+            *config = NULL;
+        return;
+    }
+
+	if (config)
+		*config = rwnx_term_save_config(rwnx_hw->plat);
+
+    rwnx_hw->plat->disable(rwnx_hw);
+
+	tasklet_kill(&rwnx_hw->task);
+	rwnx_platform_reset(rwnx_hw->plat);
+
+    rwnx_hw->plat->enabled = false;
+}
+
+/**
+ * rwnx_platform_init() - Initialize the platform
+ *
+ * @rwnx_plat: platform data (already updated by platform driver)
+ * @platform_data: Pointer to store the main driver data pointer (aka rwnx_hw)
+ *                That will be set as driver data for the platform driver
+ * Return: 0 on success, < 0 otherwise
+ *
+ * Called by the platform driver after it has been probed
+ */
+int rwnx_platform_init(struct rwnx_plat *rwnx_plat, void **platform_data)
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    rwnx_plat->enabled = false;
+    g_rwnx_plat = rwnx_plat;
+
+#if defined CONFIG_RWNX_FULLMAC
+    return rwnx_cfg80211_init(rwnx_plat, platform_data);
+#elif defined CONFIG_RWNX_FHOST
+    return rwnx_fhost_init(rwnx_plat, platform_data);
+#endif
+}
+
+/**
+ * rwnx_platform_deinit() - Deinitialize the platform
+ *
+ * @rwnx_hw: main driver data
+ *
+ * Called by the platform driver after it is removed
+ */
+void rwnx_platform_deinit(struct rwnx_hw *rwnx_hw)
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#if defined CONFIG_RWNX_FULLMAC
+    rwnx_cfg80211_deinit(rwnx_hw);
+#elif defined CONFIG_RWNX_FHOST
+    rwnx_fhost_deinit(rwnx_hw);
+#endif
+}
+
+/**
+ * rwnx_platform_register_drv() - Register all possible platform drivers
+ */
+int rwnx_platform_register_drv(void)
+{
+    return rwnx_pci_register_drv();
+}
+
+
+/**
+ * rwnx_platform_unregister_drv() - Unegister all platform drivers
+ */
+void rwnx_platform_unregister_drv(void)
+{
+    return rwnx_pci_unregister_drv();
+}
+
+struct device *rwnx_platform_get_dev(struct rwnx_plat *rwnx_plat)
+{
+#ifdef AICWF_SDIO_SUPPORT
+	return rwnx_plat->sdiodev->dev;
+#endif
+#ifdef AICWF_USB_SUPPORT
+    return rwnx_plat->usbdev->dev;
+#endif
+    return &(rwnx_plat->pci_dev->dev);
+}
+
+
+#ifndef CONFIG_RWNX_SDM
+MODULE_FIRMWARE(RWNX_AGC_FW_NAME);
+MODULE_FIRMWARE(RWNX_FCU_FW_NAME);
+MODULE_FIRMWARE(RWNX_LDPC_RAM_NAME);
+#endif
+MODULE_FIRMWARE(RWNX_MAC_FW_NAME);
+#ifndef CONFIG_RWNX_TL4
+MODULE_FIRMWARE(RWNX_MAC_FW_NAME2);
+#endif
+
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_platform.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_platform.h
new file mode 100755
index 0000000..a73b642
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_platform.h
@@ -0,0 +1,369 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_platorm.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_PLATFORM_H_
+#define _RWNX_PLATFORM_H_
+
+#include <linux/pci.h>
+#include "lmac_msg.h"
+
+#define AIC_MACCONFIG_NAME              "../../etc_rw/aic_macconfig.txt"
+#define RWNX_CONFIG_FW_NAME             "rwnx_settings.ini"
+#define RWNX_PHY_CONFIG_TRD_NAME        "rwnx_trident.ini"
+#define RWNX_PHY_CONFIG_KARST_NAME      "rwnx_karst.ini"
+#define RWNX_AGC_FW_NAME                "agcram.bin"
+#define RWNX_LDPC_RAM_NAME              "ldpcram.bin"
+#ifdef CONFIG_RWNX_FULLMAC
+#define RWNX_MAC_FW_BASE_NAME           "fmacfw"
+#define RWNX_MAC_FW_RF_BASE_NAME        "lmacfw_rf.bin"
+#elif defined CONFIG_RWNX_FHOST
+#define RWNX_MAC_FW_BASE_NAME           "fhostfw"
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#ifdef CONFIG_RWNX_TL4
+#define RWNX_MAC_FW_NAME RWNX_MAC_FW_BASE_NAME".hex"
+#else
+#define RWNX_MAC_FW_NAME  RWNX_MAC_FW_BASE_NAME".ihex"
+#ifdef CONFIG_MCU_INTEGRATED
+#ifdef CONFIG_MCU_MESSAGE
+#define RWNX_MAC_FW_NAME2 RWNX_MAC_FW_BASE_NAME"_8818m_custmsg.bin"
+#else
+#define RWNX_MAC_FW_NAME2 RWNX_MAC_FW_BASE_NAME"_8818m.bin"
+#endif
+#else
+#define RWNX_MAC_FW_NAME2 RWNX_MAC_FW_BASE_NAME".bin"
+#endif
+#endif
+#define RWNX_MAC_CALIB_BASE_NAME_8800DC        "fmacfw_calib_8800dc"
+#define RWNX_MAC_CALIB_NAME_8800DC_U02          RWNX_MAC_CALIB_BASE_NAME_8800DC"_u02.bin"
+#define RWNX_MAC_CALIB_NAME_8800DC_H_U02        RWNX_MAC_CALIB_BASE_NAME_8800DC"_h_u02.bin"
+
+#if (defined(CONFIG_DPD) && !defined(CONFIG_FORCE_DPD_CALIB))
+#define FW_DPDRESULT_NAME_8800DC        "aic_dpdresult_8800dc.bin"
+#endif
+
+#ifdef CONFIG_DPD
+#define ROM_FMAC_CALIB_ADDR            0x00130000
+#define FW_PATH_MAX_LEN 200
+int is_file_exist(char* name);
+
+typedef struct {
+    uint32_t bit_mask[3];
+    uint32_t reserved;
+    uint32_t dpd_high[96];
+    uint32_t dpd_11b[96];
+    uint32_t dpd_low[96];
+    uint32_t idac_11b[48];
+    uint32_t idac_high[48];
+    uint32_t idac_low[48];
+    uint32_t loft_res[18];
+    uint32_t rx_iqim_res[16];
+} rf_misc_ram_t;
+
+typedef struct {
+    uint32_t bit_mask[4];
+    uint32_t dpd_high[96];
+    uint32_t loft_res[18];
+} rf_misc_ram_lite_t;
+
+#define MEMBER_SIZE(type, member)   sizeof(((type *)0)->member)
+#define DPD_RESULT_SIZE_8800DC      sizeof(rf_misc_ram_lite_t)
+
+extern rf_misc_ram_lite_t dpd_res;
+#endif
+
+#ifdef CONFIG_DPD
+//int aicwf_plat_calib_load_8800dc(struct rwnx_hw *rwnx_hw);
+//int aicwf_dpd_calib_8800dc(struct rwnx_hw *rwnx_hw, rf_misc_ram_lite_t *dpd_res);
+//int aicwf_dpd_result_apply_8800dc(struct rwnx_hw *rwnx_hw, rf_misc_ram_lite_t *dpd_res);
+#ifndef CONFIG_FORCE_DPD_CALIB
+//int aicwf_dpd_result_load_8800dc(struct rwnx_hw *rwnx_hw, rf_misc_ram_lite_t *dpd_res);
+int aicwf_dpd_result_write_8800dc(void *buf, int buf_len);
+#endif
+#endif
+
+#define RWNX_MAC_PATCH_BASE_NAME        "fmacfw_patch_8800dc"
+#define RWNX_MAC_PATCH_NAME2 RWNX_MAC_PATCH_BASE_NAME".bin"
+#define RWNX_MAC_PATCH_NAME2_U02 RWNX_MAC_PATCH_BASE_NAME"_u02.bin"
+#define RWNX_MAC_PATCH_NAME2_8800DC_H_U02 RWNX_MAC_PATCH_BASE_NAME"_h_u02.bin"
+
+#define RWNX_MAC_PATCH_TABLE_NAME_8800DC "fmacfw_patch_tbl_8800dc"
+#define RWNX_MAC_PATCH_TABLE_8800DC RWNX_MAC_PATCH_TABLE_NAME_8800DC ".bin"
+#define RWNX_MAC_PATCH_TABLE_8800DC_U02 RWNX_MAC_PATCH_TABLE_NAME_8800DC "_u02.bin"
+#define RWNX_MAC_PATCH_TABLE_8800DC_H_U02 RWNX_MAC_PATCH_TABLE_NAME_8800DC "_h_u02.bin"
+
+#define RWNX_MAC_RF_PATCH_BASE_NAME     "fmacfw_rf_patch"
+#define RWNX_MAC_RF_PATCH_NAME RWNX_MAC_RF_PATCH_BASE_NAME".bin"
+
+
+#define RWNX_FCU_FW_NAME                "fcuram.bin"
+
+#define FW_USERCONFIG_NAME              "aic_userconfig.txt"
+#define FW_USERCONFIG_NAME_8800D80         "aic_userconfig_8800d80.txt"
+
+#ifdef CONFIG_VENDOR_SPECIFIED_FW_PATH
+#define VENDOR_SPECIFIED_FW_PATH    CONFIG_VENDOR_SPECIFIED_FW_PATH
+#endif
+#ifdef CONFIG_VENDOR_SUBDIR_NAME
+#define VENDOR_SUBDIR_NAME          CONFIG_VENDOR_SUBDIR_NAME
+#endif
+
+#define VENDOR_SPECIFIED_DPD_PATH "/mnt/userdata/etc_rw/wifi"
+
+enum aicbt_patch_table_type {
+	AICBT_PT_INF  = 0x00,
+	AICBT_PT_TRAP = 0x1,
+	AICBT_PT_B4,
+	AICBT_PT_BTMODE,
+	AICBT_PT_PWRON,
+	AICBT_PT_AF,
+	AICBT_PT_VER,
+};
+
+enum aicbt_btport_type {
+	AICBT_BTPORT_NULL,
+	AICBT_BTPORT_MB,
+	AICBT_BTPORT_UART,
+};
+
+/*  btmode
+ * used for force bt mode,if not AICBSP_MODE_NULL
+ * efuse valid and vendor_info will be invalid, even has beed set valid
+*/
+enum aicbt_btmode_type {
+	AICBT_BTMODE_BT_ONLY_SW = 0x0,    // bt only mode with switch
+	AICBT_BTMODE_BT_WIFI_COMBO,       // wifi/bt combo mode
+	AICBT_BTMODE_BT_ONLY,             // bt only mode without switch
+	AICBT_BTMODE_BT_ONLY_TEST,        // bt only test mode
+	AICBT_BTMODE_BT_WIFI_COMBO_TEST,  // wifi/bt combo test mode
+	AICBT_BTMODE_BT_ONLY_COANT,       // bt only mode with no external switch
+	AICBT_MODE_NULL = 0xFF,           // invalid value
+};
+
+/*  uart_baud
+ * used for config uart baud when btport set to uart,
+ * otherwise meaningless
+*/
+enum aicbt_uart_baud_type {
+	AICBT_UART_BAUD_115200     = 115200,
+	AICBT_UART_BAUD_921600     = 921600,
+	AICBT_UART_BAUD_1_5M       = 1500000,
+	AICBT_UART_BAUD_3_25M      = 3250000,
+};
+
+enum aicbt_uart_flowctrl_type {
+	AICBT_UART_FLOWCTRL_DISABLE = 0x0,    // uart without flow ctrl
+	AICBT_UART_FLOWCTRL_ENABLE,           // uart with flow ctrl
+};
+
+enum aicbsp_cpmode_type {
+	AICBSP_CPMODE_WORK,
+	AICBSP_CPMODE_TEST,
+	AICBSP_CPMODE_MAX,
+};
+
+enum chip_rev {
+	CHIP_REV_U01 = 1,
+	CHIP_REV_U02 = 3,
+	CHIP_REV_U03 = 7,
+	CHIP_REV_U04 = 7,
+};
+
+#define RAM_FMAC_FW_ADDR                    0x00120000
+#define FW_RAM_ADID_BASE_ADDR               0x00161928
+#define FW_RAM_ADID_BASE_ADDR_U03           0x00161928
+#define FW_RAM_PATCH_BASE_ADDR              0x00100000
+#define RAM_8800DC_U01_ADID_ADDR            0x00101788
+#define RAM_8800DC_U02_ADID_ADDR            0x001017d8
+#define RAM_8800DC_FW_PATCH_ADDR            0x00184000
+#define FW_RESET_START_ADDR                 0x40500128
+#define FW_RESET_START_VAL                  0x40
+#define FW_ADID_FLAG_ADDR                   0x40500150
+#define FW_ADID_FLAG_VAL                    0x01
+#define FW_RAM_ADID_BASE_ADDR_8800D80       0x002017E0
+#define FW_RAM_PATCH_BASE_ADDR_8800D80      0x0020B2B0
+#define FW_RAM_ADID_BASE_ADDR_8800D80_U02   0x00201940
+#define FW_RAM_PATCH_BASE_ADDR_8800D80_U02  0x0020b43c
+
+#define AICBT_PT_TAG                "AICBT_PT_TAG"
+///aic bt tx pwr lvl :lsb->msb: first byte, min pwr lvl; second byte, max pwr lvl;
+///pwr lvl:20(min), 30 , 40 , 50 , 60(max)
+#define AICBT_TXPWR_LVL            0x00006020
+#define AICBT_TXPWR_LVL_8800dc            0x00006f2f
+#define AICBT_TXPWR_LVL_8800d80           0x00006f2f
+
+#define AICBSP_HWINFO_DEFAULT       (-1)
+#define AICBSP_CPMODE_DEFAULT       AICBSP_CPMODE_WORK
+#define AICBSP_FWLOG_EN_DEFAULT     0
+
+#define AICBT_BTMODE_DEFAULT_8800d80    AICBT_BTMODE_BT_ONLY_COANT
+#define AICBT_BTMODE_DEFAULT            AICBT_BTMODE_BT_WIFI_COMBO
+#define AICBT_BTPORT_DEFAULT            AICBT_BTPORT_UART
+#define AICBT_UART_BAUD_DEFAULT         AICBT_UART_BAUD_1_5M
+#define AICBT_UART_FC_DEFAULT           AICBT_UART_FLOWCTRL_ENABLE
+#define AICBT_LPM_ENABLE_DEFAULT 	    0
+#define AICBT_TXPWR_LVL_DEFAULT         AICBT_TXPWR_LVL
+#define AICBT_TXPWR_LVL_DEFAULT_8800dc  AICBT_TXPWR_LVL_8800dc
+#define AICBT_TXPWR_LVL_DEFAULT_8800d80 AICBT_TXPWR_LVL_8800d80
+
+struct aicbt_patch_table {
+	char     *name;
+	uint32_t type;
+	uint32_t *data;
+	uint32_t len;
+	struct aicbt_patch_table *next;
+};
+
+struct aicbt_info_t {
+	uint32_t btmode;
+	uint32_t btport;
+	uint32_t uart_baud;
+	uint32_t uart_flowctrl;
+	uint32_t lpm_enable;
+	uint32_t txpwr_lvl;
+};
+
+struct aicbt_patch_info_t {
+	uint32_t info_len;
+	uint32_t adid_addrinf;
+	uint32_t addr_adid;
+	uint32_t patch_addrinf;
+	uint32_t addr_patch;
+	uint32_t reset_addr;
+	uint32_t reset_val;
+	uint32_t adid_flag_addr;
+	uint32_t adid_flag;
+};
+
+struct aicbsp_firmware {
+	const char *desc;
+	const char *bt_adid;
+	const char *bt_patch;
+	const char *bt_table;
+	const char *wl_fw;
+};
+
+struct aicbsp_info_t {
+	int hwinfo;
+	int hwinfo_r;
+	uint32_t cpmode;
+	uint32_t chip_rev;
+	bool fwlog_en;
+	uint8_t irqf;
+};
+
+/**
+ * Type of memory to access (cf rwnx_plat.get_address)
+ *
+ * @RWNX_ADDR_CPU To access memory of the embedded CPU
+ * @RWNX_ADDR_SYSTEM To access memory/registers of one subsystem of the
+ * embedded system
+ *
+ */
+enum rwnx_platform_addr {
+    RWNX_ADDR_CPU,
+    RWNX_ADDR_SYSTEM,
+    RWNX_ADDR_MAX,
+};
+
+struct rwnx_hw;
+
+/**
+ * struct rwnx_plat - Operation pointers for RWNX PCI platform
+ *
+ * @pci_dev: pointer to pci dev
+ * @enabled: Set if embedded platform has been enabled (i.e. fw loaded and
+ *          ipc started)
+ * @enable: Configure communication with the fw (i.e. configure the transfers
+ *         enable and register interrupt)
+ * @disable: Stop communication with the fw
+ * @deinit: Free all ressources allocated for the embedded platform
+ * @get_address: Return the virtual address to access the requested address on
+ *              the platform.
+ * @ack_irq: Acknowledge the irq at link level.
+ * @get_config_reg: Return the list (size + pointer) of registers to restore in
+ * order to reload the platform while keeping the current configuration.
+ *
+ * @priv Private data for the link driver
+ */
+struct rwnx_plat {
+    struct pci_dev *pci_dev;
+
+#ifdef AICWF_SDIO_SUPPORT
+	struct aic_sdio_dev *sdiodev;
+#endif
+
+#ifdef AICWF_USB_SUPPORT
+    struct aic_usb_dev *usbdev;
+#endif
+    bool enabled;
+
+    int (*enable)(struct rwnx_hw *rwnx_hw);
+    int (*disable)(struct rwnx_hw *rwnx_hw);
+    void (*deinit)(struct rwnx_plat *rwnx_plat);
+    u8* (*get_address)(struct rwnx_plat *rwnx_plat, int addr_name,
+                       unsigned int offset);
+    void (*ack_irq)(struct rwnx_plat *rwnx_plat);
+    int (*get_config_reg)(struct rwnx_plat *rwnx_plat, const u32 **list);
+
+    u8 priv[0] __aligned(sizeof(void *));
+};
+
+#define RWNX_ADDR(plat, base, offset)           \
+    plat->get_address(plat, base, offset)
+
+#define RWNX_REG_READ(plat, base, offset)               \
+    readl(plat->get_address(plat, base, offset))
+
+#define RWNX_REG_WRITE(val, plat, base, offset)         \
+    writel(val, plat->get_address(plat, base, offset))
+
+typedef struct
+{
+    u8_l enable;
+    u8_l xtal_cap;
+    u8_l xtal_cap_fine;
+
+} xtal_cap_conf_t;
+
+extern struct rwnx_plat *g_rwnx_plat;
+
+int rwnx_platform_init(struct rwnx_plat *rwnx_plat, void **platform_data);
+void rwnx_platform_deinit(struct rwnx_hw *rwnx_hw);
+
+int rwnx_platform_on(struct rwnx_hw *rwnx_hw, void *config);
+void rwnx_platform_off(struct rwnx_hw *rwnx_hw, void **config);
+
+int rwnx_platform_register_drv(void);
+void rwnx_platform_unregister_drv(void);
+
+#ifdef CONFIG_LOAD_USERCONFIG
+void get_userconfig_txpwr_lvl(txpwr_lvl_conf_t *txpwr_lvl);
+void get_userconfig_txpwr_lvl_v2_in_fdrv(txpwr_lvl_conf_v2_t *txpwr_lvl_v2);
+void get_userconfig_txpwr_lvl_v3_in_fdrv(txpwr_lvl_conf_v3_t *txpwr_lvl_v3);
+void get_userconfig_txpwr_lvl_adj_in_fdrv(txpwr_lvl_adj_conf_t *txpwr_lvl_adj);
+void get_userconfig_txpwr_loss(txpwr_loss_conf_t *txpwr_loss);
+void get_userconfig_txpwr_ofst(txpwr_ofst_conf_t *txpwr_ofst);
+void get_userconfig_txpwr_ofst2x_in_fdrv(txpwr_ofst2x_conf_t *txpwr_ofst2x);
+void get_userconfig_xtal_cap(xtal_cap_conf_t *xtal_cap);
+void set_txpwr_loss_ofst(s8_l value);
+#endif
+int aicwf_patch_table_load(struct rwnx_hw *rwnx_hw, char *filename);
+int rwnx_load_firmware(u32 **fw_buf, const char *name, struct device *device);
+extern struct device *rwnx_platform_get_dev(struct rwnx_plat *rwnx_plat);
+
+static inline unsigned int rwnx_platform_get_irq(struct rwnx_plat *rwnx_plat)
+{
+    return rwnx_plat->pci_dev->irq;
+}
+int aicwf_misc_ram_init_8800dc(struct rwnx_hw *rwnx_hw);
+int aicwf_plat_patch_load_8800dc(struct rwnx_hw *rwnx_hw);
+
+#endif /* _RWNX_PLATFORM_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_prof.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_prof.h
new file mode 100755
index 0000000..fa59221
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_prof.h
@@ -0,0 +1,133 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_prof.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_PROF_H_
+#define _RWNX_PROF_H_
+
+#include "reg_access.h"
+#include "rwnx_platform.h"
+
+static inline void rwnx_prof_set(struct rwnx_hw *rwnx_hw, int val)
+{
+    struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+    RWNX_REG_WRITE(val, rwnx_plat, RWNX_ADDR_SYSTEM, NXMAC_SW_SET_PROFILING_ADDR);
+}
+
+static inline void rwnx_prof_clear(struct rwnx_hw *rwnx_hw, int val)
+{
+    struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+    RWNX_REG_WRITE(val, rwnx_plat, RWNX_ADDR_SYSTEM, NXMAC_SW_CLEAR_PROFILING_ADDR);
+}
+
+#if 0
+/* Defines for SW Profiling registers values */
+enum {
+    TX_IPC_IRQ,
+    TX_IPC_EVT,
+    TX_PREP_EVT,
+    TX_DMA_IRQ,
+    TX_MAC_IRQ,
+    TX_PAYL_HDL,
+    TX_CFM_EVT,
+    TX_IPC_CFM,
+    RX_MAC_IRQ,                 // 8
+    RX_TRIGGER_EVT,
+    RX_DMA_IRQ,
+    RX_DMA_EVT,
+    RX_IPC_IND,
+    RX_MPDU_XFER,
+    DBG_PROF_MAX
+};
+#endif
+
+enum {
+    SW_PROF_HOSTBUF_IDX = 12,
+    /****** IPC IRQs related signals ******/
+    /* E2A direction */
+    SW_PROF_IRQ_E2A_RXDESC = 16,    // to make sure we let 16 bits available for LMAC FW
+    SW_PROF_IRQ_E2A_TXCFM,
+    SW_PROF_IRQ_E2A_DBG,
+    SW_PROF_IRQ_E2A_MSG,
+    SW_PROF_IPC_MSGPUSH,
+    SW_PROF_MSGALLOC,
+    SW_PROF_MSGIND,
+    SW_PROF_DBGIND,
+
+    /* A2E direction */
+    SW_PROF_IRQ_A2E_TXCFM_BACK,
+
+    /****** Driver functions related signals ******/
+    SW_PROF_WAIT_QUEUE_STOP,
+    SW_PROF_WAIT_QUEUE_WAKEUP,
+    SW_PROF_RWNXDATAIND,
+    SW_PROF_RWNX_IPC_IRQ_HDLR,
+    SW_PROF_RWNX_IPC_THR_IRQ_HDLR,
+    SW_PROF_IEEE80211RX,
+    SW_PROF_RWNX_PATTERN,
+    SW_PROF_MAX
+};
+
+// [LT]For debug purpose only
+#if (0)
+#define SW_PROF_CHAN_CTXT_CFM_HDL_BIT       (21)
+#define SW_PROF_CHAN_CTXT_CFM_BIT           (22)
+#define SW_PROF_CHAN_CTXT_CFM_SWDONE_BIT    (23)
+#define SW_PROF_CHAN_CTXT_PUSH_BIT          (24)
+#define SW_PROF_CHAN_CTXT_QUEUE_BIT         (25)
+#define SW_PROF_CHAN_CTXT_TX_BIT            (26)
+#define SW_PROF_CHAN_CTXT_TX_PAUSE_BIT      (27)
+#define SW_PROF_CHAN_CTXT_PSWTCH_BIT        (28)
+#define SW_PROF_CHAN_CTXT_SWTCH_BIT         (29)
+
+// TO DO: update this
+
+#define REG_SW_SET_PROFILING_CHAN(env, bit)             \
+    rwnx_prof_set((struct rwnx_hw*)env, BIT(bit))
+
+#define REG_SW_CLEAR_PROFILING_CHAN(env, bit) \
+    rwnx_prof_clear((struct rwnx_hw*)env, BIT(bit))
+
+#else
+#define SW_PROF_CHAN_CTXT_CFM_HDL_BIT       (0)
+#define SW_PROF_CHAN_CTXT_CFM_BIT           (0)
+#define SW_PROF_CHAN_CTXT_CFM_SWDONE_BIT    (0)
+#define SW_PROF_CHAN_CTXT_PUSH_BIT          (0)
+#define SW_PROF_CHAN_CTXT_QUEUE_BIT         (0)
+#define SW_PROF_CHAN_CTXT_TX_BIT            (0)
+#define SW_PROF_CHAN_CTXT_TX_PAUSE_BIT      (0)
+#define SW_PROF_CHAN_CTXT_PSWTCH_BIT        (0)
+#define SW_PROF_CHAN_CTXT_SWTCH_BIT         (0)
+
+#define REG_SW_SET_PROFILING_CHAN(env, bit)            do {} while (0)
+#define REG_SW_CLEAR_PROFILING_CHAN(env, bit)          do {} while (0)
+#endif
+
+#ifdef CONFIG_RWNX_SW_PROFILING
+/* Macros for SW PRofiling registers access */
+#define REG_SW_SET_PROFILING(env, bit)                  \
+    rwnx_prof_set((struct rwnx_hw*)env, BIT(bit))
+
+#define REG_SW_SET_HOSTBUF_IDX_PROFILING(env, val)      \
+    rwnx_prof_set((struct rwnx_hw*)env, val<<(SW_PROF_HOSTBUF_IDX))
+
+#define REG_SW_CLEAR_PROFILING(env, bit)                \
+    rwnx_prof_clear((struct rwnx_hw*)env, BIT(bit))
+
+#define REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env)                         \
+    rwnx_prof_clear((struct rwnx_hw*)env,0x0F<<(SW_PROF_HOSTBUF_IDX))
+
+#else
+#define REG_SW_SET_PROFILING(env, value)            do {} while (0)
+#define REG_SW_CLEAR_PROFILING(env, value)          do {} while (0)
+#define REG_SW_SET_HOSTBUF_IDX_PROFILING(env, val)  do {} while (0)
+#define REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env)     do {} while (0)
+#endif
+
+#endif /* _RWNX_PROF_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_radar.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_radar.c
new file mode 100755
index 0000000..e6dc578
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_radar.c
@@ -0,0 +1,1644 @@
+/**
+******************************************************************************
+ *
+ * @file rwnx_radar.c
+ *
+ * @brief Functions to handle radar detection
+ * Radar detection is copied (and adapted) from ath driver source code.
+ *
+ * Copyright (c) 2012 Neratec Solutions AG
+ * Copyright (C) RivieraWaves 2015-2019
+ *
+ ******************************************************************************
+ */
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <net/mac80211.h>
+
+#include "rwnx_radar.h"
+#include "rwnx_defs.h"
+#include "rwnx_msg_tx.h"
+#include "rwnx_events.h"
+#include "rwnx_compat.h"
+
+/*
+ * tolerated deviation of radar time stamp in usecs on both sides
+ * TODO: this might need to be HW-dependent
+ */
+#define PRI_TOLERANCE  16
+
+/**
+ * struct radar_types - contains array of patterns defined for one DFS domain
+ * @domain: DFS regulatory domain
+ * @num_radar_types: number of radar types to follow
+ * @radar_types: radar types array
+ */
+struct radar_types {
+    enum nl80211_dfs_regions region;
+    u32 num_radar_types;
+    const struct radar_detector_specs *spec_riu;
+    const struct radar_detector_specs *spec_fcu;
+};
+
+/**
+ * Type of radar waveform:
+ * RADAR_WAVEFORM_SHORT : waveform defined by
+ *  - pulse width
+ *  - pulse interval in a burst (pri)
+ *  - number of pulses in a burst (ppb)
+ *
+ * RADAR_WAVEFORM_WEATHER :
+ *   same than SHORT except that ppb is dependent of pri
+ *
+ * RADAR_WAVEFORM_INTERLEAVED :
+ *   same than SHORT except there are several value of pri (interleaved)
+ *
+ * RADAR_WAVEFORM_LONG :
+ *
+ */
+enum radar_waveform_type {
+    RADAR_WAVEFORM_SHORT,
+    RADAR_WAVEFORM_WEATHER,
+    RADAR_WAVEFORM_INTERLEAVED,
+    RADAR_WAVEFORM_LONG
+};
+
+/**
+ * struct radar_detector_specs - detector specs for a radar pattern type
+ * @type_id: pattern type, as defined by regulatory
+ * @width_min: minimum radar pulse width in [us]
+ * @width_max: maximum radar pulse width in [us]
+ * @pri_min: minimum pulse repetition interval in [us] (including tolerance)
+ * @pri_max: minimum pri in [us] (including tolerance)
+ * @num_pri: maximum number of different pri for this type
+ * @ppb: pulses per bursts for this type
+ * @ppb_thresh: number of pulses required to trigger detection
+ * @max_pri_tolerance: pulse time stamp tolerance on both sides [us]
+ * @type: Type of radar waveform
+ */
+struct radar_detector_specs {
+    u8 type_id;
+    u8 width_min;
+    u8 width_max;
+    u16 pri_min;
+    u16 pri_max;
+    u8 num_pri;
+    u8 ppb;
+    u8 ppb_thresh;
+    u8 max_pri_tolerance;
+    enum radar_waveform_type type;
+};
+
+
+/* percentage on ppb threshold to trigger detection */
+#define MIN_PPB_THRESH  50
+#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100)
+#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF)
+
+/* width tolerance */
+#define WIDTH_TOLERANCE 2
+#define WIDTH_LOWER(X) (X)
+#define WIDTH_UPPER(X) (X)
+
+#define ETSI_PATTERN_SHORT(ID, WMIN, WMAX, PMIN, PMAX, PPB)             \
+    {                                                                   \
+        ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),                       \
+            (PRF2PRI(PMAX) - PRI_TOLERANCE),                            \
+            (PRF2PRI(PMIN) + PRI_TOLERANCE), 1, PPB,                    \
+            PPB_THRESH(PPB), PRI_TOLERANCE,  RADAR_WAVEFORM_SHORT       \
+            }
+
+#define ETSI_PATTERN_INTERLEAVED(ID, WMIN, WMAX, PMIN, PMAX, PRFMIN, PRFMAX, PPB) \
+    {                                                                   \
+        ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),                       \
+            (PRF2PRI(PMAX) * PRFMIN- PRI_TOLERANCE),                    \
+            (PRF2PRI(PMIN) * PRFMAX + PRI_TOLERANCE),                   \
+            PRFMAX, PPB * PRFMAX,                                       \
+            PPB_THRESH(PPB), PRI_TOLERANCE, RADAR_WAVEFORM_INTERLEAVED  \
+            }
+
+/* radar types as defined by ETSI EN-301-893 v1.7.1 */
+static const struct radar_detector_specs etsi_radar_ref_types_v17_riu[] = {
+    ETSI_PATTERN_SHORT(0,  0,  8,  700,  700, 18),
+    ETSI_PATTERN_SHORT(1,  0, 10,  200, 1000, 10),
+    ETSI_PATTERN_SHORT(2,  0, 22,  200, 1600, 15),
+    ETSI_PATTERN_SHORT(3,  0, 22, 2300, 4000, 25),
+    ETSI_PATTERN_SHORT(4, 20, 38, 2000, 4000, 20),
+    ETSI_PATTERN_INTERLEAVED(5,  0,  8,  300,  400, 2, 3, 10),
+    ETSI_PATTERN_INTERLEAVED(6,  0,  8,  400, 1200, 2, 3, 15),
+};
+
+static const struct radar_detector_specs etsi_radar_ref_types_v17_fcu[] = {
+    ETSI_PATTERN_SHORT(0,  0,  8,  700,  700, 18),
+    ETSI_PATTERN_SHORT(1,  0,  8,  200, 1000, 10),
+    ETSI_PATTERN_SHORT(2,  0, 16,  200, 1600, 15),
+    ETSI_PATTERN_SHORT(3,  0, 16, 2300, 4000, 25),
+    ETSI_PATTERN_SHORT(4, 20, 34, 2000, 4000, 20),
+    ETSI_PATTERN_INTERLEAVED(5,  0,  8,  300,  400, 2, 3, 10),
+    ETSI_PATTERN_INTERLEAVED(6,  0,  8,  400, 1200, 2, 3, 15),
+};
+
+static const struct radar_types etsi_radar_types_v17 = {
+    .region          = NL80211_DFS_ETSI,
+    .num_radar_types = ARRAY_SIZE(etsi_radar_ref_types_v17_riu),
+    .spec_riu        = etsi_radar_ref_types_v17_riu,
+    .spec_fcu        = etsi_radar_ref_types_v17_fcu,
+};
+
+#define FCC_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB, TYPE) \
+    {                                                           \
+        ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),               \
+            PMIN - PRI_TOLERANCE,                               \
+            PMAX * PRF + PRI_TOLERANCE, PRF, PPB * PRF,         \
+            PPB_THRESH(PPB), PRI_TOLERANCE, TYPE                \
+            }
+
+static const struct radar_detector_specs fcc_radar_ref_types_riu[] = {
+    FCC_PATTERN(0,  0,   8, 1428, 1428, 1,  18, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(1,  0,   8,  518, 3066, 1, 102, RADAR_WAVEFORM_WEATHER),
+    FCC_PATTERN(2,  0,   8,  150,  230, 1,  23, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(3,  6,  20,  200,  500, 1,  16, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(4, 10,  28,  200,  500, 1,  12, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(5, 50, 110, 1000, 2000, 1,   8, RADAR_WAVEFORM_LONG),
+    FCC_PATTERN(6,  0,   8,  333,  333, 1,   9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_detector_specs fcc_radar_ref_types_fcu[] = {
+    FCC_PATTERN(0,  0,   8, 1428, 1428, 1,  18, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(1,  0,   8,  518, 3066, 1, 102, RADAR_WAVEFORM_WEATHER),
+    FCC_PATTERN(2,  0,   8,  150,  230, 1,  23, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(3,  6,  12,  200,  500, 1,  16, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(4, 10,  22,  200,  500, 1,  12, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(5, 50, 104, 1000, 2000, 1,   8, RADAR_WAVEFORM_LONG),
+    FCC_PATTERN(6,  0,   8,  333,  333, 1,   9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_types fcc_radar_types = {
+    .region          = NL80211_DFS_FCC,
+    .num_radar_types = ARRAY_SIZE(fcc_radar_ref_types_riu),
+    .spec_riu        = fcc_radar_ref_types_riu,
+    .spec_fcu        = fcc_radar_ref_types_fcu,
+};
+
+#define JP_PATTERN FCC_PATTERN
+static const struct radar_detector_specs jp_radar_ref_types_riu[] = {
+    JP_PATTERN(0,  0,   8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(1,  2,   8, 3846, 3846, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(2,  0,   8, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(3,  0,   8, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(4,  0,   8,  150,  230, 1, 23, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(5,  6,  20,  200,  500, 1, 16, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(6, 10,  28,  200,  500, 1, 12, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(7, 50, 110, 1000, 2000, 1,  8, RADAR_WAVEFORM_LONG),
+    JP_PATTERN(8,  0,   8,  333,  333, 1,  9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_detector_specs jp_radar_ref_types_fcu[] = {
+    JP_PATTERN(0,  0,   8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(1,  2,   6, 3846, 3846, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(2,  0,   8, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(3,  2,   2, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(4,  0,   8,  150,  230, 1, 23, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(5,  6,  12,  200,  500, 1, 16, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(6, 10,  22,  200,  500, 1, 12, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(7, 50, 104, 1000, 2000, 1,  8, RADAR_WAVEFORM_LONG),
+    JP_PATTERN(8,  0,   8,  333,  333, 1,  9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_types jp_radar_types = {
+    .region          = NL80211_DFS_JP,
+    .num_radar_types = ARRAY_SIZE(jp_radar_ref_types_riu),
+    .spec_riu        = jp_radar_ref_types_riu,
+    .spec_fcu        = jp_radar_ref_types_fcu,
+};
+
+static const struct radar_types *dfs_domains[] = {
+    &etsi_radar_types_v17,
+    &fcc_radar_types,
+    &jp_radar_types,
+};
+
+
+/**
+ * struct pri_sequence - sequence of pulses matching one PRI
+ * @head: list_head
+ * @pri: pulse repetition interval (PRI) in usecs
+ * @dur: duration of sequence in usecs
+ * @count: number of pulses in this sequence
+ * @count_falses: number of not matching pulses in this sequence
+ * @first_ts: time stamp of first pulse in usecs
+ * @last_ts: time stamp of last pulse in usecs
+ * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur)
+ * @ppb_thresh: Number of pulses to validate detection
+ *              (need for weather radar whose value depends of pri)
+ */
+struct pri_sequence {
+    struct list_head head;
+    u32 pri;
+    u32 dur;
+    u32 count;
+    u32 count_falses;
+    u64 first_ts;
+    u64 last_ts;
+    u64 deadline_ts;
+    u8 ppb_thresh;
+};
+
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct pulse_elem {
+    struct list_head head;
+    u64 ts;
+};
+
+/**
+ * struct pri_detector - PRI detector element for a dedicated radar type
+ * @head:
+ * @rs: detector specs for this detector element
+ * @last_ts: last pulse time stamp considered for this element in usecs
+ * @sequences: list_head holding potential pulse sequences
+ * @pulses: list connecting pulse_elem objects
+ * @count: number of pulses in queue
+ * @max_count: maximum number of pulses to be queued
+ * @window_size: window size back from newest pulse time stamp in usecs
+ * @freq:
+ */
+struct pri_detector {
+    struct list_head head;
+    const struct radar_detector_specs *rs;
+    u64 last_ts;
+    struct list_head sequences;
+    struct list_head pulses;
+    u32 count;
+    u32 max_count;
+    u32 window_size;
+    struct pri_detector_ops *ops;
+    u16 freq;
+};
+
+/**
+ * struct pri_detector_ops - PRI detector ops (dependent of waveform type)
+ * @init : Initialize pri_detector structure
+ * @add_pulse : Add a pulse to the pri-detector
+ * @reset_on_pri_overflow : Should the pri_detector be resetted when pri overflow
+ */
+struct pri_detector_ops {
+    void (*init)(struct pri_detector *pde);
+    struct pri_sequence * (*add_pulse)(struct pri_detector *pde, u16 len, u64 ts, u16 pri);
+    int reset_on_pri_overflow;
+};
+
+
+/******************************************************************************
+ * PRI (pulse repetition interval) sequence detection
+ *****************************************************************************/
+/**
+ * Singleton Pulse and Sequence Pools
+ *
+ * Instances of pri_sequence and pulse_elem are kept in singleton pools to
+ * reduce the number of dynamic allocations. They are shared between all
+ * instances and grow up to the peak number of simultaneously used objects.
+ *
+ * Memory is freed after all references to the pools are released.
+ */
+static u32 singleton_pool_references;
+static LIST_HEAD(pulse_pool);
+static LIST_HEAD(pseq_pool);
+static DEFINE_SPINLOCK(pool_lock);
+
+static void pool_register_ref(void)
+{
+    spin_lock_bh(&pool_lock);
+    singleton_pool_references++;
+    spin_unlock_bh(&pool_lock);
+}
+
+static void pool_deregister_ref(void)
+{
+    spin_lock_bh(&pool_lock);
+    singleton_pool_references--;
+    if (singleton_pool_references == 0) {
+        /* free singleton pools with no references left */
+        struct pri_sequence *ps, *ps0;
+        struct pulse_elem *p, *p0;
+
+        list_for_each_entry_safe(p, p0, &pulse_pool, head) {
+            list_del(&p->head);
+            kfree(p);
+        }
+        list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
+            list_del(&ps->head);
+            kfree(ps);
+        }
+    }
+    spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pulse_elem(struct pulse_elem *pe)
+{
+    spin_lock_bh(&pool_lock);
+    list_add(&pe->head, &pulse_pool);
+    spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pseq_elem(struct pri_sequence *pse)
+{
+    spin_lock_bh(&pool_lock);
+    list_add(&pse->head, &pseq_pool);
+    spin_unlock_bh(&pool_lock);
+}
+
+static struct pri_sequence *pool_get_pseq_elem(void)
+{
+    struct pri_sequence *pse = NULL;
+    spin_lock_bh(&pool_lock);
+    if (!list_empty(&pseq_pool)) {
+        pse = list_first_entry(&pseq_pool, struct pri_sequence, head);
+        list_del(&pse->head);
+    }
+    spin_unlock_bh(&pool_lock);
+
+    if (pse == NULL) {
+        pse = kmalloc(sizeof(*pse), GFP_ATOMIC);
+    }
+
+    return pse;
+}
+
+static struct pulse_elem *pool_get_pulse_elem(void)
+{
+    struct pulse_elem *pe = NULL;
+    spin_lock_bh(&pool_lock);
+    if (!list_empty(&pulse_pool)) {
+        pe = list_first_entry(&pulse_pool, struct pulse_elem, head);
+        list_del(&pe->head);
+    }
+    spin_unlock_bh(&pool_lock);
+    return pe;
+}
+
+static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)
+{
+    struct list_head *l = &pde->pulses;
+    if (list_empty(l))
+        return NULL;
+    return list_entry(l->prev, struct pulse_elem, head);
+}
+
+static bool pulse_queue_dequeue(struct pri_detector *pde)
+{
+    struct pulse_elem *p = pulse_queue_get_tail(pde);
+    if (p != NULL) {
+        list_del_init(&p->head);
+        pde->count--;
+        /* give it back to pool */
+        pool_put_pulse_elem(p);
+    }
+    return (pde->count > 0);
+}
+
+/**
+ * pulse_queue_check_window - remove pulses older than window
+ * @pde: pointer on pri_detector
+ *
+ *  dequeue pulse that are too old.
+ */
+static
+void pulse_queue_check_window(struct pri_detector *pde)
+{
+    u64 min_valid_ts;
+    struct pulse_elem *p;
+
+    /* there is no delta time with less than 2 pulses */
+    if (pde->count < 2)
+        return;
+
+    if (pde->last_ts <= pde->window_size)
+        return;
+
+    min_valid_ts = pde->last_ts - pde->window_size;
+    while ((p = pulse_queue_get_tail(pde)) != NULL) {
+        if (p->ts >= min_valid_ts)
+            return;
+        pulse_queue_dequeue(pde);
+    }
+}
+
+/**
+ * pulse_queue_enqueue - Queue one pulse
+ * @pde: pointer on pri_detector
+ *
+ * Add one pulse to the list. If the maximum number of pulses
+ * if reached, remove oldest one.
+ */
+static
+bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)
+{
+    struct pulse_elem *p = pool_get_pulse_elem();
+    if (p == NULL) {
+        p = kmalloc(sizeof(*p), GFP_ATOMIC);
+        if (p == NULL) {
+             return false;
+        }
+    }
+    INIT_LIST_HEAD(&p->head);
+    p->ts = ts;
+    list_add(&p->head, &pde->pulses);
+    pde->count++;
+    pde->last_ts = ts;
+    pulse_queue_check_window(pde);
+    if (pde->count >= pde->max_count)
+        pulse_queue_dequeue(pde);
+
+    return true;
+}
+
+
+/***************************************************************************
+ * Short waveform
+ **************************************************************************/
+/**
+ * pde_get_multiple() - get number of multiples considering a given tolerance
+ * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
+ */
+static
+u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)
+{
+    u32 remainder;
+    u32 factor;
+    u32 delta;
+
+    if (fraction == 0)
+        return 0;
+
+    delta = (val < fraction) ? (fraction - val) : (val - fraction);
+
+    if (delta <= tolerance)
+        /* val and fraction are within tolerance */
+        return 1;
+
+    factor = val / fraction;
+    remainder = val % fraction;
+    if (remainder > tolerance) {
+        /* no exact match */
+        if ((fraction - remainder) <= tolerance)
+            /* remainder is within tolerance */
+            factor++;
+        else
+            factor = 0;
+    }
+    return factor;
+}
+
+/**
+ * pde_short_create_sequences - create_sequences function for
+ *                              SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ * @ts: timestamp of the pulse
+ * @min_count: Minimum number of pulse to be present in the sequence.
+ *             (With this pulse there is already a sequence with @min_count
+ *              pulse, so if we can't create a sequence with more pulse don't
+ *              create it)
+ * @return: false if an error occured (memory allocation) true otherwise
+ *
+ * For each pulses queued check if we can create a sequence with
+ * pri = (ts - pulse_queued.ts) which contains more than @min_count pulses.
+ *
+ */
+static
+bool pde_short_create_sequences(struct pri_detector *pde,
+                                u64 ts, u32 min_count)
+{
+    struct pulse_elem *p;
+    u16 pulse_idx = 0;
+
+    list_for_each_entry(p, &pde->pulses, head) {
+        struct pri_sequence ps, *new_ps;
+        struct pulse_elem *p2;
+        u32 tmp_false_count;
+        u64 min_valid_ts;
+        u32 delta_ts = ts - p->ts;
+        pulse_idx++;
+
+        if (delta_ts < pde->rs->pri_min)
+            /* ignore too small pri */
+            continue;
+
+        if (delta_ts > pde->rs->pri_max)
+            /* stop on too large pri (sorted list) */
+            break;
+
+        /* build a new sequence with new potential pri */
+        ps.count = 2;
+        ps.count_falses = pulse_idx - 1;
+        ps.first_ts = p->ts;
+        ps.last_ts = ts;
+        ps.pri = ts - p->ts;
+        ps.dur = ps.pri * (pde->rs->ppb - 1)
+            + 2 * pde->rs->max_pri_tolerance;
+
+        p2 = p;
+        tmp_false_count = 0;
+        if (ps.dur > ts)
+            min_valid_ts = 0;
+        else
+            min_valid_ts = ts - ps.dur;
+        /* check which past pulses are candidates for new sequence */
+        list_for_each_entry_continue(p2, &pde->pulses, head) {
+            u32 factor;
+            if (p2->ts < min_valid_ts)
+                /* stop on crossing window border */
+                break;
+            /* check if pulse match (multi)PRI */
+            factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,
+                                      pde->rs->max_pri_tolerance);
+            if (factor > 0) {
+                ps.count++;
+                ps.first_ts = p2->ts;
+                /*
+                 * on match, add the intermediate falses
+                 * and reset counter
+                 */
+                ps.count_falses += tmp_false_count;
+                tmp_false_count = 0;
+            } else {
+                /* this is a potential false one */
+                tmp_false_count++;
+            }
+        }
+        if (ps.count <= min_count) {
+            /* did not reach minimum count, drop sequence */
+            continue;
+        }
+        /* this is a valid one, add it */
+        ps.deadline_ts = ps.first_ts + ps.dur;
+        if (pde->rs->type == RADAR_WAVEFORM_WEATHER) {
+            ps.ppb_thresh = 19000000 / (360 * ps.pri);
+            ps.ppb_thresh = PPB_THRESH(ps.ppb_thresh);
+        } else {
+            ps.ppb_thresh = pde->rs->ppb_thresh;
+        }
+
+        new_ps = pool_get_pseq_elem();
+        if (new_ps == NULL) {
+            return false;
+        }
+        memcpy(new_ps, &ps, sizeof(ps));
+        INIT_LIST_HEAD(&new_ps->head);
+        list_add(&new_ps->head, &pde->sequences);
+    }
+    return true;
+}
+
+/**
+ * pde_short_add_to_existing_seqs - add_to_existing_seqs function for
+ *                                  SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ * @ts: timestamp of the pulse
+ *
+ * Check all sequemces created for this pde.
+ *  - If the sequence is too old delete it.
+ *  - Else if the delta with the previous pulse match the pri of the sequence
+ *    add the pulse to this sequence. If the pulse cannot be added it is added
+ *    to the false pulses for this sequence
+ *
+ * @return the length of the longest sequence in which the pulse has been added
+ */
+static
+u32 pde_short_add_to_existing_seqs(struct pri_detector *pde, u64 ts)
+{
+    u32 max_count = 0;
+    struct pri_sequence *ps, *ps2;
+    list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {
+        u32 delta_ts;
+        u32 factor;
+
+        /* first ensure that sequence is within window */
+        if (ts > ps->deadline_ts) {
+            list_del_init(&ps->head);
+            pool_put_pseq_elem(ps);
+            continue;
+        }
+
+        delta_ts = ts - ps->last_ts;
+        factor = pde_get_multiple(delta_ts, ps->pri,
+                                  pde->rs->max_pri_tolerance);
+
+        if (factor > 0) {
+            ps->last_ts = ts;
+            ps->count++;
+
+            if (max_count < ps->count)
+                max_count = ps->count;
+        } else {
+            ps->count_falses++;
+        }
+    }
+    return max_count;
+}
+
+
+/**
+ * pde_short_check_detection - check_detection function for
+ *                             SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Check all sequemces created for this pde.
+ *  - If a sequence contains more pulses than the threshold and more matching
+ *    that false pulses.
+ *
+ * @return The first complete sequence, and NULL if no sequence is complete.
+ */
+static
+struct pri_sequence * pde_short_check_detection(struct pri_detector *pde)
+{
+    struct pri_sequence *ps;
+
+    if (list_empty(&pde->sequences))
+        return NULL;
+
+    list_for_each_entry(ps, &pde->sequences, head) {
+        /*
+         * we assume to have enough matching confidence if we
+         * 1) have enough pulses
+         * 2) have more matching than false pulses
+         */
+        if ((ps->count >= ps->ppb_thresh) &&
+            (ps->count * pde->rs->num_pri > ps->count_falses)) {
+            return ps;
+        }
+    }
+    return NULL;
+}
+
+/**
+ * pde_short_init - init function for
+ *                  SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Initialize pri_detector window size to the maximun size of one burst
+ * for the radar specification associated.
+ */
+static
+void pde_short_init(struct pri_detector *pde)
+{
+    pde->window_size = pde->rs->pri_max * pde->rs->ppb * pde->rs->num_pri;
+    pde->max_count = pde->rs->ppb * 2;
+}
+
+static void pri_detector_reset(struct pri_detector *pde, u64 ts);
+/**
+ *  pde_short_add_pulse - Add pulse to a pri_detector for
+ *                        SHORT/WEATHER/INTERLEAVED radar waveform
+ *
+ * @pde : pointer on pri_detector
+ * @len : width of the pulse
+ * @ts  : timestamp of the pulse received
+ * @pri : Delta in us with the previous pulse.
+ *        (0 means that delta in bigger than 65535 us)
+ *
+ * Process on pulse within this pri_detector
+ * - First try to add it to existing sequence
+ * - Then try to create a new and longest sequence
+ * - Check if this pulse complete a sequence
+ * - If not save this pulse in the list
+ */
+static
+struct pri_sequence *pde_short_add_pulse(struct pri_detector *pde,
+                                         u16 len, u64 ts, u16 pri)
+{
+    u32 max_updated_seq;
+    struct pri_sequence *ps;
+    const struct radar_detector_specs *rs = pde->rs;
+
+    if (pde->count == 0) {
+        /* This is the first pulse after reset, no need to check sequences */
+        pulse_queue_enqueue(pde, ts);
+        return NULL;
+    }
+
+    if ((ts - pde->last_ts) < rs->max_pri_tolerance) {
+        /* if delta to last pulse is too short, don't use this pulse */
+        return NULL;
+    }
+
+    max_updated_seq = pde_short_add_to_existing_seqs(pde, ts);
+
+    if (!pde_short_create_sequences(pde, ts, max_updated_seq)) {
+        pri_detector_reset(pde, ts);
+        return NULL;
+    }
+
+    ps = pde_short_check_detection(pde);
+
+    if (ps == NULL)
+        pulse_queue_enqueue(pde, ts);
+
+    return ps;
+}
+
+
+
+/**
+ * pri detector ops to detect short radar waveform
+ * A Short waveform is defined by :
+ *   The width of pulses.
+ *   The interval between two pulses inside a burst (called pri)
+ *   (some waveform may have or 2/3 interleaved pri)
+ *   The number of pulses per burst (ppb)
+ */
+static struct pri_detector_ops pri_detector_short = {
+    .init = pde_short_init,
+    .add_pulse = pde_short_add_pulse,
+    .reset_on_pri_overflow = 1,
+};
+
+
+/***************************************************************************
+ * Long waveform
+ **************************************************************************/
+#define LONG_RADAR_DURATION 12000000
+#define LONG_RADAR_BURST_MIN_DURATION (12000000 / 20)
+#define LONG_RADAR_MAX_BURST 20
+
+/**
+ * pde_long_init - init function for LONG radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Initialize pri_detector window size to the long waveform radar
+ * waveform (ie. 12s) and max_count
+ */
+static
+void pde_long_init(struct pri_detector *pde)
+{
+    pde->window_size = LONG_RADAR_DURATION;
+    pde->max_count = LONG_RADAR_MAX_BURST; /* only count burst not pulses */
+}
+
+
+/**
+ *  pde_long_add_pulse - Add pulse to a pri_detector for
+ *                       LONG radar waveform
+ *
+ * @pde : pointer on pri_detector
+ * @len : width of the pulse
+ * @ts  : timestamp of the pulse received
+ * @pri : Delta in us with the previous pulse.
+ *
+ *
+ * For long pulse we only handle one sequence. Since each burst
+ * have a different set of parameters (number of pulse, pri) than
+ * the previous one we only use pulse width to add the pulse in the
+ * sequence.
+ * We only queue one pulse per burst and valid the radar when enough burst
+ * has been detected.
+ */
+static
+struct pri_sequence *pde_long_add_pulse(struct pri_detector *pde,
+                                        u16 len, u64 ts, u16 pri)
+{
+    struct pri_sequence *ps;
+    const struct radar_detector_specs *rs = pde->rs;
+
+    if (list_empty(&pde->sequences)) {
+        /* First pulse, create a new sequence */
+        ps = pool_get_pseq_elem();
+        if (ps == NULL) {
+            return NULL;
+        }
+
+        /*For long waveform, "count" represents the number of burst detected */
+        ps->count = 1;
+        /*"count_false" represents the number of pulse in the current burst */
+        ps->count_falses = 1;
+        ps->first_ts = ts;
+        ps->last_ts = ts;
+        ps->deadline_ts = ts + pde->window_size;
+        ps->pri = 0;
+        INIT_LIST_HEAD(&ps->head);
+        list_add(&ps->head, &pde->sequences);
+        pulse_queue_enqueue(pde, ts);
+    } else {
+        u32 delta_ts;
+
+        ps = (struct pri_sequence *)pde->sequences.next;
+
+        delta_ts = ts - ps->last_ts;
+        ps->last_ts = ts;
+
+        if (delta_ts < rs->pri_max) {
+            /* ignore pulse too close from previous one */
+        } else if  ((delta_ts >= rs->pri_min) &&
+              (delta_ts <= rs->pri_max)) {
+            /* this is a new pulse in the current burst, ignore it
+               (i.e don't queue it) */
+            ps->count_falses++;
+        } else if ((ps->count > 2) &&
+                   (ps->dur + delta_ts) < LONG_RADAR_BURST_MIN_DURATION) {
+            /* not enough time between burst, ignore pulse */
+        } else {
+            /* a new burst */
+            ps->count++;
+            ps->count_falses = 1;
+
+            /* reset the start of the sequence if deadline reached */
+            if (ts > ps->deadline_ts) {
+                struct pulse_elem *p;
+                u64 min_valid_ts;
+
+                min_valid_ts = ts - pde->window_size;
+                while ((p = pulse_queue_get_tail(pde)) != NULL) {
+                    if (p->ts >= min_valid_ts) {
+                        ps->first_ts = p->ts;
+                        ps->deadline_ts = p->ts + pde->window_size;
+                        break;
+                    }
+                    pulse_queue_dequeue(pde);
+                    ps->count--;
+                }
+            }
+
+            /* valid radar if enough burst detected and delta with first burst
+               is at least duration/2 */
+            if (ps->count > pde->rs->ppb_thresh &&
+                (ts - ps->first_ts) > (pde->window_size / 2)) {
+                return ps;
+            } else {
+                pulse_queue_enqueue(pde, ts);
+                ps->dur = delta_ts;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * pri detector ops to detect long radar waveform
+ */
+static struct pri_detector_ops pri_detector_long = {
+    .init = pde_long_init,
+    .add_pulse = pde_long_add_pulse,
+    .reset_on_pri_overflow = 0,
+};
+
+
+/***************************************************************************
+ * PRI detector init/reset/exit/get
+ **************************************************************************/
+/**
+ * pri_detector_init- Create a new pri_detector
+ *
+ * @dpd: dfs_pattern_detector instance pointer
+ * @radar_type: index of radar pattern
+ * @freq: Frequency of the pri detector
+ */
+struct pri_detector *pri_detector_init(struct dfs_pattern_detector *dpd,
+                                       u16 radar_type, u16 freq)
+{
+    struct pri_detector *pde;
+
+    pde = kzalloc(sizeof(*pde), GFP_ATOMIC);
+    if (pde == NULL)
+        return NULL;
+
+    INIT_LIST_HEAD(&pde->sequences);
+    INIT_LIST_HEAD(&pde->pulses);
+    INIT_LIST_HEAD(&pde->head);
+    list_add(&pde->head, &dpd->detectors[radar_type]);
+
+    pde->rs = &dpd->radar_spec[radar_type];
+    pde->freq = freq;
+
+    if (pde->rs->type == RADAR_WAVEFORM_LONG) {
+        /* for LONG WAVEFORM */
+        pde->ops = &pri_detector_long;
+    } else {
+        /* for SHORT, WEATHER and INTERLEAVED */
+        pde->ops = &pri_detector_short;
+    }
+
+    /* Init dependent of specs */
+    pde->ops->init(pde);
+
+    pool_register_ref();
+    return pde;
+}
+
+/**
+ * pri_detector_reset - Reset pri_detector
+ *
+ * @pde: pointer on pri_detector
+ * @ts: New ts reference for the pri_detector
+ *
+ * free pulse queue and sequences list and give objects back to pools
+ */
+static
+void pri_detector_reset(struct pri_detector *pde, u64 ts)
+{
+    struct pri_sequence *ps, *ps0;
+    struct pulse_elem *p, *p0;
+    list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {
+        list_del_init(&ps->head);
+        pool_put_pseq_elem(ps);
+    }
+    list_for_each_entry_safe(p, p0, &pde->pulses, head) {
+        list_del_init(&p->head);
+        pool_put_pulse_elem(p);
+    }
+    pde->count = 0;
+    pde->last_ts = ts;
+}
+
+/**
+ *  pri_detector_exit - Delete pri_detector
+ *
+ *  @pde: pointer on pri_detector
+ */
+static
+void pri_detector_exit(struct pri_detector *pde)
+{
+    pri_detector_reset(pde, 0);
+    pool_deregister_ref();
+    list_del(&pde->head);
+    kfree(pde);
+}
+
+/**
+ * pri_detector_get() - get pri detector for a given frequency and type
+ * @dpd: dfs_pattern_detector instance pointer
+ * @freq: frequency in MHz
+ * @radar_type: index of radar pattern
+ * @return pointer to pri detector on success, NULL otherwise
+ *
+ * Return existing pri detector for the given frequency or return a
+ * newly create one.
+ * Pri detector are "merged" by frequency so that if a pri detector for a freq
+ * of +/- 2Mhz already exists don't create a new one.
+ *
+ * Maybe will need to adapt frequency merge for pattern with chirp.
+ */
+static struct pri_detector *
+pri_detector_get(struct dfs_pattern_detector *dpd, u16 freq, u16 radar_type)
+{
+    struct pri_detector *pde, *cur = NULL;
+    list_for_each_entry(pde, &dpd->detectors[radar_type], head) {
+        if (pde->freq == freq) {
+            if (pde->count)
+                return pde;
+            else
+                cur = pde;
+        } else if (pde->freq - 2 == freq && pde->count) {
+            return pde;
+        } else if (pde->freq + 2 == freq && pde->count) {
+            return pde;
+        }
+    }
+
+    if (cur)
+        return cur;
+    else
+        return pri_detector_init(dpd, radar_type, freq);
+}
+
+
+/******************************************************************************
+ * DFS Pattern Detector
+ *****************************************************************************/
+/**
+ * dfs_pattern_detector_reset() - reset all channel detectors
+ *
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_reset(struct dfs_pattern_detector *dpd)
+{
+    struct pri_detector *pde;
+    int i;
+
+    for (i = 0; i < dpd->num_radar_types; i++) {
+        if (!list_empty(&dpd->detectors[i]))
+            list_for_each_entry(pde, &dpd->detectors[i], head)
+                pri_detector_reset(pde, dpd->last_pulse_ts);
+    }
+
+    dpd->last_pulse_ts = 0;
+    dpd->prev_jiffies = jiffies;
+}
+
+/**
+ * dfs_pattern_detector_reset() - delete all channel detectors
+ *
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_exit(struct dfs_pattern_detector *dpd)
+{
+    struct pri_detector *pde, *pde0;
+    int i;
+
+    for (i = 0; i < dpd->num_radar_types; i++) {
+        if (!list_empty(&dpd->detectors[i]))
+            list_for_each_entry_safe(pde, pde0, &dpd->detectors[i], head)
+                pri_detector_exit(pde);
+    }
+
+    kfree(dpd);
+}
+
+/**
+ * dfs_pattern_detector_pri_overflow - reset all channel detectors on pri
+ *                                     overflow
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_pri_overflow(struct dfs_pattern_detector *dpd)
+{
+    struct pri_detector *pde;
+    int i;
+
+    for (i = 0; i < dpd->num_radar_types; i++) {
+        if (!list_empty(&dpd->detectors[i]))
+            list_for_each_entry(pde, &dpd->detectors[i], head)
+                if (pde->ops->reset_on_pri_overflow)
+                    pri_detector_reset(pde, dpd->last_pulse_ts);
+    }
+}
+
+/**
+ * dfs_pattern_detector_add_pulse - Process one pulse
+ *
+ * @dpd: dfs_pattern_detector
+ * @chain: Chain that correspond to this pattern_detector (only for debug)
+ * @freq: frequency of the pulse
+ * @pri: Delta with previous pulse. (0 if delta is too big for u16)
+ * @len: width of the pulse
+ * @now: jiffies value when pulse was received
+ *
+ * Get (or create) the channel_detector for this frequency. Then add the pulse
+ * in each pri_detector created in this channel_detector.
+ *
+ *
+ * @return True is the pulse complete a radar pattern, false otherwise
+ */
+static bool dfs_pattern_detector_add_pulse(struct dfs_pattern_detector *dpd,
+                                           enum rwnx_radar_chain chain,
+                                           u16 freq, u16 pri, u16 len, u32 now)
+{
+    u32 i;
+
+    /*
+     * pulses received for a non-supported or un-initialized
+     * domain are treated as detected radars for fail-safety
+     */
+    if (dpd->region == NL80211_DFS_UNSET)
+        return true;
+
+    /* Compute pulse time stamp */
+    if (pri == 0) {
+        u32 delta_jiffie;
+        if (unlikely(now < dpd->prev_jiffies)) {
+            delta_jiffie = 0xffffffff - dpd->prev_jiffies + now;
+        } else {
+            delta_jiffie = now - dpd->prev_jiffies;
+        }
+        dpd->last_pulse_ts += jiffies_to_usecs(delta_jiffie);
+        dpd->prev_jiffies = now;
+        dfs_pattern_detector_pri_overflow(dpd);
+    } else {
+        dpd->last_pulse_ts += pri;
+    }
+
+    for (i = 0; i < dpd->num_radar_types; i++) {
+        struct pri_sequence *ps;
+        struct pri_detector *pde;
+        const struct radar_detector_specs *rs = &dpd->radar_spec[i];
+
+        /* no need to look up for pde if len is not within range */
+        if ((rs->width_min > len) ||
+            (rs->width_max < len)) {
+            continue;
+        }
+
+        pde = pri_detector_get(dpd, freq, i);
+        ps = pde->ops->add_pulse(pde, len, dpd->last_pulse_ts, pri);
+
+        if (ps != NULL) {
+#ifdef CREATE_TRACE_POINTS
+            trace_radar_detected(chain, dpd->region, pde->freq, i, ps->pri);
+#endif
+            // reset everything instead of just the channel detector
+            dfs_pattern_detector_reset(dpd);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+/**
+ * get_dfs_domain_radar_types() - get radar types for a given DFS domain
+ * @param domain DFS domain
+ * @return radar_types ptr on success, NULL if DFS domain is not supported
+ */
+static const struct radar_types *
+get_dfs_domain_radar_types(enum nl80211_dfs_regions region)
+{
+    u32 i;
+    for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
+        if (dfs_domains[i]->region == region)
+            return dfs_domains[i];
+    }
+    return NULL;
+}
+
+/**
+ * get_dfs_max_radar_types() - get maximum radar types for all supported domain
+ * @return the maximum number of radar pattern supported by on region
+ */
+static u16 get_dfs_max_radar_types(void)
+{
+    u32 i;
+    u16 max = 0;
+    for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
+        if (dfs_domains[i]->num_radar_types > max)
+            max = dfs_domains[i]->num_radar_types;
+    }
+    return max;
+}
+
+/**
+ * dfs_pattern_detector_set_domain - set DFS domain
+ *
+ * @dpd: dfs_pattern_detector
+ * @region: DFS region
+ *
+ * set DFS domain, resets detector lines upon domain changes
+ */
+static
+bool dfs_pattern_detector_set_domain(struct dfs_pattern_detector *dpd,
+                                     enum nl80211_dfs_regions region, u8 chain)
+{
+    const struct radar_types *rt;
+    struct pri_detector *pde, *pde0;
+    int i;
+
+    if (dpd->region == region)
+        return true;
+
+    dpd->region = NL80211_DFS_UNSET;
+
+    rt = get_dfs_domain_radar_types(region);
+    if (rt == NULL)
+        return false;
+
+    /* delete all pri detectors for previous DFS domain */
+    for (i = 0; i < dpd->num_radar_types; i++) {
+        if (!list_empty(&dpd->detectors[i]))
+            list_for_each_entry_safe(pde, pde0, &dpd->detectors[i], head)
+                pri_detector_exit(pde);
+    }
+
+    if (chain == RWNX_RADAR_RIU)
+        dpd->radar_spec = rt->spec_riu;
+    else
+        dpd->radar_spec = rt->spec_fcu;
+    dpd->num_radar_types = rt->num_radar_types;
+
+    dpd->region = region;
+    return true;
+}
+
+/**
+ * dfs_pattern_detector_init - Initialize dfs_pattern_detector
+ *
+ * @region: DFS region
+ * @return: pointer on dfs_pattern_detector
+ *
+ */
+static struct dfs_pattern_detector *
+dfs_pattern_detector_init(enum nl80211_dfs_regions region, u8 chain)
+{
+    struct dfs_pattern_detector *dpd;
+    u16 i, max_radar_type = get_dfs_max_radar_types();
+
+    dpd = kmalloc(sizeof(*dpd) + max_radar_type * sizeof(dpd->detectors[0]),
+                  GFP_KERNEL);
+    if (dpd == NULL)
+        return NULL;
+
+    dpd->region = NL80211_DFS_UNSET;
+    dpd->enabled = RWNX_RADAR_DETECT_DISABLE;
+    dpd->last_pulse_ts = 0;
+    dpd->prev_jiffies = jiffies;
+    dpd->num_radar_types = 0;
+    for (i = 0; i < max_radar_type; i++)
+        INIT_LIST_HEAD(&dpd->detectors[i]);
+
+    if (dfs_pattern_detector_set_domain(dpd, region, chain))
+        return dpd;
+
+    kfree(dpd);
+    return NULL;
+}
+
+
+/******************************************************************************
+ * driver interface
+ *****************************************************************************/
+static u16 rwnx_radar_get_center_freq(struct rwnx_hw *rwnx_hw, u8 chain)
+{
+    if (chain == RWNX_RADAR_FCU)
+        return rwnx_hw->phy.sec_chan.center_freq1;
+
+    if (chain == RWNX_RADAR_RIU) {
+#ifdef CONFIG_RWNX_FULLMAC
+        if (!rwnx_chanctx_valid(rwnx_hw, rwnx_hw->cur_chanctx)) {
+            WARN(1, "Radar pulse without channel information");
+        } else
+            return rwnx_hw->chanctx_table[rwnx_hw->cur_chanctx].chan_def.center_freq1;
+#endif /* CONFIG_RWNX_FULLMAC */
+    }
+
+    return 0;
+}
+
+static void rwnx_radar_detected(struct rwnx_hw *rwnx_hw)
+{
+#ifdef CONFIG_RWNX_FULLMAC
+    struct cfg80211_chan_def chan_def;
+
+    if (!rwnx_chanctx_valid(rwnx_hw, rwnx_hw->cur_chanctx)) {
+        WARN(1, "Radar detected without channel information");
+        return;
+    }
+
+    /*
+      recopy chan_def in local variable because rwnx_radar_cancel_cac may
+      clean the variable (if in CAC and it's the only vif using this context)
+      and CAC should be aborted before reporting the radar.
+    */
+    chan_def = rwnx_hw->chanctx_table[rwnx_hw->cur_chanctx].chan_def;
+
+    rwnx_radar_cancel_cac(&rwnx_hw->radar);
+    cfg80211_radar_event(rwnx_hw->wiphy, &chan_def, GFP_KERNEL);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+}
+
+static void rwnx_radar_process_pulse(struct work_struct *ws)
+{
+    struct rwnx_radar *radar = container_of(ws, struct rwnx_radar,
+                                            detection_work);
+    struct rwnx_hw *rwnx_hw = container_of(radar, struct rwnx_hw, radar);
+    int chain;
+    u32 pulses[RWNX_RADAR_LAST][RWNX_RADAR_PULSE_MAX];
+    u16 pulses_count[RWNX_RADAR_LAST];
+    u32 now = jiffies; /* would be better to store jiffies value in IT handler */
+
+    /* recopy pulses locally to avoid too long spin_lock */
+    spin_lock_bh(&radar->lock);
+    for (chain = RWNX_RADAR_RIU; chain < RWNX_RADAR_LAST; chain++) {
+        int start, count;
+
+        count = radar->pulses[chain].count;
+        start = radar->pulses[chain].index - count;
+        if (start < 0)
+            start += RWNX_RADAR_PULSE_MAX;
+
+        pulses_count[chain] = count;
+        if (count == 0)
+            continue;
+
+        if ((start + count) > RWNX_RADAR_PULSE_MAX) {
+            u16 count1 = (RWNX_RADAR_PULSE_MAX - start);
+            memcpy(&(pulses[chain][0]),
+                   &(radar->pulses[chain].buffer[start]),
+                   count1 * sizeof(struct radar_pulse));
+            memcpy(&(pulses[chain][count1]),
+                   &(radar->pulses[chain].buffer[0]),
+                   (count - count1) * sizeof(struct radar_pulse));
+        } else {
+            memcpy(&(pulses[chain][0]),
+                   &(radar->pulses[chain].buffer[start]),
+                   count * sizeof(struct radar_pulse));
+        }
+        radar->pulses[chain].count = 0;
+    }
+    spin_unlock_bh(&radar->lock);
+
+
+    /* now process pulses */
+    for (chain = RWNX_RADAR_RIU; chain < RWNX_RADAR_LAST; chain++) {
+        int i;
+        u16 freq;
+
+        if (pulses_count[chain] == 0)
+            continue;
+
+        freq = rwnx_radar_get_center_freq(rwnx_hw, chain);
+
+        for (i = 0; i < pulses_count[chain] ; i++) {
+            struct radar_pulse *p = (struct radar_pulse *)&pulses[chain][i];
+#ifdef CREATE_TRACE_POINTS
+            trace_radar_pulse(chain, p);
+#endif
+            if (dfs_pattern_detector_add_pulse(radar->dpd[chain], chain,
+                                               (s16)freq + (2 * p->freq),
+                                               p->rep, (p->len * 2), now)) {
+                u16 idx = radar->detected[chain].index;
+
+                if (chain == RWNX_RADAR_RIU) {
+                    /* operating chain, inform upper layer to change channel */
+                    if (radar->dpd[chain]->enabled == RWNX_RADAR_DETECT_REPORT) {
+                        rwnx_radar_detected(rwnx_hw);
+                        /* no need to report new radar until upper layer set a
+                           new channel. This prevent warning if a new radar is
+                           detected while mac80211 is changing channel */
+                        rwnx_radar_detection_enable(radar,
+                                                    RWNX_RADAR_DETECT_DISABLE,
+                                                    chain);
+                        /* purge any event received since the beginning of the
+                           function (we are sure not to interfer with tasklet
+                           as we disable detection just before) */
+                        radar->pulses[chain].count = 0;
+                    }
+                } else {
+                    /* secondary radar detection chain, simply report info in
+                       debugfs for now */
+                }
+
+                radar->detected[chain].freq[idx] = (s16)freq + (2 * p->freq);
+                radar->detected[chain].time[idx] = ktime_get_real_seconds();
+                radar->detected[chain].index = ((idx + 1 ) %
+                                                NX_NB_RADAR_DETECTED);
+                radar->detected[chain].count++;
+                /* no need to process next pulses for this chain */
+                break;
+             }
+        }
+    }
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+static void rwnx_radar_cac_work(struct work_struct *ws)
+{
+    struct delayed_work *dw = container_of(ws, struct delayed_work, work);
+    struct rwnx_radar *radar = container_of(dw, struct rwnx_radar, cac_work);
+    struct rwnx_hw *rwnx_hw = container_of(radar, struct rwnx_hw, radar);
+    struct rwnx_chanctx *ctxt;
+
+    if (radar->cac_vif == NULL) {
+        WARN(1, "CAC finished but no vif set");
+        return;
+    }
+
+    ctxt = &rwnx_hw->chanctx_table[radar->cac_vif->ch_index];
+    cfg80211_cac_event(radar->cac_vif->ndev,
+                    #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+                       &ctxt->chan_def,
+                    #endif
+                       NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+    rwnx_send_apm_stop_cac_req(rwnx_hw, radar->cac_vif);
+    rwnx_chanctx_unlink(radar->cac_vif);
+
+    radar->cac_vif = NULL;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+bool rwnx_radar_detection_init(struct rwnx_radar *radar)
+{
+    spin_lock_init(&radar->lock);
+
+    radar->dpd[RWNX_RADAR_RIU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
+                                                           RWNX_RADAR_RIU);
+    if (radar->dpd[RWNX_RADAR_RIU] == NULL)
+        return false;
+
+    radar->dpd[RWNX_RADAR_FCU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
+                                                           RWNX_RADAR_FCU);
+    if (radar->dpd[RWNX_RADAR_FCU] == NULL) {
+        rwnx_radar_detection_deinit(radar);
+        return false;
+    }
+
+    INIT_WORK(&radar->detection_work, rwnx_radar_process_pulse);
+#ifdef CONFIG_RWNX_FULLMAC
+    INIT_DELAYED_WORK(&radar->cac_work, rwnx_radar_cac_work);
+    radar->cac_vif = NULL;
+#endif /* CONFIG_RWNX_FULLMAC */
+    return true;
+}
+
+void rwnx_radar_detection_deinit(struct rwnx_radar *radar)
+{
+    if (radar->dpd[RWNX_RADAR_RIU]) {
+        dfs_pattern_detector_exit(radar->dpd[RWNX_RADAR_RIU]);
+        radar->dpd[RWNX_RADAR_RIU] = NULL;
+    }
+    if (radar->dpd[RWNX_RADAR_FCU]) {
+        dfs_pattern_detector_exit(radar->dpd[RWNX_RADAR_FCU]);
+        radar->dpd[RWNX_RADAR_FCU] = NULL;
+    }
+}
+
+bool rwnx_radar_set_domain(struct rwnx_radar *radar,
+                           enum nl80211_dfs_regions region)
+{
+    if (radar->dpd[0] == NULL)
+        return false;
+#ifdef CREATE_TRACE_POINTS
+    trace_radar_set_region(region);
+#endif
+    return (dfs_pattern_detector_set_domain(radar->dpd[RWNX_RADAR_RIU],
+                                            region, RWNX_RADAR_RIU) &&
+            dfs_pattern_detector_set_domain(radar->dpd[RWNX_RADAR_FCU],
+                                            region, RWNX_RADAR_FCU));
+}
+
+void rwnx_radar_detection_enable(struct rwnx_radar *radar, u8 enable, u8 chain)
+{
+    if (chain < RWNX_RADAR_LAST ) {
+#ifdef CREATE_TRACE_POINTS
+        trace_radar_enable_detection(radar->dpd[chain]->region, enable, chain);
+#endif
+        spin_lock_bh(&radar->lock);
+        radar->dpd[chain]->enabled = enable;
+        spin_unlock_bh(&radar->lock);
+    }
+}
+
+bool rwnx_radar_detection_is_enable(struct rwnx_radar *radar, u8 chain)
+{
+    return radar->dpd[chain]->enabled != RWNX_RADAR_DETECT_DISABLE;
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_radar_start_cac(struct rwnx_radar *radar, u32 cac_time_ms,
+                          struct rwnx_vif *vif)
+{
+    WARN(radar->cac_vif != NULL, "CAC already in progress");
+    radar->cac_vif = vif;
+    schedule_delayed_work(&radar->cac_work, msecs_to_jiffies(cac_time_ms));
+}
+
+void rwnx_radar_cancel_cac(struct rwnx_radar *radar)
+{
+    struct rwnx_hw *rwnx_hw = container_of(radar, struct rwnx_hw, radar);
+
+    if (radar->cac_vif == NULL) {
+        return;
+    }
+
+    if (cancel_delayed_work(&radar->cac_work)) {
+        struct rwnx_chanctx *ctxt;
+        ctxt = &rwnx_hw->chanctx_table[radar->cac_vif->ch_index];
+        rwnx_send_apm_stop_cac_req(rwnx_hw, radar->cac_vif);
+        cfg80211_cac_event(radar->cac_vif->ndev,
+                        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+                           &ctxt->chan_def,
+                        #endif
+                           NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
+        rwnx_chanctx_unlink(radar->cac_vif);
+    }
+
+    radar->cac_vif = NULL;
+}
+
+void rwnx_radar_detection_enable_on_cur_channel(struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_chanctx *ctxt;
+
+    /* If no information on current channel do nothing */
+    if (!rwnx_chanctx_valid(rwnx_hw, rwnx_hw->cur_chanctx))
+        return;
+
+    ctxt = &rwnx_hw->chanctx_table[rwnx_hw->cur_chanctx];
+    if (ctxt->chan_def.chan->flags & IEEE80211_CHAN_RADAR) {
+        rwnx_radar_detection_enable(&rwnx_hw->radar,
+                                    RWNX_RADAR_DETECT_REPORT,
+                                    RWNX_RADAR_RIU);
+    } else {
+        rwnx_radar_detection_enable(&rwnx_hw->radar,
+                                    RWNX_RADAR_DETECT_DISABLE,
+                                    RWNX_RADAR_RIU);
+    }
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/*****************************************************************************
+ * Debug functions
+ *****************************************************************************/
+static
+int rwnx_radar_dump_pri_detector(char *buf, size_t len,
+                                 struct pri_detector *pde)
+{
+    char freq_info[] = "Freq = %3.dMhz\n";
+    char seq_info[] = " pri    | count | false \n";
+    struct pri_sequence *seq;
+    int res, write = 0;
+
+    if (list_empty(&pde->sequences)) {
+        return 0;
+    }
+
+    if (buf == NULL) {
+        int nb_seq = 1;
+        list_for_each_entry(seq, &pde->sequences, head) {
+            nb_seq++;
+        }
+
+        return (sizeof(freq_info) + nb_seq * sizeof(seq_info));
+    }
+
+    res = scnprintf(buf, len, freq_info, pde->freq);
+    write += res;
+    len -= res;
+
+    res = scnprintf(&buf[write], len, "%s", seq_info);
+    write += res;
+    len -= res;
+
+    list_for_each_entry(seq, &pde->sequences, head) {
+        res = scnprintf(&buf[write], len, " %6.d |   %2.d  |    %.2d \n",
+                        seq->pri, seq->count, seq->count_falses);
+        write += res;
+        len -= res;
+    }
+
+    return write;
+}
+
+int rwnx_radar_dump_pattern_detector(char *buf, size_t len,
+                                     struct rwnx_radar *radar, u8 chain)
+{
+    struct dfs_pattern_detector *dpd = radar->dpd[chain];
+    char info[] = "Type = %3.d\n";
+    struct pri_detector *pde;
+    int i, res, write = 0;
+
+    /* if buf is NULL return size needed for dump */
+    if (buf == NULL) {
+        int size_needed = 0;
+
+        for (i = 0; i < dpd->num_radar_types; i++) {
+            list_for_each_entry(pde, &dpd->detectors[i], head) {
+                size_needed += rwnx_radar_dump_pri_detector(NULL, 0, pde);
+            }
+            size_needed += sizeof(info);
+
+        return size_needed;
+        }
+    }
+
+    /* */
+    for (i = 0; i < dpd->num_radar_types; i++) {
+        res = scnprintf(&buf[write], len, info, i);
+
+        write += res;
+        len -= res;
+        list_for_each_entry(pde, &dpd->detectors[i], head) {
+            res = rwnx_radar_dump_pri_detector(&buf[write], len, pde);
+            write += res;
+            len -= res;
+        }
+    }
+
+    return write;
+}
+
+
+int rwnx_radar_dump_radar_detected(char *buf, size_t len,
+                                   struct rwnx_radar *radar, u8 chain)
+{
+    struct rwnx_radar_detected *detect = &(radar->detected[chain]);
+    char info[] = "2001/02/02 - 02:20 5126MHz\n";
+    int idx, i, res, write = 0;
+    int count = detect->count;
+
+    if (count > NX_NB_RADAR_DETECTED)
+        count = NX_NB_RADAR_DETECTED;
+
+    if (buf == NULL) {
+        return (count * sizeof(info)) + 1;
+     }
+
+    idx = (detect->index - detect->count) % NX_NB_RADAR_DETECTED;
+
+    for (i = 0; i < count; i++) {
+        struct tm tm;
+        time64_to_tm(detect->time[idx], 0, &tm);
+
+        res = scnprintf(&buf[write], len,
+                        "%.4d/%.2d/%.2d - %.2d:%.2d %4.4dMHz\n",
+                        (int)tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+                        tm.tm_hour, tm.tm_min, detect->freq[idx]);
+        write += res;
+        len -= res;
+
+        idx = (idx + 1 ) % NX_NB_RADAR_DETECTED;
+    }
+
+    return write;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_radar.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_radar.h
new file mode 100755
index 0000000..355320f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_radar.h
@@ -0,0 +1,160 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_radar.h
+ *
+ * @brief Functions to handle radar detection
+ *
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#ifndef _RWNX_RADAR_H_
+#define _RWNX_RADAR_H_
+
+#include <linux/nl80211.h>
+
+struct rwnx_vif;
+struct rwnx_hw;
+
+enum rwnx_radar_chain {
+    RWNX_RADAR_RIU = 0,
+    RWNX_RADAR_FCU,
+    RWNX_RADAR_LAST
+};
+
+enum rwnx_radar_detector {
+    RWNX_RADAR_DETECT_DISABLE = 0, /* Ignore radar pulses */
+    RWNX_RADAR_DETECT_ENABLE  = 1, /* Process pattern detection but do not
+                                      report radar to upper layer (for test) */
+    RWNX_RADAR_DETECT_REPORT  = 2  /* Process pattern detection and report
+                                      radar to upper layer. */
+};
+
+#ifdef CONFIG_RWNX_RADAR
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+
+#define RWNX_RADAR_PULSE_MAX  32
+
+/**
+ * struct rwnx_radar_pulses - List of pulses reported by HW
+ * @index: write index
+ * @count: number of valid pulses
+ * @buffer: buffer of pulses
+ */
+struct rwnx_radar_pulses {
+    /* Last radar pulses received */
+    int index;
+    int count;
+    u32 buffer[RWNX_RADAR_PULSE_MAX];
+};
+
+/**
+ * struct dfs_pattern_detector - DFS pattern detector
+ * @region: active DFS region, NL80211_DFS_UNSET until set
+ * @num_radar_types: number of different radar types
+ * @last_pulse_ts: time stamp of last valid pulse in usecs
+ * @prev_jiffies:
+ * @radar_detector_specs: array of radar detection specs
+ * @channel_detectors: list connecting channel_detector elements
+ */
+struct dfs_pattern_detector {
+    u8 enabled;
+    enum nl80211_dfs_regions region;
+    u8 num_radar_types;
+    u64 last_pulse_ts;
+    u32 prev_jiffies;
+    const struct radar_detector_specs *radar_spec;
+    struct list_head detectors[];
+};
+
+#define NX_NB_RADAR_DETECTED 4
+
+/**
+ * struct rwnx_radar_detected - List of radar detected
+ */
+struct rwnx_radar_detected {
+    u16 index;
+    u16 count;
+    s64 time[NX_NB_RADAR_DETECTED];
+    s16 freq[NX_NB_RADAR_DETECTED];
+};
+
+
+struct rwnx_radar {
+    struct rwnx_radar_pulses pulses[RWNX_RADAR_LAST];
+    struct dfs_pattern_detector *dpd[RWNX_RADAR_LAST];
+    struct rwnx_radar_detected detected[RWNX_RADAR_LAST];
+    struct work_struct detection_work;  /* Work used to process radar pulses */
+    spinlock_t lock;                    /* lock for pulses processing */
+
+    /* In softmac cac is handled by mac80211 */
+#ifdef CONFIG_RWNX_FULLMAC
+    struct delayed_work cac_work;       /* Work used to handle CAC */
+    struct rwnx_vif *cac_vif;           /* vif on which we started CAC */
+#endif
+};
+
+bool rwnx_radar_detection_init(struct rwnx_radar *radar);
+void rwnx_radar_detection_deinit(struct rwnx_radar *radar);
+bool rwnx_radar_set_domain(struct rwnx_radar *radar,
+                           enum nl80211_dfs_regions region);
+void rwnx_radar_detection_enable(struct rwnx_radar *radar, u8 enable, u8 chain);
+bool rwnx_radar_detection_is_enable(struct rwnx_radar *radar, u8 chain);
+void rwnx_radar_start_cac(struct rwnx_radar *radar, u32 cac_time_ms,
+                          struct rwnx_vif *vif);
+void rwnx_radar_cancel_cac(struct rwnx_radar *radar);
+void rwnx_radar_detection_enable_on_cur_channel(struct rwnx_hw *rwnx_hw);
+int  rwnx_radar_dump_pattern_detector(char *buf, size_t len,
+                                      struct rwnx_radar *radar, u8 chain);
+int  rwnx_radar_dump_radar_detected(char *buf, size_t len,
+                                    struct rwnx_radar *radar, u8 chain);
+
+#else
+
+struct rwnx_radar {
+};
+
+static inline bool rwnx_radar_detection_init(struct rwnx_radar *radar)
+{return true;}
+
+static inline void rwnx_radar_detection_deinit(struct rwnx_radar *radar)
+{}
+
+static inline bool rwnx_radar_set_domain(struct rwnx_radar *radar,
+                                         enum nl80211_dfs_regions region)
+{return true;}
+
+static inline void rwnx_radar_detection_enable(struct rwnx_radar *radar,
+                                               u8 enable, u8 chain)
+{}
+
+static inline bool rwnx_radar_detection_is_enable(struct rwnx_radar *radar,
+                                                 u8 chain)
+{return false;}
+
+static inline void rwnx_radar_start_cac(struct rwnx_radar *radar,
+                                        u32 cac_time_ms, struct rwnx_vif *vif)
+{}
+
+static inline void rwnx_radar_cancel_cac(struct rwnx_radar *radar)
+{}
+
+static inline void rwnx_radar_detection_enable_on_cur_channel(struct rwnx_hw *rwnx_hw)
+{}
+
+static inline int rwnx_radar_dump_pattern_detector(char *buf, size_t len,
+                                                   struct rwnx_radar *radar,
+                                                   u8 chain)
+{return 0;}
+
+static inline int rwnx_radar_dump_radar_detected(char *buf, size_t len,
+                                                 struct rwnx_radar *radar,
+                                                 u8 chain)
+{return 0;}
+
+#endif /* CONFIG_RWNX_RADAR */
+
+#endif // _RWNX_RADAR_H_
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_rx.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_rx.c
new file mode 100755
index 0000000..18cd68e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_rx.c
@@ -0,0 +1,2360 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_rx.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#include <linux/dma-mapping.h>
+#include <linux/ieee80211.h>
+#include <linux/etherdevice.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "rwnx_defs.h"
+#include "rwnx_rx.h"
+#include "rwnx_tx.h"
+#include "rwnx_prof.h"
+#include "ipc_host.h"
+#include "rwnx_events.h"
+#include "rwnx_compat.h"
+#include "aicwf_txrxif.h"
+#ifdef AICWF_ARP_OFFLOAD
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include "rwnx_msg_tx.h"
+#endif
+
+#ifndef IEEE80211_MAX_CHAINS
+#define IEEE80211_MAX_CHAINS 4
+#endif
+
+#define IEEE80211_RTAP_NOISE_EN 1
+
+u8 dhcped = 0;
+
+u16 tx_legrates_lut_rate[] = {
+    10 ,
+    20 ,
+    55 ,
+    110 ,
+    60 ,
+    90 ,
+    120,
+    180,
+    240,
+    360,
+    480,
+    540
+};
+
+
+u16 legrates_lut_rate[] = {
+    10 ,
+    20 ,
+    55 ,
+    110 ,
+    0 ,
+    0 ,
+    0 ,
+    0 ,
+    480 ,
+    240 ,
+    120 ,
+    60 ,
+    540 ,
+    360 ,
+    180 ,
+    90
+};
+
+
+const u8 legrates_lut[] = {
+    0,                          /* 0 */
+    1,                          /* 1 */
+    2,                          /* 2 */
+    3,                          /* 3 */
+    -1,                         /* 4 */
+    -1,                         /* 5 */
+    -1,                         /* 6 */
+    -1,                         /* 7 */
+    10,                         /* 8 */
+    8,                          /* 9 */
+    6,                          /* 10 */
+    4,                          /* 11 */
+    11,                         /* 12 */
+    9,                          /* 13 */
+    7,                          /* 14 */
+    5                           /* 15 */
+};
+
+struct vendor_radiotap_hdr {
+    u8 oui[3];
+    u8 subns;
+    u16 len;
+    u8 data[];
+};
+
+extern int (*fast_from_driver)(struct sk_buff *skb, struct net_device *dev);
+
+
+/**
+ * rwnx_rx_get_vif - Return pointer to the destination vif
+ *
+ * @rwnx_hw: main driver data
+ * @vif_idx: vif index present in rx descriptor
+ *
+ * Select the vif that should receive this frame. Returns NULL if the destination
+ * vif is not active or vif is not specified in the descriptor.
+ */
+static inline
+struct rwnx_vif *rwnx_rx_get_vif(struct rwnx_hw *rwnx_hw, int vif_idx)
+{
+    struct rwnx_vif *rwnx_vif = NULL;
+
+    if (vif_idx < NX_VIRT_DEV_MAX) {
+        rwnx_vif = rwnx_hw->vif_table[vif_idx];
+        if (!rwnx_vif || !rwnx_vif->up)
+            return NULL;
+    }
+
+    return rwnx_vif;
+}
+
+/**
+ * rwnx_rx_vector_convert - Convert a legacy RX vector into a new RX vector format
+ *
+ * @rwnx_hw: main driver data.
+ * @rx_vect1: Rx vector 1 descriptor of the received frame.
+ * @rx_vect2: Rx vector 2 descriptor of the received frame.
+ */
+static void rwnx_rx_vector_convert(struct rwnx_hw *rwnx_hw,
+                                   struct rx_vector_1 *rx_vect1,
+                                   struct rx_vector_2 *rx_vect2)
+{
+    struct rx_vector_1_old rx_vect1_leg;
+    struct rx_vector_2_old rx_vect2_leg;
+    u32_l phy_vers = rwnx_hw->version_cfm.version_phy_2;
+
+    // Check if we need to do the conversion. Only if old modem is used
+    if (__MDM_MAJOR_VERSION(phy_vers) > 0) {
+        rx_vect1->rssi1 = rx_vect1->rssi_leg;
+        return;
+    }
+
+    // Copy the received vector locally
+    memcpy(&rx_vect1_leg, rx_vect1, sizeof(struct rx_vector_1_old));
+
+    // Reset it
+    memset(rx_vect1, 0, sizeof(struct rx_vector_1));
+
+    // Perform the conversion
+    rx_vect1->format_mod = rx_vect1_leg.format_mod;
+    rx_vect1->ch_bw = rx_vect1_leg.ch_bw;
+    rx_vect1->antenna_set = rx_vect1_leg.antenna_set;
+    rx_vect1->leg_length = rx_vect1_leg.leg_length;
+    rx_vect1->leg_rate = rx_vect1_leg.leg_rate;
+    rx_vect1->rssi1 = rx_vect1_leg.rssi1;
+
+    switch (rx_vect1->format_mod) {
+        case FORMATMOD_NON_HT:
+        case FORMATMOD_NON_HT_DUP_OFDM:
+            rx_vect1->leg.lsig_valid = rx_vect1_leg.lsig_valid;
+            rx_vect1->leg.chn_bw_in_non_ht = rx_vect1_leg.num_extn_ss;
+            rx_vect1->leg.dyn_bw_in_non_ht = rx_vect1_leg.dyn_bw;
+            break;
+        case FORMATMOD_HT_MF:
+        case FORMATMOD_HT_GF:
+            rx_vect1->ht.aggregation = rx_vect1_leg.aggregation;
+            rx_vect1->ht.fec = rx_vect1_leg.fec_coding;
+            rx_vect1->ht.lsig_valid = rx_vect1_leg.lsig_valid;
+            rx_vect1->ht.length = rx_vect1_leg.ht_length;
+            rx_vect1->ht.mcs = rx_vect1_leg.mcs;
+            rx_vect1->ht.num_extn_ss = rx_vect1_leg.num_extn_ss;
+            rx_vect1->ht.short_gi = rx_vect1_leg.short_gi;
+            rx_vect1->ht.smoothing = rx_vect1_leg.smoothing;
+            rx_vect1->ht.sounding = rx_vect1_leg.sounding;
+            rx_vect1->ht.stbc = rx_vect1_leg.stbc;
+            break;
+        case FORMATMOD_VHT:
+            rx_vect1->vht.beamformed = !rx_vect1_leg.smoothing;
+            rx_vect1->vht.fec = rx_vect1_leg.fec_coding;
+            rx_vect1->vht.length = rx_vect1_leg.ht_length | rx_vect1_leg._ht_length << 8;
+            rx_vect1->vht.mcs = rx_vect1_leg.mcs & 0x0F;
+            rx_vect1->vht.nss = rx_vect1_leg.stbc ? rx_vect1_leg.n_sts/2 : rx_vect1_leg.n_sts;
+            rx_vect1->vht.doze_not_allowed = rx_vect1_leg.doze_not_allowed;
+            rx_vect1->vht.short_gi = rx_vect1_leg.short_gi;
+            rx_vect1->vht.sounding = rx_vect1_leg.sounding;
+            rx_vect1->vht.stbc = rx_vect1_leg.stbc;
+            rx_vect1->vht.group_id = rx_vect1_leg.group_id;
+            rx_vect1->vht.partial_aid = rx_vect1_leg.partial_aid;
+            rx_vect1->vht.first_user = rx_vect1_leg.first_user;
+            break;
+    }
+
+    if (!rx_vect2)
+        return;
+
+    // Copy the received vector 2 locally
+    memcpy(&rx_vect2_leg, rx_vect2, sizeof(struct rx_vector_2_old));
+
+    // Reset it
+    memset(rx_vect2, 0, sizeof(struct rx_vector_2));
+
+    rx_vect2->rcpi1 = rx_vect2_leg.rcpi;
+    rx_vect2->rcpi2 = rx_vect2_leg.rcpi;
+    rx_vect2->rcpi3 = rx_vect2_leg.rcpi;
+    rx_vect2->rcpi4 = rx_vect2_leg.rcpi;
+
+    rx_vect2->evm1 = rx_vect2_leg.evm1;
+    rx_vect2->evm2 = rx_vect2_leg.evm2;
+    rx_vect2->evm3 = rx_vect2_leg.evm3;
+    rx_vect2->evm4 = rx_vect2_leg.evm4;
+}
+
+/**
+ * rwnx_rx_statistic - save some statistics about received frames
+ *
+ * @rwnx_hw: main driver data.
+ * @hw_rxhdr: Rx Hardware descriptor of the received frame.
+ * @sta: STA that sent the frame.
+ */
+static void rwnx_rx_statistic(struct rwnx_hw *rwnx_hw, struct hw_rxhdr *hw_rxhdr,
+                              struct rwnx_sta *sta)
+{
+#if 1 //def CONFIG_RWNX_DEBUGFS
+    struct rwnx_stats *stats = &rwnx_hw->stats;
+    struct rwnx_rx_rate_stats *rate_stats = &sta->stats.rx_rate;
+    struct rx_vector_1 *rxvect = &hw_rxhdr->hwvect.rx_vect1;
+    int mpdu, ampdu, mpdu_prev, rate_idx;
+
+    /* save complete hwvect */
+    sta->stats.last_rx = hw_rxhdr->hwvect;
+
+    /* update ampdu rx stats */
+    mpdu = hw_rxhdr->hwvect.mpdu_cnt;
+    ampdu = hw_rxhdr->hwvect.ampdu_cnt;
+    mpdu_prev = stats->ampdus_rx_map[ampdu];
+
+    if (mpdu_prev < mpdu ) {
+        stats->ampdus_rx_miss += mpdu - mpdu_prev - 1;
+    } else {
+        stats->ampdus_rx[mpdu_prev]++;
+    }
+    stats->ampdus_rx_map[ampdu] = mpdu;
+
+    /* update rx rate statistic */
+    if (!rate_stats->size)
+        return;
+
+    if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) {
+        int mcs;
+        int bw = rxvect->ch_bw;
+        int sgi;
+        int nss;
+        switch (rxvect->format_mod) {
+            case FORMATMOD_HT_MF:
+            case FORMATMOD_HT_GF:
+                mcs = rxvect->ht.mcs % 8;
+                nss = rxvect->ht.mcs / 8;
+                sgi = rxvect->ht.short_gi;
+                rate_idx = N_CCK + N_OFDM + nss * 32 + mcs * 4 +  bw * 2 + sgi;
+                break;
+            case FORMATMOD_VHT:
+                mcs = rxvect->vht.mcs;
+                nss = rxvect->vht.nss;
+                sgi = rxvect->vht.short_gi;
+                rate_idx = N_CCK + N_OFDM + N_HT + nss * 80 + mcs * 8 + bw * 2 + sgi;
+                break;
+            case FORMATMOD_HE_SU:
+                mcs = rxvect->he.mcs;
+                nss = rxvect->he.nss;
+                sgi = rxvect->he.gi_type;
+                rate_idx = N_CCK + N_OFDM + N_HT + N_VHT + nss * 144 + mcs * 12 + bw * 3 + sgi;
+                break;
+            default:
+                mcs = rxvect->he.mcs;
+                nss = rxvect->he.nss;
+                sgi = rxvect->he.gi_type;
+                rate_idx = N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU
+                                                + nss * 216 + mcs * 18 + rxvect->he.ru_size * 3 + sgi;
+                break;
+        }
+    } else {
+        int idx = legrates_lut[rxvect->leg_rate];
+        if (idx < 4) {
+            rate_idx = idx * 2 + rxvect->pre_type;
+        } else {
+            rate_idx = N_CCK + idx - 4;
+        }
+    }
+    if (rate_idx < rate_stats->size) {
+        if (!rate_stats->table[rate_idx])
+            rate_stats->rate_cnt++;
+        rate_stats->table[rate_idx]++;
+        rate_stats->cpt++;
+    } else {
+        wiphy_err(rwnx_hw->wiphy, "RX: Invalid index conversion => %d/%d\n",
+                  rate_idx, rate_stats->size);
+    }
+#endif
+}
+
+/**
+ * rwnx_rx_data_skb - Process one data frame
+ *
+ * @rwnx_hw: main driver data
+ * @rwnx_vif: vif that received the buffer
+ * @skb: skb received
+ * @rxhdr: HW rx descriptor
+ * @return: true if buffer has been forwarded to upper layer
+ *
+ * If buffer is amsdu , it is first split into a list of skb.
+ * Then each skb may be:
+ * - forwarded to upper layer
+ * - resent on wireless interface
+ *
+ * When vif is a STA interface, every skb is only forwarded to upper layer.
+ * When vif is an AP interface, multicast skb are forwarded and resent, whereas
+ * skb for other BSS's STA are only resent.
+ */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+#define RAISE_RX_SOFTIRQ() \
+    cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
+#endif /* LINUX_VERSION_CODE  */
+
+void rwnx_rx_data_skb_resend(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+							 struct sk_buff *skb,  struct hw_rxhdr *rxhdr)
+{
+	struct sk_buff *rx_skb = skb;
+	bool amsdu = rxhdr->flags_is_amsdu;
+
+	rx_skb->dev = rwnx_vif->ndev;
+	const struct ethhdr *eth;
+	skb_reset_mac_header(rx_skb);
+	eth = eth_hdr(rx_skb);
+
+    //printk("resend\n");
+	/* resend pkt on wireless interface */ 
+	struct sk_buff *skb_copy;
+	/* always need to copy buffer when forward=0 to get enough headrom for tsdesc */
+	skb_copy = skb_copy_expand(rx_skb, sizeof(struct rwnx_txhdr) +
+		RWNX_SWTXHDR_ALIGN_SZ + 3 + 24 + 8, 0, GFP_ATOMIC);
+
+	if (skb_copy) {
+		int res;
+		skb_copy->protocol = htons(ETH_P_802_3);
+		skb_reset_network_header(skb_copy);
+		skb_reset_mac_header(skb_copy);
+
+		rwnx_vif->is_resending = true;
+		res = dev_queue_xmit(skb_copy);
+		rwnx_vif->is_resending = false;
+		/* note: buffer is always consummed by dev_queue_xmit */
+		if (res == NET_XMIT_DROP) {
+			rwnx_vif->net_stats.rx_dropped++;
+			rwnx_vif->net_stats.tx_dropped++;
+		} else if (res != NET_XMIT_SUCCESS) {
+			netdev_err(rwnx_vif->ndev,
+					   "Failed to re-send buffer to driver (res=%d)",
+					   res);
+			rwnx_vif->net_stats.tx_errors++;
+		}
+	} else {
+		netdev_err(rwnx_vif->ndev, "Failed to copy skb");
+	}
+}
+
+void aicwf_fast_from_driver(struct sk_buff *rx_skb, struct rwnx_vif *rwnx_vif)
+{
+#ifdef FAST_FROM_DRIVER
+	int ret;
+
+	do {
+		unsigned long offset = ((unsigned long)(rx_skb->data+sizeof(struct ethhdr)))&3;
+		if(offset == 0)
+			break;
+
+		if(skb_headroom(rx_skb) < offset)
+			break;
+
+		u8_l *newhead = skb_push(rx_skb, offset);
+		memmove(newhead, newhead + offset, rx_skb->len - offset);
+		skb_trim(rx_skb, rx_skb->len - offset);
+	} while(0);
+
+	rx_skb->protocol = eth_type_trans(rx_skb, rwnx_vif->ndev);	
+    memset(rx_skb->cb, 0, sizeof(rx_skb->cb));
+    clean_cache(skb_mac_header(rx_skb), rx_skb->len + rx_skb->data - skb_mac_header(rx_skb));
+
+#ifdef CONFIG_FILTER_TCP_ACK
+	filter_rx_tcp_ack(rwnx_vif->rwnx_hw,rx_skb->data, cpu_to_le16(rx_skb->len));
+#endif
+
+	if (fast_from_driver) {
+		rx_skb->data -= 14;
+		rx_skb->len += 14;
+	//	printk("rx:%p, %d\n",rx_skb->data, rx_skb->len);
+		if(((unsigned int)rx_skb->data) % 4 == 0)
+		{
+			//printk("will not happen\n");
+			memmove(rx_skb->data - 2, rx_skb->data, rx_skb->len);
+			rx_skb->data = rx_skb->data - 2;
+		}
+		rx_skb->dev = rwnx_vif->ndev;
+		ret = fast_from_driver(rx_skb,rx_skb->dev);
+		if(!ret)
+		{
+			rx_skb->data += 14;
+			rx_skb->len -= 14;
+	//		printk("NET:%d\n", rx_skb->len);
+		#if 0
+		    struct iphdr *iphead = (struct iphdr *)(rx_skb->data);
+		    struct tcphdr *tcph;
+	        if(iphead->protocol == IPPROTO_TCP) { // TCP
+	            tcph = (struct tcphdr *)((u8 *)iphead + (iphead->ihl << 2));
+	            if((tcph->source == 0x1389) || (tcph->source == 0x8913)
+	                || (tcph->dest == 0x1389) || (tcph->dest == 0x8913)) { 
+					    printk("\nRX1-5001\n");
+	            }else
+                    printk("RX1:%x,%x\n", ntohs(tcph->source), ntohs(tcph->dest));
+	        }
+		#endif
+            
+			netif_rx(rx_skb);
+		}
+	} else {
+		netif_rx(rx_skb);
+	}
+#else
+	rx_skb->protocol = eth_type_trans(rx_skb, rwnx_vif->ndev);	
+        memset(rx_skb->cb, 0, sizeof(rx_skb->cb));
+			do {
+		size_t offset = (size_t)rx_skb->data&3;
+		if(offset == 0)
+			break;
+
+		if(skb_headroom(rx_skb) < offset)
+			break;
+
+		u8_l *newhead = skb_push(rx_skb, offset);
+		memmove(newhead, newhead + offset, rx_skb->len - offset);
+		skb_trim(rx_skb, rx_skb->len - offset);
+	} while(0);
+	#if 0 //modify by aic
+    netif_receive_skb(rx_skb);
+	#else
+    if (in_interrupt()) {
+        netif_rx(rx_skb);
+    } else {
+    /*
+    * If the receive is not processed inside an ISR, the softirqd must be woken explicitly to service the NET_RX_SOFTIRQ.
+    * * In 2.6 kernels, this is handledby netif_rx_ni(), but in earlier kernels, we need to do it manually.
+    */
+	#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+        netif_rx_ni(rx_skb);
+	#else
+        ulong flags;
+        netif_rx(rx_skb);
+        local_irq_save(flags);
+        RAISE_RX_SOFTIRQ();
+        local_irq_restore(flags);
+	#endif
+    }
+	#endif
+#endif
+}
+
+static void rwnx_rx_data_skb_forward(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+							 struct sk_buff *skb,  struct hw_rxhdr *rxhdr)
+{
+	struct sk_buff *rx_skb;
+
+    rx_skb = skb;
+	rx_skb->dev = rwnx_vif->ndev;
+    skb_reset_mac_header(rx_skb);
+
+	aicwf_fast_from_driver(rx_skb, rwnx_vif);
+
+
+	/* Update statistics */
+	rwnx_vif->net_stats.rx_packets++;
+	rwnx_vif->net_stats.rx_bytes += rx_skb->len;
+	rwnx_hw->stats.last_rx = jiffies;
+}
+
+static bool rwnx_rx_data_skb(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                             struct sk_buff *skb,  struct hw_rxhdr *rxhdr)
+{
+    struct sk_buff_head list;
+    struct sk_buff *rx_skb;
+    bool amsdu = rxhdr->flags_is_amsdu;
+    bool resend = false, forward = true;
+
+    skb->dev = rwnx_vif->ndev;
+
+    __skb_queue_head_init(&list);
+
+    if (amsdu) {
+        int count;
+        ieee80211_amsdu_to_8023s(skb, &list, rwnx_vif->ndev->dev_addr,
+                                 RWNX_VIF_TYPE(rwnx_vif), 0, NULL, NULL);
+
+        count = skb_queue_len(&list);
+        if (count > ARRAY_SIZE(rwnx_hw->stats.amsdus_rx))
+            count = ARRAY_SIZE(rwnx_hw->stats.amsdus_rx);
+        rwnx_hw->stats.amsdus_rx[count - 1]++;
+    } else {
+        rwnx_hw->stats.amsdus_rx[0]++;
+        __skb_queue_head(&list, skb);
+    }
+
+    if (((RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP) ||
+         (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP_VLAN) ||
+         (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_GO)) &&
+        !(rwnx_vif->ap.flags & RWNX_AP_ISOLATE)) {
+        const struct ethhdr *eth;
+        rx_skb = skb_peek(&list);
+        skb_reset_mac_header(rx_skb);
+        eth = eth_hdr(rx_skb);
+
+        if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
+            /* broadcast pkt need to be forwared to upper layer and resent
+               on wireless interface */
+            resend = true;
+        } else {
+            /* unicast pkt for STA inside the BSS, no need to forward to upper
+               layer simply resend on wireless interface */
+            if (rxhdr->flags_dst_idx != RWNX_INVALID_STA)
+            {
+                struct rwnx_sta *sta = &rwnx_hw->sta_table[rxhdr->flags_dst_idx];
+                if (sta->valid && (sta->vlan_idx == rwnx_vif->vif_index))
+                {
+                    forward = false;
+                    resend = true;
+                }
+            }
+        }
+    } else if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MESH_POINT) {
+        const struct ethhdr *eth;
+        rx_skb = skb_peek(&list);
+        skb_reset_mac_header(rx_skb);
+        eth = eth_hdr(rx_skb);
+
+        if (!is_multicast_ether_addr(eth->h_dest)) {
+            /* unicast pkt for STA inside the BSS, no need to forward to upper
+               layer simply resend on wireless interface */
+            if (rxhdr->flags_dst_idx != RWNX_INVALID_STA)
+            {
+                forward = false;
+                resend = true;
+            }
+        }
+    }
+
+    while (!skb_queue_empty(&list)) {
+        rx_skb = __skb_dequeue(&list);
+
+        /* resend pkt on wireless interface */
+        if (resend) {
+            struct sk_buff *skb_copy;
+            /* always need to copy buffer when forward=0 to get enough headrom for tsdesc */
+            skb_copy = skb_copy_expand(rx_skb, sizeof(struct rwnx_txhdr) +
+                                       RWNX_SWTXHDR_ALIGN_SZ + 3 + 24 + 8, 0, GFP_ATOMIC);
+
+            if (skb_copy) {
+                int res;
+                skb_copy->protocol = htons(ETH_P_802_3);
+                skb_reset_network_header(skb_copy);
+                skb_reset_mac_header(skb_copy);
+
+                rwnx_vif->is_resending = true;
+                res = dev_queue_xmit(skb_copy);
+                rwnx_vif->is_resending = false;
+                /* note: buffer is always consummed by dev_queue_xmit */
+                if (res == NET_XMIT_DROP) {
+                    rwnx_vif->net_stats.rx_dropped++;
+                    rwnx_vif->net_stats.tx_dropped++;
+                } else if (res != NET_XMIT_SUCCESS) {
+                    netdev_err(rwnx_vif->ndev,
+                               "Failed to re-send buffer to driver (res=%d)",
+                               res);
+                    rwnx_vif->net_stats.tx_errors++;
+                }
+            } else {
+                netdev_err(rwnx_vif->ndev, "Failed to copy skb");
+            }
+        }
+
+        /* forward pkt to upper layer */
+        if (forward) {
+	    //printk("rx:%x, %x\n", rx_skb->data[12], rx_skb->data[13]);
+		#if 0
+			struct iphdr *iphead = (struct iphdr *)(rx_skb->data + 14);
+			struct udphdr *udph;
+			struct DHCPInfo *dhcph;
+			if(rx_skb->protocol == htons(ETH_P_IP)) { // IP
+			    if(iphead->protocol == IPPROTO_UDP) { // UDP
+			        udph = (struct udphdr *)((u8 *)iphead + (iphead->ihl << 2));
+			        if((udph->source == __constant_htons(SERVER_PORT))
+			            && (udph->dest == __constant_htons(CLIENT_PORT))) { // DHCP offset/ack
+			            dhcph = (struct DHCPInfo *)((u8 *)udph + sizeof(struct udphdr));
+			            if(dhcph->cookie == __constant_htonl(DHCP_MAGIC) && dhcph->op == 2 ){
+							printk("\ndata_skb:dhcp\n");
+			            }
+			        }
+			    }
+			}
+		#endif
+
+         	#if 0
+			    struct iphdr *iphead2 = (struct iphdr *)(rx_skb->data+14);
+			    struct tcphdr *tcph2;
+			    printk("LEN:%d\n", rx_skb->len);
+		        if(iphead2->protocol == IPPROTO_TCP) { // TCP
+		            tcph2 = (struct tcphdr *)((u8 *)iphead2 + (iphead2->ihl << 2));
+		            if((tcph2->source == 0x1389) || (tcph2->source == 0x8913)
+		                || (tcph2->dest == 0x1389) || (tcph2->dest == 0x8913)) { 
+						    printk("\nRX5001\n");
+		            }else
+                        printk("RX:%x,%x\n", ntohl(tcph2->source), ntohl(tcph2->dest));
+		        }
+		 #endif
+        
+			aicwf_fast_from_driver(rx_skb, rwnx_vif);
+
+            /* Update statistics */
+            rwnx_vif->net_stats.rx_packets++;
+            rwnx_vif->net_stats.rx_bytes += rx_skb->len;
+            rwnx_hw->stats.last_rx = jiffies;
+        }
+    }
+
+    return forward;
+}
+
+#ifdef CONFIG_HE_FOR_OLD_KERNEL
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0))
+const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len,
+				 const u8 *match, int match_len,
+				 int match_offset)
+{
+	const struct element *elem;
+
+	/* match_offset can't be smaller than 2, unless match_len is
+	 * zero, in which case match_offset must be zero as well.
+	 */
+	if (WARN_ON((match_len && match_offset < 2) ||
+		    (!match_len && match_offset)))
+		return NULL;
+
+	for_each_element_id(elem, eid, ies, len) {
+		if (elem->datalen >= match_offset - 2 + match_len &&
+		    !memcmp(elem->data + match_offset - 2, match, match_len))
+			return (void *)elem;
+	}
+
+	return NULL;
+}
+#endif
+static inline const u8 *cfg80211_find_ext_ie(u8 ext_eid, const u8* ies, int len)
+{
+        return cfg80211_find_ie_match(WLAN_EID_EXTENSION, ies, len,
+                                                        &ext_eid, 1, 2);
+}
+
+#endif
+
+/**
+ * rwnx_rx_mgmt - Process one 802.11 management frame
+ *
+ * @rwnx_hw: main driver data
+ * @rwnx_vif: vif to upload the buffer to
+ * @skb: skb received
+ * @rxhdr: HW rx descriptor
+ *
+ * Forward the management frame to a given interface.
+ */
+static void rwnx_rx_mgmt(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                         struct sk_buff *skb,  struct hw_rxhdr *hw_rxhdr)
+{
+    struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+    struct rx_vector_1 *rxvect = &hw_rxhdr->hwvect.rx_vect1;
+
+    //printk("rwnx_rx_mgmt\n");
+
+    #if (defined CONFIG_HE_FOR_OLD_KERNEL) || (defined CONFIG_VHT_FOR_OLD_KERNEL)
+	struct aic_sta *sta = &rwnx_hw->aic_table[rwnx_vif->ap.aic_index];
+	const u8* ie;
+	u32 len;
+
+    if (ieee80211_is_assoc_req(mgmt->frame_control) && rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) {
+        printk("ASSOC_REQ: sta_idx %d MAC %pM\n", rwnx_vif->ap.aic_index, mgmt->sa);
+        sta->sta_idx = rwnx_vif->ap.aic_index;
+        len = skb->len - (mgmt->u.assoc_req.variable - skb->data);
+
+        #ifdef CONFIG_HE_FOR_OLD_KERNEL
+        struct ieee80211_he_cap_elem *he;
+        ie = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, mgmt->u.assoc_req.variable, len);
+        if (ie && ie[1] >= sizeof(*he) + 1) {
+            printk("assoc_req: find he\n");
+            sta->he = true;
+        }
+        else {
+            printk("assoc_req: no find he\n");
+            sta->he = false;
+        }
+        #endif
+
+        #ifdef CONFIG_VHT_FOR_OLD_KERNEL
+        struct ieee80211_vht_cap *vht;
+        ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, mgmt->u.assoc_req.variable, len);
+        if (ie && ie[1] >= sizeof(*vht)) {
+            printk("assoc_req: find vht\n");
+            sta->vht = true;
+        } else {
+            printk("assoc_req: no find vht\n");
+            sta->vht = false;
+        }
+        #endif   
+    }
+    #endif
+
+	if (ieee80211_is_mgmt(mgmt->frame_control) &&
+	    (skb->len <= 24 || skb->len > 768)) {
+	    printk("mgmt err\n");
+	    return;
+	}
+	if (ieee80211_is_beacon(mgmt->frame_control)) {
+		if ((RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MESH_POINT) &&
+			hw_rxhdr->flags_new_peer) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
+            cfg80211_notify_new_peer_candidate(rwnx_vif->ndev, mgmt->sa,
+                                               mgmt->u.beacon.variable,
+                                               skb->len - offsetof(struct ieee80211_mgmt,
+                                                                   u.beacon.variable),
+                                               GFP_ATOMIC);
+#else
+            /* TODO: the value of parameter sig_dbm need to be confirmed */
+            cfg80211_notify_new_peer_candidate(rwnx_vif->ndev, mgmt->sa,
+                                               mgmt->u.beacon.variable,
+                                               skb->len - offsetof(struct ieee80211_mgmt,
+                                                                   u.beacon.variable),
+                                               rxvect->rssi1, GFP_ATOMIC);
+#endif
+        } else {
+
+	#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+            cfg80211_report_obss_beacon(rwnx_hw->wiphy, skb->data, skb->len,
+                                        hw_rxhdr->phy_info.phy_prim20_freq,
+                                        rxvect->rssi1, GFP_KERNEL);
+	#else
+	    cfg80211_report_obss_beacon(rwnx_hw->wiphy, skb->data, skb->len,
+                                        hw_rxhdr->phy_info.phy_prim20_freq,
+                                        rxvect->rssi1);
+	#endif
+        }
+    } else if ((ieee80211_is_deauth(mgmt->frame_control) ||
+                ieee80211_is_disassoc(mgmt->frame_control)) &&
+               (mgmt->u.deauth.reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA ||
+                mgmt->u.deauth.reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA)) {
+        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)  // TODO: process unprot mgmt
+        cfg80211_rx_unprot_mlme_mgmt(rwnx_vif->ndev, skb->data, skb->len);
+        #endif
+    } else if ((RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_STATION) &&
+               (ieee80211_is_action(mgmt->frame_control) &&
+                (mgmt->u.action.category == 6))) {
+/*
+        struct cfg80211_ft_event_params ft_event;
+        ft_event.target_ap = (uint8_t *)&mgmt->u.action + ETH_ALEN + 2;
+        ft_event.ies = (uint8_t *)&mgmt->u.action + ETH_ALEN * 2 + 2;
+        ft_event.ies_len = skb->len - (ft_event.ies - (uint8_t *)mgmt);
+        ft_event.ric_ies = NULL;
+        ft_event.ric_ies_len = 0;
+        cfg80211_ft_event(rwnx_vif->ndev, &ft_event);
+*/
+    } else {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+        cfg80211_rx_mgmt(&rwnx_vif->wdev, hw_rxhdr->phy_info.phy_prim20_freq,
+                         rxvect->rssi1, skb->data, skb->len, 0);
+#else
+	cfg80211_rx_mgmt(rwnx_vif->ndev, hw_rxhdr->phy_info.phy_prim20_freq,
+                         rxvect->rssi1, skb->data, skb->len, 0);
+#endif
+    }
+}
+
+/**
+ * rwnx_rx_mgmt_any - Process one 802.11 management frame
+ *
+ * @rwnx_hw: main driver data
+ * @skb: skb received
+ * @rxhdr: HW rx descriptor
+ *
+ * Process the management frame and free the corresponding skb.
+ * If vif is not specified in the rx descriptor, the the frame is uploaded
+ * on all active vifs.
+ */
+static void rwnx_rx_mgmt_any(struct rwnx_hw *rwnx_hw, struct sk_buff *skb,
+                             struct hw_rxhdr *hw_rxhdr)
+{
+    struct rwnx_vif *rwnx_vif;
+    int vif_idx = hw_rxhdr->flags_vif_idx;
+#ifdef CREATE_TRACE_POINTS
+    trace_mgmt_rx(hw_rxhdr->phy_info.phy_prim20_freq, vif_idx,
+                  hw_rxhdr->flags_sta_idx, (struct ieee80211_mgmt *)skb->data);
+#endif
+    if (vif_idx == RWNX_INVALID_VIF) {
+        list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+            if (! rwnx_vif->up)
+                continue;
+            rwnx_rx_mgmt(rwnx_hw, rwnx_vif, skb, hw_rxhdr);
+        }
+    } else {
+        rwnx_vif = rwnx_rx_get_vif(rwnx_hw, vif_idx);
+        if (rwnx_vif)
+            rwnx_rx_mgmt(rwnx_hw, rwnx_vif, skb, hw_rxhdr);
+    }
+
+    dev_kfree_skb(skb);
+}
+
+/**
+ * rwnx_rx_rtap_hdrlen - Return radiotap header length
+ *
+ * @rxvect: Rx vector used to fill the radiotap header
+ * @has_vend_rtap: boolean indicating if vendor specific data is present
+ *
+ * Compute the length of the radiotap header based on @rxvect and vendor
+ * specific data (if any).
+ */
+static u8 rwnx_rx_rtap_hdrlen(struct rx_vector_1 *rxvect,
+                              bool has_vend_rtap)
+{
+    u8 rtap_len;
+
+    /* Compute radiotap header length */
+    rtap_len = sizeof(struct ieee80211_radiotap_header) + 8;
+
+    // Check for multiple antennas
+    if (hweight32(rxvect->antenna_set) > 1)
+        // antenna and antenna signal fields
+        rtap_len += 4 * hweight8(rxvect->antenna_set);
+
+    // TSFT
+    if (!has_vend_rtap) {
+        rtap_len = ALIGN(rtap_len, 8);
+        rtap_len += 8;
+    }
+
+    // IEEE80211_HW_SIGNAL_DBM
+    rtap_len++;
+    #if (IEEE80211_RTAP_NOISE_EN)
+    // IEEE80211_HW_NOISE_DBM
+    rtap_len++;
+    #endif
+
+    // Check if single antenna
+    if (hweight32(rxvect->antenna_set) == 1)
+        rtap_len++; //Single antenna
+
+    // padding for RX FLAGS
+    rtap_len = ALIGN(rtap_len, 2);
+
+    // Check for HT frames
+    if ((rxvect->format_mod == FORMATMOD_HT_MF) ||
+        (rxvect->format_mod == FORMATMOD_HT_GF))
+        rtap_len += 3;
+
+    // Check for AMPDU
+    if (!(has_vend_rtap) && ((rxvect->format_mod >= FORMATMOD_VHT) ||
+                             ((rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) &&
+                                                     (rxvect->ht.aggregation)))) {
+        rtap_len = ALIGN(rtap_len, 4);
+        rtap_len += 8;
+    }
+
+    // Check for VHT frames
+    if (rxvect->format_mod == FORMATMOD_VHT) {
+        rtap_len = ALIGN(rtap_len, 2);
+        rtap_len += 12;
+    }
+
+    // Check for HE frames
+    if (rxvect->format_mod == FORMATMOD_HE_SU) {
+        rtap_len = ALIGN(rtap_len, 2);
+        rtap_len += sizeof(struct ieee80211_radiotap_he);
+    }
+
+    // Check for multiple antennas
+    if (hweight32(rxvect->antenna_set) > 1) {
+        // antenna and antenna signal fields
+        rtap_len += 2 * hweight8(rxvect->antenna_set);
+    }
+
+    // Check for vendor specific data
+    if (has_vend_rtap) {
+        /* vendor presence bitmap */
+        rtap_len += 4;
+        /* alignment for fixed 6-byte vendor data header */
+        rtap_len = ALIGN(rtap_len, 2);
+    }
+
+    return rtap_len;
+}
+
+/**
+ * rwnx_rx_add_rtap_hdr - Add radiotap header to sk_buff
+ *
+ * @rwnx_hw: main driver data
+ * @skb: skb received (will include the radiotap header)
+ * @rxvect: Rx vector
+ * @phy_info: Information regarding the phy
+ * @hwvect: HW Info (NULL if vendor specific data is available)
+ * @rtap_len: Length of the radiotap header
+ * @vend_rtap_len: radiotap vendor length (0 if not present)
+ * @vend_it_present: radiotap vendor present
+ *
+ * Builds a radiotap header and add it to @skb.
+ */
+static void rwnx_rx_add_rtap_hdr(struct rwnx_hw* rwnx_hw,
+                                 struct sk_buff *skb,
+                                 struct rx_vector_1 *rxvect,
+                                 struct phy_channel_info_desc *phy_info,
+                                 struct hw_vect *hwvect,
+                                 int rtap_len,
+                                 u8 vend_rtap_len,
+                                 u32 vend_it_present)
+{
+    struct ieee80211_radiotap_header *rtap;
+    u8 *pos, rate_idx;
+    __le32 *it_present;
+    u32 it_present_val = 0;
+    bool fec_coding = false;
+    bool short_gi = false;
+    bool stbc = false;
+    bool aggregation = false;
+
+    rtap = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
+    memset((u8*) rtap, 0, rtap_len);
+
+    rtap->it_version = 0;
+    rtap->it_pad = 0;
+    rtap->it_len = cpu_to_le16(rtap_len + vend_rtap_len);
+
+    it_present = &rtap->it_present;
+
+    // Check for multiple antennas
+    if (hweight32(rxvect->antenna_set) > 1) {
+        int chain;
+        unsigned long chains = rxvect->antenna_set;
+
+        for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+            it_present_val |=
+                BIT(IEEE80211_RADIOTAP_EXT) |
+                BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE);
+            put_unaligned_le32(it_present_val, it_present);
+            it_present++;
+            it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) |
+                             BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+        }
+    }
+
+    // Check if vendor specific data is present
+    if (vend_rtap_len) {
+        it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
+                          BIT(IEEE80211_RADIOTAP_EXT);
+        put_unaligned_le32(it_present_val, it_present);
+        it_present++;
+        it_present_val = vend_it_present;
+    }
+
+    put_unaligned_le32(it_present_val, it_present);
+    pos = (void *)(it_present + 1);
+
+    // IEEE80211_RADIOTAP_TSFT
+    if (hwvect) {
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
+        // padding
+        while ((pos - (u8 *)rtap) & 7)
+            *pos++ = 0;
+        put_unaligned_le64((((u64)le32_to_cpu(hwvect->tsf_hi) << 32) +
+                            (u64)le32_to_cpu(hwvect->tsf_lo)), pos);
+        pos += 8;
+    }
+
+    // IEEE80211_RADIOTAP_FLAGS
+    rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_FLAGS);
+    if (hwvect && (!hwvect->frm_successful_rx))
+        *pos |= IEEE80211_RADIOTAP_F_BADFCS;
+    if (!rxvect->pre_type
+            && (rxvect->format_mod <= FORMATMOD_NON_HT_DUP_OFDM))
+        *pos |= IEEE80211_RADIOTAP_F_SHORTPRE;
+    pos++;
+
+    // IEEE80211_RADIOTAP_RATE
+    // check for HT, VHT or HE frames
+    if (rxvect->format_mod >= FORMATMOD_HE_SU) {
+        rate_idx = rxvect->he.mcs;
+        fec_coding = rxvect->he.fec;
+        stbc = rxvect->he.stbc;
+        aggregation = true;
+        *pos = 0;
+    } else if (rxvect->format_mod == FORMATMOD_VHT) {
+        rate_idx = rxvect->vht.mcs;
+        fec_coding = rxvect->vht.fec;
+        short_gi = rxvect->vht.short_gi;
+        stbc = rxvect->vht.stbc;
+        aggregation = true;
+        *pos = 0;
+    } else if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) {
+        rate_idx = rxvect->ht.mcs;
+        fec_coding = rxvect->ht.fec;
+        short_gi = rxvect->ht.short_gi;
+        stbc = rxvect->ht.stbc;
+        aggregation = rxvect->ht.aggregation;
+        *pos = 0;
+    } else {
+        struct ieee80211_supported_band* band =
+                rwnx_hw->wiphy->bands[phy_info->phy_band];
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
+        BUG_ON((rate_idx = legrates_lut[rxvect->leg_rate]) == -1);
+        if (phy_info->phy_band == NL80211_BAND_5GHZ)
+            rate_idx -= 4;  /* rwnx_ratetable_5ghz[0].hw_value == 4 */
+        *pos = DIV_ROUND_UP(band->bitrates[rate_idx].bitrate, 5);
+    }
+    pos++;
+
+    // IEEE80211_RADIOTAP_CHANNEL
+    rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL);
+    put_unaligned_le16(phy_info->phy_prim20_freq, pos);
+    pos += 2;
+
+    if (phy_info->phy_band == NL80211_BAND_5GHZ)
+        put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ, pos);
+    else if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM)
+        put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ, pos);
+    else
+        put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ, pos);
+    pos += 2;
+
+    if (hweight32(rxvect->antenna_set) == 1) {
+        // IEEE80211_RADIOTAP_DBM_ANTSIGNAL
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+        *pos++ = rxvect->rssi1;
+
+        #if (IEEE80211_RTAP_NOISE_EN)
+        {
+            u8 snr = hwvect->rx_vect2.evm1;
+            u8 noise = ((u8)rxvect->rssi1 - hwvect->rx_vect2.evm1);
+            // IEEE80211_RADIOTAP_DBM_ANTNOISE
+            rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE);
+            *pos++ = noise;
+        }
+        #endif
+
+        // IEEE80211_RADIOTAP_ANTENNA
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_ANTENNA);
+        *pos++ = rxvect->antenna_set;
+    }
+
+    // IEEE80211_RADIOTAP_LOCK_QUALITY is missing
+    // IEEE80211_RADIOTAP_DB_ANTNOISE is missing
+
+    // IEEE80211_RADIOTAP_RX_FLAGS
+    rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RX_FLAGS);
+    // 2 byte alignment
+    if ((pos - (u8 *)rtap) & 1)
+        *pos++ = 0;
+    put_unaligned_le16(0, pos);
+    //Right now, we only support fcs error (no RX_FLAG_FAILED_PLCP_CRC)
+    pos += 2;
+
+    // Check if HT
+    if ((rxvect->format_mod == FORMATMOD_HT_MF)
+            || (rxvect->format_mod == FORMATMOD_HT_GF)) {
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
+        *pos++ = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
+                 IEEE80211_RADIOTAP_MCS_HAVE_GI |
+                 IEEE80211_RADIOTAP_MCS_HAVE_BW;
+        *pos = 0;
+        if (short_gi)
+            *pos |= IEEE80211_RADIOTAP_MCS_SGI;
+        if (rxvect->ch_bw  == PHY_CHNL_BW_40)
+            *pos |= IEEE80211_RADIOTAP_MCS_BW_40;
+        if (rxvect->format_mod == FORMATMOD_HT_GF)
+            *pos |= IEEE80211_RADIOTAP_MCS_FMT_GF;
+        if (fec_coding)
+            *pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;
+        #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+        *pos++ |= stbc << 5;
+        #else
+        *pos++ |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT;
+        #endif
+        *pos++ = rate_idx;
+    }
+
+    // check for HT or VHT frames
+    if (aggregation && hwvect) {
+        // 4 byte alignment
+        while ((pos - (u8 *)rtap) & 3)
+            pos++;
+        rtap->it_present |=(1 << IEEE80211_RADIOTAP_AMPDU_STATUS); //cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS);
+        put_unaligned_le32(hwvect->ampdu_cnt, pos);
+        pos += 4;
+        put_unaligned_le32(0, pos);
+        pos += 4;
+    }
+
+    // Check for VHT frames
+    if (rxvect->format_mod == FORMATMOD_VHT) {
+        u16 vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
+                          IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+        u8 vht_nss = rxvect->vht.nss + 1;
+
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
+
+        if ((rxvect->ch_bw == PHY_CHNL_BW_160)
+                && phy_info->phy_center2_freq)
+            vht_details &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+        put_unaligned_le16(vht_details, pos);
+        pos += 2;
+
+        // flags
+        if (short_gi)
+            *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+        if (stbc)
+            *pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC;
+        pos++;
+
+        // bandwidth
+        if (rxvect->ch_bw == PHY_CHNL_BW_40)
+            *pos++ = 1;
+        if (rxvect->ch_bw == PHY_CHNL_BW_80)
+            *pos++ = 4;
+        else if ((rxvect->ch_bw == PHY_CHNL_BW_160)
+                && phy_info->phy_center2_freq)
+            *pos++ = 0; //80P80
+        else if  (rxvect->ch_bw == PHY_CHNL_BW_160)
+            *pos++ = 11;
+        else // 20 MHz
+            *pos++ = 0;
+
+        // MCS/NSS
+        *pos = (rate_idx << 4) | vht_nss;
+        pos += 4;
+        if (fec_coding)
+            #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)
+            *pos |= 0x01;
+            #else
+            *pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0;
+            #endif
+        pos++;
+        // group ID
+        pos++;
+        // partial_aid
+        pos += 2;
+    }
+
+    // Check for HE frames
+    if (rxvect->format_mod == FORMATMOD_HE_SU) {
+        struct ieee80211_radiotap_he he;
+        #define HE_PREP(f, val) cpu_to_le16(FIELD_PREP(IEEE80211_RADIOTAP_HE_##f, val))
+        #define D1_KNOWN(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_##f##_KNOWN)
+        #define D2_KNOWN(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_##f##_KNOWN)
+
+        he.data1 = D1_KNOWN(DATA_MCS) | D1_KNOWN(BSS_COLOR) | D1_KNOWN(BEAM_CHANGE) |
+                   D1_KNOWN(UL_DL) | D1_KNOWN(CODING) |  D1_KNOWN(STBC) |
+                   D1_KNOWN(BW_RU_ALLOC) | D1_KNOWN(DOPPLER) | D1_KNOWN(DATA_DCM);
+        he.data2 = D2_KNOWN(GI) | D2_KNOWN(TXBF);
+
+        if (stbc) {
+            he.data6 |= HE_PREP(DATA6_NSTS, 2);
+            he.data3 |= HE_PREP(DATA3_STBC, 1);
+        } else {
+            he.data6 |= HE_PREP(DATA6_NSTS, rxvect->he.nss);
+        }
+
+        he.data3 |= HE_PREP(DATA3_BSS_COLOR, rxvect->he.bss_color);
+        he.data3 |= HE_PREP(DATA3_BEAM_CHANGE, rxvect->he.beam_change);
+        he.data3 |= HE_PREP(DATA3_UL_DL, rxvect->he.uplink_flag);
+        he.data3 |= HE_PREP(DATA3_BSS_COLOR, rxvect->he.bss_color);
+        he.data3 |= HE_PREP(DATA3_DATA_MCS, rxvect->he.mcs);
+        he.data3 |= HE_PREP(DATA3_DATA_DCM, rxvect->he.dcm);
+        he.data3 |= HE_PREP(DATA3_CODING, rxvect->he.fec);
+
+        he.data5 |= HE_PREP(DATA5_GI, rxvect->he.gi_type);
+        he.data5 |= HE_PREP(DATA5_TXBF, rxvect->he.beamformed);
+        he.data5 |= HE_PREP(DATA5_LTF_SIZE, rxvect->he.he_ltf_type + 1);
+
+        switch (rxvect->ch_bw) {
+        case PHY_CHNL_BW_20:
+            he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+                        IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ);
+            break;
+        case PHY_CHNL_BW_40:
+            he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+                        IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ);
+            break;
+        case PHY_CHNL_BW_80:
+            he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+                        IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ);
+            break;
+        case PHY_CHNL_BW_160:
+            he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+                        IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ);
+            break;
+        default:
+            WARN_ONCE(1, "Invalid SU BW %d\n", rxvect->ch_bw);
+        }
+
+        he.data6 |= HE_PREP(DATA6_DOPPLER, rxvect->he.doppler);
+
+        /* ensure 2 byte alignment */
+        while ((pos - (u8 *)rtap) & 1)
+            pos++;
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_HE);
+        memcpy(pos, &he, sizeof(he));
+        pos += sizeof(he);
+    }
+
+    // Rx Chains
+    if (hweight32(rxvect->antenna_set) > 1) {
+        int chain;
+        unsigned long chains = rxvect->antenna_set;
+        u8 rssis[4] = {rxvect->rssi1, rxvect->rssi1, rxvect->rssi1, rxvect->rssi1};
+
+        for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+            *pos++ = rssis[chain];
+            *pos++ = chain;
+        }
+    }
+}
+
+/**
+ * rwnx_rx_monitor - Build radiotap header for skb an send it to netdev
+ *
+ * @rwnx_hw: main driver data
+ * @rwnx_vif: vif that received the buffer
+ * @skb: sk_buff received
+ * @hw_rxhdr_ptr: Pointer to HW RX header
+ * @rtap_len: Radiotap Header length
+ *
+ * Add radiotap header to the receved skb and send it to netdev
+ */
+static int rwnx_rx_monitor(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                           struct sk_buff *skb,  struct hw_rxhdr *hw_rxhdr_ptr,
+                           u8 rtap_len)
+{
+    skb->dev = rwnx_vif->ndev;
+
+    if (rwnx_vif->wdev.iftype != NL80211_IFTYPE_MONITOR) {
+        netdev_err(rwnx_vif->ndev, "not a monitor vif\n");
+        return -1;
+    }
+
+    /* Add RadioTap Header */
+    rwnx_rx_add_rtap_hdr(rwnx_hw, skb, &hw_rxhdr_ptr->hwvect.rx_vect1,
+                         &hw_rxhdr_ptr->phy_info, &hw_rxhdr_ptr->hwvect,
+                         rtap_len, 0, 0);
+
+    skb_reset_mac_header(skb);
+    skb->ip_summed = CHECKSUM_UNNECESSARY;
+    skb->pkt_type = PACKET_OTHERHOST;
+    skb->protocol = htons(ETH_P_802_2);
+
+#ifdef CONFIG_FILTER_TCP_ACK
+	filter_rx_tcp_ack(rwnx_hw,skb->data, cpu_to_le16(skb->len));
+#endif
+
+    netif_receive_skb(skb);
+
+    return 0;
+}
+
+#ifdef AICWF_ARP_OFFLOAD
+void arpoffload_proc(struct sk_buff *skb, struct rwnx_vif *rwnx_vif)
+{
+    struct iphdr *iphead = (struct iphdr *)(skb->data);
+    struct udphdr *udph;
+    struct DHCPInfo *dhcph;
+
+    if(skb->protocol == htons(ETH_P_IP)) { // IP
+        if(iphead->protocol == IPPROTO_UDP) { // UDP
+            udph = (struct udphdr *)((u8 *)iphead + (iphead->ihl << 2));
+            if((udph->source == __constant_htons(SERVER_PORT))
+                && (udph->dest == __constant_htons(CLIENT_PORT))) { // DHCP offset/ack
+                dhcph =	(struct DHCPInfo *)((u8 *)udph + sizeof(struct udphdr));
+                if(dhcph->cookie == htonl(DHCP_MAGIC) && dhcph->op == 2 &&
+                    !memcmp(dhcph->chaddr, rwnx_vif->ndev->dev_addr, 6)) { // match magic word
+                    u32 length = ntohs(udph->len) - sizeof(struct udphdr) - offsetof(struct DHCPInfo, options);
+                    u16 offset = 0;
+                    u8 *option = dhcph->options;
+                    while (option[offset]!= DHCP_OPTION_END && offset<length) {
+                        if (option[offset] == DHCP_OPTION_MESSAGE_TYPE) {
+                            if (option[offset+2] == DHCP_ACK) {
+                                dhcped = 1;
+                                if(rwnx_vif->sta.group_cipher_type == WLAN_CIPHER_SUITE_CCMP)
+                                    rwnx_send_arpoffload_en_req(rwnx_vif->rwnx_hw, rwnx_vif, dhcph->yiaddr, 1);
+                                else
+                                    rwnx_send_arpoffload_en_req(rwnx_vif->rwnx_hw, rwnx_vif, dhcph->yiaddr, 0);
+                             }
+                        }
+                        offset += 2 + option[offset+1];
+                    }
+                }
+            }
+        }
+    }
+}
+#endif
+
+#ifdef AICWF_RX_REORDER
+void reord_rxframe_free(spinlock_t *lock, struct list_head *q, struct list_head *list)
+{
+    spin_lock_bh(lock);
+    list_add(list, q);
+    spin_unlock_bh(lock);
+}
+
+struct recv_msdu *reord_rxframe_alloc(spinlock_t *lock, struct list_head *q)
+{
+    struct recv_msdu *rxframe;
+
+    spin_lock_bh(lock);
+    if (list_empty(q)) {
+        spin_unlock_bh(lock);
+        return NULL;
+    }
+    rxframe = list_entry(q->next, struct recv_msdu, rxframe_list);
+    list_del_init(q->next);
+    spin_unlock_bh(lock);
+    return rxframe;
+}
+
+struct reord_ctrl_info *reord_init_sta(struct aicwf_rx_priv* rx_priv, const u8 *mac_addr)
+{
+    u8 i = 0;
+    struct reord_ctrl *preorder_ctrl = NULL;
+    struct reord_ctrl_info *reord_info;
+#ifdef AICWF_SDIO_SUPPORT
+    struct aicwf_bus *bus_if = rx_priv->sdiodev->bus_if;
+#else
+    struct aicwf_bus *bus_if = rx_priv->usbdev->bus_if;
+#endif
+
+    if (bus_if->state == BUS_DOWN_ST || rx_priv == NULL) {
+        printk("bad stat!\n");
+        return NULL;
+    }
+
+    reord_info = kmalloc(sizeof(struct reord_ctrl_info), GFP_ATOMIC);
+    if (!reord_info)
+        return NULL;
+
+    memcpy(reord_info->mac_addr, mac_addr, ETH_ALEN);
+    for (i=0; i < 8; i++) {
+        preorder_ctrl = &reord_info->preorder_ctrl[i];
+        preorder_ctrl->enable = true;
+        preorder_ctrl->ind_sn = 0xffff;
+        preorder_ctrl->wsize_b = AICWF_REORDER_WINSIZE;
+        preorder_ctrl->rx_priv= rx_priv;
+        INIT_LIST_HEAD(&preorder_ctrl->reord_list);
+        spin_lock_init(&preorder_ctrl->reord_list_lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+        init_timer(&preorder_ctrl->reord_timer);
+        preorder_ctrl->reord_timer.data = (ulong) preorder_ctrl;
+        preorder_ctrl->reord_timer.function = reord_timeout_handler;
+#else
+        timer_setup(&preorder_ctrl->reord_timer, reord_timeout_handler, 0);
+#endif
+        INIT_WORK(&preorder_ctrl->reord_timer_work, reord_timeout_worker);
+    }
+
+    return reord_info;
+}
+
+int reord_flush_tid(struct aicwf_rx_priv *rx_priv, struct sk_buff *skb, u8 tid)
+{
+    struct reord_ctrl_info *reord_info;
+    struct reord_ctrl *preorder_ctrl;
+    struct rwnx_vif *rwnx_vif = (struct rwnx_vif *)rx_priv->rwnx_vif;
+    struct ethhdr *eh = (struct ethhdr *)(skb->data);
+    u8 *mac;
+    unsigned long flags;
+    u8 found = 0;
+    struct list_head *phead, *plist;
+    struct recv_msdu *prframe;
+    int ret;
+
+    if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
+        mac = eh->h_dest;
+    else if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO))
+        mac = eh->h_source;
+    else {
+        printk("error mode:%d!\n", rwnx_vif->wdev.iftype);
+        dev_kfree_skb(skb);
+        return -1;
+    }
+
+    spin_lock_bh(&rx_priv->stas_reord_lock);
+    list_for_each_entry(reord_info, &rx_priv->stas_reord_list, list) {
+        if (!memcmp(mac, reord_info->mac_addr, ETH_ALEN)) {
+            found = 1;
+            preorder_ctrl = &reord_info->preorder_ctrl[tid];
+            break;
+        }
+    }
+    if (!found) {
+        spin_unlock_bh(&rx_priv->stas_reord_lock);
+        return 0;
+    }
+    spin_unlock_bh(&rx_priv->stas_reord_lock);
+
+    if(preorder_ctrl->enable == false)
+        return 0;
+    spin_lock_irqsave(&preorder_ctrl->reord_list_lock, flags);
+    phead = &preorder_ctrl->reord_list;
+    while (1) {
+        if (list_empty(phead)) {
+            break;
+        }
+        plist = phead->next;
+        prframe = list_entry(plist, struct recv_msdu, reord_pending_list);
+        reord_single_frame_ind(rx_priv, prframe);
+        list_del_init(&(prframe->reord_pending_list));
+    }
+
+    //printk("flush:tid=%d", tid);
+    preorder_ctrl->enable = false;
+    spin_unlock_irqrestore(&preorder_ctrl->reord_list_lock, flags);
+    if (timer_pending(&preorder_ctrl->reord_timer))
+        ret = del_timer_sync(&preorder_ctrl->reord_timer);
+    cancel_work_sync(&preorder_ctrl->reord_timer_work);
+
+    return 0;
+}
+
+void reord_deinit_sta(struct aicwf_rx_priv* rx_priv, struct reord_ctrl_info *reord_info)
+{
+    u8 i = 0;
+    unsigned long flags;
+    struct reord_ctrl *preorder_ctrl = NULL;
+    int ret;
+
+    if (rx_priv == NULL) {
+        txrx_err("bad rx_priv!\n");
+        return;
+    }
+
+    for (i=0; i < 8; i++) {
+        struct recv_msdu *req, *next;
+        preorder_ctrl = &reord_info->preorder_ctrl[i];
+        spin_lock_irqsave(&preorder_ctrl->reord_list_lock, flags);
+        list_for_each_entry_safe(req, next, &preorder_ctrl->reord_list, reord_pending_list) {
+            list_del_init(&req->reord_pending_list);
+            if(req->pkt != NULL)
+                dev_kfree_skb(req->pkt);
+            req->pkt = NULL;
+            reord_rxframe_free(&rx_priv->freeq_lock, &rx_priv->rxframes_freequeue, &req->rxframe_list);
+        }
+        //printk("reord dinit");
+        spin_unlock_irqrestore(&preorder_ctrl->reord_list_lock, flags);
+        if (timer_pending(&preorder_ctrl->reord_timer)) {
+            ret = del_timer_sync(&preorder_ctrl->reord_timer);
+        }
+        cancel_work_sync(&preorder_ctrl->reord_timer_work);
+    }
+    list_del(&reord_info->list);
+    kfree(reord_info);
+}
+
+int reord_single_frame_ind(struct aicwf_rx_priv *rx_priv, struct recv_msdu *prframe)
+{
+    struct list_head *rxframes_freequeue = NULL;
+    struct sk_buff *skb = NULL;
+    struct rwnx_vif *rwnx_vif = (struct rwnx_vif *)rx_priv->rwnx_vif;
+
+	rxframes_freequeue = &rx_priv->rxframes_freequeue;
+	skb = prframe->pkt;
+	if (skb == NULL) {
+		txrx_err("skb is NULL\n");
+		return -1;
+	}
+	
+	if(!prframe->forward) {
+		//printk("single: %d not forward: drop\n", prframe->seq_num);
+		dev_kfree_skb(skb);
+		prframe->pkt = NULL;
+		reord_rxframe_free(&rx_priv->freeq_lock, rxframes_freequeue, &prframe->rxframe_list);
+		return 0;
+	}
+
+    skb->data = prframe->rx_data;
+    skb_set_tail_pointer(skb, prframe->len);
+    skb->len = prframe->len;
+    //printk("netif sn=%d, len=%d\n", precv_frame->attrib.seq_num, skb->len);
+
+    skb->dev = rwnx_vif->ndev;
+
+#ifdef AICWF_ARP_OFFLOAD
+    if(RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_STATION || RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_CLIENT) {
+        arpoffload_proc(skb, rwnx_vif);
+    }
+#endif
+	aicwf_fast_from_driver(skb, rwnx_vif);
+
+    rwnx_vif->net_stats.rx_packets++;
+    rwnx_vif->net_stats.rx_bytes += skb->len;
+    prframe->pkt = NULL;
+    reord_rxframe_free(&rx_priv->freeq_lock, rxframes_freequeue, &prframe->rxframe_list);
+
+    return 0;
+}
+
+bool reord_rxframes_process(struct aicwf_rx_priv *rx_priv, struct reord_ctrl *preorder_ctrl, int bforced)
+{
+    struct list_head *phead, *plist;
+    struct recv_msdu *prframe;
+    bool bPktInBuf = false;
+
+    if (bforced == true) {
+        phead = &preorder_ctrl->reord_list;
+        if (list_empty(phead)) {
+            return false;
+        }
+
+        plist = phead->next;
+        prframe = list_entry(plist, struct recv_msdu, reord_pending_list);
+        preorder_ctrl->ind_sn = prframe->seq_num;
+    }
+
+    phead = &preorder_ctrl->reord_list;
+    if (list_empty(phead)) {
+        return bPktInBuf;
+    }
+
+    list_for_each_entry(prframe, phead, reord_pending_list) {
+        if (!SN_LESS(preorder_ctrl->ind_sn, prframe->seq_num)) {
+            if (SN_EQUAL(preorder_ctrl->ind_sn, prframe->seq_num)) {
+                preorder_ctrl->ind_sn = (preorder_ctrl->ind_sn + 1) & 0xFFF;
+            }
+        } else {
+            bPktInBuf = true;
+            break;
+        }
+    }
+
+    return bPktInBuf;
+}
+
+void reord_rxframes_ind(struct aicwf_rx_priv *rx_priv,
+    struct reord_ctrl *preorder_ctrl)
+{
+    struct list_head *phead, *plist;
+    struct recv_msdu *prframe;
+
+    phead = &preorder_ctrl->reord_list;
+    while (1) {
+        spin_lock_bh(&preorder_ctrl->reord_list_lock);
+        if (list_empty(phead)) {
+            spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+            break;
+        }
+
+        plist = phead->next;
+        prframe = list_entry(plist, struct recv_msdu, reord_pending_list);
+
+        if (!SN_LESS(preorder_ctrl->ind_sn, prframe->seq_num)) {
+            list_del_init(&(prframe->reord_pending_list));
+            spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+            reord_single_frame_ind(rx_priv, prframe);
+        } else {
+            spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+            break;
+        }
+    }
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+void reord_timeout_handler (ulong data)
+#else
+void reord_timeout_handler (struct timer_list *t)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+	struct reord_ctrl *preorder_ctrl = (struct reord_ctrl *)data;
+#else
+	struct reord_ctrl *preorder_ctrl = from_timer(preorder_ctrl, t, reord_timer);
+#endif
+    struct aicwf_rx_priv *rx_priv = preorder_ctrl->rx_priv;
+
+    if (reord_rxframes_process(rx_priv, preorder_ctrl, true)==true) {
+        mod_timer(&preorder_ctrl->reord_timer, jiffies + msecs_to_jiffies(REORDER_UPDATE_TIME));
+    }
+
+    if(!work_pending(&preorder_ctrl->reord_timer_work))
+        schedule_work(&preorder_ctrl->reord_timer_work);
+}
+
+void reord_timeout_worker(struct work_struct *work)
+{
+    struct reord_ctrl *preorder_ctrl = container_of(work, struct reord_ctrl, reord_timer_work);
+    struct aicwf_rx_priv *rx_priv = preorder_ctrl->rx_priv;
+
+    reord_rxframes_ind(rx_priv, preorder_ctrl);
+    return ;
+}
+
+int reord_process_unit(struct aicwf_rx_priv *rx_priv, struct sk_buff *skb, u16 seq_num, u8 tid, u8 forward)
+{
+    int ret=0;
+    u8 *mac;
+    struct recv_msdu *pframe;
+    struct reord_ctrl *preorder_ctrl;
+    struct reord_ctrl_info *reord_info;
+    struct rwnx_vif *rwnx_vif = (struct rwnx_vif *)rx_priv->rwnx_vif;
+    struct ethhdr *eh = (struct ethhdr *)(skb->data);
+    u8 *da = eh->h_dest;
+    u8 is_mcast = ((*da) & 0x01)? 1 : 0;
+
+    if (rwnx_vif == NULL || skb->len <= 14) {
+        dev_kfree_skb(skb);
+        return -1;
+    }
+
+    pframe = reord_rxframe_alloc(&rx_priv->freeq_lock, &rx_priv->rxframes_freequeue);
+    if (!pframe) {
+        dev_kfree_skb(skb);
+        return -1;
+    }
+
+	INIT_LIST_HEAD(&pframe->reord_pending_list);
+	pframe->seq_num = seq_num;
+	pframe->tid = tid;
+	pframe->rx_data = skb->data;
+	pframe->len = skb->len;
+	pframe->pkt = skb;
+	pframe->forward = forward;
+	preorder_ctrl = pframe->preorder_ctrl;
+
+    if ((ntohs(eh->h_proto) == ETH_P_PAE) || is_mcast)
+        return reord_single_frame_ind(rx_priv, pframe);
+
+    if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
+        mac = eh->h_dest;
+    else if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO))
+        mac = eh->h_source;
+    else {
+        dev_kfree_skb(skb);
+        return -1;
+    }
+
+    spin_lock_bh(&rx_priv->stas_reord_lock);
+    list_for_each_entry(reord_info, &rx_priv->stas_reord_list, list) {
+        if (!memcmp(mac, reord_info->mac_addr, ETH_ALEN)) {
+            preorder_ctrl = &reord_info->preorder_ctrl[pframe->tid];
+            break;
+        }
+    }
+
+    if (&reord_info->list == &rx_priv->stas_reord_list) {
+        reord_info = reord_init_sta(rx_priv, mac);
+        if (!reord_info) {
+            spin_unlock_bh(&rx_priv->stas_reord_lock);
+            dev_kfree_skb(skb);
+            return -1;
+        }
+        list_add_tail(&reord_info->list, &rx_priv->stas_reord_list);
+        preorder_ctrl = &reord_info->preorder_ctrl[pframe->tid];
+    } else {
+        if(preorder_ctrl->enable == false) {
+            preorder_ctrl->enable = true;
+            preorder_ctrl->ind_sn = 0xffff;
+            preorder_ctrl->wsize_b = AICWF_REORDER_WINSIZE;
+            preorder_ctrl->rx_priv= rx_priv;
+        }
+    }
+    spin_unlock_bh(&rx_priv->stas_reord_lock);
+
+    if (preorder_ctrl->enable == false) {
+        preorder_ctrl->ind_sn = pframe->seq_num;
+        reord_single_frame_ind(rx_priv, pframe);
+        preorder_ctrl->ind_sn = (preorder_ctrl->ind_sn + 1)%4096;
+        return 0;
+    }
+
+    spin_lock_bh(&preorder_ctrl->reord_list_lock);
+    if (reord_need_check(preorder_ctrl, pframe->seq_num)) {
+        reord_single_frame_ind(rx_priv, pframe);
+        spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+	return 0;
+    }
+
+    if (reord_rxframe_enqueue(preorder_ctrl, pframe)) {
+        spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+        goto fail;
+    }
+
+    if (reord_rxframes_process(rx_priv, preorder_ctrl, false) == true) {
+        if (!timer_pending(&preorder_ctrl->reord_timer)) {
+            ret = mod_timer(&preorder_ctrl->reord_timer, jiffies + msecs_to_jiffies(REORDER_UPDATE_TIME));
+        }
+    } else {
+	if(timer_pending(&preorder_ctrl->reord_timer)) {
+        	ret = del_timer(&preorder_ctrl->reord_timer);
+	}
+    }
+    spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+    reord_rxframes_ind(rx_priv, preorder_ctrl);
+
+    return 0;
+
+fail:
+    if (pframe->pkt){
+        dev_kfree_skb(pframe->pkt);
+        pframe->pkt = NULL;
+    }
+   	reord_rxframe_free(&rx_priv->freeq_lock, &rx_priv->rxframes_freequeue, &pframe->rxframe_list);
+    return ret;
+}
+
+int reord_need_check(struct reord_ctrl *preorder_ctrl, u16 seq_num)
+{
+    u8 wsize = preorder_ctrl->wsize_b;
+    u16 wend = (preorder_ctrl->ind_sn + wsize -1) & 0xFFF;
+
+    if (preorder_ctrl->ind_sn == 0xFFFF) {
+        preorder_ctrl->ind_sn = seq_num;
+    }
+
+    if( SN_LESS(seq_num, preorder_ctrl->ind_sn)) {
+        return -1;
+    }
+
+    if (SN_EQUAL(seq_num, preorder_ctrl->ind_sn)) {
+        preorder_ctrl->ind_sn = (preorder_ctrl->ind_sn + 1) & 0xFFF;
+    } else if (SN_LESS(wend, seq_num)) {
+        if (seq_num >= (wsize-1))
+            preorder_ctrl->ind_sn = seq_num-(wsize-1);
+        else
+            preorder_ctrl->ind_sn = 0xFFF - (wsize - (seq_num + 1)) + 1;
+    }
+
+    return 0;
+}
+
+int reord_rxframe_enqueue(struct reord_ctrl *preorder_ctrl, struct recv_msdu *prframe)
+{
+    struct list_head *preord_list = &preorder_ctrl->reord_list;
+    struct list_head *phead, *plist;
+    struct recv_msdu *pnextrframe;
+
+    phead = preord_list;
+    plist = phead->next;
+
+    while(phead != plist) {
+        pnextrframe = list_entry(plist, struct recv_msdu, reord_pending_list);
+        if(SN_LESS(pnextrframe->seq_num, prframe->seq_num))	{
+            plist = plist->next;
+            continue;
+        } else if(SN_EQUAL(pnextrframe->seq_num, prframe->seq_num)) {
+            return -1;
+        } else {
+            break;
+        }
+    }
+    list_add_tail(&(prframe->reord_pending_list), plist);
+
+    return 0;
+}
+#endif /* AICWF_RX_REORDER */
+
+void remove_sec_hdr_mgmt_frame(struct hw_rxhdr *hw_rxhdr,struct sk_buff *skb)
+{
+    u8 hdr_len = 24;
+    u8 mgmt_header[24] = {0};
+
+    if(!hw_rxhdr->hwvect.ga_frame){
+        if(((skb->data[0] & 0x0C) == 0) && (skb->data[1] & 0x40) == 0x40){ //protect management frame
+            printk("frame type %x\n",skb->data[0]);
+            if(hw_rxhdr->hwvect.decr_status == RWNX_RX_HD_DECR_CCMP128){
+                memcpy(mgmt_header,skb->data,hdr_len);
+                skb_pull(skb,8);
+                memcpy(skb->data,mgmt_header,hdr_len);
+                hw_rxhdr->hwvect.len -= 8;
+            }
+            else {
+                printk("unsupport decr_status:%d\n",hw_rxhdr->hwvect.decr_status);
+            }
+        }
+    }
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+void defrag_timeout_cb(ulong data)
+#else
+void defrag_timeout_cb(struct timer_list *t)
+#endif
+{
+	struct defrag_ctrl_info *defrag_ctrl = NULL;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+	defrag_ctrl = (struct defrag_ctrl_info *)data;
+#else
+	defrag_ctrl = from_timer(defrag_ctrl, t, defrag_timer);
+#endif
+
+	printk("%s:%p\r\n", __func__, defrag_ctrl);
+	spin_lock_bh(&defrag_ctrl->rwnx_hw->defrag_lock);
+	list_del_init(&defrag_ctrl->list);
+	dev_kfree_skb(defrag_ctrl->skb);
+	kfree(defrag_ctrl);
+	spin_unlock_bh(&defrag_ctrl->rwnx_hw->defrag_lock);
+}
+
+u8 rwnx_rxdataind_aicwf(struct rwnx_hw *rwnx_hw, void *hostid, void *rx_priv)
+{
+	struct hw_rxhdr *hw_rxhdr;
+	struct rxdesc_tag *rxdesc = NULL;
+	struct rwnx_vif *rwnx_vif;
+	struct sk_buff *skb  = hostid;
+	int msdu_offset = sizeof(struct hw_rxhdr) + 2;
+	u16_l status = 0;
+	struct aicwf_rx_priv *rx_priv_tmp;
+	u8 hdr_len = 24;
+	u8 ra[MAC_ADDR_LEN] = {0};
+	u8 ta[MAC_ADDR_LEN] = {0};
+	u8 ether_type[2] = {0};
+	u8 pull_len = 0;
+	u16 seq_num = 0;
+	u8_l frag_num = 0;
+	u8 tid = 0;
+	u8 is_qos = 0;
+	u8 is_frag = 0;
+	struct defrag_ctrl_info *defrag_info = NULL;
+	struct defrag_ctrl_info *defrag_info_tmp = NULL;
+	struct sk_buff *skb_tmp = NULL;
+	int ret;
+	u8 sta_idx = 0;
+	u16_l frame_ctrl;
+	u8 is_amsdu = 0;
+	u16 len_alligned = 0;
+	u16 sublen = 0;
+	struct sk_buff *sub_skb = NULL;
+	bool resend = false, forward = true;
+	const struct ethhdr *eth;
+
+	hw_rxhdr = (struct hw_rxhdr *)skb->data;
+
+    if(hw_rxhdr->is_monitor_vif) {
+        status = RX_STAT_MONITOR;
+        printk("monitor rx\n");
+    }
+
+    if(hw_rxhdr->flags_upload)
+        status |= RX_STAT_FORWARD;
+
+    /* Check if we need to delete the buffer */
+    if (status & RX_STAT_DELETE) {
+        /* Remove the SK buffer from the rxbuf_elems table */
+    #if 0
+        rwnx_ipc_rxbuf_elem_pull(rwnx_hw, skb);
+    #endif
+        /* Free the buffer */
+        dev_kfree_skb(skb);
+        goto end;
+    }
+
+    /* Check if we need to forward the buffer coming from a monitor interface */
+    if (status & RX_STAT_MONITOR) {
+        struct sk_buff *skb_monitor;
+        struct hw_rxhdr hw_rxhdr_copy;
+        u8 rtap_len;
+        u16 frm_len;
+
+        //Check if monitor interface exists and is open
+        rwnx_vif = rwnx_rx_get_vif(rwnx_hw, rwnx_hw->monitor_vif);
+        if (!rwnx_vif) {
+            dev_err(rwnx_hw->dev, "Received monitor frame but there is no monitor interface open\n");
+            goto check_len_update;
+        }
+
+		rwnx_rx_vector_convert(rwnx_hw,
+							   &hw_rxhdr->hwvect.rx_vect1,
+							   &hw_rxhdr->hwvect.rx_vect2);
+        rtap_len = rwnx_rx_rtap_hdrlen(&hw_rxhdr->hwvect.rx_vect1, false);
+
+        // Move skb->data pointer to MAC Header or Ethernet header
+        skb->data += (msdu_offset + 2); //sdio/usb word allign
+
+        //Save frame length
+        frm_len = le32_to_cpu(hw_rxhdr->hwvect.len);
+
+        // Reserve space for frame
+        skb->len = frm_len;
+
+        if (status == RX_STAT_MONITOR) {
+            /* Remove the SK buffer from the rxbuf_elems table. It will also
+               unmap the buffer and then sync the buffer for the cpu */
+            //rwnx_ipc_rxbuf_elem_pull(rwnx_hw, skb);
+
+            //Check if there is enough space to add the radiotap header
+            if (skb_headroom(skb) > rtap_len) {
+                skb_monitor = skb;
+
+                //Duplicate the HW Rx Header to override with the radiotap header
+                memcpy(&hw_rxhdr_copy, hw_rxhdr, sizeof(hw_rxhdr_copy));
+
+                hw_rxhdr = &hw_rxhdr_copy;
+            } else {
+                //Duplicate the skb and extend the headroom
+                skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);
+
+                //Reset original skb->data pointer
+                skb->data = (void*) hw_rxhdr;
+            }
+        }
+        else
+        {
+        //#ifdef CONFIG_RWNX_MON_DATA
+        #if 0
+            // Check if MSDU
+            if (!hw_rxhdr->flags_is_80211_mpdu) {
+                // MSDU
+                //Extract MAC header
+                u16 machdr_len = hw_rxhdr->mac_hdr_backup.buf_len;
+                u8* machdr_ptr = hw_rxhdr->mac_hdr_backup.buffer;
+
+                //Pull Ethernet header from skb
+                skb_pull(skb, sizeof(struct ethhdr));
+
+                // Copy skb and extend for adding the radiotap header and the MAC header
+                skb_monitor = skb_copy_expand(skb,
+                                              rtap_len + machdr_len,
+                                              0, GFP_ATOMIC);
+
+                //Reserve space for the MAC Header
+                skb_push(skb_monitor, machdr_len);
+
+                //Copy MAC Header
+                memcpy(skb_monitor->data, machdr_ptr, machdr_len);
+
+                //Update frame length
+                frm_len += machdr_len - sizeof(struct ethhdr);
+            } else {
+                // MPDU
+                skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);
+            }
+
+            //Reset original skb->data pointer
+            skb->data = (void*) hw_rxhdr;
+        #else
+            //Reset original skb->data pointer
+            skb->data = (void*) hw_rxhdr;
+
+            wiphy_err(rwnx_hw->wiphy, "RX status %d is invalid when MON_DATA is disabled\n", status);
+            goto check_len_update;
+        #endif
+        }
+
+        skb_reset_tail_pointer(skb);
+        skb->len = 0;
+        skb_reset_tail_pointer(skb_monitor);
+        skb_monitor->len = 0;
+
+        skb_put(skb_monitor, frm_len);
+        if (rwnx_rx_monitor(rwnx_hw, rwnx_vif, skb_monitor, hw_rxhdr, rtap_len))
+            dev_kfree_skb(skb_monitor);
+
+        if (status == RX_STAT_MONITOR) {
+            status |= RX_STAT_ALLOC;
+            if (skb_monitor != skb) {
+                dev_kfree_skb(skb);
+            }
+        }
+    }
+
+check_len_update:
+    /* Check if we need to update the length */
+    if (status & RX_STAT_LEN_UPDATE) {
+		if (rxdesc)
+        hw_rxhdr->hwvect.len = rxdesc->frame_len;
+
+        if (status & RX_STAT_ETH_LEN_UPDATE) {
+            /* Update Length Field inside the Ethernet Header */
+            struct ethhdr *hdr = (struct ethhdr *)((u8 *)hw_rxhdr + msdu_offset);
+
+			if (rxdesc)
+            hdr->h_proto = htons(rxdesc->frame_len - sizeof(struct ethhdr));
+        }
+
+        goto end;
+    }
+
+    /* Check if it must be discarded after informing upper layer */
+    if (status & RX_STAT_SPURIOUS) {
+        struct ieee80211_hdr *hdr;
+
+        hdr = (struct ieee80211_hdr *)(skb->data + msdu_offset);
+        rwnx_vif = rwnx_rx_get_vif(rwnx_hw, hw_rxhdr->flags_vif_idx);
+        if (rwnx_vif) {
+            cfg80211_rx_spurious_frame(rwnx_vif->ndev, hdr->addr2, GFP_ATOMIC);
+        }
+        goto end;
+    }
+
+    /* Check if we need to forward the buffer */
+    if (status & RX_STAT_FORWARD) {
+		rwnx_rx_vector_convert(rwnx_hw,
+							   &hw_rxhdr->hwvect.rx_vect1,
+							   &hw_rxhdr->hwvect.rx_vect2);
+        skb_pull(skb, msdu_offset + 2); //+2 since sdio allign 58->60
+
+#define MAC_FCTRL_MOREFRAG 0x0400
+		frame_ctrl = (skb->data[1] << 8) | skb->data[0];
+		seq_num = ((skb->data[22] & 0xf0) >> 4) | (skb->data[23] << 4);
+		frag_num = (skb->data[22] & 0x0f);
+		is_amsdu = 0;
+
+		if ((skb->data[0] & 0x0f) == 0x08) {
+			if ((skb->data[0] & 0x80) == 0x80) {//qos data
+				hdr_len = 26;
+				tid = skb->data[24] & 0x0F;
+				is_qos = 1;
+				if (skb->data[24] & 0x80)
+					is_amsdu = 1;
+			}
+
+            if(skb->data[1] & 0x80)//htc
+		hdr_len += 4;
+            if((skb->data[1] & 0x3) == 0x1)  {// to ds
+                memcpy(ra, &skb->data[16], MAC_ADDR_LEN);
+                memcpy(ta, &skb->data[10], MAC_ADDR_LEN);
+            } else if((skb->data[1] & 0x3) == 0x2) { //from ds
+                memcpy(ta, &skb->data[16], MAC_ADDR_LEN);
+                memcpy(ra, &skb->data[4], MAC_ADDR_LEN);
+            }
+
+			pull_len += (hdr_len + 8);
+
+			switch (hw_rxhdr->hwvect.decr_status) {
+			case RWNX_RX_HD_DECR_CCMP128:
+				pull_len += 8;//ccmp_header
+				//skb_pull(&skb->data[skb->len-8], 8); //ccmp_mic_len
+				memcpy(ether_type, &skb->data[hdr_len + 6 + 8], 2);
+				break;
+			case RWNX_RX_HD_DECR_TKIP:
+				pull_len += 8;//tkip_header
+				memcpy(ether_type, &skb->data[hdr_len + 6 + 8], 2);
+				break;
+			case RWNX_RX_HD_DECR_WEP:
+				pull_len += 4;//wep_header
+				memcpy(ether_type, &skb->data[hdr_len + 6 + 4], 2);
+				break;
+
+                default:
+                    memcpy(ether_type, &skb->data[hdr_len + 6], 2);
+                    break;
+            }
+
+			if (is_amsdu) {
+				skb_pull(skb, pull_len-8);
+				/* |amsdu sub1 | amsdu sub2 | ... */
+				len_alligned = 0;
+				sublen = 0;
+				sub_skb = NULL;
+				//printk("is_len:%d, pull:%d\n", skb->len, pull_len);
+				while (skb->len > 16) {
+					sublen = (skb->data[12]<<8)|(skb->data[13]);
+					if (skb->len > (sublen+14))
+						len_alligned = roundup(sublen + 14, 4);
+					else if (skb->len == (sublen+14))
+						len_alligned = sublen+14;
+					else {
+						printk("accroding to amsdu: this will not happen\n");
+						break;
+					}
+					//printk("sublen = %d, %x, %x, %x, %x\r\n", sublen,skb->data[0], skb->data[1], skb->data[12], skb->data[13]);
+#if 1
+					sub_skb = __dev_alloc_skb(sublen - 6 + 12, GFP_KERNEL);
+					skb_put(sub_skb, sublen - 6 + 12);
+					memcpy(sub_skb->data, skb->data, MAC_ADDR_LEN);
+					memcpy(&sub_skb->data[6], &skb->data[6], MAC_ADDR_LEN);
+					memcpy(&sub_skb->data[12], &skb->data[14 + 6], sublen - 6);
+
+					rwnx_vif = rwnx_rx_get_vif(rwnx_hw, hw_rxhdr->flags_vif_idx);
+					if (!rwnx_vif) {
+						printk("Frame received but no active vif (%d)", hw_rxhdr->flags_vif_idx);
+						dev_kfree_skb(sub_skb);
+						break;
+					}
+
+					if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, sub_skb, hw_rxhdr))
+						dev_kfree_skb(sub_skb);
+#endif
+					skb_pull(skb, len_alligned);
+				}
+				dev_kfree_skb(skb);
+				return 0;
+			}
+
+			if (hw_rxhdr->flags_dst_idx != RWNX_INVALID_STA)
+				sta_idx = hw_rxhdr->flags_dst_idx;
+
+			if (!hw_rxhdr->flags_need_reord && ((frame_ctrl & MAC_FCTRL_MOREFRAG) || frag_num)) {
+				//printk("rxfrag:%d,%d,%d,sn=%d,pull_len=%d\r\n", (frame_ctrl & MAC_FCTRL_MOREFRAG), frag_num, skb->len, seq_num,pull_len);
+				if (frame_ctrl & MAC_FCTRL_MOREFRAG) {
+					spin_lock_bh(&rwnx_hw->defrag_lock);
+					if (!list_empty(&rwnx_hw->defrag_list)) {
+						list_for_each_entry(defrag_info_tmp, &rwnx_hw->defrag_list, list) {
+							if ((defrag_info_tmp->sn == seq_num) && (defrag_info_tmp->tid == tid) && \
+									defrag_info_tmp->sta_idx == sta_idx) {
+								defrag_info = defrag_info_tmp;
+								break;
+							}
+						}
+					}
+
+					//printk("rx frag: sn=%d, fn=%d, skb->len=%d\r\n", seq_num, frag_num, skb->len);
+					if (defrag_info) {
+						is_frag = 1;
+						if (defrag_info->next_fn != frag_num) {
+							printk("discard:%d:%d\n", defrag_info->next_fn, frag_num);
+							dev_kfree_skb(skb);
+							spin_unlock_bh(&rwnx_hw->defrag_lock);
+							return 0;
+						}
+
+						skb_put(defrag_info->skb, skb->len-(pull_len-8));
+						memcpy(&defrag_info->skb->data[defrag_info->frm_len], \
+								&skb->data[pull_len-8], skb->len - (pull_len-8));
+						//printk("middle:%d,%d\n", skb->len-(pull_len-8), skb->len);
+						defrag_info->frm_len += (skb->len - (pull_len - 8));
+						defrag_info->next_fn++;
+						dev_kfree_skb(skb);
+						spin_unlock_bh(&rwnx_hw->defrag_lock);
+						return 0;
+					} else {
+						defrag_info = kzalloc(sizeof(struct defrag_ctrl_info), GFP_KERNEL);
+						if (defrag_info == NULL) {
+							printk("no defrag_ctrl_info\r\n");
+							dev_kfree_skb(skb);
+							spin_unlock_bh(&rwnx_hw->defrag_lock);
+							return 0;
+						}
+						defrag_info->skb = __dev_alloc_skb(2000, GFP_KERNEL);
+						if (defrag_info->skb == NULL) {
+							printk("no fragment skb\r\n");
+							dev_kfree_skb(skb);
+							kfree(defrag_info);
+							spin_unlock_bh(&rwnx_hw->defrag_lock);
+							return 0;
+						}
+						is_frag = 1;
+						skb_pull(skb, pull_len);
+						skb_push(skb, 14);
+						memcpy(skb->data, ra, MAC_ADDR_LEN);
+						memcpy(&skb->data[6], ta, MAC_ADDR_LEN);
+						memcpy(&skb->data[12], ether_type, 2);
+
+						defrag_info->sn = seq_num;
+						defrag_info->next_fn = 1;
+						defrag_info->tid = tid;
+						defrag_info->sta_idx = sta_idx;
+
+						skb_put(defrag_info->skb, skb->len);
+						memcpy(defrag_info->skb->data, skb->data, skb->len);
+						defrag_info->frm_len = skb->len;
+						defrag_info->rwnx_hw = rwnx_hw;
+						//printk("first:%p,%p,%p,%p,%p, %d,%d\r\n", defrag_info, defrag_info->skb, defrag_info->skb->head, defrag_info->skb->tail, defrag_info->skb->end, defrag_info->frm_len, skb->len);
+						list_add_tail(&defrag_info->list, &rwnx_hw->defrag_list);
+						spin_unlock_bh(&rwnx_hw->defrag_lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+						init_timer(&defrag_info->defrag_timer);
+						defrag_info->defrag_timer.data = (unsigned long)defrag_info;
+						defrag_info->defrag_timer.function = defrag_timeout_cb;
+#else
+						timer_setup(&defrag_info->defrag_timer, defrag_timeout_cb, 0);
+#endif
+						ret = mod_timer(&defrag_info->defrag_timer, jiffies + msecs_to_jiffies(DEFRAG_MAX_WAIT));
+						dev_kfree_skb(skb);
+						return 0;
+					}
+				} else {
+					//check whether the last fragment
+					if (!list_empty(&rwnx_hw->defrag_list)) {
+						spin_lock_bh(&rwnx_hw->defrag_lock);
+						list_for_each_entry(defrag_info_tmp, &rwnx_hw->defrag_list, list) {
+							if (((defrag_info_tmp->sn == seq_num) && (defrag_info_tmp->tid == tid) && \
+										defrag_info_tmp->sta_idx == sta_idx)) {
+								defrag_info = defrag_info_tmp;
+								break;
+							}
+						}
+
+						if (!defrag_info) 
+							spin_unlock_bh(&rwnx_hw->defrag_lock);
+						else {
+							if (defrag_info->next_fn != frag_num) {
+								printk("discard:%d:%d\n", defrag_info->next_fn, frag_num);
+								dev_kfree_skb(skb);
+								spin_unlock_bh(&rwnx_hw->defrag_lock);
+								return 0;
+							}
+
+							skb_put(defrag_info->skb, skb->len - (pull_len-8));
+							memcpy(&defrag_info->skb->data[defrag_info->frm_len], \
+										&skb->data[pull_len-8], skb->len - (pull_len-8));
+							defrag_info->frm_len += (skb->len - (pull_len-8));
+							is_frag = 1;
+							//printk("last: sn=%d, fn=%d, %d, %d\r\n", seq_num, frag_num, defrag_info->frm_len, skb->len);
+
+							rwnx_vif = rwnx_rx_get_vif(rwnx_hw, hw_rxhdr->flags_vif_idx);
+							if (!rwnx_vif) {
+								printk("Frame received but no active vif (%d)", hw_rxhdr->flags_vif_idx);
+								dev_kfree_skb(skb);
+								spin_unlock_bh(&rwnx_hw->defrag_lock);
+								return 0;
+							}
+							dev_kfree_skb(skb);
+
+							skb_tmp = defrag_info->skb;
+							list_del_init(&defrag_info->list);
+							if (timer_pending(&defrag_info->defrag_timer)) {
+								ret = del_timer(&defrag_info->defrag_timer);
+							}
+							kfree(defrag_info);
+							spin_unlock_bh(&rwnx_hw->defrag_lock);
+
+							if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb_tmp, hw_rxhdr))
+								dev_kfree_skb(skb_tmp);
+
+							return 0;
+						}
+					}
+				}
+			}
+
+			if (!is_frag) {
+				skb_pull(skb, pull_len);
+				skb_push(skb, 14);
+				memcpy(skb->data, ra, MAC_ADDR_LEN);
+				memcpy(&skb->data[6], ta, MAC_ADDR_LEN);
+				memcpy(&skb->data[12], ether_type, 2);
+			}
+		}
+
+		if (hw_rxhdr->flags_is_80211_mpdu) {
+			remove_sec_hdr_mgmt_frame(hw_rxhdr, skb);
+			rwnx_rx_mgmt_any(rwnx_hw, skb, hw_rxhdr);
+		} else {
+			rwnx_vif = rwnx_rx_get_vif(rwnx_hw, hw_rxhdr->flags_vif_idx);
+
+            if (!rwnx_vif) {
+                dev_err(rwnx_hw->dev, "Frame received but no active vif (%d)",
+                        hw_rxhdr->flags_vif_idx);
+                dev_kfree_skb(skb);
+                goto end;
+            }
+
+            if (hw_rxhdr->flags_sta_idx != RWNX_INVALID_STA) {
+                struct rwnx_sta *sta;
+
+                sta = &rwnx_hw->sta_table[hw_rxhdr->flags_sta_idx];
+		if(!sta->valid){
+			dev_kfree_skb(skb);
+			goto end;	
+		}
+				rwnx_rx_statistic(rwnx_hw, hw_rxhdr, sta);
+
+                if (sta->vlan_idx != rwnx_vif->vif_index) {
+                    rwnx_vif = rwnx_hw->vif_table[sta->vlan_idx];
+                    if (!rwnx_vif) {
+                        dev_kfree_skb(skb);
+                        goto end;
+                    }
+                }
+
+                if (hw_rxhdr->flags_is_4addr && !rwnx_vif->use_4addr) {
+                    cfg80211_rx_unexpected_4addr_frame(rwnx_vif->ndev,
+                                                       sta->mac_addr, GFP_ATOMIC);
+                }
+            }
+
+			skb->priority = 256 + tid;//hw_rxhdr->flags_user_prio;
+
+#ifdef AICWF_RX_REORDER
+	     rx_priv_tmp = rx_priv;
+            rx_priv_tmp->rwnx_vif = (void *)rwnx_vif;
+
+	    if( (rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ){
+            if(is_qos && hw_rxhdr->flags_need_reord)
+					reord_process_unit((struct aicwf_rx_priv *)rx_priv, skb, seq_num, tid, 1);
+            else if(is_qos  && !hw_rxhdr->flags_need_reord) {
+                 reord_flush_tid((struct aicwf_rx_priv *)rx_priv, skb, tid);
+                if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb, hw_rxhdr))
+                    dev_kfree_skb(skb);
+            }
+            else {
+                if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb, hw_rxhdr))
+                    dev_kfree_skb(skb);
+            }
+	    } else if( (rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ) {
+#if 0
+				skb_reset_mac_header(skb);
+				eth = eth_hdr(skb);
+				//printk("da:%pM, %x,%x, len=%d\n", eth->h_dest, skb->data[12], skb->data[13], skb->len);
+
+                if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
+                    /* broadcast pkt need to be forwared to upper layer and resent
+                       on wireless interface */
+                    resend = true;
+                } else {
+                    /* unicast pkt for STA inside the BSS, no need to forward to upper
+                       layer simply resend on wireless interface */
+                    if (hw_rxhdr->flags_dst_idx != RWNX_INVALID_STA) {
+                        struct rwnx_sta *sta = &rwnx_hw->sta_table[hw_rxhdr->flags_dst_idx];
+                        if (sta->valid && (sta->vlan_idx == rwnx_vif->vif_index)) {
+                            resend = true;
+                            forward = false;
+                        }
+                    }
+                }
+
+                if(resend)
+                    rwnx_rx_data_skb_resend(rwnx_hw, rwnx_vif, skb, hw_rxhdr);
+
+				if (forward) {
+					if (is_qos && hw_rxhdr->flags_need_reord)
+						reord_process_unit((struct aicwf_rx_priv *)rx_priv, skb, seq_num, tid, 1);
+					else if (is_qos  && !hw_rxhdr->flags_need_reord) {
+						reord_flush_tid((struct aicwf_rx_priv *)rx_priv, skb, tid);
+						rwnx_rx_data_skb_forward(rwnx_hw, rwnx_vif, skb, hw_rxhdr);
+					} else
+						rwnx_rx_data_skb_forward(rwnx_hw, rwnx_vif, skb, hw_rxhdr);
+				} else if(resend) {
+					if (is_qos && hw_rxhdr->flags_need_reord)
+						reord_process_unit((struct aicwf_rx_priv *)rx_priv, skb, seq_num, tid, 0);
+					else if (is_qos  && !hw_rxhdr->flags_need_reord) {
+						reord_flush_tid((struct aicwf_rx_priv *)rx_priv, skb, tid);
+						dev_kfree_skb(skb);
+					} 
+				}else
+					dev_kfree_skb(skb);
+#else
+				if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb, hw_rxhdr))
+					dev_kfree_skb(skb);
+#endif
+			}
+#else
+            if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb, hw_rxhdr))
+                    dev_kfree_skb(skb);
+#endif
+        }
+    }
+
+end:
+	return 0;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_rx.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_rx.h
new file mode 100755
index 0000000..f2d2bde
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_rx.h
@@ -0,0 +1,399 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_rx.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#ifndef _RWNX_RX_H_
+#define _RWNX_RX_H_
+
+#include "aicwf_txrxif.h"
+
+#define SERVER_PORT			67
+#define CLIENT_PORT			68
+#define DHCP_MAGIC			0x63825363
+#define DHCP_ACK      		5
+#define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */
+#define DHCP_OPTION_END 255
+
+enum rx_status_bits
+{
+    /// The buffer can be forwarded to the networking stack
+    RX_STAT_FORWARD = 1 << 0,
+    /// A new buffer has to be allocated
+    RX_STAT_ALLOC = 1 << 1,
+    /// The buffer has to be deleted
+    RX_STAT_DELETE = 1 << 2,
+    /// The length of the buffer has to be updated
+    RX_STAT_LEN_UPDATE = 1 << 3,
+    /// The length in the Ethernet header has to be updated
+    RX_STAT_ETH_LEN_UPDATE = 1 << 4,
+    /// Simple copy
+    RX_STAT_COPY = 1 << 5,
+    /// Spurious frame (inform upper layer and discard)
+    RX_STAT_SPURIOUS = 1 << 6,
+    /// packet for monitor interface
+    RX_STAT_MONITOR = 1 << 7,
+};
+
+
+/*
+ * Decryption status subfields.
+ * {
+ */
+#define RWNX_RX_HD_DECR_UNENC           0 // ENCRYPTION TYPE NONE
+#define RWNX_RX_HD_DECR_WEP             1 // ENCRYPTION TYPE WEP
+#define RWNX_RX_HD_DECR_TKIP            2 // ENCRYPTION TYPE TKIP
+#define RWNX_RX_HD_DECR_CCMP128         3 // ENCRYPTION TYPE CCMP128
+#define RWNX_RX_HD_DECR_CCMP256         4 // ENCRYPTION TYPE CCMP256
+#define RWNX_RX_HD_DECR_GCMP128         5 // ENCRYPTION TYPE GCMP128
+#define RWNX_RX_HD_DECR_GCMP256         6 // ENCRYPTION TYPE GCMP256
+#define RWNX_RX_HD_DECR_WAPI            7 // ENCRYPTION TYPE WAPI
+// @}
+
+//#ifdef CONFIG_RWNX_MON_DATA
+#if 0
+#define RX_MACHDR_BACKUP_LEN    64
+#endif
+
+struct rx_vector_1_old {
+    /** Receive Vector 1a */
+    u32    leg_length         :12;
+    u32    leg_rate           : 4;
+    u32    ht_length          :16;
+
+    /** Receive Vector 1b */
+    u32    _ht_length         : 4; // FIXME
+    u32    short_gi           : 1;
+    u32    stbc               : 2;
+    u32    smoothing          : 1;
+    u32    mcs                : 7;
+    u32    pre_type           : 1;
+    u32    format_mod         : 3;
+    u32    ch_bw              : 2;
+    u32    n_sts              : 3;
+    u32    lsig_valid         : 1;
+    u32    sounding           : 1;
+    u32    num_extn_ss        : 2;
+    u32    aggregation        : 1;
+    u32    fec_coding         : 1;
+    u32    dyn_bw             : 1;
+    u32    doze_not_allowed   : 1;
+
+    /** Receive Vector 1c */
+    u32    antenna_set        : 8;
+    u32    partial_aid        : 9;
+    u32    group_id           : 6;
+    u32    first_user         : 1;
+    s32    rssi1              : 8;
+
+    /** Receive Vector 1d */
+    s32    rssi2              : 8;
+    s32    rssi3              : 8;
+    s32    rssi4              : 8;
+    u32    reserved_1d        : 8;
+};
+
+struct rx_leg_vect
+{
+    u8    dyn_bw_in_non_ht     : 1;
+    u8    chn_bw_in_non_ht     : 2;
+    u8    rsvd_nht             : 4;
+    u8    lsig_valid           : 1;
+} __packed;
+
+struct rx_ht_vect
+{
+    u16   sounding             : 1;
+    u16   smoothing            : 1;
+    u16   short_gi             : 1;
+    u16   aggregation          : 1;
+    u16   stbc                 : 1;
+    u16   num_extn_ss          : 2;
+    u16   lsig_valid           : 1;
+    u16   mcs                  : 7;
+    u16   fec                  : 1;
+    u16   length               :16;
+} __packed;
+
+struct rx_vht_vect
+{
+    u8   sounding              : 1;
+    u8   beamformed            : 1;
+    u8   short_gi              : 1;
+    u8   rsvd_vht1             : 1;
+    u8   stbc                  : 1;
+    u8   doze_not_allowed      : 1;
+    u8   first_user            : 1;
+    u8   rsvd_vht2             : 1;
+    u16  partial_aid           : 9;
+    u16  group_id              : 6;
+    u16  rsvd_vht3             : 1;
+    u32  mcs                   : 4;
+    u32  nss                   : 3;
+    u32  fec                   : 1;
+    u32  length                :20;
+    u32  rsvd_vht4             : 4;
+} __packed;
+
+struct rx_he_vect
+{
+    u8   sounding              : 1;
+    u8   beamformed            : 1;
+    u8   gi_type               : 2;
+    u8   stbc                  : 1;
+    u8   rsvd_he1              : 3;
+
+    u8   uplink_flag           : 1;
+    u8   beam_change           : 1;
+    u8   dcm                   : 1;
+    u8   he_ltf_type           : 2;
+    u8   doppler               : 1;
+    u8   rsvd_he2              : 2;
+
+    u8   bss_color             : 6;
+    u8   rsvd_he3              : 2;
+
+    u8   txop_duration         : 7;
+    u8   rsvd_he4              : 1;
+
+    u8   pe_duration           : 4;
+    u8   spatial_reuse         : 4;
+
+    u8   sig_b_comp_mode       : 1;
+    u8   dcm_sig_b             : 1;
+    u8   mcs_sig_b             : 3;
+    u8   ru_size               : 3;
+
+    u32  mcs                   : 4;
+    u32  nss                   : 3;
+    u32  fec                   : 1;
+    u32  length                :20;
+    u32  rsvd_he6              : 4;
+} __packed;
+
+struct rx_vector_1 {
+    u8     format_mod         : 4;
+    u8     ch_bw              : 3;
+    u8     pre_type           : 1;
+    u8     antenna_set        : 8;
+    s32    rssi_leg           : 8;
+    u32    leg_length         :12;
+    u32    leg_rate           : 4;
+    s32    rssi1              : 8;
+
+    union
+    {
+        struct rx_leg_vect leg;
+        struct rx_ht_vect ht;
+        struct rx_vht_vect vht;
+        struct rx_he_vect he;
+    };
+} __packed;
+
+struct rx_vector_2_old {
+    /** Receive Vector 2a */
+    u32    rcpi               : 8;
+    u32    evm1               : 8;
+    u32    evm2               : 8;
+    u32    evm3               : 8;
+
+    /** Receive Vector 2b */
+    u32    evm4               : 8;
+    u32    reserved2b_1       : 8;
+    u32    reserved2b_2       : 8;
+    u32    reserved2b_3       : 8;
+
+};
+
+struct rx_vector_2 {
+    /** Receive Vector 2a */
+    u32    rcpi1              : 8;
+    u32    rcpi2              : 8;
+    u32    rcpi3              : 8;
+    u32    rcpi4              : 8;
+
+    /** Receive Vector 2b */
+    u32    evm1               : 8;
+    u32    evm2               : 8;
+    u32    evm3               : 8;
+    u32    evm4               : 8;
+};
+
+struct phy_channel_info_desc {
+    /** PHY channel information 1 */
+    u32    phy_band           : 8;
+    u32    phy_channel_type   : 8;
+    u32    phy_prim20_freq    : 16;
+    /** PHY channel information 2 */
+    u32    phy_center1_freq   : 16;
+    u32    phy_center2_freq   : 16;
+};
+
+struct hw_vect {
+    /** Total length for the MPDU transfer */
+    u32 len                   :16;
+
+    u32 reserved              : 8;//data type is included
+    /** AMPDU Status Information */
+    u32 mpdu_cnt              : 6;
+    u32 ampdu_cnt             : 2;
+
+    /** TSF Low */
+    __le32 tsf_lo;
+    /** TSF High */
+    __le32 tsf_hi;
+
+    /** Receive Vector 1 */
+    struct rx_vector_1 rx_vect1;
+    /** Receive Vector 2 */
+    struct rx_vector_2 rx_vect2;
+
+    /** Status **/
+    u32    rx_vect2_valid     : 1;
+    u32    resp_frame         : 1;
+    /** Decryption Status */
+    u32    decr_status        : 3;
+    u32    rx_fifo_oflow      : 1;
+
+    /** Frame Unsuccessful */
+    u32    undef_err          : 1;
+    u32    phy_err            : 1;
+    u32    fcs_err            : 1;
+    u32    addr_mismatch      : 1;
+    u32    ga_frame           : 1;
+    u32    current_ac         : 2;
+
+    u32    frm_successful_rx  : 1;
+    /** Descriptor Done  */
+    u32    desc_done_rx       : 1;
+    /** Key Storage RAM Index */
+    u32    key_sram_index     : 10;
+    /** Key Storage RAM Index Valid */
+    u32    key_sram_v         : 1;
+    u32    type               : 2;
+    u32    subtype            : 4;
+};
+
+//#ifdef CONFIG_RWNX_MON_DATA
+#if 0
+/// MAC header backup descriptor
+struct mon_machdrdesc
+{
+    /// Length of the buffer
+    u32 buf_len;
+    /// Buffer containing mac header, LLC and SNAP
+    u8 buffer[RX_MACHDR_BACKUP_LEN];
+};
+#endif
+
+struct hw_rxhdr {
+    /** RX vector */
+    struct hw_vect hwvect;
+
+    /** PHY channel information */
+    struct phy_channel_info_desc phy_info;
+
+    /** RX flags */
+    u32    flags_is_amsdu     : 1;
+    u32    flags_is_80211_mpdu: 1;
+    u32    flags_is_4addr     : 1;
+    u32    flags_new_peer     : 1;
+#if defined(AICWF_SDIO_SUPPORT) || defined(AICWF_USB_SUPPORT)
+    u32    flags_user_prio    : 1; // aic: fw not fill any more
+    u32    flags_need_reord   : 1;
+    u32    flags_upload       : 1;
+#else
+    u32    flags_user_prio    : 3;
+#endif
+#ifndef AICWF_RX_REORDER
+    u32    flags_rsvd0        : 1;
+#else
+    u32    is_monitor_vif     : 1;
+#endif
+    u32    flags_vif_idx      : 8;    // 0xFF if invalid VIF index
+    u32    flags_sta_idx      : 8;    // 0xFF if invalid STA index
+    u32    flags_dst_idx      : 8;    // 0xFF if unknown destination STA
+//#ifdef CONFIG_RWNX_MON_DATA
+#if 0
+    /// MAC header backup descriptor (used only for MSDU when there is a monitor and a data interface)
+    struct mon_machdrdesc mac_hdr_backup;
+#endif
+    /** Pattern indicating if the buffer is available for the driver */
+    u32    pattern;
+};
+
+extern const u8 legrates_lut[];
+extern u16 legrates_lut_rate[];
+extern u16 tx_legrates_lut_rate[];
+
+struct DHCPInfo {
+    u8 op;
+    u8 htype;
+    u8 hlen;
+    u8 hops;
+    u32 xid;
+    u16 secs;
+    u16 flags;
+    u32 ciaddr;
+    u32 yiaddr;
+    u32 siaddr;
+    u32 giaddr;
+    u8 chaddr[16];
+    u8 sname[64];
+    u8 file[128];
+    u32 cookie;
+    u8 options[308]; /* 312 - cookie */
+};
+
+u8 rwnx_rxdataind_aicwf(struct rwnx_hw *rwnx_hw, void *hostid, void *rx_priv);
+int aicwf_process_rxframes(struct aicwf_rx_priv *rx_priv);
+#ifdef CONFIG_USB_MSG_IN_EP
+int aicwf_process_msg_rxframes(struct aicwf_rx_priv *rx_priv);
+#endif
+
+#ifdef AICWF_ARP_OFFLOAD
+void arpoffload_proc(struct sk_buff *skb, struct rwnx_vif *rwnx_vif);
+#endif
+#ifdef AICWF_RX_REORDER
+struct recv_msdu *reord_rxframe_alloc(spinlock_t *lock, struct list_head *q);
+void reord_rxframe_free(spinlock_t *lock, struct list_head *q, struct list_head *list);
+struct reord_ctrl_info *reord_init_sta( struct aicwf_rx_priv *rx_priv, const u8 *mac_addr);
+void reord_deinit_sta(struct aicwf_rx_priv *rx_priv, struct reord_ctrl_info *reord_info);
+int reord_need_check(struct reord_ctrl *preorder_ctrl, u16 seq_num);
+int reord_rxframe_enqueue(struct reord_ctrl *preorder_ctrl, struct recv_msdu *prframe);
+void reord_timeout_worker(struct work_struct *work);
+int reord_single_frame_ind(struct aicwf_rx_priv *rx_priv, struct recv_msdu *prframe);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+void reord_timeout_handler (ulong data);
+#else
+void reord_timeout_handler (struct timer_list *t);
+#endif
+#endif
+
+#ifdef CONFIG_HE_FOR_OLD_KERNEL
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 197)
+struct element {
+    u8 id;
+    u8 datalen;
+    u8 data[];
+};
+/* element iteration helpers */
+#define for_each_element(_elem, _data, _datalen)			\
+	for (_elem = (const struct element *)(_data);			\
+	     (const u8 *)(_data) + (_datalen) - (const u8 *)_elem >=	\
+		(int)sizeof(*_elem) &&					\
+	     (const u8 *)(_data) + (_datalen) - (const u8 *)_elem >=	\
+		(int)sizeof(*_elem) + _elem->datalen;			\
+	     _elem = (const struct element *)(_elem->data + _elem->datalen))
+
+#define for_each_element_id(element, _id, data, datalen)		\
+	for_each_element(element, data, datalen)			\
+		if (element->id == (_id))
+#endif
+#endif
+
+#endif /* _RWNX_RX_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_strs.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_strs.c
new file mode 100755
index 0000000..bd5502b
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_strs.c
@@ -0,0 +1,261 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_strs.c
+ *
+ * @brief Miscellaneous debug strings
+ *
+ * Copyright (C) RivieraWaves 2014-2019
+ *
+ ******************************************************************************
+ */
+
+#include "lmac_msg.h"
+
+static const char *const rwnx_mmid2str[MSG_I(MM_MAX)] = {
+    [MSG_I(MM_RESET_REQ)]                 = "MM_RESET_REQ",
+    [MSG_I(MM_RESET_CFM)]                 = "MM_RESET_CFM",
+    [MSG_I(MM_START_REQ)]                 = "MM_START_REQ",
+    [MSG_I(MM_START_CFM)]                 = "MM_START_CFM",
+    [MSG_I(MM_VERSION_REQ)]               = "MM_VERSION_REQ",
+    [MSG_I(MM_VERSION_CFM)]               = "MM_VERSION_CFM",
+    [MSG_I(MM_ADD_IF_REQ)]                = "MM_ADD_IF_REQ",
+    [MSG_I(MM_ADD_IF_CFM)]                = "MM_ADD_IF_CFM",
+    [MSG_I(MM_REMOVE_IF_REQ)]             = "MM_REMOVE_IF_REQ",
+    [MSG_I(MM_REMOVE_IF_CFM)]             = "MM_REMOVE_IF_CFM",
+    [MSG_I(MM_STA_ADD_REQ)]               = "MM_STA_ADD_REQ",
+    [MSG_I(MM_STA_ADD_CFM)]               = "MM_STA_ADD_CFM",
+    [MSG_I(MM_STA_DEL_REQ)]               = "MM_STA_DEL_REQ",
+    [MSG_I(MM_STA_DEL_CFM)]               = "MM_STA_DEL_CFM",
+    [MSG_I(MM_SET_FILTER_REQ)]            = "MM_SET_FILTER_REQ",
+    [MSG_I(MM_SET_FILTER_CFM)]            = "MM_SET_FILTER_CFM",
+    [MSG_I(MM_SET_CHANNEL_REQ)]           = "MM_SET_CHANNEL_REQ",
+    [MSG_I(MM_SET_CHANNEL_CFM)]           = "MM_SET_CHANNEL_CFM",
+    [MSG_I(MM_SET_DTIM_REQ)]              = "MM_SET_DTIM_REQ",
+    [MSG_I(MM_SET_DTIM_CFM)]              = "MM_SET_DTIM_CFM",
+    [MSG_I(MM_SET_BEACON_INT_REQ)]        = "MM_SET_BEACON_INT_REQ",
+    [MSG_I(MM_SET_BEACON_INT_CFM)]        = "MM_SET_BEACON_INT_CFM",
+    [MSG_I(MM_SET_BASIC_RATES_REQ)]       = "MM_SET_BASIC_RATES_REQ",
+    [MSG_I(MM_SET_BASIC_RATES_CFM)]       = "MM_SET_BASIC_RATES_CFM",
+    [MSG_I(MM_SET_BSSID_REQ)]             = "MM_SET_BSSID_REQ",
+    [MSG_I(MM_SET_BSSID_CFM)]             = "MM_SET_BSSID_CFM",
+    [MSG_I(MM_SET_EDCA_REQ)]              = "MM_SET_EDCA_REQ",
+    [MSG_I(MM_SET_EDCA_CFM)]              = "MM_SET_EDCA_CFM",
+    [MSG_I(MM_SET_MODE_REQ)]              = "MM_SET_MODE_REQ",
+    [MSG_I(MM_SET_MODE_CFM)]              = "MM_SET_MODE_CFM",
+    [MSG_I(MM_SET_VIF_STATE_REQ)]         = "MM_SET_VIF_STATE_REQ",
+    [MSG_I(MM_SET_VIF_STATE_CFM)]         = "MM_SET_VIF_STATE_CFM",
+    [MSG_I(MM_SET_SLOTTIME_REQ)]          = "MM_SET_SLOTTIME_REQ",
+    [MSG_I(MM_SET_SLOTTIME_CFM)]          = "MM_SET_SLOTTIME_CFM",
+    [MSG_I(MM_SET_IDLE_REQ)]              = "MM_SET_IDLE_REQ",
+    [MSG_I(MM_SET_IDLE_CFM)]              = "MM_SET_IDLE_CFM",
+    [MSG_I(MM_KEY_ADD_REQ)]               = "MM_KEY_ADD_REQ",
+    [MSG_I(MM_KEY_ADD_CFM)]               = "MM_KEY_ADD_CFM",
+    [MSG_I(MM_KEY_DEL_REQ)]               = "MM_KEY_DEL_REQ",
+    [MSG_I(MM_KEY_DEL_CFM)]               = "MM_KEY_DEL_CFM",
+    [MSG_I(MM_BA_ADD_REQ)]                = "MM_BA_ADD_REQ",
+    [MSG_I(MM_BA_ADD_CFM)]                = "MM_BA_ADD_CFM",
+    [MSG_I(MM_BA_DEL_REQ)]                = "MM_BA_DEL_REQ",
+    [MSG_I(MM_BA_DEL_CFM)]                = "MM_BA_DEL_CFM",
+    [MSG_I(MM_PRIMARY_TBTT_IND)]          = "MM_PRIMARY_TBTT_IND",
+    [MSG_I(MM_SECONDARY_TBTT_IND)]        = "MM_SECONDARY_TBTT_IND",
+    [MSG_I(MM_SET_POWER_REQ)]             = "MM_SET_POWER_REQ",
+    [MSG_I(MM_SET_POWER_CFM)]             = "MM_SET_POWER_CFM",
+    [MSG_I(MM_DBG_TRIGGER_REQ)]           = "MM_DBG_TRIGGER_REQ",
+    [MSG_I(MM_SET_PS_MODE_REQ)]           = "MM_SET_PS_MODE_REQ",
+    [MSG_I(MM_SET_PS_MODE_CFM)]           = "MM_SET_PS_MODE_CFM",
+    [MSG_I(MM_CHAN_CTXT_ADD_REQ)]         = "MM_CHAN_CTXT_ADD_REQ",
+    [MSG_I(MM_CHAN_CTXT_ADD_CFM)]         = "MM_CHAN_CTXT_ADD_CFM",
+    [MSG_I(MM_CHAN_CTXT_DEL_REQ)]         = "MM_CHAN_CTXT_DEL_REQ",
+    [MSG_I(MM_CHAN_CTXT_DEL_CFM)]         = "MM_CHAN_CTXT_DEL_CFM",
+    [MSG_I(MM_CHAN_CTXT_LINK_REQ)]        = "MM_CHAN_CTXT_LINK_REQ",
+    [MSG_I(MM_CHAN_CTXT_LINK_CFM)]        = "MM_CHAN_CTXT_LINK_CFM",
+    [MSG_I(MM_CHAN_CTXT_UNLINK_REQ)]      = "MM_CHAN_CTXT_UNLINK_REQ",
+    [MSG_I(MM_CHAN_CTXT_UNLINK_CFM)]      = "MM_CHAN_CTXT_UNLINK_CFM",
+    [MSG_I(MM_CHAN_CTXT_UPDATE_REQ)]      = "MM_CHAN_CTXT_UPDATE_REQ",
+    [MSG_I(MM_CHAN_CTXT_UPDATE_CFM)]      = "MM_CHAN_CTXT_UPDATE_CFM",
+    [MSG_I(MM_CHAN_CTXT_SCHED_REQ)]       = "MM_CHAN_CTXT_SCHED_REQ",
+    [MSG_I(MM_CHAN_CTXT_SCHED_CFM)]       = "MM_CHAN_CTXT_SCHED_CFM",
+    [MSG_I(MM_BCN_CHANGE_REQ)]            = "MM_BCN_CHANGE_REQ",
+    [MSG_I(MM_BCN_CHANGE_CFM)]            = "MM_BCN_CHANGE_CFM",
+    [MSG_I(MM_TIM_UPDATE_REQ)]            = "MM_TIM_UPDATE_REQ",
+    [MSG_I(MM_TIM_UPDATE_CFM)]            = "MM_TIM_UPDATE_CFM",
+    [MSG_I(MM_CONNECTION_LOSS_IND)]       = "MM_CONNECTION_LOSS_IND",
+    [MSG_I(MM_CHANNEL_SWITCH_IND)]        = "MM_CHANNEL_SWITCH_IND",
+    [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)]    = "MM_CHANNEL_PRE_SWITCH_IND",
+    [MSG_I(MM_REMAIN_ON_CHANNEL_REQ)]     = "MM_REMAIN_ON_CHANNEL_REQ",
+    [MSG_I(MM_REMAIN_ON_CHANNEL_CFM)]     = "MM_REMAIN_ON_CHANNEL_CFM",
+    [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = "MM_REMAIN_ON_CHANNEL_EXP_IND",
+    [MSG_I(MM_PS_CHANGE_IND)]             = "MM_PS_CHANGE_IND",
+    [MSG_I(MM_TRAFFIC_REQ_IND)]           = "MM_TRAFFIC_REQ_IND",
+    [MSG_I(MM_SET_PS_OPTIONS_REQ)]        = "MM_SET_PS_OPTIONS_REQ",
+    [MSG_I(MM_SET_PS_OPTIONS_CFM)]        = "MM_SET_PS_OPTIONS_CFM",
+    [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)]     = "MM_P2P_VIF_PS_CHANGE_IND",
+    [MSG_I(MM_CSA_COUNTER_IND)]           = "MM_CSA_COUNTER_IND",
+    [MSG_I(MM_CHANNEL_SURVEY_IND)]        = "MM_CHANNEL_SURVEY_IND",
+    [MSG_I(MM_SET_P2P_NOA_REQ)]           = "MM_SET_P2P_NOA_REQ",
+    [MSG_I(MM_SET_P2P_OPPPS_REQ)]         = "MM_SET_P2P_OPPPS_REQ",
+    [MSG_I(MM_SET_P2P_NOA_CFM)]           = "MM_SET_P2P_NOA_CFM",
+    [MSG_I(MM_SET_P2P_OPPPS_CFM)]         = "MM_SET_P2P_OPPPS_CFM",
+    [MSG_I(MM_CFG_RSSI_REQ)]              = "MM_CFG_RSSI_REQ",
+    [MSG_I(MM_RSSI_STATUS_IND)]           = "MM_RSSI_STATUS_IND",
+    [MSG_I(MM_CSA_FINISH_IND)]            = "MM_CSA_FINISH_IND",
+    [MSG_I(MM_CSA_TRAFFIC_IND)]           = "MM_CSA_TRAFFIC_IND",
+    [MSG_I(MM_MU_GROUP_UPDATE_REQ)]       = "MM_MU_GROUP_UPDATE_REQ",
+    [MSG_I(MM_MU_GROUP_UPDATE_CFM)]       = "MM_MU_GROUP_UPDATE_CFM",
+
+        [MSG_I(MM_SET_ARPOFFLOAD_REQ)]  = "MM_SET_ARPOFFLOAD_REQ",
+        [MSG_I(MM_SET_ARPOFFLOAD_CFM)]  = "MM_SET_ARPOFFLOAD_CFM",
+        [MSG_I(MM_SET_AGG_DISABLE_REQ)] = "MM_SET_AGG_DISABLE_REQ",
+        [MSG_I(MM_SET_AGG_DISABLE_CFM)] = "MM_SET_AGG_DISABLE_CFM",
+        [MSG_I(MM_SET_COEX_REQ)]        = "MM_SET_COEX_REQ",
+        [MSG_I(MM_SET_COEX_CFM)]        = "MM_SET_COEX_CFM",
+        [MSG_I(MM_SET_RF_CONFIG_REQ)]   = "MM_SET_RF_CONFIG_REQ",
+        [MSG_I(MM_SET_RF_CONFIG_CFM)]   = "MM_SET_RF_CONFIG_CFM",
+        [MSG_I(MM_SET_RF_CALIB_REQ)]    = "MM_SET_RF_CALIB_REQ",
+        [MSG_I(MM_SET_RF_CALIB_CFM)]    = "MM_SET_RF_CALIB_CFM",
+
+        [MSG_I(MM_GET_MAC_ADDR_REQ)]            = "MM_GET_MAC_ADDR_REQ",
+        [MSG_I(MM_GET_MAC_ADDR_CFM)]            = "MM_GET_MAC_ADDR_CFM",
+        [MSG_I(MM_GET_STA_INFO_REQ)]            = "MM_GET_STA_INFO_REQ",
+        [MSG_I(MM_GET_STA_INFO_CFM)]            = "MM_GET_STA_INFO_CFM",
+#if 0
+        [MSG_I(MM_SET_TXPWR_IDX_REQ)]   = "MM_SET_TXPWR_IDX_REQ",
+        [MSG_I(MM_SET_TXPWR_IDX_CFM)]   = "MM_SET_TXPWR_IDX_CFM",
+#endif
+        [MSG_I(MM_SET_TXPWR_OFST_REQ)]  = "MM_SET_TXPWR_OFST_REQ",
+        [MSG_I(MM_SET_TXPWR_OFST_CFM)]  = "MM_SET_TXPWR_OFST_CFM",
+        [MSG_I(MM_SET_STACK_START_REQ)] = "MM_SET_STACK_START_REQ",
+        [MSG_I(MM_SET_STACK_START_CFM)] = "MM_SET_STACK_START_CFM",
+        [MSG_I(MM_APM_STALOSS_IND)]     = "MM_APM_STALOSS_IND",
+        [MSG_I(MM_SET_VENDOR_HWCONFIG_REQ)]        = "MM_SET_VENDOR_HWCONFIG_REQ",
+        [MSG_I(MM_SET_VENDOR_HWCONFIG_CFM)]        = "MM_SET_VENDOR_HWCONFIG_CFM",
+        [MSG_I(MM_GET_FW_VERSION_REQ)]  = "MM_GET_FW_VERSION_REQ",
+        [MSG_I(MM_GET_FW_VERSION_CFM)]  = "MM_GET_FW_VERSION_CFM",
+};
+
+static const char *const rwnx_dbgid2str[MSG_I(DBG_MAX)] = {
+    [MSG_I(DBG_MEM_READ_REQ)]        = "DBG_MEM_READ_REQ",
+    [MSG_I(DBG_MEM_READ_CFM)]        = "DBG_MEM_READ_CFM",
+    [MSG_I(DBG_MEM_WRITE_REQ)]       = "DBG_MEM_WRITE_REQ",
+    [MSG_I(DBG_MEM_WRITE_CFM)]       = "DBG_MEM_WRITE_CFM",
+    [MSG_I(DBG_SET_MOD_FILTER_REQ)]  = "DBG_SET_MOD_FILTER_REQ",
+    [MSG_I(DBG_SET_MOD_FILTER_CFM)]  = "DBG_SET_MOD_FILTER_CFM",
+    [MSG_I(DBG_SET_SEV_FILTER_REQ)]  = "DBG_SET_SEV_FILTER_REQ",
+    [MSG_I(DBG_SET_SEV_FILTER_CFM)]  = "DBG_SET_SEV_FILTER_CFM",
+    [MSG_I(DBG_ERROR_IND)]           = "DBG_ERROR_IND",
+    [MSG_I(DBG_GET_SYS_STAT_REQ)]    = "DBG_GET_SYS_STAT_REQ",
+    [MSG_I(DBG_GET_SYS_STAT_CFM)]    = "DBG_GET_SYS_STAT_CFM",
+};
+
+static const char *const rwnx_scanid2str[MSG_I(SCAN_MAX)] = {
+    [MSG_I(SCAN_START_REQ)]          = "SCAN_START_REQ",
+    [MSG_I(SCAN_START_CFM)]          = "SCAN_START_CFM",
+    [MSG_I(SCAN_DONE_IND)]           = "SCAN_DONE_IND",
+};
+
+static const char *const rwnx_tdlsid2str[MSG_I(TDLS_MAX)] = {
+    [MSG_I(TDLS_CHAN_SWITCH_CFM)]        = "TDLS_CHAN_SWITCH_CFM",
+    [MSG_I(TDLS_CHAN_SWITCH_REQ)]        = "TDLS_CHAN_SWITCH_REQ",
+    [MSG_I(TDLS_CHAN_SWITCH_IND)]        = "TDLS_CHAN_SWITCH_IND",
+    [MSG_I(TDLS_CHAN_SWITCH_BASE_IND)]   = "TDLS_CHAN_SWITCH_BASE_IND",
+    [MSG_I(TDLS_CANCEL_CHAN_SWITCH_REQ)] = "TDLS_CANCEL_CHAN_SWITCH_REQ",
+    [MSG_I(TDLS_CANCEL_CHAN_SWITCH_CFM)] = "TDLS_CANCEL_CHAN_SWITCH_CFM",
+    [MSG_I(TDLS_PEER_PS_IND)]            = "TDLS_PEER_PS_IND",
+    [MSG_I(TDLS_PEER_TRAFFIC_IND_REQ)]   = "TDLS_PEER_TRAFFIC_IND_REQ",
+    [MSG_I(TDLS_PEER_TRAFFIC_IND_CFM)]   = "TDLS_PEER_TRAFFIC_IND_CFM",
+};
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+static const char *const rwnx_scanuid2str[MSG_I(SCANU_MAX)] = {
+    [MSG_I(SCANU_START_REQ)]  = "SCANU_START_REQ",
+    [MSG_I(SCANU_START_CFM)]  = "SCANU_START_CFM",
+    [MSG_I(SCANU_JOIN_REQ)]   = "SCANU_JOIN_REQ",
+    [MSG_I(SCANU_JOIN_CFM)]   = "SCANU_JOIN_CFM",
+    [MSG_I(SCANU_RESULT_IND)] = "SCANU_RESULT_IND",
+    [MSG_I(SCANU_FAST_REQ)]   = "SCANU_FAST_REQ",
+    [MSG_I(SCANU_FAST_CFM)]   = "SCANU_FAST_CFM",
+	[MSG_I(SCANU_VENDOR_IE_REQ)]   = "SCANU_VENDOR_IE_REQ",
+	[MSG_I(SCANU_VENDOR_IE_CFM)]   = "SCANU_VENDOR_IE_CFM",
+	[MSG_I(SCANU_START_CFM_ADDTIONAL)]   = "SCANU_START_CFM_ADDTIONAL",
+	[MSG_I(SCANU_CANCEL_REQ)]   = "SCANU_CANCEL_REQ",
+	[MSG_I(SCANU_CANCEL_CFM)]   = "SCANU_CANCEL_CFM",
+};
+
+static const char *const rwnx_meid2str[MSG_I(ME_MAX)] = {
+    [MSG_I(ME_CONFIG_REQ)]           = "ME_CONFIG_REQ",
+    [MSG_I(ME_CONFIG_CFM)]           = "ME_CONFIG_CFM",
+    [MSG_I(ME_CHAN_CONFIG_REQ)]      = "ME_CHAN_CONFIG_REQ",
+    [MSG_I(ME_CHAN_CONFIG_CFM)]      = "ME_CHAN_CONFIG_CFM",
+    [MSG_I(ME_SET_CONTROL_PORT_REQ)] = "ME_SET_CONTROL_PORT_REQ",
+    [MSG_I(ME_SET_CONTROL_PORT_CFM)] = "ME_SET_CONTROL_PORT_CFM",
+    [MSG_I(ME_TKIP_MIC_FAILURE_IND)] = "ME_TKIP_MIC_FAILURE_IND",
+    [MSG_I(ME_STA_ADD_REQ)]          = "ME_STA_ADD_REQ",
+    [MSG_I(ME_STA_ADD_CFM)]          = "ME_STA_ADD_CFM",
+    [MSG_I(ME_STA_DEL_REQ)]          = "ME_STA_DEL_REQ",
+    [MSG_I(ME_STA_DEL_CFM)]          = "ME_STA_DEL_CFM",
+    [MSG_I(ME_TX_CREDITS_UPDATE_IND)]= "ME_TX_CREDITS_UPDATE_IND",
+    [MSG_I(ME_RC_STATS_REQ)]         = "ME_RC_STATS_REQ",
+    [MSG_I(ME_RC_STATS_CFM)]         = "ME_RC_STATS_CFM",
+    [MSG_I(ME_RC_SET_RATE_REQ)]      = "ME_RC_SET_RATE_REQ",
+    [MSG_I(ME_TRAFFIC_IND_REQ)]      = "ME_TRAFFIC_IND_REQ",
+    [MSG_I(ME_TRAFFIC_IND_CFM)]      = "ME_TRAFFIC_IND_CFM",
+    [MSG_I(ME_SET_PS_MODE_REQ)]      = "ME_SET_PS_MODE_REQ",
+    [MSG_I(ME_SET_PS_MODE_CFM)]      = "ME_SET_PS_MODE_CFM",
+};
+
+static const char *const rwnx_smid2str[MSG_I(SM_MAX)] = {
+    [MSG_I(SM_CONNECT_REQ)]       = "SM_CONNECT_REQ",
+    [MSG_I(SM_CONNECT_CFM)]       = "SM_CONNECT_CFM",
+    [MSG_I(SM_CONNECT_IND)]       = "SM_CONNECT_IND",
+    [MSG_I(SM_DISCONNECT_REQ)]    = "SM_DISCONNECT_REQ",
+    [MSG_I(SM_DISCONNECT_CFM)]    = "SM_DISCONNECT_CFM",
+    [MSG_I(SM_DISCONNECT_IND)]    = "SM_DISCONNECT_IND",
+    [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_IND)] = "SM_EXTERNAL_AUTH_REQUIRED_IND",
+    [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_RSP)] = "SM_EXTERNAL_AUTH_REQUIRED_RSP",
+};
+
+static const char *const rwnx_apmid2str[MSG_I(APM_MAX)] = {
+    [MSG_I(APM_START_REQ)]     = "APM_START_REQ",
+    [MSG_I(APM_START_CFM)]     = "APM_START_CFM",
+    [MSG_I(APM_STOP_REQ)]      = "APM_STOP_REQ",
+    [MSG_I(APM_STOP_CFM)]      = "APM_STOP_CFM",
+    [MSG_I(APM_START_CAC_REQ)] = "APM_START_CAC_REQ",
+    [MSG_I(APM_START_CAC_CFM)] = "APM_START_CAC_CFM",
+    [MSG_I(APM_STOP_CAC_REQ)]  = "APM_STOP_CAC_REQ",
+    [MSG_I(APM_STOP_CAC_CFM)]  = "APM_STOP_CAC_CFM",
+};
+
+static const char *const rwnx_meshid2str[MSG_I(MESH_MAX)] = {
+    [MSG_I(MESH_START_REQ)]        = "MESH_START_REQ",
+    [MSG_I(MESH_START_CFM)]        = "MESH_START_CFM",
+    [MSG_I(MESH_STOP_REQ)]         = "MESH_STOP_REQ",
+    [MSG_I(MESH_STOP_CFM)]         = "MESH_STOP_CFM",
+    [MSG_I(MESH_UPDATE_REQ)]       = "MESH_UPDATE_REQ",
+    [MSG_I(MESH_UPDATE_CFM)]       = "MESH_UPDATE_CFM",
+    [MSG_I(MESH_PATH_CREATE_REQ)]  = "MESH_PATH_CREATE_REQ",
+    [MSG_I(MESH_PATH_CREATE_CFM)]  = "MESH_PATH_CREATE_CFM",
+    [MSG_I(MESH_PATH_UPDATE_REQ)]  = "MESH_PATH_UPDATE_REQ",
+    [MSG_I(MESH_PATH_UPDATE_CFM)]  = "MESH_PATH_UPDATE_CFM",
+    [MSG_I(MESH_PROXY_ADD_REQ)]    = "MESH_PROXY_ADD_REQ",
+    [MSG_I(MESH_PEER_UPDATE_IND)]  = "MESH_PEER_UPDATE_IND",
+    [MSG_I(MESH_PATH_UPDATE_IND)]  = "MESH_PATH_UPDATE_IND",
+    [MSG_I(MESH_PROXY_UPDATE_IND)] = "MESH_PROXY_UPDATE_IND",
+};
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+const char *const *rwnx_id2str[TASK_LAST_EMB + 1] = {
+    [TASK_MM]    = rwnx_mmid2str,
+    [TASK_DBG]   = rwnx_dbgid2str,
+    [TASK_SCAN]  = rwnx_scanid2str,
+    [TASK_TDLS]  = rwnx_tdlsid2str,
+#ifdef CONFIG_RWNX_FULLMAC
+    [TASK_SCANU] = rwnx_scanuid2str,
+    [TASK_ME]    = rwnx_meid2str,
+    [TASK_SM]    = rwnx_smid2str,
+    [TASK_APM]   = rwnx_apmid2str,
+    [TASK_MESH]  = rwnx_meshid2str,
+#endif
+};
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_strs.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_strs.h
new file mode 100755
index 0000000..5cf3f0d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_strs.h
@@ -0,0 +1,31 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_strs.h
+ *
+ * @brief Miscellaneous debug strings
+ *
+ * Copyright (C) RivieraWaves 2014-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_STRS_H_
+#define _RWNX_STRS_H_
+
+#ifdef CONFIG_RWNX_FHOST
+
+#define RWNX_ID2STR(tag) "Cmd"
+
+#else
+#include "lmac_msg.h"
+
+#define RWNX_ID2STR(tag) (((MSG_T(tag) < ARRAY_SIZE(rwnx_id2str)) &&        \
+                           (rwnx_id2str[MSG_T(tag)]) &&          \
+                           ((rwnx_id2str[MSG_T(tag)])[MSG_I(tag)])) ?   \
+                          (rwnx_id2str[MSG_T(tag)])[MSG_I(tag)] : "unknown")
+
+extern const char *const *rwnx_id2str[TASK_LAST_EMB + 1];
+#endif /* CONFIG_RWNX_FHOST */
+
+#endif /* _RWNX_STRS_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_tdls.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_tdls.c
new file mode 100755
index 0000000..877fb56
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_tdls.c
@@ -0,0 +1,805 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_tx.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+
+#include "rwnx_tdls.h"
+#include "rwnx_compat.h"
+
+/**
+ * FUNCTION DEFINITIONS
+ ******************************************************************************
+ */
+
+static u16
+rwnx_get_tdls_sta_capab(struct rwnx_vif *rwnx_vif, u16 status_code)
+{
+    u16 capab = 0;
+
+    /* The capability will be 0 when sending a failure code */
+    if (status_code != 0)
+        return capab;
+
+    if (rwnx_vif->sta.ap->band != NL80211_BAND_2GHZ)
+        return capab;
+
+    capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+    capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+    return capab;
+}
+
+static int
+rwnx_tdls_prepare_encap_data(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                                 const u8 *peer, u8 action_code, u8 dialog_token,
+                                 u16 status_code, struct sk_buff *skb)
+{
+    struct ieee80211_tdls_data *tf;
+    tf = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_data) - sizeof(tf->u));
+
+    // set eth header
+    memcpy(tf->da, peer, ETH_ALEN);
+    memcpy(tf->sa, rwnx_hw->wiphy->perm_addr, ETH_ALEN);
+    tf->ether_type = cpu_to_be16(ETH_P_TDLS);
+
+    // set common TDLS info
+    tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
+    tf->category = WLAN_CATEGORY_TDLS;
+    tf->action_code = action_code;
+
+    // set action specific TDLS info
+    switch (action_code) {
+    case WLAN_TDLS_SETUP_REQUEST:
+        skb_put(skb, sizeof(tf->u.setup_req));
+        tf->u.setup_req.dialog_token = dialog_token;
+        tf->u.setup_req.capability =
+                cpu_to_le16(rwnx_get_tdls_sta_capab(rwnx_vif, status_code));
+        break;
+
+    case WLAN_TDLS_SETUP_RESPONSE:
+        skb_put(skb, sizeof(tf->u.setup_resp));
+        tf->u.setup_resp.status_code = cpu_to_le16(status_code);
+        tf->u.setup_resp.dialog_token = dialog_token;
+        tf->u.setup_resp.capability =
+                cpu_to_le16(rwnx_get_tdls_sta_capab(rwnx_vif, status_code));
+        break;
+
+    case WLAN_TDLS_SETUP_CONFIRM:
+        skb_put(skb, sizeof(tf->u.setup_cfm));
+        tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
+        tf->u.setup_cfm.dialog_token = dialog_token;
+        break;
+
+    case WLAN_TDLS_TEARDOWN:
+        skb_put(skb, sizeof(tf->u.teardown));
+        tf->u.teardown.reason_code = cpu_to_le16(status_code);
+        break;
+
+    case WLAN_TDLS_DISCOVERY_REQUEST:
+        skb_put(skb, sizeof(tf->u.discover_req));
+        tf->u.discover_req.dialog_token = dialog_token;
+        break;
+
+    default:
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static int
+rwnx_prep_tdls_direct(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                      const u8 *peer, u8 action_code, u8 dialog_token,
+                      u16 status_code, struct sk_buff *skb)
+{
+    struct ieee80211_mgmt *mgmt;
+
+    mgmt = (void *)skb_put(skb, 24);
+    memset(mgmt, 0, 24);
+    memcpy(mgmt->da, peer, ETH_ALEN);
+    memcpy(mgmt->sa, rwnx_hw->wiphy->perm_addr, ETH_ALEN);
+    memcpy(mgmt->bssid, rwnx_vif->sta.ap->mac_addr, ETH_ALEN);
+
+    mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                      IEEE80211_STYPE_ACTION);
+
+    switch (action_code) {
+    case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+        skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
+        mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
+        mgmt->u.action.u.tdls_discover_resp.action_code = WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
+        mgmt->u.action.u.tdls_discover_resp.dialog_token = dialog_token;
+        mgmt->u.action.u.tdls_discover_resp.capability =
+            cpu_to_le16(rwnx_get_tdls_sta_capab(rwnx_vif, status_code));
+        break;
+    default:
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static int
+rwnx_add_srates_ie(struct rwnx_hw *rwnx_hw, struct sk_buff *skb)
+{
+    u8 i, rates, *pos;
+    int rate;
+    struct ieee80211_supported_band *rwnx_band_2GHz = rwnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+
+    rates = 8;
+
+    if (skb_tailroom(skb) < rates + 2)
+        return -ENOMEM;
+
+    pos = skb_put(skb, rates + 2);
+    *pos++ = WLAN_EID_SUPP_RATES;
+    *pos++ = rates;
+    for (i = 0; i < rates; i++) {
+        rate = rwnx_band_2GHz->bitrates[i].bitrate;
+        rate = DIV_ROUND_UP(rate, 5);
+        *pos++ = (u8)rate;
+    }
+
+    return 0;
+}
+
+static int
+rwnx_add_ext_srates_ie(struct rwnx_hw *rwnx_hw, struct sk_buff *skb)
+{
+    u8 i, exrates, *pos;
+    int rate;
+    struct ieee80211_supported_band *rwnx_band_2GHz = rwnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+
+    exrates = rwnx_band_2GHz->n_bitrates - 8;
+
+    if (skb_tailroom(skb) < exrates + 2)
+        return -ENOMEM;
+
+    pos = skb_put(skb, exrates + 2);
+    *pos++ = WLAN_EID_EXT_SUPP_RATES;
+    *pos++ = exrates;
+    for (i = 8; i < (8+exrates); i++) {
+        rate = rwnx_band_2GHz->bitrates[i].bitrate;
+        rate = DIV_ROUND_UP(rate, 5);
+        *pos++ = (u8)rate;
+    }
+
+    return 0;
+}
+
+static void
+rwnx_tdls_add_supp_channels(struct rwnx_hw *rwnx_hw, struct sk_buff *skb)
+{
+    /*
+     * Add possible channels for TDLS. These are channels that are allowed
+     * to be active.
+     */
+    u8 subband_cnt = 0;
+    u8 *pos_subband;
+    u8 *pos = skb_put(skb, 2);
+    struct ieee80211_supported_band *rwnx_band_2GHz = rwnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+	#ifdef USE_5G
+    struct ieee80211_supported_band *rwnx_band_5GHz = rwnx_hw->wiphy->bands[NL80211_BAND_5GHZ];
+	#endif
+
+    *pos++ = WLAN_EID_SUPPORTED_CHANNELS;
+
+    /*
+     * 5GHz and 2GHz channels numbers can overlap. Ignore this for now, as
+     * this doesn't happen in real world scenarios.
+     */
+
+    /* 2GHz, with 5MHz spacing */
+    pos_subband = skb_put(skb, 2);
+    if (rwnx_band_2GHz->n_channels > 0)
+    {
+        *pos_subband++ = ieee80211_frequency_to_channel(rwnx_band_2GHz->channels[0].center_freq);
+        *pos_subband++ = rwnx_band_2GHz->n_channels;
+        subband_cnt++;
+    }
+
+    /* 5GHz, with 20MHz spacing */
+    pos_subband = skb_put(skb, 2);
+	#ifdef USE_5G
+    if (rwnx_band_5GHz->n_channels > 0)
+    {
+        *pos_subband++ = ieee80211_frequency_to_channel(rwnx_band_5GHz->channels[0].center_freq);
+        *pos_subband++ = rwnx_band_5GHz->n_channels;
+        subband_cnt++;
+    }
+	#endif
+    /* length */
+    *pos = 2 * subband_cnt;
+}
+
+static void
+rwnx_tdls_add_ext_capab(struct rwnx_hw *rwnx_hw, struct sk_buff *skb)
+{
+    u8 *pos = (void *)skb_put(skb, 7);
+    bool chan_switch = rwnx_hw->wiphy->features &
+               NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+
+    *pos++ = WLAN_EID_EXT_CAPABILITY;
+    *pos++ = 5; /* len */
+    *pos++ = 0x0;
+    *pos++ = 0x0;
+    *pos++ = 0x0;
+    *pos++ = WLAN_EXT_CAPA4_TDLS_BUFFER_STA |
+             (chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0);
+    *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
+}
+
+static void
+rwnx_add_wmm_info_ie(struct sk_buff *skb, u8 qosinfo)
+{
+    u8 *pos = (void *)skb_put(skb, 9);
+
+    *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+    *pos++ = 7; /* len */
+    *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
+    *pos++ = 0x50;
+    *pos++ = 0xf2;
+    *pos++ = 2; /* WME */
+    *pos++ = 0; /* WME info */
+    *pos++ = 1; /* WME ver */
+    *pos++ = qosinfo; /* U-APSD no in use */
+}
+
+/* translate numbering in the WMM parameter IE to the mac80211 notation */
+static u8 rwnx_ac_from_wmm(int ac)
+{
+	switch (ac) {
+	default:
+		WARN_ON_ONCE(1);
+	case 0:
+		return AC_BE;
+	case 1:
+		return AC_BK;
+	case 2:
+		return AC_VI;
+	case 3:
+		return AC_VO;
+	}
+}
+
+static void
+rwnx_add_wmm_param_ie(struct sk_buff *skb, u8 acm_bits, u32 *ac_params)
+{
+    struct ieee80211_wmm_param_ie *wmm;
+    int i, j;
+    u8 cw_min, cw_max;
+    bool acm;
+
+    wmm = (void *)skb_put(skb, sizeof(struct ieee80211_wmm_param_ie));
+    memset(wmm, 0, sizeof(*wmm));
+
+    wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
+    wmm->len = sizeof(*wmm) - 2;
+
+    wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
+    wmm->oui[1] = 0x50;
+    wmm->oui[2] = 0xf2;
+    wmm->oui_type = 2; /* WME */
+    wmm->oui_subtype = 1; /* WME param */
+    wmm->version = 1; /* WME ver */
+    wmm->qos_info = 0; /* U-APSD not in use */
+
+    /*
+     * Use the EDCA parameters defined for the BSS, or default if the AP
+     * doesn't support it, as mandated by 802.11-2012 section 10.22.4
+     */
+    for (i = 0; i < AC_MAX; i++) {
+        j = rwnx_ac_from_wmm(i);
+        cw_min = (ac_params[j] & 0xF0 ) >> 4;
+        cw_max = (ac_params[j] & 0xF00 ) >> 8;
+        acm = (acm_bits & (1 << j)) != 0;
+
+        wmm->ac[i].aci_aifsn = (i << 5) | (acm << 4) | (ac_params[j] & 0xF);
+        wmm->ac[i].cw = (cw_max << 4) | cw_min;
+        wmm->ac[i].txop_limit = (ac_params[j] & 0x0FFFF000 ) >> 12;
+    }
+}
+
+static void
+rwnx_tdls_add_oper_classes(struct rwnx_vif *rwnx_vif, struct sk_buff *skb)
+{
+    u8 *pos;
+    u8 op_class;
+    struct cfg80211_chan_def chan_def;
+    struct ieee80211_channel chan;
+
+    chan.band = rwnx_vif->sta.ap->band;
+    chan.center_freq = rwnx_vif->sta.ap->center_freq;
+    chan_def.chan = &chan;
+    chan_def.width = rwnx_vif->sta.ap->width;
+    chan_def.center_freq1 = rwnx_vif->sta.ap->center_freq1;
+    chan_def.center_freq2 = rwnx_vif->sta.ap->center_freq2;
+
+    if (!ieee80211_chandef_to_operating_class(&chan_def, &op_class))
+        return;
+
+    pos = skb_put(skb, 4);
+    *pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES;
+    *pos++ = 2; /* len */
+
+    // current op class
+    *pos++ = op_class;
+    *pos++ = op_class; /* give current operating class as alternate too */
+
+    // need to add 5GHz classes?
+}
+
+static void
+rwnx_ie_build_ht_cap(struct sk_buff *skb, struct ieee80211_sta_ht_cap *ht_cap,
+                  u16 cap)
+{
+    u8 *pos;
+    __le16 tmp;
+
+    pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+    *pos++ = WLAN_EID_HT_CAPABILITY;
+    *pos++ = sizeof(struct ieee80211_ht_cap);
+    memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+
+    /* capability flags */
+    tmp = cpu_to_le16(cap);
+    memcpy(pos, &tmp, sizeof(u16));
+    pos += sizeof(u16);
+
+    /* AMPDU parameters */
+    *pos++ = ht_cap->ampdu_factor |
+         (ht_cap->ampdu_density <<
+            IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+
+    /* MCS set */
+    memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs));
+    pos += sizeof(ht_cap->mcs);
+
+    /* extended capabilities */
+    pos += sizeof(__le16);
+
+    /* BF capabilities */
+    pos += sizeof(__le32);
+
+    /* antenna selection */
+    pos += sizeof(u8);
+}
+
+static void
+rwnx_ie_build_vht_cap(struct sk_buff *skb, struct ieee80211_sta_vht_cap *vht_cap,
+                   u32 cap)
+{
+#if 0
+    u8 *pos;
+    __le32 tmp;
+
+    pos = skb_put(skb, 14);
+
+    *pos++ = WLAN_EID_VHT_CAPABILITY;
+    *pos++ = sizeof(struct ieee80211_vht_cap);
+    memset(pos, 0, sizeof(struct ieee80211_vht_cap));
+
+    /* capability flags */
+    tmp = cpu_to_le32(cap);
+    memcpy(pos, &tmp, sizeof(u32));
+    pos += sizeof(u32);
+
+    /* VHT MCS set */
+    memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs));
+    pos += sizeof(vht_cap->vht_mcs);
+#endif
+}
+
+static void
+rwnx_tdls_add_bss_coex_ie(struct sk_buff *skb)
+{
+    u8 *pos = (void *)skb_put(skb, 3);
+
+    *pos++ = WLAN_EID_BSS_COEX_2040;
+    *pos++ = 1; /* len */
+
+    *pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST;
+}
+
+static void
+rwnx_tdls_add_link_ie(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                       struct sk_buff *skb, const u8 *peer,
+                       bool initiator)
+{
+    struct ieee80211_tdls_lnkie *lnkid;
+    const u8 *init_addr, *rsp_addr;
+
+    if (initiator) {
+        init_addr = rwnx_hw->wiphy->perm_addr;
+        rsp_addr = peer;
+    } else {
+        init_addr = peer;
+        rsp_addr = rwnx_hw->wiphy->perm_addr;
+    }
+
+    lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
+
+    lnkid->ie_type = WLAN_EID_LINK_ID;
+    lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
+
+    memcpy(lnkid->bssid, rwnx_vif->sta.ap->mac_addr, ETH_ALEN);
+    memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
+    memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
+}
+
+static void
+rwnx_tdls_add_aid_ie(struct rwnx_vif *rwnx_vif, struct sk_buff *skb)
+{
+    u8 *pos = (void *)skb_put(skb, 4);
+
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+    *pos++ = WLAN_EID_AID;
+    #else
+    *pos++ = 197;
+    #endif
+    *pos++ = 2; /* len */
+    *pos++ = rwnx_vif->sta.ap->aid;
+}
+
+static u8 *
+rwnx_ie_build_ht_oper(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                          u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
+                          u16 prot_mode)
+{
+#if 0
+    struct ieee80211_ht_operation *ht_oper;
+    /* Build HT Information */
+    *pos++ = WLAN_EID_HT_OPERATION;
+    *pos++ = sizeof(struct ieee80211_ht_operation);
+    ht_oper = (struct ieee80211_ht_operation *)pos;
+    ht_oper->primary_chan = ieee80211_frequency_to_channel(
+                    rwnx_vif->sta.ap->center_freq);
+    switch (rwnx_vif->sta.ap->width) {
+    case NL80211_CHAN_WIDTH_160:
+    case NL80211_CHAN_WIDTH_80P80:
+    case NL80211_CHAN_WIDTH_80:
+    case NL80211_CHAN_WIDTH_40:
+        if (rwnx_vif->sta.ap->center_freq1 > rwnx_vif->sta.ap->center_freq)
+            ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+        else
+            ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+        break;
+    default:
+        ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+        break;
+    }
+    if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
+        rwnx_vif->sta.ap->width != NL80211_CHAN_WIDTH_20_NOHT &&
+        rwnx_vif->sta.ap->width != NL80211_CHAN_WIDTH_20)
+        ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
+
+    ht_oper->operation_mode = cpu_to_le16(prot_mode);
+    ht_oper->stbc_param = 0x0000;
+
+    /* It seems that Basic MCS set and Supported MCS set
+       are identical for the first 10 bytes */
+    memset(&ht_oper->basic_set, 0, 16);
+    memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10);
+
+    return pos + sizeof(struct ieee80211_ht_operation);
+#else
+    return 0;
+#endif
+}
+
+static u8 *
+rwnx_ie_build_vht_oper(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                          u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
+                          u16 prot_mode)
+{
+#if 0
+    struct ieee80211_vht_operation *vht_oper;
+    /* Build HT Information */
+    *pos++ = WLAN_EID_VHT_OPERATION;
+    *pos++ = sizeof(struct ieee80211_vht_operation);
+    vht_oper = (struct ieee80211_vht_operation *)pos;
+
+    switch (rwnx_vif->sta.ap->width) {
+    case NL80211_CHAN_WIDTH_80:
+        vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; // Channel Width
+        CCFS0(vht_oper) =
+                ieee80211_frequency_to_channel(rwnx_vif->sta.ap->center_freq); // Channel Center Frequency Segment 0
+        CCFS1(vht_oper) = 0; // Channel Center Frequency Segment 1 (N.A.)
+        break;
+    case NL80211_CHAN_WIDTH_160:
+        vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; // Channel Width
+        CCFS0(vht_oper) =
+                ieee80211_frequency_to_channel(rwnx_vif->sta.ap->center_freq); // Channel Center Frequency Segment 0
+        CCFS1(vht_oper) = 0; // Channel Center Frequency Segment 1 (N.A.)
+        break;
+    case NL80211_CHAN_WIDTH_80P80:
+        vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; // Channel Width
+        CCFS0(vht_oper) =
+                ieee80211_frequency_to_channel(rwnx_vif->sta.ap->center_freq1); // Channel Center Frequency Segment 0
+        CCFS1(vht_oper) =
+                ieee80211_frequency_to_channel(rwnx_vif->sta.ap->center_freq2); // Channel Center Frequency Segment 1
+        break;
+    default:
+        vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
+        CCFS0(vht_oper) = 0;
+        CCFS1(vht_oper) = 0;
+        break;
+    }
+
+    vht_oper->basic_mcs_set = cpu_to_le16(rwnx_hw->mod_params->mcs_map);
+
+    return pos + sizeof(struct ieee80211_vht_operation);
+#else
+    return 0;
+#endif
+}
+
+static void
+rwnx_tdls_add_setup_start_ies(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                              struct sk_buff *skb, const u8 *peer,
+                              u8 action_code, bool initiator,
+                              const u8 *extra_ies, size_t extra_ies_len)
+{
+#if 0
+    enum nl80211_band band = rwnx_vif->sta.ap->band;
+    struct ieee80211_supported_band *sband;
+    struct ieee80211_sta_ht_cap ht_cap;
+    struct ieee80211_sta_vht_cap vht_cap;
+    size_t offset = 0, noffset;
+    u8 *pos;
+
+    rcu_read_lock();
+
+    rwnx_add_srates_ie(rwnx_hw, skb);
+    rwnx_add_ext_srates_ie(rwnx_hw, skb);
+    rwnx_tdls_add_supp_channels(rwnx_hw, skb);
+    rwnx_tdls_add_ext_capab(rwnx_hw, skb);
+
+    /* add the QoS element if we support it */
+    if (/*local->hw.queues >= IEEE80211_NUM_ACS &&*/
+        action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES)
+        rwnx_add_wmm_info_ie(skb, 0); /* no U-APSD */
+
+    rwnx_tdls_add_oper_classes(rwnx_vif, skb);
+
+    /*
+     * with TDLS we can switch channels, and HT-caps are not necessarily
+     * the same on all bands. The specification limits the setup to a
+     * single HT-cap, so use the current band for now.
+     */
+    sband = rwnx_hw->wiphy->bands[band];
+    memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+    if (((action_code == WLAN_TDLS_SETUP_REQUEST) ||
+         (action_code == WLAN_TDLS_SETUP_RESPONSE) ||
+         (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES)) &&
+         ht_cap.ht_supported /* (!sta || sta->sta.ht_cap.ht_supported)*/) {
+        rwnx_ie_build_ht_cap(skb, &ht_cap, ht_cap.cap);
+    }
+
+    if (ht_cap.ht_supported &&
+        (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
+        rwnx_tdls_add_bss_coex_ie(skb);
+
+    rwnx_tdls_add_link_ie(rwnx_hw, rwnx_vif, skb, peer, initiator);
+
+    memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+    if (vht_cap.vht_supported) {
+        rwnx_tdls_add_aid_ie(rwnx_vif, skb);
+        rwnx_ie_build_vht_cap(skb, &vht_cap, vht_cap.cap);
+        // Operating mode Notification (optional)
+    }
+
+    /* add any remaining IEs */
+    if (extra_ies_len) {
+        noffset = extra_ies_len;
+        pos = skb_put(skb, noffset - offset);
+        memcpy(pos, extra_ies + offset, noffset - offset);
+    }
+
+    rcu_read_unlock();
+#endif
+}
+
+
+static void
+rwnx_tdls_add_setup_cfm_ies(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                              struct sk_buff *skb, const u8 *peer, bool initiator,
+                              const u8 *extra_ies, size_t extra_ies_len)
+{
+#if 0
+    struct ieee80211_supported_band *sband;
+    enum nl80211_band band = rwnx_vif->sta.ap->band;
+    struct ieee80211_sta_ht_cap ht_cap;
+    struct ieee80211_sta_vht_cap vht_cap;
+
+    size_t offset = 0, noffset;
+    struct rwnx_sta *sta, *ap_sta;
+    u8 *pos;
+
+    rcu_read_lock();
+
+    sta = rwnx_get_sta(rwnx_hw, peer);
+    ap_sta = rwnx_vif->sta.ap;
+    if (WARN_ON_ONCE(!sta || !ap_sta)) {
+        rcu_read_unlock();
+        return;
+    }
+
+    /* add the QoS param IE if both the peer and we support it */
+    if (sta->qos)
+    	rwnx_add_wmm_param_ie(skb, ap_sta->acm, ap_sta->ac_param);
+
+    /* if HT support is only added in TDLS, we need an HT-operation IE */
+    sband = rwnx_hw->wiphy->bands[band];
+    memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+    if (ht_cap.ht_supported && !ap_sta->ht && sta->ht) {
+        pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
+        /* send an empty HT operation IE */
+        rwnx_ie_build_ht_oper(rwnx_hw, rwnx_vif, pos, &ht_cap, 0);
+    }
+
+    rwnx_tdls_add_link_ie(rwnx_hw, rwnx_vif, skb, peer, initiator);
+
+    memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+    if (vht_cap.vht_supported && !ap_sta->vht && sta->vht) {
+        pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation));
+        rwnx_ie_build_vht_oper(rwnx_hw, rwnx_vif, pos, &ht_cap, 0);
+        // Operating mode Notification (optional)
+    }
+
+    /* add any remaining IEs */
+    if (extra_ies_len) {
+        noffset = extra_ies_len;
+        pos = skb_put(skb, noffset - offset);
+        memcpy(pos, extra_ies + offset, noffset - offset);
+    }
+
+    rcu_read_unlock();
+#endif
+}
+
+static void
+rwnx_tdls_add_ies(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                                   struct sk_buff *skb, const u8 *peer,
+                                   u8 action_code, u16 status_code,
+                                   bool initiator, const u8 *extra_ies,
+                                   size_t extra_ies_len, u8 oper_class,
+                                   struct cfg80211_chan_def *chandef)
+{
+    switch (action_code) {
+    case WLAN_TDLS_SETUP_REQUEST:
+    case WLAN_TDLS_SETUP_RESPONSE:
+    case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+        if (status_code == 0)
+            rwnx_tdls_add_setup_start_ies(rwnx_hw, rwnx_vif, skb, peer, action_code,
+                                          initiator, extra_ies, extra_ies_len);
+        break;
+    case WLAN_TDLS_SETUP_CONFIRM:
+        if (status_code == 0)
+            rwnx_tdls_add_setup_cfm_ies(rwnx_hw, rwnx_vif, skb, peer, initiator,
+                                        extra_ies, extra_ies_len);
+        break;
+
+    case WLAN_TDLS_TEARDOWN:
+    case WLAN_TDLS_DISCOVERY_REQUEST:
+        if (extra_ies_len)
+            memcpy(skb_put(skb, extra_ies_len), extra_ies,
+                   extra_ies_len);
+        if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
+            rwnx_tdls_add_link_ie(rwnx_hw, rwnx_vif, skb, peer, initiator);
+        break;
+    }
+}
+
+int
+rwnx_tdls_send_mgmt_packet_data(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                         const u8 *peer, u8 action_code, u8 dialog_token,
+                         u16 status_code, u32 peer_capability, bool initiator,
+                         const u8 *extra_ies, size_t extra_ies_len, u8 oper_class,
+                         struct cfg80211_chan_def *chandef)
+{
+#if 0
+    struct sk_buff *skb;
+    int ret = 0;
+    struct ieee80211_supported_band *rwnx_band_2GHz = rwnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+    #ifdef USE_5G
+    struct ieee80211_supported_band *rwnx_band_5GHz = rwnx_hw->wiphy->bands[NL80211_BAND_5GHZ];
+    #endif
+
+    skb = netdev_alloc_skb(rwnx_vif->ndev,
+              sizeof(struct ieee80211_tdls_data) + // ethhdr + TDLS info
+              10 +  /* supported rates */
+              6 +  /* extended supported rates */
+              #ifdef USE_5G
+              (2 + rwnx_band_2GHz->n_channels + rwnx_band_5GHz->n_channels) + /* supported channels */
+              #else
+			  (2 + rwnx_band_2GHz->n_channels) + /* supported channels */
+			  #endif
+              sizeof(struct ieee_types_extcap) +
+              sizeof(struct ieee80211_wmm_param_ie) +
+              4 + /* oper classes */
+              28 + //sizeof(struct ieee80211_ht_cap) +
+              sizeof(struct ieee_types_bss_co_2040) +
+              sizeof(struct ieee80211_tdls_lnkie) +
+              (2 + sizeof(struct ieee80211_vht_cap)) +
+              4 + /*AID*/
+              (2 + sizeof(struct ieee80211_ht_operation)) +
+              extra_ies_len);
+
+    if (!skb)
+        return 0;
+
+    switch (action_code) {
+    case WLAN_TDLS_SETUP_REQUEST:
+    case WLAN_TDLS_SETUP_RESPONSE:
+    case WLAN_TDLS_SETUP_CONFIRM:
+    case WLAN_TDLS_TEARDOWN:
+    case WLAN_TDLS_DISCOVERY_REQUEST:
+        ret = rwnx_tdls_prepare_encap_data(rwnx_hw, rwnx_vif, peer, action_code,
+                                           dialog_token, status_code, skb);
+        break;
+
+    case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+        ret = rwnx_prep_tdls_direct(rwnx_hw, rwnx_vif, peer, action_code,
+                                    dialog_token, status_code, skb);
+        break;
+
+    default:
+        ret = -ENOTSUPP;
+        break;
+    }
+
+    if (ret < 0)
+        goto fail;
+
+    rwnx_tdls_add_ies(rwnx_hw, rwnx_vif, skb, peer, action_code, status_code,
+                      initiator, extra_ies, extra_ies_len, oper_class, chandef);
+
+    if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
+        u64 cookie;
+        #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+        struct cfg80211_mgmt_tx_params params;
+
+        params.len = skb->len;
+        params.buf = skb->data;
+        ret = rwnx_start_mgmt_xmit(rwnx_vif, NULL, &params, false, &cookie);
+        #else
+        ret = rwnx_start_mgmt_xmit(rwnx_vif, NULL, NULL, false, 0, skb->data, skb->len, false, false, &cookie);
+        #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+
+        return ret;
+    }
+
+    switch (action_code) {
+    case WLAN_TDLS_SETUP_REQUEST:
+    case WLAN_TDLS_SETUP_RESPONSE:
+    case WLAN_TDLS_SETUP_CONFIRM:
+        skb->priority = 2;
+        break;
+    default:
+        skb->priority = 5;
+        break;
+    }
+
+    ret = rwnx_select_txq(rwnx_vif, skb);
+    ret = rwnx_start_xmit(skb, rwnx_vif->ndev);
+
+   return ret;
+
+fail:
+    dev_kfree_skb(skb);
+    return ret;
+#else
+    return 0;
+#endif
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_tdls.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_tdls.h
new file mode 100755
index 0000000..9e4aa90
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_tdls.h
@@ -0,0 +1,54 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_tdls.h
+ *
+ * @brief TDLS function declarations
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef RWNX_TDLS_H_
+#define RWNX_TDLS_H_
+
+#include "rwnx_defs.h"
+
+struct ieee_types_header {
+    u8 element_id;
+    u8 len;
+} __packed;
+
+struct ieee_types_bss_co_2040 {
+    struct ieee_types_header ieee_hdr;
+    u8 bss_2040co;
+} __packed;
+
+struct ieee_types_extcap {
+    struct ieee_types_header ieee_hdr;
+    u8 ext_capab[8];
+} __packed;
+
+struct ieee_types_vht_cap {
+    struct ieee_types_header ieee_hdr;
+    struct ieee80211_vht_cap vhtcap;
+} __packed;
+
+struct ieee_types_vht_oper {
+    struct ieee_types_header ieee_hdr;
+    struct ieee80211_vht_operation vhtoper;
+} __packed;
+
+struct ieee_types_aid {
+    struct ieee_types_header ieee_hdr;
+    u16 aid;
+} __packed;
+
+int rwnx_tdls_send_mgmt_packet_data(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                         const u8 *peer, u8 action_code, u8 dialog_token,
+                         u16 status_code, u32 peer_capability, bool initiator,
+                         const u8 *extra_ies, size_t extra_ies_len, u8 oper_class,
+                         struct cfg80211_chan_def *chandef);
+
+#endif /* RWNX_TDLS_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_testmode.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_testmode.c
new file mode 100755
index 0000000..638b70e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_testmode.c
@@ -0,0 +1,227 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_testmode.c
+ *
+ * @brief Test mode function definitions
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+#if 0
+#include <net/mac80211.h>
+#include <net/netlink.h>
+
+#include "rwnx_testmode.h"
+#include "rwnx_msg_tx.h"
+#include "rwnx_dini.h"
+#include "reg_access.h"
+
+/*
+ * This function handles the user application commands for register access.
+ *
+ * It retrieves command ID carried with RWNX_TM_ATTR_COMMAND and calls to the
+ * handlers respectively.
+ *
+ * If it's an unknown commdn ID, -ENOSYS is returned; or -ENOMSG if the
+ * mandatory fields(RWNX_TM_ATTR_REG_OFFSET,RWNX_TM_ATTR_REG_VALUE32)
+ * are missing; Otherwise 0 is replied indicating the success of the command execution.
+ *
+ * If RWNX_TM_ATTR_COMMAND is RWNX_TM_CMD_APP2DEV_REG_READ, the register read
+ * value is returned with RWNX_TM_ATTR_REG_VALUE32.
+ *
+ * @hw: ieee80211_hw object that represents the device
+ * @tb: general message fields from the user space
+ */
+int rwnx_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb)
+{
+    struct rwnx_hw *rwnx_hw = hw->priv;
+    u32 mem_addr, val32;
+    struct sk_buff *skb;
+    int status = 0;
+
+    /* First check if register address is there */
+    if (!tb[RWNX_TM_ATTR_REG_OFFSET]) {
+        printk("Error finding register offset\n");
+        return -ENOMSG;
+    }
+
+    mem_addr = nla_get_u32(tb[RWNX_TM_ATTR_REG_OFFSET]);
+
+    switch (nla_get_u32(tb[RWNX_TM_ATTR_COMMAND])) {
+    case RWNX_TM_CMD_APP2DEV_REG_READ:
+        {
+            struct dbg_mem_read_cfm mem_read_cfm;
+
+            /*** Send the command to the LMAC ***/
+            if ((status = rwnx_send_dbg_mem_read_req(rwnx_hw, mem_addr, &mem_read_cfm)))
+                return status;
+
+            /* Allocate the answer message */
+            skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+            if (!skb) {
+                printk("Error allocating memory\n");
+                return -ENOMEM;
+            }
+
+            val32 = mem_read_cfm.memdata;
+            if (nla_put_u32(skb, RWNX_TM_ATTR_REG_VALUE32, val32))
+                goto nla_put_failure;
+
+            /* Send the answer to upper layer */
+            status = cfg80211_testmode_reply(skb);
+            if (status < 0)
+                printk("Error sending msg : %d\n", status);
+        }
+        break;
+
+    case RWNX_TM_CMD_APP2DEV_REG_WRITE:
+        {
+            if (!tb[RWNX_TM_ATTR_REG_VALUE32]) {
+                printk("Error finding value to write\n");
+                return -ENOMSG;
+            } else {
+                val32 = nla_get_u32(tb[RWNX_TM_ATTR_REG_VALUE32]);
+                /* Send the command to the LMAC */
+                if ((status = rwnx_send_dbg_mem_write_req(rwnx_hw, mem_addr, val32)))
+                    return status;
+            }
+        }
+        break;
+
+    default:
+        printk("Unknown testmode register command ID\n");
+        return -ENOSYS;
+    }
+
+    return status;
+
+nla_put_failure:
+    kfree_skb(skb);
+    return -EMSGSIZE;
+}
+
+/*
+ * This function handles the user application commands for Debug filter settings.
+ *
+ * @hw: ieee80211_hw object that represents the device
+ * @tb: general message fields from the user space
+ */
+int rwnx_testmode_dbg_filter(struct ieee80211_hw *hw, struct nlattr **tb)
+{
+    struct rwnx_hw *rwnx_hw = hw->priv;
+    u32 filter;
+    int status = 0;
+
+    /* First check if the filter is there */
+    if (!tb[RWNX_TM_ATTR_REG_FILTER]) {
+        printk("Error finding filter value\n");
+        return -ENOMSG;
+    }
+
+    filter = nla_get_u32(tb[RWNX_TM_ATTR_REG_FILTER]);
+    RWNX_DBG("testmode debug filter, setting: 0x%x\n", filter);
+
+    switch (nla_get_u32(tb[RWNX_TM_ATTR_COMMAND])) {
+    case RWNX_TM_CMD_APP2DEV_SET_DBGMODFILTER:
+        {
+            /* Send the command to the LMAC */
+            if ((status = rwnx_send_dbg_set_mod_filter_req(rwnx_hw, filter)))
+                return status;
+        }
+        break;
+    case RWNX_TM_CMD_APP2DEV_SET_DBGSEVFILTER:
+        {
+            /* Send the command to the LMAC */
+            if ((status = rwnx_send_dbg_set_sev_filter_req(rwnx_hw, filter)))
+                return status;
+        }
+        break;
+
+    default:
+        printk("Unknown testmode register command ID\n");
+        return -ENOSYS;
+    }
+
+    return status;
+}
+
+/*
+ * This function handles the user application commands for register access without using
+ * the normal LMAC messaging way.
+ * This time register access will be done through direct PCI BAR windows. This can be used
+ * to access registers even when the :AMC FW is stuck.
+ *
+ * @hw: ieee80211_hw object that represents the device
+ * @tb: general message fields from the user space
+ */
+int rwnx_testmode_reg_dbg(struct ieee80211_hw *hw, struct nlattr **tb)
+{
+    struct rwnx_hw *rwnx_hw = hw->priv;
+    struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+    u32 mem_addr;
+    struct sk_buff *skb;
+    int status = 0;
+    volatile unsigned int reg_value = 0;
+    unsigned int offset;
+
+    /* First check if register address is there */
+    if (!tb[RWNX_TM_ATTR_REG_OFFSET]) {
+        printk("Error finding register offset\n");
+        return -ENOMSG;
+    }
+
+    mem_addr = nla_get_u32(tb[RWNX_TM_ATTR_REG_OFFSET]);
+    offset = mem_addr & 0x00FFFFFF;
+
+    switch (nla_get_u32(tb[RWNX_TM_ATTR_COMMAND])) {
+    case RWNX_TM_CMD_APP2DEV_REG_READ_DBG:
+        {
+            /*** Send the command to the LMAC ***/
+            reg_value = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, offset);
+
+            /* Allocate the answer message */
+            skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+            if (!skb) {
+                printk("Error allocating memory\n");
+                return -ENOMEM;
+            }
+
+            if (nla_put_u32(skb, RWNX_TM_ATTR_REG_VALUE32, reg_value))
+                goto nla_put_failure;
+
+            /* Send the answer to upper layer */
+            status = cfg80211_testmode_reply(skb);
+            if (status < 0)
+                printk("Error sending msg : %d\n", status);
+        }
+        break;
+
+    case RWNX_TM_CMD_APP2DEV_REG_WRITE_DBG:
+        {
+            if (!tb[RWNX_TM_ATTR_REG_VALUE32]) {
+                printk("Error finding value to write\n");
+                return -ENOMSG;
+            } else {
+                reg_value = nla_get_u32(tb[RWNX_TM_ATTR_REG_VALUE32]);
+
+                /* Send the command to the LMAC */
+                RWNX_REG_WRITE(reg_value, rwnx_plat, RWNX_ADDR_SYSTEM,
+                               offset);
+            }
+        }
+        break;
+
+    default:
+        printk("Unknown testmode register command ID\n");
+        return -ENOSYS;
+    }
+
+    return status;
+
+nla_put_failure:
+    kfree_skb(skb);
+    return -EMSGSIZE;
+}
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_testmode.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_testmode.h
new file mode 100755
index 0000000..0115e0f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_testmode.h
@@ -0,0 +1,64 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_testmode.h
+ *
+ * @brief Test mode function declarations
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef RWNX_TESTMODE_H_
+#define RWNX_TESTMODE_H_
+
+#include <net/mac80211.h>
+#include <net/netlink.h>
+
+/* Commands from user space to kernel space(RWNX_TM_CMD_APP2DEV_XX) and
+ * from and kernel space to user space(RWNX_TM_CMD_DEV2APP_XX).
+ * The command ID is carried with RWNX_TM_ATTR_COMMAND.
+ */
+enum rwnx_tm_cmd_t {
+    /* commands from user application to access register */
+    RWNX_TM_CMD_APP2DEV_REG_READ = 1,
+    RWNX_TM_CMD_APP2DEV_REG_WRITE,
+
+    /* commands from user application to select the Debug levels */
+    RWNX_TM_CMD_APP2DEV_SET_DBGMODFILTER,
+    RWNX_TM_CMD_APP2DEV_SET_DBGSEVFILTER,
+
+    /* commands to access registers without sending messages to LMAC layer,
+     * this must be used when LMAC FW is stuck. */
+    RWNX_TM_CMD_APP2DEV_REG_READ_DBG,
+    RWNX_TM_CMD_APP2DEV_REG_WRITE_DBG,
+
+    RWNX_TM_CMD_MAX,
+};
+
+enum rwnx_tm_attr_t {
+    RWNX_TM_ATTR_NOT_APPLICABLE = 0,
+
+    RWNX_TM_ATTR_COMMAND,
+
+    /* When RWNX_TM_ATTR_COMMAND is RWNX_TM_CMD_APP2DEV_REG_XXX,
+     * The mandatory fields are:
+     * RWNX_TM_ATTR_REG_OFFSET for the offset of the target register;
+     * RWNX_TM_ATTR_REG_VALUE32 for value */
+    RWNX_TM_ATTR_REG_OFFSET,
+    RWNX_TM_ATTR_REG_VALUE32,
+
+    /* When RWNX_TM_ATTR_COMMAND is RWNX_TM_CMD_APP2DEV_SET_DBGXXXFILTER,
+     * The mandatory field is RWNX_TM_ATTR_REG_FILTER. */
+    RWNX_TM_ATTR_REG_FILTER,
+
+    RWNX_TM_ATTR_MAX,
+};
+
+/***********************************************************************/
+int rwnx_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb);
+int rwnx_testmode_dbg_filter(struct ieee80211_hw *hw, struct nlattr **tb);
+int rwnx_testmode_reg_dbg(struct ieee80211_hw *hw, struct nlattr **tb);
+
+#endif /* RWNX_TESTMODE_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_tx.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_tx.c
new file mode 100755
index 0000000..de79775
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_tx.c
@@ -0,0 +1,2176 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_tx.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <net/sock.h>
+
+#include "rwnx_defs.h"
+#include "rwnx_tx.h"
+#include "rwnx_msg_tx.h"
+#include "rwnx_mesh.h"
+#include "rwnx_events.h"
+#include "rwnx_compat.h"
+#include "aicwf_txrxif.h"
+#ifdef CONFIG_RWNX_MON_XMIT
+#include <net/ieee80211_radiotap.h>
+#endif
+
+/******************************************************************************
+ * Power Save functions
+ *****************************************************************************/
+/**
+ * rwnx_set_traffic_status - Inform FW if traffic is available for STA in PS
+ *
+ * @rwnx_hw: Driver main data
+ * @sta: Sta in PS mode
+ * @available: whether traffic is buffered for the STA
+ * @ps_id: type of PS data requested (@LEGACY_PS_ID or @UAPSD_ID)
+  */
+void rwnx_set_traffic_status(struct rwnx_hw *rwnx_hw,
+                             struct rwnx_sta *sta,
+                             bool available,
+                             u8 ps_id)
+{
+    if (sta->tdls.active) {
+        rwnx_send_tdls_peer_traffic_ind_req(rwnx_hw,
+                                            rwnx_hw->vif_table[sta->vif_idx]);
+    } else {
+        bool uapsd = (ps_id != LEGACY_PS_ID);
+        rwnx_send_me_traffic_ind(rwnx_hw, sta->sta_idx, uapsd, available);
+#ifdef CREATE_TRACE_POINTS
+        trace_ps_traffic_update(sta->sta_idx, available, uapsd);
+#endif
+    }
+}
+
+/**
+ * rwnx_ps_bh_enable - Enable/disable PS mode for one STA
+ *
+ * @rwnx_hw: Driver main data
+ * @sta: Sta which enters/leaves PS mode
+ * @enable: PS mode status
+ *
+ * This function will enable/disable PS mode for one STA.
+ * When enabling PS mode:
+ *  - Stop all STA's txq for RWNX_TXQ_STOP_STA_PS reason
+ *  - Count how many buffers are already ready for this STA
+ *  - For BC/MC sta, update all queued SKB to use hw_queue BCMC
+ *  - Update TIM if some packet are ready
+ *
+ * When disabling PS mode:
+ *  - Start all STA's txq for RWNX_TXQ_STOP_STA_PS reason
+ *  - For BC/MC sta, update all queued SKB to use hw_queue AC_BE
+ *  - Update TIM if some packet are ready (otherwise fw will not update TIM
+ *    in beacon for this STA)
+ *
+ * All counter/skb updates are protected from TX path by taking tx_lock
+ *
+ * NOTE: _bh_ in function name indicates that this function is called
+ * from a bottom_half tasklet.
+ */
+void rwnx_ps_bh_enable(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+                       bool enable)
+{
+    struct rwnx_txq *txq;
+
+    if (enable) {
+#ifdef CREATE_TRACE_POINTS
+        trace_ps_enable(sta);
+#endif
+        spin_lock_bh(&rwnx_hw->tx_lock);
+        sta->ps.active = true;
+        sta->ps.sp_cnt[LEGACY_PS_ID] = 0;
+        sta->ps.sp_cnt[UAPSD_ID] = 0;
+        rwnx_txq_sta_stop(sta, RWNX_TXQ_STOP_STA_PS, rwnx_hw);
+
+        if (is_multicast_sta(sta->sta_idx)) {
+            txq = rwnx_txq_sta_get(sta, 0, rwnx_hw);
+            sta->ps.pkt_ready[LEGACY_PS_ID] = skb_queue_len(&txq->sk_list);
+            sta->ps.pkt_ready[UAPSD_ID] = 0;
+			//txq->hwq = &rwnx_hw->hwq[RWNX_HWQ_BCMC];
+        } else {
+            int i;
+            sta->ps.pkt_ready[LEGACY_PS_ID] = 0;
+            sta->ps.pkt_ready[UAPSD_ID] = 0;
+            foreach_sta_txq(sta, txq, i, rwnx_hw) {
+                sta->ps.pkt_ready[txq->ps_id] += skb_queue_len(&txq->sk_list);
+            }
+        }
+
+        spin_unlock_bh(&rwnx_hw->tx_lock);
+
+#if 0
+        if (sta->ps.pkt_ready[LEGACY_PS_ID])
+            rwnx_set_traffic_status(rwnx_hw, sta, true, LEGACY_PS_ID);
+
+        if (sta->ps.pkt_ready[UAPSD_ID])
+            rwnx_set_traffic_status(rwnx_hw, sta, true, UAPSD_ID);
+#endif
+    } else {
+#ifdef CREATE_TRACE_POINTS
+        trace_ps_disable(sta->sta_idx);
+#endif
+        spin_lock_bh(&rwnx_hw->tx_lock);
+        sta->ps.active = false;
+
+        if (is_multicast_sta(sta->sta_idx)) {
+            txq = rwnx_txq_sta_get(sta, 0, rwnx_hw);
+            txq->hwq = &rwnx_hw->hwq[RWNX_HWQ_BE];
+            txq->push_limit = 0;
+        } else {
+            int i;
+            foreach_sta_txq(sta, txq, i, rwnx_hw) {
+                txq->push_limit = 0;
+            }
+        }
+
+        rwnx_txq_sta_start(sta, RWNX_TXQ_STOP_STA_PS, rwnx_hw);
+        spin_unlock_bh(&rwnx_hw->tx_lock);
+
+#if 0
+        if (sta->ps.pkt_ready[LEGACY_PS_ID])
+            rwnx_set_traffic_status(rwnx_hw, sta, false, LEGACY_PS_ID);
+
+        if (sta->ps.pkt_ready[UAPSD_ID])
+            rwnx_set_traffic_status(rwnx_hw, sta, false, UAPSD_ID);
+#endif
+        tasklet_schedule(&rwnx_hw->task);
+    }
+}
+
+/**
+ * rwnx_ps_bh_traffic_req - Handle traffic request for STA in PS mode
+ *
+ * @rwnx_hw: Driver main data
+ * @sta: Sta which enters/leaves PS mode
+ * @pkt_req: number of pkt to push
+ * @ps_id: type of PS data requested (@LEGACY_PS_ID or @UAPSD_ID)
+ *
+ * This function will make sure that @pkt_req are pushed to fw
+ * whereas the STA is in PS mode.
+ * If request is 0, send all traffic
+ * If request is greater than available pkt, reduce request
+ * Note: request will also be reduce if txq credits are not available
+ *
+ * All counter updates are protected from TX path by taking tx_lock
+ *
+ * NOTE: _bh_ in function name indicates that this function is called
+ * from the bottom_half tasklet.
+ */
+void rwnx_ps_bh_traffic_req(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+                            u16 pkt_req, u8 ps_id)
+{
+    int pkt_ready_all;
+    struct rwnx_txq *txq;
+    int schedule = 0;
+
+    //if (WARN(!sta->ps.active, "sta %pM is not in Power Save mode",
+    //         sta->mac_addr))
+    //    return;
+    if(!sta->ps.active) {
+       return;
+    }
+
+#ifdef CREATE_TRACE_POINTS
+    trace_ps_traffic_req(sta, pkt_req, ps_id);
+#endif
+    spin_lock_bh(&rwnx_hw->tx_lock);
+
+    /* Fw may ask to stop a service period with PS_SP_INTERRUPTED. This only
+       happens for p2p-go interface if NOA starts during a service period */
+    if ((pkt_req == PS_SP_INTERRUPTED) && (ps_id == UAPSD_ID)) {
+        int tid;
+        sta->ps.sp_cnt[ps_id] = 0;
+        foreach_sta_txq(sta, txq, tid, rwnx_hw) {
+            txq->push_limit = 0;
+        }
+        goto done;
+    }
+
+    pkt_ready_all = (sta->ps.pkt_ready[ps_id] - sta->ps.sp_cnt[ps_id]);
+
+    /* Don't start SP until previous one is finished or we don't have
+       packet ready (which must not happen for U-APSD) */
+    if (sta->ps.sp_cnt[ps_id] || pkt_ready_all <= 0) {
+        goto done;
+    }
+
+    /* Adapt request to what is available. */
+    if (pkt_req == 0 || pkt_req > pkt_ready_all) {
+        pkt_req = pkt_ready_all;
+    }
+
+    /* Reset the SP counter */
+    sta->ps.sp_cnt[ps_id] = 0;
+    schedule = 1;
+
+    /* "dispatch" the request between txq */
+    if (is_multicast_sta(sta->sta_idx)) {
+        txq = rwnx_txq_sta_get(sta, 0, rwnx_hw);
+        //if (txq->credits <= 0)
+        //    goto done;
+        if (pkt_req > txq->credits)
+            pkt_req = txq->credits;
+        txq->push_limit = pkt_req;
+        sta->ps.sp_cnt[ps_id] = pkt_req;
+        rwnx_txq_add_to_hw_list(txq);
+    } else {
+        int i, tid;
+
+        foreach_sta_txq_prio(sta, txq, tid, i, rwnx_hw) {
+            u16 txq_len = skb_queue_len(&txq->sk_list);
+
+            if (txq->ps_id != ps_id)
+                continue;
+
+            if (txq_len > txq->credits)
+                txq_len = txq->credits;
+
+            if (txq_len == 0)
+                continue;
+
+            if (txq_len < pkt_req) {
+                /* Not enough pkt queued in this txq, add this
+                   txq to hwq list and process next txq */
+                pkt_req -= txq_len;
+                txq->push_limit = txq_len;
+                sta->ps.sp_cnt[ps_id] += txq_len;
+                rwnx_txq_add_to_hw_list(txq);
+            } else {
+                /* Enough pkt in this txq to comlete the request
+                   add this txq to hwq list and stop processing txq */
+                txq->push_limit = pkt_req;
+                sta->ps.sp_cnt[ps_id] += pkt_req;
+                rwnx_txq_add_to_hw_list(txq);
+                break;
+            }
+        }
+    }
+
+  done:
+    spin_unlock_bh(&rwnx_hw->tx_lock);
+    if(schedule)
+	tasklet_schedule(&rwnx_hw->task);
+}
+
+/******************************************************************************
+ * TX functions
+ *****************************************************************************/
+#define PRIO_STA_NULL 0xAA
+
+static const int rwnx_down_hwq2tid[3] = {
+    [RWNX_HWQ_BK] = 2,
+    [RWNX_HWQ_BE] = 3,
+    [RWNX_HWQ_VI] = 5,
+};
+
+static void rwnx_downgrade_ac(struct rwnx_sta *sta, struct sk_buff *skb)
+{
+    int8_t ac = rwnx_tid2hwq[skb->priority];
+
+    if (WARN((ac > RWNX_HWQ_VO),
+             "Unexepcted ac %d for skb before downgrade", ac))
+        ac = RWNX_HWQ_VO;
+
+    while (sta->acm & BIT(ac)) {
+        if (ac == RWNX_HWQ_BK) {
+            skb->priority = 1;
+            return;
+        }
+        ac--;
+        skb->priority = rwnx_down_hwq2tid[ac];
+    }
+}
+
+u16 rwnx_select_txq(struct rwnx_vif *rwnx_vif, struct sk_buff *skb)
+{
+    struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+    struct wireless_dev *wdev = &rwnx_vif->wdev;
+    struct rwnx_sta *sta = NULL;
+    struct rwnx_txq *txq;
+    u16 netdev_queue;
+    bool tdls_mgmgt_frame = false;
+
+    switch (wdev->iftype) {
+    case NL80211_IFTYPE_STATION:
+    case NL80211_IFTYPE_P2P_CLIENT:
+    {
+        struct ethhdr *eth;
+        eth = (struct ethhdr *)skb->data;
+        if (eth->h_proto == cpu_to_be16(ETH_P_TDLS)) {
+            tdls_mgmgt_frame = true;
+        }
+        if ((rwnx_vif->tdls_status == TDLS_LINK_ACTIVE) &&
+            (rwnx_vif->sta.tdls_sta != NULL) &&
+            (memcmp(eth->h_dest, rwnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN) == 0))
+            sta = rwnx_vif->sta.tdls_sta;
+        else
+            sta = rwnx_vif->sta.ap;
+        break;
+    }
+    case NL80211_IFTYPE_AP_VLAN:
+        if (rwnx_vif->ap_vlan.sta_4a) {
+            sta = rwnx_vif->ap_vlan.sta_4a;
+            break;
+        }
+
+        /* AP_VLAN interface is not used for a 4A STA,
+           fallback searching sta amongs all AP's clients */
+        rwnx_vif = rwnx_vif->ap_vlan.master;
+    case NL80211_IFTYPE_AP:
+    case NL80211_IFTYPE_P2P_GO:
+    {
+        struct rwnx_sta *cur;
+        struct ethhdr *eth = (struct ethhdr *)skb->data;
+
+        if (is_multicast_ether_addr(eth->h_dest)) {
+            sta = &rwnx_hw->sta_table[rwnx_vif->ap.bcmc_index];
+        } else {
+        	spin_lock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+            list_for_each_entry(cur, &rwnx_vif->ap.sta_list, list) {
+                if (!memcmp(cur->mac_addr, eth->h_dest, ETH_ALEN)) {
+                    sta = cur;
+                    break;
+                }
+            }
+			spin_unlock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+        }
+
+        break;
+    }
+    case NL80211_IFTYPE_MESH_POINT:
+    {
+        struct ethhdr *eth = (struct ethhdr *)skb->data;
+
+        if (!rwnx_vif->is_resending) {
+            /*
+             * If ethernet source address is not the address of a mesh wireless interface, we are proxy for
+             * this address and have to inform the HW
+             */
+            if (memcmp(&eth->h_source[0], &rwnx_vif->ndev->perm_addr[0], ETH_ALEN)) {
+                /* Check if LMAC is already informed */
+                if (!rwnx_get_mesh_proxy_info(rwnx_vif, (u8 *)&eth->h_source, true)) {
+                    rwnx_send_mesh_proxy_add_req(rwnx_hw, rwnx_vif, (u8 *)&eth->h_source);
+                }
+            }
+        }
+
+        if (is_multicast_ether_addr(eth->h_dest)) {
+            sta = &rwnx_hw->sta_table[rwnx_vif->ap.bcmc_index];
+        } else {
+            /* Path to be used */
+            struct rwnx_mesh_path *p_mesh_path = NULL;
+            struct rwnx_mesh_path *p_cur_path;
+            /* Check if destination is proxied by a peer Mesh STA */
+            struct rwnx_mesh_proxy *p_mesh_proxy = rwnx_get_mesh_proxy_info(rwnx_vif, (u8 *)&eth->h_dest, false);
+            /* Mesh Target address */
+            struct mac_addr *p_tgt_mac_addr;
+
+            if (p_mesh_proxy) {
+                p_tgt_mac_addr = &p_mesh_proxy->proxy_addr;
+            } else {
+                p_tgt_mac_addr = (struct mac_addr *)&eth->h_dest;
+            }
+
+            /* Look for path with provided target address */
+            list_for_each_entry(p_cur_path, &rwnx_vif->ap.mpath_list, list) {
+                if (!memcmp(&p_cur_path->tgt_mac_addr, p_tgt_mac_addr, ETH_ALEN)) {
+                    p_mesh_path = p_cur_path;
+                    break;
+                }
+            }
+
+            if (p_mesh_path) {
+                sta = p_mesh_path->p_nhop_sta;
+            } else {
+                rwnx_send_mesh_path_create_req(rwnx_hw, rwnx_vif, (u8 *)p_tgt_mac_addr);
+            }
+        }
+
+        break;
+    }
+    default:
+        break;
+    }
+
+    if (sta && sta->qos)
+    {
+        if (tdls_mgmgt_frame) {
+            skb_set_queue_mapping(skb, NX_STA_NDEV_IDX(skb->priority, sta->sta_idx));
+        } else {
+            /* use the data classifier to determine what 802.1d tag the
+             * data frame has */
+            #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
+            skb->priority = cfg80211_classify8021d(skb) & IEEE80211_QOS_CTL_TAG1D_MASK;
+            #else
+            skb->priority = cfg80211_classify8021d(skb, NULL) & IEEE80211_QOS_CTL_TAG1D_MASK;
+            #endif
+        }
+        if (sta->acm)
+            rwnx_downgrade_ac(sta, skb);
+
+        txq = rwnx_txq_sta_get(sta, skb->priority, rwnx_hw);
+        netdev_queue = txq->ndev_idx;
+    }
+    else if (sta)
+    {
+        skb->priority = 0xFF;
+        txq = rwnx_txq_sta_get(sta, 0, rwnx_hw);
+        netdev_queue = txq->ndev_idx;
+    }
+    else
+    {
+        /* This packet will be dropped in xmit function, still need to select
+           an active queue for xmit to be called. As it most likely to happen
+           for AP interface, select BCMC queue
+           (TODO: select another queue if BCMC queue is stopped) */
+        skb->priority = PRIO_STA_NULL;
+        netdev_queue = NX_BCMC_TXQ_NDEV_IDX;
+	struct ethhdr *eth = (struct ethhdr *)skb->data;
+	printk("DROP:%pM\n", eth->h_dest);
+    }
+
+#ifndef CONFIG_ONE_TXQ
+    BUG_ON(netdev_queue >= NX_NB_NDEV_TXQ);
+#endif
+    return netdev_queue;
+}
+
+/**
+ * rwnx_set_more_data_flag - Update MORE_DATA flag in tx sw desc
+ *
+ * @rwnx_hw: Driver main data
+ * @sw_txhdr: Header for pkt to be pushed
+ *
+ * If STA is in PS mode
+ *  - Set EOSP in case the packet is the last of the UAPSD service period
+ *  - Set MORE_DATA flag if more pkt are ready for this sta
+ *  - Update TIM if this is the last pkt buffered for this sta
+ *
+ * note: tx_lock already taken.
+ */
+ #ifdef CONFIG_SDIO_AGGR
+static inline void rwnx_set_more_data_flag(struct rwnx_hw *rwnx_hw,
+                                           struct rwnx_sw_txhdr *sw_txhdr)
+{
+    struct rwnx_sta *sta = sw_txhdr->rwnx_sta;
+    struct rwnx_vif *vif = sw_txhdr->rwnx_vif;
+    struct rwnx_txq *txq = sw_txhdr->txq;
+
+    if (unlikely(sta->ps.active)) {
+        sta->ps.pkt_ready[txq->ps_id]--;
+        sta->ps.sp_cnt[txq->ps_id]--;
+#ifdef CREATE_TRACE_POINTS
+        trace_ps_push(sta);
+#endif
+        if (((txq->ps_id == UAPSD_ID) || (vif->wdev.iftype == NL80211_IFTYPE_MESH_POINT) || (sta->tdls.active))
+                && !sta->ps.sp_cnt[txq->ps_id]) {
+            sw_txhdr->desc.host.flags |= TXU_CNTRL_EOSP;
+        }
+
+        if (sta->ps.pkt_ready[txq->ps_id]) {
+            sw_txhdr->desc.host.flags |= TXU_CNTRL_MORE_DATA;
+        } else {
+            rwnx_set_traffic_status(rwnx_hw, sta, false, txq->ps_id);
+        }
+    }
+}
+#else
+static inline void rwnx_set_more_data_flag(struct rwnx_hw *rwnx_hw,
+                                           struct rwnx_txhdr *txhdr)
+{
+    struct rwnx_sta *sta = txhdr->sw_hdr.rwnx_sta;
+    struct rwnx_vif *vif = txhdr->sw_hdr.rwnx_vif;
+    struct rwnx_txq *txq = txhdr->sw_hdr.txq;
+
+    if (unlikely(sta->ps.active)) {
+        sta->ps.pkt_ready[txq->ps_id]--;
+        sta->ps.sp_cnt[txq->ps_id]--;
+
+	//printk("more:%d\n", sta->ps.sp_cnt[txq->ps_id]);
+#ifdef CREATE_TRACE_POINTS
+        trace_ps_push(sta);
+#endif
+        if (((txq->ps_id == UAPSD_ID) || (vif->wdev.iftype == NL80211_IFTYPE_MESH_POINT) || (sta->tdls.active))
+                && !sta->ps.sp_cnt[txq->ps_id]) {
+            txhdr->desc.host.flags |= TXU_CNTRL_EOSP;
+        }
+
+        if (sta->ps.pkt_ready[txq->ps_id]) {
+            txhdr->desc.host.flags |= TXU_CNTRL_MORE_DATA;
+        } else {
+            rwnx_set_traffic_status(rwnx_hw, sta, false, txq->ps_id);
+        }
+    }
+}
+
+#endif
+/**
+ * rwnx_get_tx_priv - Get STA and tid for one skb
+ *
+ * @rwnx_vif: vif ptr
+ * @skb: skb
+ * @tid: pointer updated with the tid to use for this skb
+ *
+ * @return: pointer on the destination STA (may be NULL)
+ *
+ * skb has already been parsed in rwnx_select_queue function
+ * simply re-read information form skb.
+ */
+static struct rwnx_sta *rwnx_get_tx_priv(struct rwnx_vif *rwnx_vif,
+                                         struct sk_buff *skb,
+                                         u8 *tid)
+{
+    struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+    struct rwnx_sta *sta;
+    int sta_idx;
+
+    *tid = skb->priority;
+    if (unlikely(skb->priority == PRIO_STA_NULL)) {
+        return NULL;
+    } else {
+        int ndev_idx = skb_get_queue_mapping(skb);
+
+        if (ndev_idx == NX_BCMC_TXQ_NDEV_IDX)
+            sta_idx = NX_REMOTE_STA_MAX + master_vif_idx(rwnx_vif);
+        else
+            sta_idx = ndev_idx / NX_NB_TID_PER_STA;
+
+        sta = &rwnx_hw->sta_table[sta_idx];
+    }
+
+    return sta;
+}
+
+/**
+ * rwnx_prep_tx - Prepare buffer for DMA transmission
+ *
+ * @rwnx_hw: Driver main data
+ * @txhdr: Tx descriptor
+ *
+ * Maps hw_txhdr and buffer data for transmission via DMA.
+ * - Data buffer with be downloaded by embebded side.
+ * - hw_txhdr will be uploaded by embedded side when buffer has been
+ *   transmitted over the air.
+ */
+static int rwnx_prep_tx(struct rwnx_hw *rwnx_hw, struct rwnx_txhdr *txhdr)
+{
+#if 0
+    struct rwnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+    struct rwnx_hw_txhdr *hw_txhdr = &txhdr->hw_hdr;
+    dma_addr_t dma_addr;
+
+    /* MAP (and sync) memory for DMA */
+    dma_addr = dma_map_single(rwnx_hw->dev, hw_txhdr,
+                              sw_txhdr->map_len, DMA_BIDIRECTIONAL);
+    if (WARN_ON(dma_mapping_error(rwnx_hw->dev, dma_addr)))
+        return -1;
+
+    sw_txhdr->dma_addr = dma_addr;
+#endif
+    return 0;
+}
+
+/**
+ *  rwnx_tx_push - Push one packet to fw
+ *
+ * @rwnx_hw: Driver main data
+ * @txhdr: tx desc of the buffer to push
+ * @flags: push flags (see @rwnx_push_flags)
+ *
+ * Push one packet to fw. Sw desc of the packet has already been updated.
+ * Only MORE_DATA flag will be set if needed.
+ */
+void rwnx_tx_push(struct rwnx_hw *rwnx_hw, struct rwnx_txhdr *txhdr, int flags)
+{
+    struct rwnx_sw_txhdr *sw_txhdr = &txhdr->sw_hdr;
+    struct sk_buff *skb = sw_txhdr->skb;
+    struct rwnx_txq *txq = sw_txhdr->txq;
+    u16 hw_queue = txq->hwq->id;
+    int user = 0;
+
+    lockdep_assert_held(&rwnx_hw->tx_lock);
+
+    //printk("rwnx_tx_push\n");
+    /* RETRY flag is not always set so retest here */
+    if (txq->nb_retry) {
+        flags |= RWNX_PUSH_RETRY;
+        txq->nb_retry--;
+        if (txq->nb_retry == 0) {
+            WARN(skb != txq->last_retry_skb,
+                 "last retry buffer is not the expected one");
+            txq->last_retry_skb = NULL;
+        }
+    } else if (!(flags & RWNX_PUSH_RETRY)) {
+        txq->pkt_sent++;
+    }
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+    if (txq->amsdu == sw_txhdr) {
+        WARN((flags & RWNX_PUSH_RETRY), "End A-MSDU on a retry");
+        rwnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
+        txq->amsdu = NULL;
+    }
+    #ifdef CONFIG_SDIO_AGGR
+     else if (!(flags & RWNX_PUSH_RETRY) &&
+               !(sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU)) {
+        rwnx_hw->stats.amsdus[0].done++;
+    }
+    #else
+     else if (!(flags & RWNX_PUSH_RETRY) &&
+               !(txhdr->desc.host.flags & TXU_CNTRL_AMSDU)) {
+        rwnx_hw->stats.amsdus[0].done++;
+    }
+    #endif
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+
+    /* Wait here to update hw_queue, as for multicast STA hwq may change
+       between queue and push (because of PS) */
+    #ifdef CONFIG_SDIO_AGGR
+    sw_txhdr->desc.host.ac = hw_queue; //use ac field for hw_txq
+    #else
+    txhdr->desc.host.ac = hw_queue; //use packet_addr field for hw_txq
+    #endif
+    
+#ifdef CONFIG_RWNX_MUMIMO_TX
+    /* MU group is only selected during hwq processing */
+    #ifdef CONFIG_SDIO_AGGR
+    sw_txhdr->desc.host.mumimo_info = txq->mumimo_info;
+    #else
+    txhdr->desc.host.mumimo_info = txq->mumimo_info;
+    #endif
+    user = RWNX_TXQ_POS_ID(txq);
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+    if (sw_txhdr->rwnx_sta) {
+        /* only for AP mode */
+    #ifdef CONFIG_SDIO_AGGR
+        rwnx_set_more_data_flag(rwnx_hw, sw_txhdr);
+    #else
+        rwnx_set_more_data_flag(rwnx_hw, txhdr);
+    #endif
+    }
+#ifdef CREATE_TRACE_POINTS
+    trace_push_desc(skb, sw_txhdr, flags);
+#endif
+    #if 0
+    txq->credits--;
+    #endif
+    txq->pkt_pushed[user]++;
+    //printk("txq->credits=%d\n",txq->credits);
+    #if 0
+    if (txq->credits <= 0)
+        rwnx_txq_stop(txq, RWNX_TXQ_STOP_FULL);
+    #endif
+
+    if (txq->push_limit)
+        txq->push_limit--;
+#if 0
+    rwnx_ipc_txdesc_push(rwnx_hw, &sw_txhdr->desc, skb, hw_queue, user);
+#else
+#ifdef  AICWF_SDIO_SUPPORT
+    #ifdef CONFIG_SDIO_AGGR
+    if( ((sw_txhdr->desc.host.flags & TXU_CNTRL_MGMT) && ((*(skb->data+sw_txhdr->headroom)==0xd0) || (*(skb->data+sw_txhdr->headroom)==0x10) || (*(skb->data+sw_txhdr->headroom)==0xc0) || ((*(skb->data+sw_txhdr->headroom)==0x30)))) || \
+        (sw_txhdr->desc.host.ethertype == 0x8e88) || (sw_txhdr->is_dhcp == 1) ){
+        sw_txhdr->need_cfm = 1;
+        sw_txhdr->desc.host.status_desc_addr = ((1<<31) | rwnx_hw->sdio_env.txdesc_free_idx[0]);
+        aicwf_sdio_host_txdesc_push(&(rwnx_hw->sdio_env), 0, (long)skb);
+        printk("need cfm ethertype:%8x,user_idx=%d, skb=%p\n", sw_txhdr->desc.host.ethertype, rwnx_hw->sdio_env.txdesc_free_idx[0], skb);
+    } else {
+        sw_txhdr->need_cfm = 0;
+        sw_txhdr->desc.host.status_desc_addr = 0;
+
+        //sw_txhdr->rwnx_vif->net_stats.tx_packets++;
+        //sw_txhdr->rwnx_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
+        rwnx_hw->stats.last_tx = jiffies;
+    }
+    #else
+    if( ((txhdr->desc.host.flags & TXU_CNTRL_MGMT) && ((*(skb->data+sw_txhdr->headroom)==0xd0) || (*(skb->data+sw_txhdr->headroom)==0x10) || (*(skb->data+sw_txhdr->headroom)==0xc0) || ((*(skb->data+sw_txhdr->headroom)==0x30)))) || \
+        (txhdr->desc.host.ethertype == 0x8e88) || (sw_txhdr->is_dhcp == 1) ){
+        sw_txhdr->need_cfm = 1;//((1<<31) | rwnx_hw->sdio_env.txdesc_free_idx[0]);
+        printk("need cfm ethertype:%8x,user_idx=%d, skb=%p\n", txhdr->desc.host.ethertype, rwnx_hw->sdio_env.txdesc_free_idx[0], skb);
+        txhdr->desc.host.status_desc_addr = ((1<<31) | rwnx_hw->sdio_env.txdesc_free_idx[0]);
+        aicwf_sdio_host_txdesc_push(&(rwnx_hw->sdio_env), 0, (long)skb);
+    } else {
+        sw_txhdr->need_cfm = 0;
+        txhdr->desc.host.status_desc_addr = 0;
+
+        //sw_txhdr->rwnx_vif->net_stats.tx_packets++;
+        //sw_txhdr->rwnx_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
+        rwnx_hw->stats.last_tx = jiffies;
+    }
+    #endif//CONFIG_SDIO_AGGR
+    aicwf_frame_tx((void *)(rwnx_hw->sdiodev), skb);
+#endif
+#ifdef AICWF_USB_SUPPORT
+    if( ((sw_txhdr->desc.host.flags & TXU_CNTRL_MGMT) && ((*(skb->data+sw_txhdr->headroom)==0xd0) || (*(skb->data+sw_txhdr->headroom)==0x10) || (*(skb->data+sw_txhdr->headroom)==0xc0) || ((*(skb->data+sw_txhdr->headroom)==0x30)))) || \
+        (sw_txhdr->desc.host.ethertype == 0x8e88) ) { // 0xd0:Action, 0x10:AssocRsp, 0x8e88:EAPOL
+        sw_txhdr->need_cfm = 1;
+        sw_txhdr->desc.host.status_desc_addr = ((1<<31) | rwnx_hw->usb_env.txdesc_free_idx[0]);
+        aicwf_usb_host_txdesc_push(&(rwnx_hw->usb_env), 0, (long)(skb));
+        printk("need cfm ethertype:%8x,user_idx=%d, skb=%p\n", sw_txhdr->desc.host.ethertype, rwnx_hw->usb_env.txdesc_free_idx[0], skb);
+    } else {
+        sw_txhdr->need_cfm = 0;
+		sw_txhdr->desc.host.status_desc_addr = 0;
+
+        sw_txhdr->rwnx_vif->net_stats.tx_packets++;
+        sw_txhdr->rwnx_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
+        rwnx_hw->stats.last_tx = jiffies;
+    }
+    aicwf_frame_tx((void *)(rwnx_hw->usbdev), skb);
+#endif
+#endif
+    #if 0
+    txq->hwq->credits[user]--;
+    #endif
+    rwnx_hw->stats.cfm_balance[hw_queue]++;
+}
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+/* return size of subframe (including header) */
+static inline int rwnx_amsdu_subframe_length(struct ethhdr *eth, int eth_len)
+{
+    /* ethernet header is replaced with amdsu header that have the same size
+       Only need to check if LLC/SNAP header will be added */
+    int len = eth_len;
+
+    if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+        len += sizeof(rfc1042_header) + 2;
+    }
+
+    return len;
+}
+
+static inline bool rwnx_amsdu_is_aggregable(struct sk_buff *skb)
+{
+    /* need to add some check on buffer to see if it can be aggregated ? */
+    return true;
+}
+
+
+/**
+ * rwnx_amsdu_del_subframe_header - remove AMSDU header
+ *
+ * amsdu_txhdr: amsdu tx descriptor
+ *
+ * Move back the ethernet header at the "beginning" of the data buffer.
+ * (which has been moved in @rwnx_amsdu_add_subframe_header)
+ */
+static void rwnx_amsdu_del_subframe_header(struct rwnx_amsdu_txhdr *amsdu_txhdr)
+{
+    struct sk_buff *skb = amsdu_txhdr->skb;
+    struct ethhdr *eth;
+    u8 *pos;
+
+    pos = skb->data;
+    pos += sizeof(struct rwnx_amsdu_txhdr);
+    eth = (struct ethhdr*)pos;
+    pos += amsdu_txhdr->pad + sizeof(struct ethhdr);
+
+    if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+        pos += sizeof(rfc1042_header) + 2;
+    }
+
+    memmove(pos, eth, sizeof(*eth));
+    skb_pull(skb, (pos - skb->data));
+}
+
+/**
+ * rwnx_amsdu_add_subframe_header - Add AMSDU header and link subframe
+ *
+ * @rwnx_hw Driver main data
+ * @skb Buffer to aggregate
+ * @sw_txhdr Tx descriptor for the first A-MSDU subframe
+ *
+ * return 0 on sucess, -1 otherwise
+ *
+ * This functions Add A-MSDU header and LLC/SNAP header in the buffer
+ * and update sw_txhdr of the first subframe to link this buffer.
+ * If an error happens, the buffer will be queued as a normal buffer.
+ *
+ *
+ *            Before           After
+ *         +-------------+  +-------------+
+ *         | HEADROOM    |  | HEADROOM    |
+ *         |             |  +-------------+ <- data
+ *         |             |  | amsdu_txhdr |
+ *         |             |  | * pad size  |
+ *         |             |  +-------------+
+ *         |             |  | ETH hdr     | keep original eth hdr
+ *         |             |  |             | to restore it once transmitted
+ *         |             |  +-------------+ <- packet_addr[x]
+ *         |             |  | Pad         |
+ *         |             |  +-------------+
+ * data -> +-------------+  | AMSDU HDR   |
+ *         | ETH hdr     |  +-------------+
+ *         |             |  | LLC/SNAP    |
+ *         +-------------+  +-------------+
+ *         | DATA        |  | DATA        |
+ *         |             |  |             |
+ *         +-------------+  +-------------+
+ *
+ * Called with tx_lock hold
+ */
+static int rwnx_amsdu_add_subframe_header(struct rwnx_hw *rwnx_hw,
+                                          struct sk_buff *skb,
+                                          struct rwnx_sw_txhdr *sw_txhdr)
+{
+    struct rwnx_amsdu *amsdu = &sw_txhdr->amsdu;
+    struct rwnx_amsdu_txhdr *amsdu_txhdr;
+    struct ethhdr *amsdu_hdr, *eth = (struct ethhdr *)skb->data;
+    int headroom_need, map_len, msdu_len;
+    dma_addr_t dma_addr;
+    u8 *pos, *map_start;
+
+    msdu_len = skb->len - sizeof(*eth);
+    headroom_need = sizeof(*amsdu_txhdr) + amsdu->pad +
+        sizeof(*amsdu_hdr);
+    if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+        headroom_need += sizeof(rfc1042_header) + 2;
+        msdu_len += sizeof(rfc1042_header) + 2;
+    }
+
+    /* we should have enough headroom (checked in xmit) */
+    if (WARN_ON(skb_headroom(skb) < headroom_need)) {
+        return -1;
+    }
+
+    /* allocate headroom */
+    pos = skb_push(skb, headroom_need);
+    amsdu_txhdr = (struct rwnx_amsdu_txhdr *)pos;
+    pos += sizeof(*amsdu_txhdr);
+
+    /* move eth header */
+    memmove(pos, eth, sizeof(*eth));
+    eth = (struct ethhdr *)pos;
+    pos += sizeof(*eth);
+
+    /* Add padding from previous subframe */
+    map_start = pos;
+    memset(pos, 0, amsdu->pad);
+    pos += amsdu->pad;
+
+    /* Add AMSDU hdr */
+    amsdu_hdr = (struct ethhdr *)pos;
+    memcpy(amsdu_hdr->h_dest, eth->h_dest, ETH_ALEN);
+    memcpy(amsdu_hdr->h_source, eth->h_source, ETH_ALEN);
+    amsdu_hdr->h_proto = htons(msdu_len);
+    pos += sizeof(*amsdu_hdr);
+
+    if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+        memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
+        pos += sizeof(rfc1042_header);
+    }
+
+    /* MAP (and sync) memory for DMA */
+    map_len = msdu_len + amsdu->pad + sizeof(*amsdu_hdr);
+    dma_addr = dma_map_single(rwnx_hw->dev, map_start, map_len,
+                              DMA_BIDIRECTIONAL);
+    if (WARN_ON(dma_mapping_error(rwnx_hw->dev, dma_addr))) {
+        pos -= sizeof(*eth);
+        memmove(pos, eth, sizeof(*eth));
+        skb_pull(skb, headroom_need);
+        return -1;
+    }
+
+    /* update amdsu_txhdr */
+    amsdu_txhdr->map_len = map_len;
+    amsdu_txhdr->dma_addr = dma_addr;
+    amsdu_txhdr->skb = skb;
+    amsdu_txhdr->pad = amsdu->pad;
+    amsdu_txhdr->msdu_len = msdu_len;
+
+    /* update rwnx_sw_txhdr (of the first subframe) */
+    BUG_ON(amsdu->nb != sw_txhdr->desc.host.packet_cnt);
+    sw_txhdr->desc.host.packet_addr[amsdu->nb] = dma_addr;
+    sw_txhdr->desc.host.packet_len[amsdu->nb] = map_len;
+    sw_txhdr->desc.host.packet_cnt++;
+    amsdu->nb++;
+
+    amsdu->pad = AMSDU_PADDING(map_len - amsdu->pad);
+    list_add_tail(&amsdu_txhdr->list, &amsdu->hdrs);
+    amsdu->len += map_len;
+#ifdef CREATE_TRACE_POINTS
+    trace_amsdu_subframe(sw_txhdr);
+#endif
+    return 0;
+}
+
+/**
+ * rwnx_amsdu_add_subframe - Add this buffer as an A-MSDU subframe if possible
+ *
+ * @rwnx_hw Driver main data
+ * @skb Buffer to aggregate if possible
+ * @sta Destination STA
+ * @txq sta's txq used for this buffer
+ *
+ * Tyr to aggregate the buffer in an A-MSDU. If it succeed then the
+ * buffer is added as a new A-MSDU subframe with AMSDU and LLC/SNAP
+ * headers added (so FW won't have to modify this subframe).
+ *
+ * To be added as subframe :
+ * - sta must allow amsdu
+ * - buffer must be aggregable (to be defined)
+ * - at least one other aggregable buffer is pending in the queue
+ *  or an a-msdu (with enough free space) is currently in progress
+ *
+ * returns true if buffer has been added as A-MDSP subframe, false otherwise
+ *
+ */
+static bool rwnx_amsdu_add_subframe(struct rwnx_hw *rwnx_hw, struct sk_buff *skb,
+                                    struct rwnx_sta *sta, struct rwnx_txq *txq)
+{
+    bool res = false;
+    struct ethhdr *eth;
+
+    /* immediately return if amsdu are not allowed for this sta */
+    if (!txq->amsdu_len || rwnx_hw->mod_params->amsdu_maxnb < 2 ||
+        !rwnx_amsdu_is_aggregable(skb)
+       )
+        return false;
+
+    spin_lock_bh(&rwnx_hw->tx_lock);
+    if (txq->amsdu) {
+        /* aggreagation already in progress, add this buffer if enough space
+           available, otherwise end the current amsdu */
+        struct rwnx_sw_txhdr *sw_txhdr = txq->amsdu;
+        eth = (struct ethhdr *)(skb->data);
+
+        if (((sw_txhdr->amsdu.len + sw_txhdr->amsdu.pad +
+              rwnx_amsdu_subframe_length(eth, skb->len)) > txq->amsdu_len) ||
+            rwnx_amsdu_add_subframe_header(rwnx_hw, skb, sw_txhdr)) {
+            txq->amsdu = NULL;
+            goto end;
+        }
+
+        if (sw_txhdr->amsdu.nb >= rwnx_hw->mod_params->amsdu_maxnb) {
+            rwnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
+            /* max number of subframes reached */
+            txq->amsdu = NULL;
+        }
+    } else {
+        /* Check if a new amsdu can be started with the previous buffer
+           (if any) and this one */
+        struct sk_buff *skb_prev = skb_peek_tail(&txq->sk_list);
+        struct rwnx_txhdr *txhdr;
+        struct rwnx_sw_txhdr *sw_txhdr;
+        int len1, len2;
+
+        if (!skb_prev || !rwnx_amsdu_is_aggregable(skb_prev))
+            goto end;
+
+        txhdr = (struct rwnx_txhdr *)skb_prev->data;
+        sw_txhdr = &txhdr->sw_hdr;
+        if ((sw_txhdr->amsdu.len) ||
+            (sw_txhdr->desc.host.flags & TXU_CNTRL_RETRY))
+            /* previous buffer is already a complete amsdu or a retry */
+            goto end;
+
+        eth = (struct ethhdr *)(skb_prev->data + sw_txhdr->headroom);
+        len1 = rwnx_amsdu_subframe_length(eth, (sw_txhdr->frame_len +
+                                                sizeof(struct ethhdr)));
+
+        eth = (struct ethhdr *)(skb->data);
+        len2 = rwnx_amsdu_subframe_length(eth, skb->len);
+
+        if (len1 + AMSDU_PADDING(len1) + len2 > txq->amsdu_len)
+            /* not enough space to aggregate those two buffers */
+            goto end;
+
+        /* Add subframe header.
+           Note: Fw will take care of adding AMDSU header for the first
+           subframe while generating 802.11 MAC header */
+        INIT_LIST_HEAD(&sw_txhdr->amsdu.hdrs);
+        sw_txhdr->amsdu.len = len1;
+        sw_txhdr->amsdu.nb = 1;
+        sw_txhdr->amsdu.pad = AMSDU_PADDING(len1);
+        if (rwnx_amsdu_add_subframe_header(rwnx_hw, skb, sw_txhdr))
+            goto end;
+
+        sw_txhdr->desc.host.flags |= TXU_CNTRL_AMSDU;
+
+        if (sw_txhdr->amsdu.nb < rwnx_hw->mod_params->amsdu_maxnb)
+            txq->amsdu = sw_txhdr;
+        else
+            rwnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
+    }
+
+    res = true;
+
+  end:
+    spin_unlock_bh(&rwnx_hw->tx_lock);
+    return res;
+}
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+
+#ifdef CONFIG_FILTER_TCP_ACK
+/* return:
+ *      0, msg buf freed by the real driver
+ *      others, skb need free by the caller,remember not use msg->skb!
+ */
+
+int intf_tx(struct rwnx_hw *priv,struct msg_buf *msg)
+{
+		struct rwnx_vif *rwnx_vif = msg->rwnx_vif;
+		struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+		struct rwnx_txhdr *txhdr;
+		struct rwnx_sw_txhdr *sw_txhdr;
+		struct txdesc_api *desc;
+		struct rwnx_sta *sta;
+		struct rwnx_txq *txq;
+		int headroom;
+		int max_headroom;
+		int hdr_pads;
+
+		u16 frame_len;
+		u16 frame_oft;
+		u8 tid;
+		struct sk_buff *skb=msg->skb;
+		struct ethhdr eth_t;
+
+		move_tcpack_msg(rwnx_hw,msg);
+		kfree(msg);
+
+			memcpy(&eth_t, skb->data, sizeof(struct ethhdr));
+				/* Retrieve the pointer to the Ethernet data */
+#ifdef CONFIG_SDIO_AGGR
+			skb_pull(skb, 14);
+#else
+			skb_pull(skb, 12);
+#endif
+
+			sk_pacing_shift_update(skb->sk, rwnx_hw->tcp_pacing_shift);
+			max_headroom = sizeof(struct rwnx_txhdr);
+			u32 allign = (long)(skb->data) & 0x3;
+			/* check whether the current skb can be used */
+			if (skb_shared(skb) || (skb_headroom(skb) < max_headroom) ||
+		#ifndef CONFIG_SDIO_AGGR
+				(allign!=0) ||
+		#endif
+					(skb_cloned(skb) && (rwnx_vif->ndev->priv_flags & IFF_BRIDGE_PORT))) {
+					struct sk_buff *newskb = skb_copy_expand(skb, max_headroom, 0,
+															 GFP_ATOMIC);
+					if (unlikely(newskb == NULL))
+						goto free;
+
+					dev_kfree_skb_any(skb);
+					skb = newskb;
+			}
+
+			/* Get the STA id and TID information */
+			sta = rwnx_get_tx_priv(rwnx_vif, skb, &tid);
+			if (!sta){
+				//printk("NO sta:%pM\n", eth_t.h_dest);
+				goto free;
+			}
+
+			txq = rwnx_txq_sta_get(sta, tid, rwnx_hw);
+			if (txq->idx == TXQ_INACTIVE){
+			//printk("staidx=%d\n", sta->sta_idx);
+				//printk("Inactive:%pM\n", eth_t.h_dest);
+				goto free;
+			}
+#ifdef CONFIG_RWNX_AMSDUS_TX
+			if (rwnx_amsdu_add_subframe(rwnx_hw, skb, sta, txq))
+				return NETDEV_TX_OK;
+#endif
+
+			//hdr_pads	= RWNX_SWTXHDR_ALIGN_PADS((long)skb->data);
+			headroom  = sizeof(struct rwnx_txhdr) ;//+ hdr_pads;
+			skb_push(skb, headroom);
+
+			txhdr = (struct rwnx_txhdr *)skb->data;
+			sw_txhdr = &txhdr->sw_hdr;
+			//sw_txhdr = kmem_cache_alloc(rwnx_hw->sw_txhdr_cache, GFP_ATOMIC);
+			//if (unlikely(sw_txhdr == NULL))
+			//	  goto free;
+			//txhdr->sw_hdr = sw_txhdr;
+#ifdef CONFIG_SDIO_AGGR
+			desc = &sw_txhdr->desc;
+			frame_len = (u16)skb->len - headroom;	//- 2;// - sizeof(*eth);
+#else
+			desc = &txhdr->desc;
+			frame_len = (u16)skb->len - headroom - 2;// - sizeof(*eth);
+#endif
+
+			sw_txhdr->txq		= txq;
+			sw_txhdr->frame_len = frame_len;
+			sw_txhdr->rwnx_sta	= sta;
+			sw_txhdr->rwnx_vif	= rwnx_vif;
+			sw_txhdr->skb		= skb;
+			sw_txhdr->headroom	= headroom;
+			//sw_txhdr->map_len   = skb->len - offsetof(struct rwnx_txhdr, hw_hdr);
+
+			sw_txhdr->is_dhcp	= 0;
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+			sw_txhdr->amsdu.len = 0;
+			sw_txhdr->amsdu.nb = 0;
+#endif
+			// Fill-in the descriptor
+			memcpy(&desc->host.eth_dest_addr, eth_t.h_dest, ETH_ALEN);
+			memcpy(&desc->host.eth_src_addr, eth_t.h_source, ETH_ALEN);
+			desc->host.ethertype = eth_t.h_proto;
+			desc->host.staid = sta->sta_idx;
+			desc->host.tid = tid;
+			if (unlikely(rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN))
+				desc->host.vif_idx = rwnx_vif->ap_vlan.master->vif_index;
+			else
+				desc->host.vif_idx = rwnx_vif->vif_index;
+			if (rwnx_vif->use_4addr && (sta->sta_idx < NX_REMOTE_STA_MAX))
+				desc->host.flags = TXU_CNTRL_USE_4ADDR;
+			else
+				desc->host.flags = 0;
+
+#if 0
+			if ((rwnx_vif->tdls_status == TDLS_LINK_ACTIVE) &&
+				rwnx_vif->sta.tdls_sta &&
+				(memcmp(desc->host.eth_dest_addr.array, rwnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN) == 0)) {
+				desc->host.flags |= TXU_CNTRL_TDLS;
+				rwnx_vif->sta.tdls_sta->tdls.last_tid = desc->host.tid;
+				//rwnx_vif->sta.tdls_sta->tdls.last_sn = desc->host.sn;
+			}
+#endif
+
+			if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_MESH_POINT) {
+				if (rwnx_vif->is_resending) {
+					desc->host.flags |= TXU_CNTRL_MESH_FWD;
+				}
+			}
+
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+			desc->host.packet_len[0] = frame_len;
+#else
+			desc->host.packet_len = frame_len;
+#endif
+
+			txhdr->hw_hdr.cfm.status.value = 0;
+
+			if (unlikely(rwnx_prep_tx(rwnx_hw, txhdr))) {
+				printk("TX_BUSY:%pM\n", eth_t.h_dest);
+				//kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+				skb_pull(skb, headroom);
+				dev_kfree_skb_any(skb);
+				return NETDEV_TX_BUSY;
+			}
+
+			/* Fill-in TX descriptor */
+		/*
+			frame_oft = sizeof(struct rwnx_txhdr) - offsetof(struct rwnx_txhdr, hw_hdr)
+						+ hdr_pads;// + sizeof(*eth);
+		*/
+		/*
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+			desc->host.packet_addr[0] = sw_txhdr->dma_addr + frame_oft;
+			desc->host.packet_cnt = 1;
+#else
+			desc->host.packet_addr = sw_txhdr->dma_addr + frame_oft;
+#endif
+			desc->host.status_desc_addr = sw_txhdr->dma_addr;
+		*/
+
+			spin_lock_bh(&rwnx_hw->tx_lock);
+			if (rwnx_txq_queue_skb(skb, txq, rwnx_hw, false))
+				rwnx_hwq_process(rwnx_hw, txq->hwq);
+			spin_unlock_bh(&rwnx_hw->tx_lock);
+
+		return 0;//NETDEV_TX_OK;
+
+	free:
+		dev_kfree_skb_any(skb);
+
+		return 0;//NETDEV_TX_OK;
+	}
+#endif
+
+/**
+ * netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
+ *                               struct net_device *dev);
+ *	Called when a packet needs to be transmitted.
+ *	Must return NETDEV_TX_OK , NETDEV_TX_BUSY.
+ *        (can also return NETDEV_TX_LOCKED if NETIF_F_LLTX)
+ *
+ *  - Initialize the desciptor for this pkt (stored in skb before data)
+ *  - Push the pkt in the corresponding Txq
+ *  - If possible (i.e. credit available and not in PS) the pkt is pushed
+ *    to fw
+ */
+netdev_tx_t rwnx_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+    struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+    struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+    struct rwnx_txhdr *txhdr;
+    struct rwnx_sw_txhdr *sw_txhdr;
+    struct txdesc_api *desc;
+    struct rwnx_sta *sta;
+    struct rwnx_txq *txq;
+    int headroom;
+    int max_headroom;
+    int hdr_pads;
+
+    u16 frame_len;
+    u16 frame_oft;
+    u8 tid;
+#ifdef CONFIG_FILTER_TCP_ACK
+	struct msg_buf *msgbuf;
+#endif
+
+
+#ifdef CONFIG_ONE_TXQ
+    skb->queue_mapping = rwnx_select_txq(rwnx_vif, skb);
+#endif
+
+    struct ethhdr eth_t;
+	int dhcp_flags = 0;
+    struct iphdr *iphead = (struct iphdr *)(skb->data + 14);
+    struct udphdr *udph;
+    struct DHCPInfo *dhcph;
+    if(skb->protocol == htons(ETH_P_IP)) { // IP
+        if(iphead->protocol == IPPROTO_UDP) { // UDP
+            udph = (struct udphdr *)((u8 *)iphead + (iphead->ihl << 2));
+            if((udph->source == __constant_htons(SERVER_PORT))
+                && (udph->dest == __constant_htons(CLIENT_PORT))) { // DHCP offset/ack
+                dhcph = (struct DHCPInfo *)((u8 *)udph + sizeof(struct udphdr));
+                if(dhcph->cookie == __constant_htonl(DHCP_MAGIC) && dhcph->op == 2 ){
+					dhcp_flags = 1;
+					printk("\ndhcp\n");
+					#if 0
+					
+                    u32 length = ntohs(udph->len) - sizeof(struct udphdr) - offsetof(struct DHCPInfo, options);
+                    u16 offset = 0;
+                    u8 *option = dhcph->options;
+                    while (option[offset]!= DHCP_OPTION_END && offset<length) {
+                        if (option[offset] == DHCP_OPTION_MESSAGE_TYPE) {
+                            if (option[offset+2] == 2 || option[offset+2] == 5) { //dhcp offer or dhcp ack
+                                printk("xmit dhcp offer or ack: \n");
+                                u16_l i=0;
+                                for(i=0; i<skb->len; i++) {
+                                    printk("%x, ", skb->data[i]);
+                                    if((i+1)%8==0)
+                                        printk("\n");
+                                }
+                             }
+                        }
+                        offset += 2 + option[offset+1];
+                    }
+					#endif
+                }
+            }
+        }
+        else if(iphead->protocol == IPPROTO_TCP) { // TCP
+            struct tcphdr *tcph;
+            tcph = (struct tcphdr *)((u8 *)iphead + (iphead->ihl << 2));                
+		}//TCP
+
+    }
+#ifdef CONFIG_FILTER_TCP_ACK
+	msgbuf=intf_tcp_alloc_msg(msgbuf);
+	msgbuf->rwnx_vif=rwnx_vif;
+					msgbuf->skb=skb;
+					if(filter_send_tcp_ack(rwnx_hw,msgbuf,skb->data,cpu_to_le16(skb->len))){
+						return NETDEV_TX_OK;
+					}else{
+						move_tcpack_msg(rwnx_hw,msgbuf);
+						kfree(msgbuf);
+					}
+#endif
+
+    memcpy(&eth_t, skb->data, sizeof(struct ethhdr));
+		/* Retrieve the pointer to the Ethernet data */
+#ifdef CONFIG_SDIO_AGGR
+	skb_pull(skb, 14);
+#else
+	skb_pull(skb, 12);
+#endif
+
+    sk_pacing_shift_update(skb->sk, rwnx_hw->tcp_pacing_shift);
+    max_headroom = sizeof(struct rwnx_txhdr);
+    u32 allign = (long)(skb->data) & 0x3;
+    /* check whether the current skb can be used */
+    if (skb_shared(skb) || (skb_headroom(skb) < max_headroom) || 
+		#ifndef CONFIG_SDIO_AGGR
+		(allign!=0) ||
+		#endif
+            (skb_cloned(skb) && (dev->priv_flags & IFF_BRIDGE_PORT))) {
+            struct sk_buff *newskb = skb_copy_expand(skb, max_headroom, 0,
+                                                     GFP_ATOMIC);
+            if (unlikely(newskb == NULL))
+                goto free;
+    
+            dev_kfree_skb_any(skb);
+            skb = newskb;
+    }
+
+    /* Get the STA id and TID information */
+    sta = rwnx_get_tx_priv(rwnx_vif, skb, &tid);
+    if (!sta){
+		//printk("NO sta:%pM\n", eth_t.h_dest);
+        goto free;
+    }
+
+    txq = rwnx_txq_sta_get(sta, tid, rwnx_hw);
+    if (txq->idx == TXQ_INACTIVE){
+	//printk("staidx=%d\n", sta->sta_idx);
+        //printk("Inactive:%pM\n", eth_t.h_dest);
+        goto free;
+	}
+#ifdef CONFIG_RWNX_AMSDUS_TX
+    if (rwnx_amsdu_add_subframe(rwnx_hw, skb, sta, txq))
+        return NETDEV_TX_OK;
+#endif
+
+    //hdr_pads  = RWNX_SWTXHDR_ALIGN_PADS((long)skb->data);  
+    headroom  = sizeof(struct rwnx_txhdr) ;//+ hdr_pads;
+    skb_push(skb, headroom);
+
+    txhdr = (struct rwnx_txhdr *)skb->data;
+	sw_txhdr = &txhdr->sw_hdr;
+    //sw_txhdr = kmem_cache_alloc(rwnx_hw->sw_txhdr_cache, GFP_ATOMIC);
+    //if (unlikely(sw_txhdr == NULL))
+    //    goto free;
+    //txhdr->sw_hdr = sw_txhdr;
+#ifdef CONFIG_SDIO_AGGR
+    desc = &sw_txhdr->desc;
+    frame_len = (u16)skb->len - headroom;   //- 2;// - sizeof(*eth);
+#else
+    desc = &txhdr->desc;
+    frame_len = (u16)skb->len - headroom - 2;// - sizeof(*eth);
+#endif
+
+    sw_txhdr->txq       = txq;
+    sw_txhdr->frame_len = frame_len;
+    sw_txhdr->rwnx_sta  = sta;
+    sw_txhdr->rwnx_vif  = rwnx_vif;
+    sw_txhdr->skb       = skb;
+    sw_txhdr->headroom  = headroom;
+    //sw_txhdr->map_len   = skb->len - offsetof(struct rwnx_txhdr, hw_hdr);
+
+	sw_txhdr->is_dhcp   = dhcp_flags;
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+    sw_txhdr->amsdu.len = 0;
+    sw_txhdr->amsdu.nb = 0;
+#endif
+    // Fill-in the descriptor
+    memcpy(&desc->host.eth_dest_addr, eth_t.h_dest, ETH_ALEN);
+    memcpy(&desc->host.eth_src_addr, eth_t.h_source, ETH_ALEN);
+    desc->host.ethertype = eth_t.h_proto;
+    desc->host.staid = sta->sta_idx;
+    desc->host.tid = tid;
+    if (unlikely(rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN))
+        desc->host.vif_idx = rwnx_vif->ap_vlan.master->vif_index;
+    else
+        desc->host.vif_idx = rwnx_vif->vif_index;
+
+    if (rwnx_vif->use_4addr && (sta->sta_idx < NX_REMOTE_STA_MAX))
+        desc->host.flags = TXU_CNTRL_USE_4ADDR;
+    else
+        desc->host.flags = 0;
+
+#if 0
+    if ((rwnx_vif->tdls_status == TDLS_LINK_ACTIVE) &&
+        rwnx_vif->sta.tdls_sta &&
+        (memcmp(desc->host.eth_dest_addr.array, rwnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN) == 0)) {
+        desc->host.flags |= TXU_CNTRL_TDLS;
+        rwnx_vif->sta.tdls_sta->tdls.last_tid = desc->host.tid;
+        //rwnx_vif->sta.tdls_sta->tdls.last_sn = desc->host.sn;
+    }
+#endif 
+
+    if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_MESH_POINT) {
+        if (rwnx_vif->is_resending) {
+            desc->host.flags |= TXU_CNTRL_MESH_FWD;
+        }
+    }
+
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+    desc->host.packet_len[0] = frame_len;
+#else
+    desc->host.packet_len = frame_len;
+#endif
+
+    txhdr->hw_hdr.cfm.status.value = 0;
+
+    if (unlikely(rwnx_prep_tx(rwnx_hw, txhdr))) {
+		printk("TX_BUSY:%pM\n", eth_t.h_dest);
+        //kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+        skb_pull(skb, headroom);
+        dev_kfree_skb_any(skb);
+        return NETDEV_TX_BUSY;
+    }
+
+    /* Fill-in TX descriptor */
+/*
+    frame_oft = sizeof(struct rwnx_txhdr) - offsetof(struct rwnx_txhdr, hw_hdr)
+                + hdr_pads;// + sizeof(*eth);
+*/
+/*
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+    desc->host.packet_addr[0] = sw_txhdr->dma_addr + frame_oft;
+    desc->host.packet_cnt = 1;
+#else
+    desc->host.packet_addr = sw_txhdr->dma_addr + frame_oft;
+#endif
+    desc->host.status_desc_addr = sw_txhdr->dma_addr;
+*/
+
+    spin_lock_bh(&rwnx_hw->tx_lock);
+    if (rwnx_txq_queue_skb(skb, txq, rwnx_hw, false))
+        rwnx_hwq_process(rwnx_hw, txq->hwq);
+    spin_unlock_bh(&rwnx_hw->tx_lock);
+
+    return NETDEV_TX_OK;
+
+free:
+    dev_kfree_skb_any(skb);
+
+    return NETDEV_TX_OK;
+}
+
+/**
+ * rwnx_start_mgmt_xmit - Transmit a management frame
+ *
+ * @vif: Vif that send the frame
+ * @sta: Destination of the frame. May be NULL if the destiantion is unknown
+ *       to the AP.
+ * @params: Mgmt frame parameters
+ * @offchan: Indicate whether the frame must be send via the offchan TXQ.
+ *           (is is redundant with params->offchan ?)
+ * @cookie: updated with a unique value to identify the frame with upper layer
+ *
+ */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta,
+                         struct cfg80211_mgmt_tx_params *params, bool offchan,
+                         u64 *cookie)
+#else
+int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta,
+                         struct ieee80211_channel *channel, bool offchan,
+                         unsigned int wait, const u8* buf, size_t len,
+                    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
+                         bool no_cck,
+                    #endif
+                    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
+                         bool dont_wait_for_ack,
+                    #endif
+                         u64 *cookie)
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+{
+    struct rwnx_hw *rwnx_hw = vif->rwnx_hw;
+    struct rwnx_txhdr *txhdr;
+    struct rwnx_sw_txhdr *sw_txhdr;
+    struct txdesc_api *desc;
+    struct sk_buff *skb;
+    u16 frame_len, headroom, frame_oft;
+    u8 *data;
+    struct rwnx_txq *txq;
+    bool robust;
+    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+    const u8 *buf = params->buf;
+    size_t len = params->len;
+    bool no_cck = params->no_cck;
+    #endif
+
+    headroom = sizeof(struct rwnx_txhdr);
+    frame_len = len;
+
+    //printk("buf:%x, %x, %x, %x\n", buf[0], buf[1], buf[4], buf[5]);
+#if 1
+    if(buf[0] == 0x10 && buf[1] == 0x00) {
+        u8 *htcap_ie;
+        int var_offset = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+        u8 *var_pos;
+        len = frame_len - var_offset;
+        var_pos = buf + var_offset;
+
+#if 0
+        htcap_ie = cfg80211_find_ie(45, var_pos, len);
+        if (htcap_ie) {
+            printk("find htcap ie %x\n", htcap_ie[2]);
+            //htcap_ie[2] &= 0xFD;
+        } else
+            printk("not find htcap ie\n");
+		u8* rate_ie;
+    	int i = 0;
+
+    	#define IS_BASIC_RATE(r) (r & 0x80) && ((r & ~0x80) <= (54 * 2))
+
+    	rate_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
+    	if (rate_ie) {
+        	u8 *rates = rate_ie + 2;
+        	for (i = 0; (i < rate_ie[1]) ; i++) {
+            	if (i>=4){
+                	rates[i] = rates[i] | CO_BIT(7);
+					//printk("assoc rates[%d]=%d\n",i, rates[i]);
+	    		}
+        	}
+    	}
+
+    	u8* erp_info_ie = cfg80211_find_ie(42, var_pos, len);
+    	if(erp_info_ie){
+    		//printk("assoc ie found: %x\n", erp_info_ie[2]);
+			erp_info_ie[2] &=0xF9;
+    	} 
+//	else
+// 			printk("assoc ie not found\n");
+#endif
+	}
+#endif
+    //----------------------------------------------------------------------
+
+#if 1
+    if(buf[0] == 0x50 && buf[1] == 0x00) {
+        int var_offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+        u8 *var_pos;
+        len = frame_len - var_offset;
+        var_pos = buf + var_offset;	
+	u8* erp_info_ie = cfg80211_find_ie(42, var_pos, len);
+    	if(erp_info_ie){
+        	//printk("probe_resp ie found: %x\n", erp_info_ie[2]);
+        	erp_info_ie[2] &=0xF9;
+    	}
+    }
+#endif
+
+
+    /* Set TID and Queues indexes */
+    if (sta) {
+        txq = rwnx_txq_sta_get(sta, 8, rwnx_hw);
+    } else {
+        if (offchan)
+            txq = &rwnx_hw->txq[NX_OFF_CHAN_TXQ_IDX];
+        else
+            txq = rwnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE);
+    }
+
+    /* Ensure that TXQ is active */
+    if (txq->idx == TXQ_INACTIVE) {
+        netdev_dbg(vif->ndev, "TXQ inactive\n");
+        return -EBUSY;
+    }
+
+    /*
+     * Create a SK Buff object that will contain the provided data
+     */
+    skb = dev_alloc_skb(headroom + frame_len);
+
+    if (!skb) {
+        return -ENOMEM;
+    }
+
+    *cookie = (unsigned long)skb;
+
+    /*
+     * Move skb->data pointer in order to reserve room for rwnx_txhdr
+     * headroom value will be equal to sizeof(struct rwnx_txhdr)
+     */
+    skb_reserve(skb, headroom);
+
+    /*
+     * Extend the buffer data area in order to contain the provided packet
+     * len value (for skb) will be equal to param->len
+     */
+    data = skb_put(skb, frame_len);
+    /* Copy the provided data */
+    memcpy(data, buf, frame_len);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
+    robust = ieee80211_is_robust_mgmt_frame(skb);
+#else
+	robust = ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *)skb->data);
+#endif
+
+    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+    /* Update CSA counter if present */
+    if (unlikely(params->n_csa_offsets) &&
+        vif->wdev.iftype == NL80211_IFTYPE_AP &&
+        vif->ap.csa) {
+        int i;
+
+        data = skb->data;
+        for (i = 0; i < params->n_csa_offsets ; i++) {
+            data[params->csa_offsets[i]] = vif->ap.csa->count;
+        }
+    }
+    #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+
+    /*
+     * Go back to the beginning of the allocated data area
+     * skb->data pointer will move backward
+     */
+    skb_push(skb, headroom);
+
+    //----------------------------------------------------------------------
+
+    /* Fill the TX Header */
+    txhdr = (struct rwnx_txhdr *)skb->data;
+
+    txhdr->hw_hdr.cfm.status.value = 0;
+
+    //----------------------------------------------------------------------
+
+    /* Fill the SW TX Header */
+    //sw_txhdr = kmem_cache_alloc(rwnx_hw->sw_txhdr_cache, GFP_ATOMIC);
+    sw_txhdr = &txhdr->sw_hdr;
+    //if (unlikely(sw_txhdr == NULL)) {
+    //    dev_kfree_skb(skb);
+    //    return -ENOMEM;
+    //}
+    //txhdr->sw_hdr = sw_txhdr;
+
+    sw_txhdr->txq = txq;
+    sw_txhdr->frame_len = frame_len;
+    sw_txhdr->rwnx_sta = sta;
+    sw_txhdr->rwnx_vif = vif;
+    sw_txhdr->skb = skb;
+    sw_txhdr->headroom = headroom;
+    //sw_txhdr->map_len = skb->len - offsetof(struct rwnx_txhdr, hw_hdr);
+#ifdef CONFIG_RWNX_AMSDUS_TX
+    sw_txhdr->amsdu.len = 0;
+    sw_txhdr->amsdu.nb = 0;
+#endif
+    //----------------------------------------------------------------------
+
+    /* Fill the Descriptor to be provided to the MAC SW */
+    #ifdef CONFIG_SDIO_AGGR
+    desc = &sw_txhdr->desc;
+    #else
+    desc = &txhdr->desc;
+    #endif
+
+    desc->host.ethertype = 0;
+    desc->host.staid = (sta) ? sta->sta_idx : 0xFF;
+    desc->host.vif_idx = vif->vif_index;
+    desc->host.tid = 0xFF;
+    desc->host.flags = TXU_CNTRL_MGMT;
+    if (robust)
+        desc->host.flags |= TXU_CNTRL_MGMT_ROBUST;
+
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+    desc->host.packet_len[0] = frame_len;
+#else
+    desc->host.packet_len = frame_len;
+#endif
+
+    if (no_cck) {
+        desc->host.flags |= TXU_CNTRL_MGMT_NO_CCK;
+    }
+
+    /* Get DMA Address */
+    if (unlikely(rwnx_prep_tx(rwnx_hw, txhdr))) {
+        //kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+        dev_kfree_skb(skb);
+        return -EBUSY;
+    }
+	skb->priority = 0x8;
+
+    //frame_oft = sizeof(struct rwnx_txhdr) - offsetof(struct rwnx_txhdr, hw_hdr);
+/*
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+    desc->host.packet_addr[0] = sw_txhdr->dma_addr + frame_oft;
+    desc->host.packet_cnt = 1;
+#else
+    desc->host.packet_addr = sw_txhdr->dma_addr + frame_oft;
+#endif
+    desc->host.status_desc_addr = sw_txhdr->dma_addr;
+*/
+    //----------------------------------------------------------------------
+
+    spin_lock_bh(&rwnx_hw->tx_lock);
+    if (rwnx_txq_queue_skb(skb, txq, rwnx_hw, false))
+        rwnx_hwq_process(rwnx_hw, txq->hwq);
+    spin_unlock_bh(&rwnx_hw->tx_lock);
+
+    return 0;
+}
+
+#ifdef CONFIG_RWNX_MON_XMIT
+/**
+ * netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
+ *                               struct net_device *dev);
+ *	Called when a packet needs to be transmitted.
+ *	Must return NETDEV_TX_OK , NETDEV_TX_BUSY.
+ *        (can also return NETDEV_TX_LOCKED if NETIF_F_LLTX)
+ *
+ *  - Initialize the desciptor for this pkt (stored in skb before data)
+ *  - Push the pkt in the corresponding Txq
+ *  - If possible (i.e. credit available and not in PS) the pkt is pushed
+ *    to fw
+ */
+netdev_tx_t rwnx_start_monitor_if_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+    int rtap_len, ret, idx, tmp_len;
+    struct ieee80211_radiotap_header *rtap_hdr; // net/ieee80211_radiotap.h
+    struct ieee80211_radiotap_iterator iterator; // net/cfg80211.h
+    u8_l *rtap_buf = (u8_l *)skb->data;
+    u8_l rate;
+
+    struct rwnx_vif *vif = netdev_priv(dev);
+    struct rwnx_hw *rwnx_hw = vif->rwnx_hw;
+    struct rwnx_txhdr *txhdr;
+    struct rwnx_sw_txhdr *sw_txhdr;
+    struct txdesc_api *desc;
+    struct rwnx_sta *sta;
+    struct rwnx_txq *txq;
+    u16_l frame_len, headroom, frame_oft;
+    u8_l tid, rate_fmt = FORMATMOD_NON_HT, rate_idx = 0, txsig_bw = PHY_CHNL_BW_20;
+    u8_l *pframe, *data;
+    bool robust;
+    struct sk_buff *skb_mgmt;
+    bool offchan = false;
+
+    rtap_hdr = (struct ieee80211_radiotap_header*)(rtap_buf);
+    rtap_len = ieee80211_get_radiotap_len(rtap_buf);
+    frame_len = skb->len;
+
+    printk("rwnx_start_monitor_if_xmit, skb_len=%d, rtap_len=%d\n", skb->len, rtap_len);
+
+    if (unlikely(rtap_hdr->it_version))
+        goto free_tag;
+
+    if (unlikely(skb->len < rtap_len))
+        goto free_tag;
+
+    if (unlikely(rtap_len < sizeof(struct ieee80211_radiotap_header)))
+        goto free_tag;
+
+    frame_len -= rtap_len;
+    pframe = rtap_buf + rtap_len;
+
+    // Parse radiotap for injection items and overwrite attribs as needed
+    ret = ieee80211_radiotap_iterator_init(&iterator, rtap_hdr, rtap_len, NULL);
+    while (!ret) {
+        ret = ieee80211_radiotap_iterator_next(&iterator);
+        if (ret) {
+            continue;
+        }
+        switch (iterator.this_arg_index) {
+            case IEEE80211_RADIOTAP_RATE:
+                // This is basic 802.11b/g rate; use MCS/VHT for higher rates
+                rate = *iterator.this_arg;
+                printk("rate=0x%x\n", rate);
+                for (idx = 0; idx < HW_RATE_MAX; idx++) {
+                    if ((rate * 5) == tx_legrates_lut_rate[idx]) {
+                        break;
+                    }
+                }
+                if (idx < HW_RATE_MAX) {
+                    rate_idx = idx;
+                } else {
+                    printk("invalid radiotap rate: %d\n", rate);
+                }
+                break;
+
+            case IEEE80211_RADIOTAP_TX_FLAGS: {
+                u16_l txflags = get_unaligned_le16(iterator.this_arg);
+                printk("txflags=0x%x\n", txflags);
+                if ((txflags & IEEE80211_RADIOTAP_F_TX_NOACK) == 0) {
+                    printk("  TX_NOACK\n");
+                }
+                if (txflags & 0x0010) { // Use preconfigured seq num
+                    // NOTE: this is currently ignored due to qos_en=_FALSE and HW seq num override
+                    printk("  GetSequence\n");
+                }
+            }
+            break;
+
+            case IEEE80211_RADIOTAP_MCS: {
+                u8_l mcs_have = iterator.this_arg[0];
+                printk("mcs_have=0x%x\n", mcs_have);
+                rate_fmt = FORMATMOD_HT_MF;
+                if (mcs_have & IEEE80211_RADIOTAP_MCS_HAVE_BW) {
+                    u8_l bw = (iterator.this_arg[1] & IEEE80211_RADIOTAP_MCS_BW_MASK);
+                    u8_l ch_offset = 0;
+                    if (bw == IEEE80211_RADIOTAP_MCS_BW_40) {
+                        txsig_bw = PHY_CHNL_BW_40;
+                    } else if (bw == IEEE80211_RADIOTAP_MCS_BW_20L) {
+                        bw = IEEE80211_RADIOTAP_MCS_BW_20;
+                        ch_offset = 1; // CHNL_OFFSET_LOWER;
+                    } else if (bw == IEEE80211_RADIOTAP_MCS_BW_20U) {
+                        bw = IEEE80211_RADIOTAP_MCS_BW_20;
+                        ch_offset = 2; // CHNL_OFFSET_UPPER;
+                    }
+                    printk("  bw=%d, ch_offset=%d\n", bw, ch_offset);
+                }
+                if (mcs_have & IEEE80211_RADIOTAP_MCS_HAVE_MCS) {
+                    u8_l fixed_rate = iterator.this_arg[2] & 0x7f;
+                    if (fixed_rate > 31) {
+                        fixed_rate = 0;
+                    }
+                    rate_idx = fixed_rate;
+                    printk("  fixed_rate=0x%x\n", fixed_rate);
+                }
+                if ((mcs_have & IEEE80211_RADIOTAP_MCS_HAVE_GI) && (iterator.this_arg[1] & IEEE80211_RADIOTAP_MCS_SGI)) {
+                    printk("  sgi\n");
+                }
+                if ((mcs_have & IEEE80211_RADIOTAP_MCS_HAVE_FEC) && (iterator.this_arg[1] & IEEE80211_RADIOTAP_MCS_FEC_LDPC)) {
+                    printk("  ldpc\n");
+                }
+                if (mcs_have & IEEE80211_RADIOTAP_MCS_HAVE_STBC) {
+                    u8 stbc = (iterator.this_arg[1] & IEEE80211_RADIOTAP_MCS_STBC_MASK) >> IEEE80211_RADIOTAP_MCS_STBC_SHIFT;
+                    printk("  stbc=0x%x\n", stbc);
+                }
+            }
+            break;
+
+            case IEEE80211_RADIOTAP_VHT: {
+                unsigned int mcs, nss;
+                u8 known = iterator.this_arg[0];
+                u8 flags = iterator.this_arg[2];
+                rate_fmt = FORMATMOD_VHT;
+                printk("known=0x%x, flags=0x%x\n", known, flags);
+                // NOTE: this code currently only supports 1SS for radiotap defined rates
+                if ((known & IEEE80211_RADIOTAP_VHT_KNOWN_STBC) && (flags & IEEE80211_RADIOTAP_VHT_FLAG_STBC)) {
+                    printk("  stbc\n");
+                }
+                if ((known & IEEE80211_RADIOTAP_VHT_KNOWN_GI) && (flags & IEEE80211_RADIOTAP_VHT_FLAG_SGI)) {
+                    printk("  sgi\n");
+                }
+                if (known & IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH) {
+                    u8_l bw = iterator.this_arg[3] & 0x1F;
+                    printk("  bw=0x%x\n",bw);
+                    // NOTE: there are various L and U, but we just use straight 20/40/80
+                    // since it's not clear how to set CHNL_OFFSET_LOWER/_UPPER with different
+                    // sideband sizes/configurations.  TODO.
+                    // Also, any 160 is treated as 80 due to lack of WIDTH_160.
+                    txsig_bw = PHY_CHNL_BW_40;
+                    if (bw == 0) {
+                        txsig_bw = PHY_CHNL_BW_20;
+                        printk("  20M\n");
+                    } else if (bw >=1 && bw <= 3) {
+                        printk("  40M\n");
+                    } else if (bw >=4 && bw <= 10) {
+                        printk("  80M\n");
+                    } else if (bw >= 11 && bw <= 25) {
+                        printk("  160M\n");
+                    }
+                }
+                // User 0
+                nss = iterator.this_arg[4] & 0x0F; // Number of spatial streams
+                printk("  nss=0x%x\n", nss);
+                if (nss > 0) {
+                    if (nss > 4) nss = 4;
+                    mcs = (iterator.this_arg[4]>>4) & 0x0F; // MCS rate index
+                    if (mcs > 8) mcs = 9;
+                    rate_idx = mcs;
+                    printk("    mcs=0x%x\n", mcs);
+                    if (iterator.this_arg[8] & IEEE80211_RADIOTAP_CODING_LDPC_USER0) {
+                        printk("    ldpc\n");
+                    }
+                }
+            }
+            break;
+
+            case IEEE80211_RADIOTAP_HE: {
+                u16 data1 = ((u16)iterator.this_arg[1] << 8) | iterator.this_arg[0];
+                u16 data2 = ((u16)iterator.this_arg[3] << 8) | iterator.this_arg[2];
+                u16 data3 = ((u16)iterator.this_arg[5] << 8) | iterator.this_arg[4];
+                u16 data5 = ((u16)iterator.this_arg[9] << 8) | iterator.this_arg[8];
+                u8 fmt_he = data1 & IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MASK;
+                if (fmt_he == IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU) {
+                    rate_fmt = FORMATMOD_HE_MU;
+                } else if (fmt_he == IEEE80211_RADIOTAP_HE_DATA1_FORMAT_EXT_SU) {
+                    rate_fmt = FORMATMOD_HE_ER;
+                } else {
+                    rate_fmt = FORMATMOD_HE_SU;
+                }
+                if (data1 & IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN) {
+                    u8 mcs = (data3 & IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS) >> 8;
+                    if (mcs > 11) mcs = 11;
+                    rate_idx = mcs;
+                }
+                if (data1 & IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN) {
+                    u8 bw = data5 & IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC;
+                    txsig_bw = (bw == IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ) ? PHY_CHNL_BW_20 : PHY_CHNL_BW_40;
+                }
+                if (data2 & IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN) {
+                    u8 gi = (data5 & IEEE80211_RADIOTAP_HE_DATA5_GI) >> 4;
+                    printk("  gi: %d\n", gi);
+                }
+            }
+            break;
+
+            default:
+                printk("unparsed arg: 0x%x\n",iterator.this_arg_index);
+                break;
+        }
+    }
+
+    #if 0
+    // dump buffer
+    tmp_len = 128;
+    if (skb->len < 128) {
+        tmp_len = skb->len;
+    }
+    for (idx = 0; idx < tmp_len; idx+=16) {
+        printk("[%04X] %02X %02X %02X %02X %02X %02X %02X %02X   %02X %02X %02X %02X %02X %02X %02X %02X\n", idx,
+            rtap_buf[idx+0],rtap_buf[idx+1],rtap_buf[idx+2],rtap_buf[idx+3],
+            rtap_buf[idx+4],rtap_buf[idx+5],rtap_buf[idx+6],rtap_buf[idx+7],
+            rtap_buf[idx+8],rtap_buf[idx+9],rtap_buf[idx+10],rtap_buf[idx+11],
+            rtap_buf[idx+12],rtap_buf[idx+13],rtap_buf[idx+14],rtap_buf[idx+15]);
+    }
+    #endif
+
+    /* Get the STA id and TID information */
+    sta = rwnx_get_tx_priv(vif, skb, &tid);
+    //if (!sta) {
+    //    printk("sta=null, tid=0x%x\n", tid);
+    //}
+    /* Set TID and Queues indexes */
+    if (sta) {
+        txq = rwnx_txq_sta_get(sta, 8, rwnx_hw);
+    } else {
+        if (offchan)
+            txq = &rwnx_hw->txq[NX_OFF_CHAN_TXQ_IDX];
+        else
+            txq = rwnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE);
+    }
+    if (txq->idx == TXQ_INACTIVE) {
+        printk("TXQ_INACTIVE\n");
+        goto free_tag;
+    }
+    // prepare to xmit
+    headroom = sizeof(struct rwnx_txhdr);
+    skb_mgmt = dev_alloc_skb(headroom + frame_len);
+    if (!skb_mgmt) {
+        printk("skb_mgmt alloc fail\n");
+        goto free_tag;
+    }
+    skb_reserve(skb_mgmt, headroom);
+    data = skb_put(skb_mgmt, frame_len);
+    /* Copy the provided data */
+    memcpy(data, pframe, frame_len);
+    robust = ieee80211_is_robust_mgmt_frame(skb_mgmt);
+    skb_push(skb_mgmt, headroom);
+    /* Fill the TX Header */
+    txhdr = (struct rwnx_txhdr *)skb_mgmt->data;
+    txhdr->hw_hdr.cfm.status.value = 0;
+	sw_txhdr = &txhdr->sw_hdr;
+    /* Fill the SW TX Header */
+    //sw_txhdr = kmem_cache_alloc(rwnx_hw->sw_txhdr_cache, GFP_ATOMIC);
+    //if (unlikely(sw_txhdr == NULL)) {
+    //    dev_kfree_skb(skb_mgmt);
+    //    printk("sw_txhdr alloc fail\n");
+    //    goto free_tag;
+   // }
+   // txhdr->sw_hdr = sw_txhdr;
+    sw_txhdr->txq = txq;
+    sw_txhdr->frame_len = frame_len;
+    sw_txhdr->rwnx_sta = sta;
+    sw_txhdr->rwnx_vif = vif;
+    sw_txhdr->skb = skb_mgmt;
+    sw_txhdr->headroom = headroom;
+    //sw_txhdr->map_len = skb_mgmt->len - offsetof(struct rwnx_txhdr, hw_hdr);
+    //sw_txhdr->raw_frame = 1;
+    //sw_txhdr->fixed_rate = 1;
+    //sw_txhdr->rate_config = ((rate_fmt << FORMAT_MOD_TX_RCX_OFT) & FORMAT_MOD_TX_RCX_MASK) |
+    //                        ((txsig_bw << BW_TX_RCX_OFT) & BW_TX_RCX_MASK) |
+    //                        ((rate_idx << MCS_INDEX_TX_RCX_OFT) & MCS_INDEX_TX_RCX_MASK); // from radiotap
+    /* Fill the Descriptor to be provided to the MAC SW */
+    desc = &sw_txhdr->desc;
+    desc->host.staid = (sta) ? sta->sta_idx : 0xFF;
+    desc->host.vif_idx = vif->vif_index;
+    desc->host.tid = 0xFF;
+    desc->host.flags = TXU_CNTRL_MGMT;
+    if (robust) {
+        desc->host.flags |= TXU_CNTRL_MGMT_ROBUST;
+    }
+    frame_oft = sizeof(struct rwnx_txhdr) - offsetof(struct rwnx_txhdr, hw_hdr);
+    #if 0
+    #ifdef CONFIG_RWNX_SPLIT_TX_BUF
+    desc->host.packet_addr[0] = sw_txhdr->dma_addr + frame_oft;
+    desc->host.packet_len[0] = frame_len;
+    desc->host.packet_cnt = 1;
+    #else
+    desc->host.packet_addr = sw_txhdr->dma_addr + frame_oft;
+    desc->host.packet_len = frame_len;
+    #endif
+    #else
+    desc->host.packet_len = frame_len;
+    #endif
+    desc->host.status_desc_addr = 0;//sw_txhdr->dma_addr;
+
+    spin_lock_bh(&rwnx_hw->tx_lock);
+    if (rwnx_txq_queue_skb(skb_mgmt, txq, rwnx_hw, false))
+        rwnx_hwq_process(rwnx_hw, txq->hwq);
+    spin_unlock_bh(&rwnx_hw->tx_lock);
+
+free_tag:
+    dev_kfree_skb_any(skb);
+    return NETDEV_TX_OK;
+}
+#endif
+
+/**
+ * rwnx_txdatacfm - FW callback for TX confirmation
+ *
+ * called with tx_lock hold
+ */
+int rwnx_txdatacfm(void *pthis, void *host_id)
+{
+    struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)pthis;
+    struct sk_buff *skb = host_id;
+    struct rwnx_txhdr *txhdr;
+    union rwnx_hw_txstatus rwnx_txst;
+    struct rwnx_sw_txhdr *sw_txhdr;
+    //struct rwnx_hwq *hwq;
+    struct rwnx_txq *txq;
+    int headroom;
+    //int peek_off = offsetof(struct rwnx_hw_txhdr, cfm);
+    ///int peek_len = sizeof(((struct rwnx_hw_txhdr *)0)->cfm);
+
+    txhdr = (struct rwnx_txhdr *)skb->data;
+    sw_txhdr = &txhdr->sw_hdr;
+
+    /* Read status in the TX control header */
+    rwnx_txst = txhdr->hw_hdr.cfm.status;
+
+    /* Check status in the header. If status is null, it means that the buffer
+     * was not transmitted and we have to return immediately */
+    if (rwnx_txst.value == 0) {
+        return -1;
+    }
+
+#ifdef AICWF_USB_SUPPORT
+    if (rwnx_hw->usbdev->state == USB_DOWN_ST) {
+        headroom = sw_txhdr->headroom;
+        //kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+        skb_pull(skb, headroom);
+        consume_skb(skb);
+        return 0;
+    }
+#endif
+#ifdef AICWF_SDIO_SUPPORT
+    if(rwnx_hw->sdiodev->bus_if->state == BUS_DOWN_ST) {
+        headroom = sw_txhdr->headroom;
+        //kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+        skb_pull(skb, headroom);
+        consume_skb(skb);
+        return 0;
+    }
+#endif
+
+    txq = sw_txhdr->txq;
+    /* don't use txq->hwq as it may have changed between push and confirm */
+    //hwq = &rwnx_hw->hwq[sw_txhdr->hw_queue];
+    //rwnx_txq_confirm_any(rwnx_hw, txq, hwq, sw_txhdr);
+
+    /* Update txq and HW queue credits */
+    #ifdef CONFIG_SDIO_AGGR
+    if (sw_txhdr->desc.host.flags & TXU_CNTRL_MGMT)
+    #else
+    if (txhdr->desc.host.flags & TXU_CNTRL_MGMT)
+    #endif
+    {
+#ifdef CREATE_TRACE_POINTS
+        trace_printk("done=%d retry_required=%d sw_retry_required=%d acknowledged=%d\n",
+                     rwnx_txst.tx_done, rwnx_txst.retry_required,
+                     rwnx_txst.sw_retry_required, rwnx_txst.acknowledged);
+
+        trace_mgmt_cfm(sw_txhdr->rwnx_vif->vif_index,
+                       (sw_txhdr->rwnx_sta) ? sw_txhdr->rwnx_sta->sta_idx : 0xFF,
+                       rwnx_txst.acknowledged);
+#endif
+        /* Confirm transmission to CFG80211 */
+	#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+        cfg80211_mgmt_tx_status(&sw_txhdr->rwnx_vif->wdev,
+	#else
+	if (sw_txhdr->rwnx_vif->up && sw_txhdr->rwnx_vif->ndev && sw_txhdr->rwnx_vif->ndev->reg_state == NETREG_REGISTERED)
+        	cfg80211_mgmt_tx_status(sw_txhdr->rwnx_vif->ndev,
+	#endif
+                                (unsigned long)skb,
+                                (skb->data + sw_txhdr->headroom),
+                                sw_txhdr->frame_len,
+                                rwnx_txst.acknowledged,
+                                GFP_ATOMIC);
+    }
+
+#ifdef CREATE_TRACE_POINTS
+    trace_skb_confirm(skb, txq, hwq, &txhdr->hw_hdr.cfm);
+#endif
+    /* STA may have disconnect (and txq stopped) when buffers were stored
+       in fw. In this case do nothing when they're returned */
+    if (txq->idx != TXQ_INACTIVE) {
+        #if 0
+        if (txhdr->hw_hdr.cfm.credits) {
+            txq->credits += txhdr->hw_hdr.cfm.credits;
+            if (txq->credits <= 0)
+                rwnx_txq_stop(txq, RWNX_TXQ_STOP_FULL);
+            else if (txq->credits > 0)
+                rwnx_txq_start(txq, RWNX_TXQ_STOP_FULL);
+        }
+        #endif
+
+        /* continue service period */
+        if (unlikely(txq->push_limit && !rwnx_txq_is_full(txq))) {
+            rwnx_txq_add_to_hw_list(txq);
+        }
+    }
+
+    /* Release SKBs */
+#ifdef CONFIG_RWNX_AMSDUS_TX
+    #ifdef CONFIG_SDIO_AGGR
+    if (sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU)
+    #else
+    if (txhdr->desc.host.flags & TXU_CNTRL_AMSDU)
+    #endif
+    {
+        struct rwnx_amsdu_txhdr *amsdu_txhdr;
+        list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+            rwnx_amsdu_del_subframe_header(amsdu_txhdr);
+            consume_skb(amsdu_txhdr->skb);
+        }
+    }
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+
+    headroom = sw_txhdr->headroom;
+    //kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+    skb_pull(skb, headroom);
+    consume_skb(skb);
+
+    return 0;
+}
+
+/**
+ * rwnx_txq_credit_update - Update credit for one txq
+ *
+ * @rwnx_hw: Driver main data
+ * @sta_idx: STA idx
+ * @tid: TID
+ * @update: offset to apply in txq credits
+ *
+ * Called when fw send ME_TX_CREDITS_UPDATE_IND message.
+ * Apply @update to txq credits, and stop/start the txq if needed
+ */
+void rwnx_txq_credit_update(struct rwnx_hw *rwnx_hw, int sta_idx, u8 tid,
+                            s8 update)
+{
+    struct rwnx_sta *sta = &rwnx_hw->sta_table[sta_idx];
+    struct rwnx_txq *txq;
+
+    txq = rwnx_txq_sta_get(sta, tid, rwnx_hw);
+
+    spin_lock_bh(&rwnx_hw->tx_lock);
+
+    if (txq->idx != TXQ_INACTIVE) {
+        //txq->credits += update;
+#ifdef CREATE_TRACE_POINTS
+        trace_credit_update(txq, update);
+#endif
+        if (txq->credits <= 0)
+            rwnx_txq_stop(txq, RWNX_TXQ_STOP_FULL);
+        else
+            rwnx_txq_start(txq, RWNX_TXQ_STOP_FULL);
+    }
+
+    spin_unlock_bh(&rwnx_hw->tx_lock);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_tx.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_tx.h
new file mode 100755
index 0000000..276466d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_tx.h
@@ -0,0 +1,222 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_tx.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#ifndef _RWNX_TX_H_
+#define _RWNX_TX_H_
+
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <linux/netdevice.h>
+#include "lmac_types.h"
+#include "ipc_shared.h"
+#include "rwnx_txq.h"
+#include "hal_desc.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+#define IEEE80211_NUM_TIDS              16
+#endif
+
+#define RWNX_HWQ_BK                     0
+#define RWNX_HWQ_BE                     1
+#define RWNX_HWQ_VI                     2
+#define RWNX_HWQ_VO                     3
+#define RWNX_HWQ_BCMC                   4
+#define RWNX_HWQ_NB                     NX_TXQ_CNT
+#define RWNX_HWQ_ALL_ACS (RWNX_HWQ_BK | RWNX_HWQ_BE | RWNX_HWQ_VI | RWNX_HWQ_VO)
+#define RWNX_HWQ_ALL_ACS_BIT ( BIT(RWNX_HWQ_BK) | BIT(RWNX_HWQ_BE) |    \
+                               BIT(RWNX_HWQ_VI) | BIT(RWNX_HWQ_VO) )
+
+#define RWNX_TX_LIFETIME_MS             200
+#define RWNX_TX_MAX_RATES               NX_TX_MAX_RATES
+
+#define RWNX_SWTXHDR_ALIGN_SZ           4
+#define RWNX_SWTXHDR_ALIGN_MSK (RWNX_SWTXHDR_ALIGN_SZ - 1)
+#define RWNX_SWTXHDR_ALIGN_PADS(x) \
+                    ((RWNX_SWTXHDR_ALIGN_SZ - ((x) & RWNX_SWTXHDR_ALIGN_MSK)) \
+                     & RWNX_SWTXHDR_ALIGN_MSK)
+#if RWNX_SWTXHDR_ALIGN_SZ & RWNX_SWTXHDR_ALIGN_MSK
+#error bad RWNX_SWTXHDR_ALIGN_SZ
+#endif
+
+#define AMSDU_PADDING(x) ((4 - ((x) & 0x3)) & 0x3)
+
+#define TXU_CNTRL_RETRY        BIT(0)
+#define TXU_CNTRL_MORE_DATA    BIT(2)
+#define TXU_CNTRL_MGMT         BIT(3)
+#define TXU_CNTRL_MGMT_NO_CCK  BIT(4)
+#define TXU_CNTRL_AMSDU        BIT(6)
+#define TXU_CNTRL_MGMT_ROBUST  BIT(7)
+#define TXU_CNTRL_USE_4ADDR    BIT(8)
+#define TXU_CNTRL_EOSP         BIT(9)
+#define TXU_CNTRL_MESH_FWD     BIT(10)
+#define TXU_CNTRL_TDLS         BIT(11)
+
+extern const int rwnx_tid2hwq[IEEE80211_NUM_TIDS];
+
+/**
+ * struct rwnx_amsdu_txhdr - Structure added in skb headroom (instead of
+ * rwnx_txhdr) for amsdu subframe buffer (except for the first subframe
+ * that has a normal rwnx_txhdr)
+ *
+ * @list     List of other amsdu subframe (rwnx_sw_txhdr.amsdu.hdrs)
+ * @map_len  Length to be downloaded for this subframe
+ * @dma_addr Buffer address form embedded point of view
+ * @skb      skb
+ * @pad      padding added before this subframe
+ *           (only use when amsdu must be dismantled)
+ * @msdu_len Size, in bytes, of the MSDU (without padding nor amsdu header)
+ */
+struct rwnx_amsdu_txhdr {
+    struct list_head list;
+    size_t map_len;
+    dma_addr_t dma_addr;
+    struct sk_buff *skb;
+    u16 pad;
+    u16 msdu_len;
+};
+
+/**
+ * struct rwnx_amsdu - Structure to manage creation of an A-MSDU, updated
+ * only In the first subframe of an A-MSDU
+ *
+ * @hdrs List of subframe of rwnx_amsdu_txhdr
+ * @len  Current size for this A-MDSU (doesn't take padding into account)
+ *       0 means that no amsdu is in progress
+ * @nb   Number of subframe in the amsdu
+ * @pad  Padding to add before adding a new subframe
+ */
+struct rwnx_amsdu {
+    struct list_head hdrs;
+    u16 len;
+    u8 nb;
+    u8 pad;
+};
+
+/**
+ * struct rwnx_sw_txhdr - Software part of tx header
+ *
+ * @rwnx_sta sta to which this buffer is addressed
+ * @rwnx_vif vif that send the buffer
+ * @txq pointer to TXQ used to send the buffer
+ * @hw_queue Index of the HWQ used to push the buffer.
+ *           May be different than txq->hwq->id on confirmation.
+ * @frame_len Size of the frame (doesn't not include mac header)
+ *            (Only used to update stat, can't we use skb->len instead ?)
+ * @headroom Headroom added in skb to add rwnx_txhdr
+ *           (Only used to remove it before freeing skb, is it needed ?)
+ * @amsdu Description of amsdu whose first subframe is this buffer
+ *        (amsdu.nb = 0 means this buffer is not part of amsdu)
+ * @skb skb received from transmission
+ * @map_len  Length mapped for DMA (only rwnx_hw_txhdr and data are mapped)
+ * @dma_addr DMA address after mapping
+ * @desc Buffer description that will be copied in shared mem for FW
+ */
+#ifdef CONFIG_SDIO_AGGR
+struct rwnx_sw_txhdr {
+    struct rwnx_sta *rwnx_sta;
+    struct rwnx_vif *rwnx_vif;
+    struct rwnx_txq *txq;
+    struct sk_buff *skb;
+#ifdef CONFIG_RWNX_AMSDUS_TX
+    struct rwnx_amsdu amsdu;
+#endif
+    u16 headroom;
+	u16 frame_len;
+	u8 need_cfm;
+	u8 is_dhcp;
+    struct txdesc_api desc;
+};
+
+#else
+struct rwnx_sw_txhdr {
+    struct rwnx_sta *rwnx_sta;
+    struct rwnx_vif *rwnx_vif;
+    struct rwnx_txq *txq;
+    struct sk_buff *skb;
+#ifdef CONFIG_RWNX_AMSDUS_TX
+    struct rwnx_amsdu amsdu;
+#endif
+    u16 headroom;
+	u16 frame_len;
+	u8 need_cfm;
+	u8 is_dhcp;
+
+    //struct txdesc_api desc;
+};
+
+#endif
+
+/**
+ * struct rwnx_txhdr - Stucture to control transimission of packet
+ * (Added in skb headroom)
+ *
+ * @sw_hdr: Information from driver
+ * @cache_guard:
+ * @hw_hdr: Information for/from hardware
+ */
+#ifdef CONFIG_SDIO_AGGR
+struct rwnx_txhdr {
+    struct rwnx_sw_txhdr sw_hdr;
+    struct rwnx_hw_txhdr hw_hdr;
+};
+
+#else
+struct rwnx_txhdr {
+    struct rwnx_sw_txhdr sw_hdr;
+    struct rwnx_hw_txhdr hw_hdr;
+    u8 sdio_hdr[4];
+    struct txdesc_api desc;
+}__packed;
+
+#endif
+
+u16 rwnx_select_txq(struct rwnx_vif *rwnx_vif, struct sk_buff *skb);
+int rwnx_start_xmit(struct sk_buff *skb, struct net_device *dev);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta,
+                         struct cfg80211_mgmt_tx_params *params, bool offchan,
+                         u64 *cookie);
+#else
+int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta,
+                         struct ieee80211_channel *channel, bool offchan,
+                         unsigned int wait, const u8* buf, size_t len,
+                    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
+                         bool no_cck,
+                    #endif
+                    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
+                         bool dont_wait_for_ack,
+                    #endif
+                         u64 *cookie);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+#ifdef CONFIG_RWNX_MON_XMIT
+int rwnx_start_monitor_if_xmit(struct sk_buff *skb, struct net_device *dev);
+#endif
+int rwnx_txdatacfm(void *pthis, void *host_id);
+
+struct rwnx_hw;
+struct rwnx_sta;
+void rwnx_set_traffic_status(struct rwnx_hw *rwnx_hw,
+                             struct rwnx_sta *sta,
+                             bool available,
+                             u8 ps_id);
+void rwnx_ps_bh_enable(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+                       bool enable);
+void rwnx_ps_bh_traffic_req(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+                            u16 pkt_req, u8 ps_id);
+
+void rwnx_switch_vif_sta_txq(struct rwnx_sta *sta, struct rwnx_vif *old_vif,
+                             struct rwnx_vif *new_vif);
+
+int rwnx_dbgfs_print_sta(char *buf, size_t size, struct rwnx_sta *sta,
+                         struct rwnx_hw *rwnx_hw);
+void rwnx_txq_credit_update(struct rwnx_hw *rwnx_hw, int sta_idx, u8 tid,
+                            s8 update);
+void rwnx_tx_push(struct rwnx_hw *rwnx_hw, struct rwnx_txhdr *txhdr, int flags);
+
+#endif /* _RWNX_TX_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_txq.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_txq.c
new file mode 100755
index 0000000..e2e00ed
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_txq.c
@@ -0,0 +1,1328 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_txq.c
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ******************************************************************************
+ */
+
+#include "rwnx_defs.h"
+#include "rwnx_tx.h"
+#include "ipc_host.h"
+#include "rwnx_events.h"
+
+/******************************************************************************
+ * Utils functions
+ *****************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+const int nx_tid_prio[NX_NB_TID_PER_STA] = {7, 6, 5, 4, 3, 0, 2, 1};
+
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+extern int tx_fc_low_water;
+extern int tx_fc_high_water;
+#endif
+
+static inline int rwnx_txq_sta_idx(struct rwnx_sta *sta, u8 tid)
+{
+    if (is_multicast_sta(sta->sta_idx))
+        return NX_FIRST_VIF_TXQ_IDX + sta->vif_idx;
+    else
+        return (sta->sta_idx * NX_NB_TXQ_PER_STA) + tid;
+}
+
+static inline int rwnx_txq_vif_idx(struct rwnx_vif *vif, u8 type)
+{
+    return NX_FIRST_VIF_TXQ_IDX + master_vif_idx(vif) + (type * NX_VIRT_DEV_MAX);
+}
+
+struct rwnx_txq *rwnx_txq_sta_get(struct rwnx_sta *sta, u8 tid,
+                                  struct rwnx_hw * rwnx_hw)
+{
+    if (tid >= NX_NB_TXQ_PER_STA)
+        tid = 0;
+
+    return &rwnx_hw->txq[rwnx_txq_sta_idx(sta, tid)];
+}
+
+struct rwnx_txq *rwnx_txq_vif_get(struct rwnx_vif *vif, u8 type)
+{
+    if (type > NX_UNK_TXQ_TYPE)
+        type = NX_BCMC_TXQ_TYPE;
+
+    return &vif->rwnx_hw->txq[rwnx_txq_vif_idx(vif, type)];
+}
+
+static inline struct rwnx_sta *rwnx_txq_2_sta(struct rwnx_txq *txq)
+{
+    return txq->sta;
+}
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+
+/******************************************************************************
+ * Init/Deinit functions
+ *****************************************************************************/
+/**
+ * rwnx_txq_init - Initialize a TX queue
+ *
+ * @txq: TX queue to be initialized
+ * @idx: TX queue index
+ * @status: TX queue initial status
+ * @hwq: Associated HW queue
+ * @ndev: Net device this queue belongs to
+ *        (may be null for non netdev txq)
+ *
+ * Each queue is initialized with the credit of @NX_TXQ_INITIAL_CREDITS.
+ */
+static void rwnx_txq_init(struct rwnx_txq *txq, int idx, u8 status,
+                          struct rwnx_hwq *hwq, int tid,
+#ifdef CONFIG_RWNX_FULLMAC
+                          struct rwnx_sta *sta, struct net_device *ndev
+#endif
+                          )
+{
+    int i;
+
+    txq->idx = idx;
+    txq->status = status;
+    txq->credits = NX_TXQ_INITIAL_CREDITS;
+    txq->pkt_sent = 0;
+    skb_queue_head_init(&txq->sk_list);
+    txq->last_retry_skb = NULL;
+    txq->nb_retry = 0;
+    txq->hwq = hwq;
+    txq->sta = sta;
+    for (i = 0; i < CONFIG_USER_MAX ; i++)
+        txq->pkt_pushed[i] = 0;
+    txq->push_limit = 0;
+    txq->tid = tid;
+#ifdef CONFIG_MAC80211_TXQ
+    txq->nb_ready_mac80211 = 0;
+#endif
+#ifdef CONFIG_RWNX_FULLMAC
+    txq->ps_id = LEGACY_PS_ID;
+    if (idx < NX_FIRST_VIF_TXQ_IDX) {
+        int sta_idx = sta->sta_idx;
+        int tid = idx - (sta_idx * NX_NB_TXQ_PER_STA);
+        if (tid < NX_NB_TID_PER_STA)
+            txq->ndev_idx = NX_STA_NDEV_IDX(tid, sta_idx);
+        else
+            txq->ndev_idx = NDEV_NO_TXQ;
+    } else if (idx < NX_FIRST_UNK_TXQ_IDX) {
+        txq->ndev_idx = NX_BCMC_TXQ_NDEV_IDX;
+    } else {
+        txq->ndev_idx = NDEV_NO_TXQ;
+    }
+    txq->ndev = ndev;
+#ifdef CONFIG_RWNX_AMSDUS_TX
+    txq->amsdu = NULL;
+    txq->amsdu_len = 0;
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+#endif /* CONFIG_RWNX_FULLMAC */
+}
+
+/**
+ * rwnx_txq_flush - Flush all buffers queued for a TXQ
+ *
+ * @rwnx_hw: main driver data
+ * @txq: txq to flush
+ */
+void rwnx_txq_flush(struct rwnx_hw *rwnx_hw, struct rwnx_txq *txq)
+{
+    struct sk_buff *skb;
+	printk("%s: idx %d, %d\n", __func__, txq->idx, skb_queue_len(&txq->sk_list));
+
+    while((skb = skb_dequeue(&txq->sk_list)) != NULL) {
+        struct rwnx_sw_txhdr *sw_txhdr = &((struct rwnx_txhdr *)skb->data)->sw_hdr;
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+        if (sw_txhdr->desc.host.packet_cnt > 1) {
+            struct rwnx_amsdu_txhdr *amsdu_txhdr;
+            list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+                dma_unmap_single(rwnx_hw->dev, amsdu_txhdr->dma_addr,
+                                 amsdu_txhdr->map_len, DMA_TO_DEVICE);
+                dev_kfree_skb_any(amsdu_txhdr->skb);
+            }
+        }
+#endif
+        //kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+        //dma_unmap_single(rwnx_hw->dev, sw_txhdr->dma_addr, sw_txhdr->map_len,
+        //                 DMA_TO_DEVICE);
+
+#ifdef CONFIG_RWNX_FULLMAC
+        dev_kfree_skb_any(skb);
+#endif /* CONFIG_RWNX_FULLMAC */
+    }
+}
+
+/**
+ * rwnx_txq_deinit - De-initialize a TX queue
+ *
+ * @rwnx_hw: Driver main data
+ * @txq: TX queue to be de-initialized
+ * Any buffer stuck in a queue will be freed.
+ */
+static void rwnx_txq_deinit(struct rwnx_hw *rwnx_hw, struct rwnx_txq *txq)
+{
+	printk("%s: %d\n", __func__, txq->idx);
+    if (txq->idx == TXQ_INACTIVE)
+        return;
+
+    spin_lock_bh(&rwnx_hw->tx_lock);
+    rwnx_txq_del_from_hw_list(txq);
+    txq->idx = TXQ_INACTIVE;
+    if(unlikely(txq->status & RWNX_TXQ_NDEV_FLOW_CTRL)) {
+        txq->status &= ~RWNX_TXQ_NDEV_FLOW_CTRL;
+        netif_wake_subqueue(txq->ndev, txq->ndev_idx);
+    }
+    spin_unlock_bh(&rwnx_hw->tx_lock);
+
+    rwnx_txq_flush(rwnx_hw, txq);
+}
+
+/**
+ * rwnx_txq_vif_init - Initialize all TXQ linked to a vif
+ *
+ * @rwnx_hw: main driver data
+ * @rwnx_vif: Pointer on VIF
+ * @status: Intial txq status
+ *
+ * Softmac : 1 VIF TXQ per HWQ
+ *
+ * Fullmac : 1 VIF TXQ for BC/MC
+ *           1 VIF TXQ for MGMT to unknown STA
+ */
+void rwnx_txq_vif_init(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                       u8 status)
+{
+    struct rwnx_txq *txq;
+    int idx;
+
+#ifdef CONFIG_RWNX_FULLMAC
+    txq = rwnx_txq_vif_get(rwnx_vif, NX_BCMC_TXQ_TYPE);
+    idx = rwnx_txq_vif_idx(rwnx_vif, NX_BCMC_TXQ_TYPE);
+    rwnx_txq_init(txq, idx, status, &rwnx_hw->hwq[RWNX_HWQ_BE], 0,
+                  &rwnx_hw->sta_table[rwnx_vif->ap.bcmc_index], rwnx_vif->ndev);
+
+    txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+    idx = rwnx_txq_vif_idx(rwnx_vif, NX_UNK_TXQ_TYPE);
+    rwnx_txq_init(txq, idx, status, &rwnx_hw->hwq[RWNX_HWQ_VO], TID_MGT,
+                  NULL, rwnx_vif->ndev);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+}
+
+/**
+ * rwnx_txq_vif_deinit - Deinitialize all TXQ linked to a vif
+ *
+ * @rwnx_hw: main driver data
+ * @rwnx_vif: Pointer on VIF
+ */
+void rwnx_txq_vif_deinit(struct rwnx_hw * rwnx_hw, struct rwnx_vif *rwnx_vif)
+{
+    struct rwnx_txq *txq;
+
+#ifdef CONFIG_RWNX_FULLMAC
+    txq = rwnx_txq_vif_get(rwnx_vif, NX_BCMC_TXQ_TYPE);
+    rwnx_txq_deinit(rwnx_hw, txq);
+
+    txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+    rwnx_txq_deinit(rwnx_hw, txq);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+}
+
+
+/**
+ * rwnx_txq_sta_init - Initialize TX queues for a STA
+ *
+ * @rwnx_hw: Main driver data
+ * @rwnx_sta: STA for which tx queues need to be initialized
+ * @status: Intial txq status
+ *
+ * This function initialize all the TXQ associated to a STA.
+ * Softmac : 1 TXQ per TID
+ *
+ * Fullmac : 1 TXQ per TID (limited to 8)
+ *           1 TXQ for MGMT
+ */
+void rwnx_txq_sta_init(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta,
+                       u8 status)
+{
+    struct rwnx_txq *txq;
+    int tid, idx;
+
+#ifdef CONFIG_RWNX_FULLMAC
+    struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[rwnx_sta->vif_idx];
+    idx = rwnx_txq_sta_idx(rwnx_sta, 0);
+
+    foreach_sta_txq(rwnx_sta, txq, tid, rwnx_hw) {
+        rwnx_txq_init(txq, idx, status, &rwnx_hw->hwq[rwnx_tid2hwq[tid]], tid,
+                      rwnx_sta, rwnx_vif->ndev);
+        txq->ps_id = rwnx_sta->uapsd_tids & (1 << tid) ? UAPSD_ID : LEGACY_PS_ID;
+        idx++;
+    }
+
+#endif /* CONFIG_RWNX_FULLMAC*/
+}
+
+/**
+ * rwnx_txq_sta_deinit - Deinitialize TX queues for a STA
+ *
+ * @rwnx_hw: Main driver data
+ * @rwnx_sta: STA for which tx queues need to be deinitialized
+ */
+void rwnx_txq_sta_deinit(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta)
+{
+    struct rwnx_txq *txq;
+    int tid;
+
+    foreach_sta_txq(rwnx_sta, txq, tid, rwnx_hw) {
+        rwnx_txq_deinit(rwnx_hw, txq);
+    }
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+/**
+ * rwnx_txq_unk_vif_init - Initialize TXQ for unknown STA linked to a vif
+ *
+ * @rwnx_vif: Pointer on VIF
+ */
+void rwnx_txq_unk_vif_init(struct rwnx_vif *rwnx_vif)
+{
+    struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+    struct rwnx_txq *txq;
+    int idx;
+
+    txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+    idx = rwnx_txq_vif_idx(rwnx_vif, NX_UNK_TXQ_TYPE);
+    rwnx_txq_init(txq, idx, 0, &rwnx_hw->hwq[RWNX_HWQ_VO], TID_MGT, NULL, rwnx_vif->ndev);
+}
+
+/**
+ * rwnx_txq_tdls_vif_deinit - Deinitialize TXQ for unknown STA linked to a vif
+ *
+ * @rwnx_vif: Pointer on VIF
+ */
+void rwnx_txq_unk_vif_deinit(struct rwnx_vif *rwnx_vif)
+{
+    struct rwnx_txq *txq;
+
+    txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+    rwnx_txq_deinit(rwnx_vif->rwnx_hw, txq);
+}
+
+/**
+ * rwnx_init_unk_txq - Initialize TX queue for the transmission on a offchannel
+ *
+ * @vif: Interface for which the queue has to be initialized
+ *
+ * NOTE: Offchannel txq is only active for the duration of the ROC
+ */
+void rwnx_txq_offchan_init(struct rwnx_vif *rwnx_vif)
+{
+    struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+    struct rwnx_txq *txq;
+
+    txq = &rwnx_hw->txq[NX_OFF_CHAN_TXQ_IDX];
+    rwnx_txq_init(txq, NX_OFF_CHAN_TXQ_IDX, RWNX_TXQ_STOP_CHAN,
+                  &rwnx_hw->hwq[RWNX_HWQ_VO], TID_MGT, NULL, rwnx_vif->ndev);
+}
+
+/**
+ * rwnx_deinit_offchan_txq - Deinitialize TX queue for offchannel
+ *
+ * @vif: Interface that manages the STA
+ *
+ * This function deintialize txq for one STA.
+ * Any buffer stuck in a queue will be freed.
+ */
+void rwnx_txq_offchan_deinit(struct rwnx_vif *rwnx_vif)
+{
+    struct rwnx_txq *txq;
+
+    txq = &rwnx_vif->rwnx_hw->txq[NX_OFF_CHAN_TXQ_IDX];
+    rwnx_txq_deinit(rwnx_vif->rwnx_hw, txq);
+}
+
+
+/**
+ * rwnx_txq_tdls_vif_init - Initialize TXQ vif for TDLS
+ *
+ * @rwnx_vif: Pointer on VIF
+ */
+void rwnx_txq_tdls_vif_init(struct rwnx_vif *rwnx_vif)
+{
+    struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+
+    if (!(rwnx_hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+        return;
+
+    rwnx_txq_unk_vif_init(rwnx_vif);
+}
+
+/**
+ * rwnx_txq_tdls_vif_deinit - Deinitialize TXQ vif for TDLS
+ *
+ * @rwnx_vif: Pointer on VIF
+ */
+void rwnx_txq_tdls_vif_deinit(struct rwnx_vif *rwnx_vif)
+{
+    struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+
+    if (!(rwnx_hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+        return;
+
+    rwnx_txq_unk_vif_deinit(rwnx_vif);
+}
+#endif
+
+/******************************************************************************
+ * Start/Stop functions
+ *****************************************************************************/
+/**
+ * rwnx_txq_add_to_hw_list - Add TX queue to a HW queue schedule list.
+ *
+ * @txq: TX queue to add
+ *
+ * Add the TX queue if not already present in the HW queue list.
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_add_to_hw_list(struct rwnx_txq *txq)
+{
+    if (!(txq->status & RWNX_TXQ_IN_HWQ_LIST)) {
+#ifdef CREATE_TRACE_POINTS
+        trace_txq_add_to_hw(txq);
+#endif
+        txq->status |= RWNX_TXQ_IN_HWQ_LIST;
+        list_add_tail(&txq->sched_list, &txq->hwq->list);
+        txq->hwq->need_processing = true;
+    }
+}
+
+/**
+ * rwnx_txq_del_from_hw_list - Delete TX queue from a HW queue schedule list.
+ *
+ * @txq: TX queue to delete
+ *
+ * Remove the TX queue from the HW queue list if present.
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_del_from_hw_list(struct rwnx_txq *txq)
+{
+    if (txq->status & RWNX_TXQ_IN_HWQ_LIST) {
+#ifdef CREATE_TRACE_POINTS
+        trace_txq_del_from_hw(txq);
+#endif
+        txq->status &= ~RWNX_TXQ_IN_HWQ_LIST;
+        list_del(&txq->sched_list);
+    }
+}
+
+/**
+ * rwnx_txq_skb_ready - Check if skb are available for the txq
+ *
+ * @txq: Pointer on txq
+ * @return True if there are buffer ready to be pushed on this txq,
+ * false otherwise
+ */
+static inline bool rwnx_txq_skb_ready(struct rwnx_txq *txq)
+{
+#ifdef CONFIG_MAC80211_TXQ
+    if (txq->nb_ready_mac80211 != NOT_MAC80211_TXQ)
+        return ((txq->nb_ready_mac80211 > 0) || !skb_queue_empty(&txq->sk_list));
+    else
+#endif
+    return !skb_queue_empty(&txq->sk_list);
+}
+
+/**
+ * rwnx_txq_start - Try to Start one TX queue
+ *
+ * @txq: TX queue to start
+ * @reason: reason why the TX queue is started (among RWNX_TXQ_STOP_xxx)
+ *
+ * Re-start the TX queue for one reason.
+ * If after this the txq is no longer stopped and some buffers are ready,
+ * the TX queue is also added to HW queue list.
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_start(struct rwnx_txq *txq, u16 reason)
+{
+    BUG_ON(txq==NULL);
+    if (txq->idx != TXQ_INACTIVE && (txq->status & reason))
+    {
+#ifdef CREATE_TRACE_POINTS
+        trace_txq_start(txq, reason);
+#endif
+        txq->status &= ~reason;
+        if (!rwnx_txq_is_stopped(txq) && rwnx_txq_skb_ready(txq))
+            rwnx_txq_add_to_hw_list(txq);
+    }
+}
+
+/**
+ * rwnx_txq_stop - Stop one TX queue
+ *
+ * @txq: TX queue to stop
+ * @reason: reason why the TX queue is stopped (among RWNX_TXQ_STOP_xxx)
+ *
+ * Stop the TX queue. It will remove the TX queue from HW queue list
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_stop(struct rwnx_txq *txq, u16 reason)
+{
+    BUG_ON(txq==NULL);
+    if (txq->idx != TXQ_INACTIVE)
+    {
+#ifdef CREATE_TRACE_POINTS
+        trace_txq_stop(txq, reason);
+#endif
+        txq->status |= reason;
+        rwnx_txq_del_from_hw_list(txq);
+    }
+}
+
+
+/**
+ * rwnx_txq_sta_start - Start all the TX queue linked to a STA
+ *
+ * @sta: STA whose TX queues must be re-started
+ * @reason: Reason why the TX queue are restarted (among RWNX_TXQ_STOP_xxx)
+ * @rwnx_hw: Driver main data
+ *
+ * This function will re-start all the TX queues of the STA for the reason
+ * specified. It can be :
+ * - RWNX_TXQ_STOP_STA_PS: the STA is no longer in power save mode
+ * - RWNX_TXQ_STOP_VIF_PS: the VIF is in power save mode (p2p absence)
+ * - RWNX_TXQ_STOP_CHAN: the STA's VIF is now on the current active channel
+ *
+ * Any TX queue with buffer ready and not Stopped for other reasons, will be
+ * added to the HW queue list
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_sta_start(struct rwnx_sta *rwnx_sta, u16 reason
+#ifdef CONFIG_RWNX_FULLMAC
+                        , struct rwnx_hw *rwnx_hw
+#endif
+                        )
+{
+    struct rwnx_txq *txq;
+    int tid;
+#ifdef CREATE_TRACE_POINTS
+    trace_txq_sta_start(rwnx_sta->sta_idx);
+#endif
+    foreach_sta_txq(rwnx_sta, txq, tid, rwnx_hw) {
+        rwnx_txq_start(txq, reason);
+    }
+}
+
+
+/**
+ * rwnx_stop_sta_txq - Stop all the TX queue linked to a STA
+ *
+ * @sta: STA whose TX queues must be stopped
+ * @reason: Reason why the TX queue are stopped (among RWNX_TX_STOP_xxx)
+ * @rwnx_hw: Driver main data
+ *
+ * This function will stop all the TX queues of the STA for the reason
+ * specified. It can be :
+ * - RWNX_TXQ_STOP_STA_PS: the STA is in power save mode
+ * - RWNX_TXQ_STOP_VIF_PS: the VIF is in power save mode (p2p absence)
+ * - RWNX_TXQ_STOP_CHAN: the STA's VIF is not on the current active channel
+ *
+ * Any TX queue present in a HW queue list will be removed from this list.
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_sta_stop(struct rwnx_sta *rwnx_sta, u16 reason
+#ifdef CONFIG_RWNX_FULLMAC
+                       , struct rwnx_hw *rwnx_hw
+#endif
+                       )
+{
+    struct rwnx_txq *txq;
+    int tid;
+
+    if (!rwnx_sta)
+        return;
+#ifdef CREATE_TRACE_POINTS
+    trace_txq_sta_stop(rwnx_sta->sta_idx);
+#endif
+    foreach_sta_txq(rwnx_sta, txq, tid, rwnx_hw) {
+        rwnx_txq_stop(txq, reason);
+    }
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_txq_tdls_sta_start(struct rwnx_vif *rwnx_vif, u16 reason,
+                             struct rwnx_hw *rwnx_hw)
+{
+#ifdef CREATE_TRACE_POINTS
+    trace_txq_vif_start(rwnx_vif->vif_index);
+#endif
+    spin_lock_bh(&rwnx_hw->tx_lock);
+
+    if (rwnx_vif->sta.tdls_sta)
+        rwnx_txq_sta_start(rwnx_vif->sta.tdls_sta, reason, rwnx_hw);
+
+    spin_unlock_bh(&rwnx_hw->tx_lock);
+}
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_txq_tdls_sta_stop(struct rwnx_vif *rwnx_vif, u16 reason,
+                            struct rwnx_hw *rwnx_hw)
+{
+#ifdef CREATE_TRACE_POINTS
+    trace_txq_vif_stop(rwnx_vif->vif_index);
+#endif
+    spin_lock_bh(&rwnx_hw->tx_lock);
+
+    if (rwnx_vif->sta.tdls_sta)
+        rwnx_txq_sta_stop(rwnx_vif->sta.tdls_sta, reason, rwnx_hw);
+
+    spin_unlock_bh(&rwnx_hw->tx_lock);
+}
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+static inline
+void rwnx_txq_vif_for_each_sta(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+                               void (*f)(struct rwnx_sta *, u16, struct rwnx_hw *),
+                               u16 reason)
+{
+
+    switch (RWNX_VIF_TYPE(rwnx_vif)) {
+    case NL80211_IFTYPE_STATION:
+    case NL80211_IFTYPE_P2P_CLIENT:
+    {
+        if (rwnx_vif->tdls_status == TDLS_LINK_ACTIVE)
+            f(rwnx_vif->sta.tdls_sta, reason, rwnx_hw);
+        if (!WARN_ON(rwnx_vif->sta.ap == NULL))
+            f(rwnx_vif->sta.ap, reason, rwnx_hw);
+        break;
+    }
+    case NL80211_IFTYPE_AP_VLAN:
+        rwnx_vif = rwnx_vif->ap_vlan.master;
+    case NL80211_IFTYPE_AP:
+    case NL80211_IFTYPE_MESH_POINT:
+    case NL80211_IFTYPE_P2P_GO:
+    {
+        struct rwnx_sta *sta;
+        list_for_each_entry(sta, &rwnx_vif->ap.sta_list, list) {
+            f(sta, reason, rwnx_hw);
+        }
+        break;
+    }
+    default:
+        BUG();
+        break;
+    }
+}
+
+#endif
+
+/**
+ * rwnx_txq_vif_start - START TX queues of all STA associated to the vif
+ *                      and vif's TXQ
+ *
+ * @vif: Interface to start
+ * @reason: Start reason (RWNX_TXQ_STOP_CHAN or RWNX_TXQ_STOP_VIF_PS)
+ * @rwnx_hw: Driver main data
+ *
+ * Iterate over all the STA associated to the vif and re-start them for the
+ * reason @reason
+ * Take tx_lock
+ */
+void rwnx_txq_vif_start(struct rwnx_vif *rwnx_vif, u16 reason,
+                        struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_txq *txq;
+#ifdef CREATE_TRACE_POINTS
+    trace_txq_vif_start(rwnx_vif->vif_index);
+#endif
+    spin_lock_bh(&rwnx_hw->tx_lock);
+
+#ifdef CONFIG_RWNX_FULLMAC
+    //Reject if monitor interface
+    if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_MONITOR)
+        goto end;
+
+    if (rwnx_vif->roc_tdls && rwnx_vif->sta.tdls_sta && rwnx_vif->sta.tdls_sta->tdls.chsw_en) {
+        rwnx_txq_sta_start(rwnx_vif->sta.tdls_sta, reason, rwnx_hw);
+    }
+    if (!rwnx_vif->roc_tdls) {
+        rwnx_txq_vif_for_each_sta(rwnx_hw, rwnx_vif, rwnx_txq_sta_start, reason);
+    }
+
+    txq = rwnx_txq_vif_get(rwnx_vif, NX_BCMC_TXQ_TYPE);
+    rwnx_txq_start(txq, reason);
+    txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+    rwnx_txq_start(txq, reason);
+
+end:
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    spin_unlock_bh(&rwnx_hw->tx_lock);
+}
+
+
+/**
+ * rwnx_txq_vif_stop - STOP TX queues of all STA associated to the vif
+ *
+ * @vif: Interface to stop
+ * @arg: Stop reason (RWNX_TXQ_STOP_CHAN or RWNX_TXQ_STOP_VIF_PS)
+ * @rwnx_hw: Driver main data
+ *
+ * Iterate over all the STA associated to the vif and stop them for the
+ * reason RWNX_TXQ_STOP_CHAN or RWNX_TXQ_STOP_VIF_PS
+ * Take tx_lock
+ */
+void rwnx_txq_vif_stop(struct rwnx_vif *rwnx_vif, u16 reason,
+                       struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_txq *txq;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifdef CREATE_TRACE_POINTS
+    trace_txq_vif_stop(rwnx_vif->vif_index);
+#endif
+    spin_lock_bh(&rwnx_hw->tx_lock);
+
+#ifdef CONFIG_RWNX_FULLMAC
+    //Reject if monitor interface
+    if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_MONITOR)
+        goto end;
+
+    rwnx_txq_vif_for_each_sta(rwnx_hw, rwnx_vif, rwnx_txq_sta_stop, reason);
+
+    txq = rwnx_txq_vif_get(rwnx_vif, NX_BCMC_TXQ_TYPE);
+    rwnx_txq_stop(txq, reason);
+    txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+    rwnx_txq_stop(txq, reason);
+
+end:
+#endif /* CONFIG_RWNX_FULLMAC*/
+
+    spin_unlock_bh(&rwnx_hw->tx_lock);
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+/**
+ * rwnx_start_offchan_txq - START TX queue for offchannel frame
+ *
+ * @rwnx_hw: Driver main data
+ */
+void rwnx_txq_offchan_start(struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_txq *txq;
+
+    txq = &rwnx_hw->txq[NX_OFF_CHAN_TXQ_IDX];
+    spin_lock_bh(&rwnx_hw->tx_lock);
+    rwnx_txq_start(txq, RWNX_TXQ_STOP_CHAN);
+    spin_unlock_bh(&rwnx_hw->tx_lock);
+}
+
+/**
+ * rwnx_switch_vif_sta_txq - Associate TXQ linked to a STA to a new vif
+ *
+ * @sta: STA whose txq must be switched
+ * @old_vif: Vif currently associated to the STA (may no longer be active)
+ * @new_vif: vif which should be associated to the STA for now on
+ *
+ * This function will switch the vif (i.e. the netdev) associated to all STA's
+ * TXQ. This is used when AP_VLAN interface are created.
+ * If one STA is associated to an AP_vlan vif, it will be moved from the master
+ * AP vif to the AP_vlan vif.
+ * If an AP_vlan vif is removed, then STA will be moved back to mastert AP vif.
+ *
+ */
+void rwnx_txq_sta_switch_vif(struct rwnx_sta *sta, struct rwnx_vif *old_vif,
+                             struct rwnx_vif *new_vif)
+{
+    struct rwnx_hw *rwnx_hw = new_vif->rwnx_hw;
+    struct rwnx_txq *txq;
+    int i;
+
+    /* start TXQ on the new interface, and update ndev field in txq */
+    if (!netif_carrier_ok(new_vif->ndev))
+        netif_carrier_on(new_vif->ndev);
+    txq = rwnx_txq_sta_get(sta, 0, rwnx_hw);
+    for (i = 0; i < NX_NB_TID_PER_STA; i++, txq++) {
+        txq->ndev = new_vif->ndev;
+        netif_wake_subqueue(txq->ndev, txq->ndev_idx);
+    }
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/******************************************************************************
+ * TXQ queue/schedule functions
+ *****************************************************************************/
+/**
+ * rwnx_txq_queue_skb - Queue a buffer in a TX queue
+ *
+ * @skb: Buffer to queue
+ * @txq: TX Queue in which the buffer must be added
+ * @rwnx_hw: Driver main data
+ * @retry: Should it be queued in the retry list
+ *
+ * @return: Retrun 1 if txq has been added to hwq list, 0 otherwise
+ *
+ * Add a buffer in the buffer list of the TX queue
+ * and add this TX queue in the HW queue list if the txq is not stopped.
+ * If this is a retry packet it is added after the last retry packet or at the
+ * beginning if there is no retry packet queued.
+ *
+ * If the STA is in PS mode and this is the first packet queued for this txq
+ * update TIM.
+ *
+ * To be called with tx_lock hold
+ */
+int rwnx_txq_queue_skb(struct sk_buff *skb, struct rwnx_txq *txq,
+                       struct rwnx_hw *rwnx_hw,  bool retry)
+{
+
+#ifdef CONFIG_RWNX_FULLMAC
+    if (unlikely(txq->sta && txq->sta->ps.active)) {
+        txq->sta->ps.pkt_ready[txq->ps_id]++;
+#ifdef CREATE_TRACE_POINTS
+        trace_ps_queue(txq->sta);
+#endif
+        if (txq->sta->ps.pkt_ready[txq->ps_id] == 1) {
+            rwnx_set_traffic_status(rwnx_hw, txq->sta, true, txq->ps_id);
+        }
+    }
+#endif
+
+    if (!retry) {
+        /* add buffer in the sk_list */
+        skb_queue_tail(&txq->sk_list, skb);
+    } else {
+        if (txq->last_retry_skb)
+            skb_append(txq->last_retry_skb, skb, &txq->sk_list);
+        else
+            skb_queue_head(&txq->sk_list, skb);
+
+        txq->last_retry_skb = skb;
+        txq->nb_retry++;
+    }
+#ifdef CREATE_TRACE_POINTS
+    trace_txq_queue_skb(skb, txq, retry);
+#endif
+    /* Flowctrl corresponding netdev queue if needed */
+#ifdef CONFIG_RWNX_FULLMAC
+#ifndef CONFIG_ONE_TXQ
+    /* If too many buffer are queued for this TXQ stop netdev queue */
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+	if ((txq->ndev_idx != NDEV_NO_TXQ) && ((skb_queue_len(&txq->sk_list) > RWNX_NDEV_FLOW_CTRL_STOP) &&
+	!rwnx_hw->sdiodev->flowctrl)) {
+//        (atomic_read(&rwnx_hw->sdiodev->tx_priv->tx_pktcnt) >= tx_fc_high_water))) {
+#else
+    if ((txq->ndev_idx != NDEV_NO_TXQ) &&
+        (skb_queue_len(&txq->sk_list) > RWNX_NDEV_FLOW_CTRL_STOP)) {
+#endif
+        txq->status |= RWNX_TXQ_NDEV_FLOW_CTRL;
+        netif_stop_subqueue(txq->ndev, txq->ndev_idx);
+#ifdef CREATE_TRACE_POINTS
+        trace_txq_flowctrl_stop(txq);
+#endif
+    }
+#endif /* CONFIG_ONE_TXQ */
+#else /* ! CONFIG_RWNX_FULLMAC */
+
+    if (!retry && ++txq->hwq->len == txq->hwq->len_stop) {
+#ifdef CREATE_TRACE_POINTS
+         trace_hwq_flowctrl_stop(txq->hwq->id);
+#endif
+         ieee80211_stop_queue(rwnx_hw->hw, txq->hwq->id);
+         rwnx_hw->stats.queues_stops++;
+     }
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    /* add it in the hwq list if not stopped and not yet present */
+    if (!rwnx_txq_is_stopped(txq)) {
+        rwnx_txq_add_to_hw_list(txq);
+        return 1;
+    }
+
+    return 0;
+}
+
+/**
+ * rwnx_txq_confirm_any - Process buffer confirmed by fw
+ *
+ * @rwnx_hw: Driver main data
+ * @txq: TX Queue
+ * @hwq: HW Queue
+ * @sw_txhdr: software descriptor of the confirmed packet
+ *
+ * Process a buffer returned by the fw. It doesn't check buffer status
+ * and only does systematic counter update:
+ * - hw credit
+ * - buffer pushed to fw
+ *
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_confirm_any(struct rwnx_hw *rwnx_hw, struct rwnx_txq *txq,
+                          struct rwnx_hwq *hwq, struct rwnx_sw_txhdr *sw_txhdr)
+{
+    int user = 0;
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+    int group_id;
+
+    user = RWNX_MUMIMO_INFO_POS_ID(sw_txhdr->desc.host.mumimo_info);
+    group_id = RWNX_MUMIMO_INFO_GROUP_ID(sw_txhdr->desc.host.mumimo_info);
+
+    if ((txq->idx != TXQ_INACTIVE) &&
+        (txq->pkt_pushed[user] == 1) &&
+        (txq->status & RWNX_TXQ_STOP_MU_POS))
+        rwnx_txq_start(txq, RWNX_TXQ_STOP_MU_POS);
+
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+    if (txq->pkt_pushed[user])
+        txq->pkt_pushed[user]--;
+
+    hwq->need_processing = true;
+    rwnx_hw->stats.cfm_balance[hwq->id]--;
+}
+
+/******************************************************************************
+ * HWQ processing
+ *****************************************************************************/
+static inline
+bool rwnx_txq_take_mu_lock(struct rwnx_hw *rwnx_hw)
+{
+    bool res = false;
+#ifdef CONFIG_RWNX_MUMIMO_TX
+    if (rwnx_hw->mod_params->mutx)
+        res = (down_trylock(&rwnx_hw->mu.lock) == 0);
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+    return res;
+}
+
+static inline
+void rwnx_txq_release_mu_lock(struct rwnx_hw *rwnx_hw)
+{
+#ifdef CONFIG_RWNX_MUMIMO_TX
+    up(&rwnx_hw->mu.lock);
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+}
+
+static inline
+void rwnx_txq_set_mu_info(struct rwnx_hw *rwnx_hw, struct rwnx_txq *txq,
+                          int group_id, int pos)
+{
+#ifdef CONFIG_RWNX_MUMIMO_TX
+#ifdef CREATE_TRACE_POINTS
+    trace_txq_select_mu_group(txq, group_id, pos);
+#endif
+    if (group_id) {
+        txq->mumimo_info = group_id | (pos << 6);
+        rwnx_mu_set_active_group(rwnx_hw, group_id);
+    } else
+        txq->mumimo_info = 0;
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+}
+
+static inline
+s8 rwnx_txq_get_credits(struct rwnx_txq *txq)
+{
+    s8 cred = txq->credits;
+    /* if destination is in PS mode, push_limit indicates the maximum
+       number of packet that can be pushed on this txq. */
+    if (txq->push_limit && (cred > txq->push_limit)) {
+        cred = txq->push_limit;
+    }
+    return cred;
+}
+
+/**
+ * skb_queue_extract - Extract buffer from skb list
+ *
+ * @list: List of skb to extract from
+ * @head: List of skb to append to
+ * @nb_elt: Number of skb to extract
+ *
+ * extract the first @nb_elt of @list and append them to @head
+ * It is assume that:
+ * - @list contains more that @nb_elt
+ * - There is no need to take @list nor @head lock to modify them
+ */
+static inline void skb_queue_extract(struct sk_buff_head *list,
+                                     struct sk_buff_head *head, int nb_elt)
+{
+    int i;
+    struct sk_buff *first, *last, *ptr;
+
+    first = ptr = list->next;
+    for (i = 0; i < nb_elt; i++) {
+        ptr = ptr->next;
+    }
+    last = ptr->prev;
+
+    /* unlink nb_elt in list */
+    list->qlen -= nb_elt;
+    list->next = ptr;
+    ptr->prev = (struct sk_buff *)list;
+
+    /* append nb_elt at end of head */
+    head->qlen += nb_elt;
+    last->next = (struct sk_buff *)head;
+    head->prev->next = first;
+    first->prev = head->prev;
+    head->prev = last;
+}
+
+
+#ifdef CONFIG_MAC80211_TXQ
+/**
+ * rwnx_txq_mac80211_dequeue - Dequeue buffer from mac80211 txq and
+ *                             add them to push list
+ *
+ * @rwnx_hw: Main driver data
+ * @sk_list: List of buffer to push (initialized without lock)
+ * @txq: TXQ to dequeue buffers from
+ * @max: Max number of buffer to dequeue
+ *
+ * Dequeue buffer from mac80211 txq, prepare them for transmission and chain them
+ * to the list of buffer to push.
+ *
+ * @return true if no more buffer are queued in mac80211 txq and false otherwise.
+ */
+static bool rwnx_txq_mac80211_dequeue(struct rwnx_hw *rwnx_hw,
+                                      struct sk_buff_head *sk_list,
+                                      struct rwnx_txq *txq, int max)
+{
+    struct ieee80211_txq *mac_txq;
+    struct sk_buff *skb;
+    unsigned long mac_txq_len;
+
+    if (txq->nb_ready_mac80211 == NOT_MAC80211_TXQ)
+        return true;
+
+    mac_txq = container_of((void *)txq, struct ieee80211_txq, drv_priv);
+
+    for (; max > 0; max--) {
+        skb = rwnx_tx_dequeue_prep(rwnx_hw, mac_txq);
+        if (skb == NULL)
+            return true;
+
+        __skb_queue_tail(sk_list, skb);
+    }
+
+    /* re-read mac80211 txq current length.
+       It is mainly for debug purpose to trace dropped packet. There is no
+       problems to have nb_ready_mac80211 != actual mac80211 txq length */
+    ieee80211_txq_get_depth(mac_txq, &mac_txq_len, NULL);
+#ifdef CREATE_TRACE_POINTS
+    if (txq->nb_ready_mac80211 > mac_txq_len)
+        trace_txq_drop(txq, txq->nb_ready_mac80211 - mac_txq_len);
+#endif
+    txq->nb_ready_mac80211 = mac_txq_len;
+
+    return (txq->nb_ready_mac80211 == 0);
+}
+#endif
+
+/**
+ * rwnx_txq_get_skb_to_push - Get list of buffer to push for one txq
+ *
+ * @rwnx_hw: main driver data
+ * @hwq: HWQ on wich buffers will be pushed
+ * @txq: TXQ to get buffers from
+ * @user: user postion to use
+ * @sk_list_push: list to update
+ *
+ *
+ * This function will returned a list of buffer to push for one txq.
+ * It will take into account the number of credit of the HWQ for this user
+ * position and TXQ (and push_limit).
+ * This allow to get a list that can be pushed without having to test for
+ * hwq/txq status after each push
+ *
+ * If a MU group has been selected for this txq, it will also update the
+ * counter for the group
+ *
+ * @return true if txq no longer have buffer ready after the ones returned.
+ *         false otherwise
+ */
+static
+bool rwnx_txq_get_skb_to_push(struct rwnx_hw *rwnx_hw, struct rwnx_hwq *hwq,
+                              struct rwnx_txq *txq, int user,
+                              struct sk_buff_head *sk_list_push)
+{
+    int nb_ready = skb_queue_len(&txq->sk_list);
+    int credits = rwnx_txq_get_credits(txq);
+    bool res = false;
+
+    __skb_queue_head_init(sk_list_push);
+
+    if (credits >= nb_ready) {
+        skb_queue_splice_init(&txq->sk_list, sk_list_push);
+#ifdef CONFIG_MAC80211_TXQ
+        res = rwnx_txq_mac80211_dequeue(rwnx_hw, sk_list_push, txq, credits - nb_ready);
+        credits = skb_queue_len(sk_list_push);
+#else
+        res = true;
+        credits = nb_ready;
+#endif
+    } else {
+        skb_queue_extract(&txq->sk_list, sk_list_push, credits);
+
+        /* When processing PS service period (i.e. push_limit != 0), no longer
+           process this txq if the buffers extracted will complete the SP for
+           this txq */
+        if (txq->push_limit && (credits == txq->push_limit))
+            res = true;
+    }
+
+    rwnx_mu_set_active_sta(rwnx_hw, rwnx_txq_2_sta(txq), credits);
+
+    return res;
+}
+
+/**
+ * rwnx_txq_select_user - Select User queue for a txq
+ *
+ * @rwnx_hw: main driver data
+ * @mu_lock: true is MU lock is taken
+ * @txq: TXQ to select MU group for
+ * @hwq: HWQ for the TXQ
+ * @user: Updated with user position selected
+ *
+ * @return false if it is no possible to process this txq.
+ *         true otherwise
+ *
+ * This function selects the MU group to use for a TXQ.
+ * The selection is done as follow:
+ *
+ * - return immediately for STA that don't belongs to any group and select
+ *   group 0 / user 0
+ *
+ * - If MU tx is disabled (by user mutx_on, or because mu group are being
+ *   updated !mu_lock), select group 0 / user 0
+ *
+ * - Use the best group selected by @rwnx_mu_group_sta_select.
+ *
+ *   Each time a group is selected (except for the first case where sta
+ *   doesn't belongs to a MU group), the function checks that no buffer is
+ *   pending for this txq on another user position. If this is the case stop
+ *   the txq (RWNX_TXQ_STOP_MU_POS) and return false.
+ *
+ */
+static
+bool rwnx_txq_select_user(struct rwnx_hw *rwnx_hw, bool mu_lock,
+                          struct rwnx_txq *txq, struct rwnx_hwq *hwq, int *user)
+{
+    int pos = 0;
+#ifdef CONFIG_RWNX_MUMIMO_TX
+    int id, group_id = 0;
+    struct rwnx_sta *sta = rwnx_txq_2_sta(txq);
+
+    /* for sta that belong to no group return immediately */
+    if (!sta || !sta->group_info.cnt)
+        goto end;
+
+    /* If MU is disabled, need to check user */
+    if (!rwnx_hw->mod_params->mutx_on || !mu_lock)
+        goto check_user;
+
+    /* Use the "best" group selected */
+    group_id = sta->group_info.group;
+
+    if (group_id > 0)
+        pos = rwnx_mu_group_sta_get_pos(rwnx_hw, sta, group_id);
+
+  check_user:
+    /* check that we can push on this user position */
+#if CONFIG_USER_MAX == 2
+    id = (pos + 1) & 0x1;
+    if (txq->pkt_pushed[id]) {
+        rwnx_txq_stop(txq, RWNX_TXQ_STOP_MU_POS);
+        return false;
+    }
+
+#else
+    for (id = 0 ; id < CONFIG_USER_MAX ; id++) {
+        if (id != pos && txq->pkt_pushed[id]) {
+            rwnx_txq_stop(txq, RWNX_TXQ_STOP_MU_POS);
+            return false;
+        }
+    }
+#endif
+
+  end:
+    rwnx_txq_set_mu_info(rwnx_hw, txq, group_id, pos);
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+    *user = pos;
+    return true;
+}
+
+
+/**
+ * rwnx_hwq_process - Process one HW queue list
+ *
+ * @rwnx_hw: Driver main data
+ * @hw_queue: HW queue index to process
+ *
+ * The function will iterate over all the TX queues linked in this HW queue
+ * list. For each TX queue, push as many buffers as possible in the HW queue.
+ * (NB: TX queue have at least 1 buffer, otherwise it wouldn't be in the list)
+ * - If TX queue no longer have buffer, remove it from the list and check next
+ *   TX queue
+ * - If TX queue no longer have credits or has a push_limit (PS mode) and it
+ *   is reached , remove it from the list and check next TX queue
+ * - If HW queue is full, update list head to start with the next TX queue on
+ *   next call if current TX queue already pushed "too many" pkt in a row, and
+ *   return
+ *
+ * To be called when HW queue list is modified:
+ * - when a buffer is pushed on a TX queue
+ * - when new credits are received
+ * - when a STA returns from Power Save mode or receives traffic request.
+ * - when Channel context change
+ *
+ * To be called with tx_lock hold
+ */
+#define ALL_HWQ_MASK  ((1 << CONFIG_USER_MAX) - 1)
+
+void rwnx_hwq_process(struct rwnx_hw *rwnx_hw, struct rwnx_hwq *hwq)
+{
+    struct rwnx_txq *txq, *next;
+    int user, credit_map = 0;
+    bool mu_enable;
+#ifdef CREATE_TRACE_POINTS
+    trace_process_hw_queue(hwq);
+#endif
+    hwq->need_processing = false;
+
+    mu_enable = rwnx_txq_take_mu_lock(rwnx_hw);
+    if (!mu_enable)
+        credit_map = ALL_HWQ_MASK - 1;
+
+    list_for_each_entry_safe(txq, next, &hwq->list, sched_list) {
+        struct rwnx_txhdr *txhdr = NULL;
+        struct sk_buff_head sk_list_push;
+        struct sk_buff *skb;
+        bool txq_empty;
+#ifdef CREATE_TRACE_POINTS
+        trace_process_txq(txq);
+#endif
+        /* sanity check for debug */
+        BUG_ON(!(txq->status & RWNX_TXQ_IN_HWQ_LIST));
+	if(txq->idx == TXQ_INACTIVE){
+		printk("%s txq->idx == TXQ_INACTIVE \r\n", __func__);
+		continue;
+	}
+        BUG_ON(txq->idx == TXQ_INACTIVE);
+        BUG_ON(txq->credits <= 0);
+        BUG_ON(!rwnx_txq_skb_ready(txq));
+
+        if (!rwnx_txq_select_user(rwnx_hw, mu_enable, txq, hwq, &user))
+            continue;
+
+        txq_empty = rwnx_txq_get_skb_to_push(rwnx_hw, hwq, txq, user,
+                                             &sk_list_push);
+
+        while ((skb = __skb_dequeue(&sk_list_push)) != NULL) {
+            txhdr = (struct rwnx_txhdr *)skb->data;
+            rwnx_tx_push(rwnx_hw, txhdr, 0);
+        }
+
+        if (txq_empty) {
+            rwnx_txq_del_from_hw_list(txq);
+            txq->pkt_sent = 0;
+        } else if (rwnx_txq_is_scheduled(txq)) {
+            /* txq not empty,
+               - To avoid starving need to process other txq in the list
+               - For better aggregation, need to send "as many consecutive
+               pkt as possible" for he same txq
+               ==> Add counter to trigger txq switch
+            */
+            if (txq->pkt_sent > hwq->size) {
+                txq->pkt_sent = 0;
+                list_rotate_left(&hwq->list);
+            }
+        }
+
+#ifdef CONFIG_RWNX_FULLMAC
+        /* Unable to complete PS traffic request because of hwq credit */
+        if (txq->push_limit && txq->sta) {
+            if (txq->ps_id == LEGACY_PS_ID) {
+                /* for legacy PS abort SP and wait next ps-poll */
+                txq->sta->ps.sp_cnt[txq->ps_id] -= txq->push_limit;
+                txq->push_limit = 0;
+            }
+            /* for u-apsd need to complete the SP to send EOSP frame */
+        }
+#ifndef CONFIG_ONE_TXQ
+        /* restart netdev queue if number of queued buffer is below threshold */
+#ifdef CONFIG_TX_NETIF_FLOWCTRL
+		if (unlikely(txq->status & RWNX_TXQ_NDEV_FLOW_CTRL) &&
+			(skb_queue_len(&txq->sk_list) < RWNX_NDEV_FLOW_CTRL_RESTART)) {
+#else
+        if (unlikely(txq->status & RWNX_TXQ_NDEV_FLOW_CTRL) &&
+            skb_queue_len(&txq->sk_list) < RWNX_NDEV_FLOW_CTRL_RESTART) {
+#endif
+            txq->status &= ~RWNX_TXQ_NDEV_FLOW_CTRL;
+            netif_wake_subqueue(txq->ndev, txq->ndev_idx);
+#ifdef CREATE_TRACE_POINTS
+            trace_txq_flowctrl_restart(txq);
+#endif
+        }
+#endif /* CONFIG_ONE_TXQ */
+#endif /* CONFIG_RWNX_FULLMAC */
+    }
+
+
+    if (mu_enable)
+        rwnx_txq_release_mu_lock(rwnx_hw);
+}
+
+/**
+ * rwnx_hwq_process_all - Process all HW queue list
+ *
+ * @rwnx_hw: Driver main data
+ *
+ * Loop over all HWQ, and process them if needed
+ * To be called with tx_lock hold
+ */
+void rwnx_hwq_process_all(struct rwnx_hw *rwnx_hw)
+{
+    int id;
+
+    rwnx_mu_group_sta_select(rwnx_hw);
+
+    for (id = ARRAY_SIZE(rwnx_hw->hwq) - 1; id >= 0 ; id--) {
+        if (rwnx_hw->hwq[id].need_processing) {
+            rwnx_hwq_process(rwnx_hw, &rwnx_hw->hwq[id]);
+        }
+    }
+}
+
+/**
+ * rwnx_hwq_init - Initialize all hwq structures
+ *
+ * @rwnx_hw: Driver main data
+ *
+ */
+void rwnx_hwq_init(struct rwnx_hw *rwnx_hw)
+{
+    int i, j;
+
+    for (i = 0; i < ARRAY_SIZE(rwnx_hw->hwq); i++) {
+        struct rwnx_hwq *hwq = &rwnx_hw->hwq[i];
+
+        hwq->id = i;
+        hwq->size = nx_txdesc_cnt[i];
+        INIT_LIST_HEAD(&hwq->list);
+
+    }
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_txq.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_txq.h
new file mode 100755
index 0000000..5f72dd7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_txq.h
@@ -0,0 +1,382 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_txq.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+#ifndef _RWNX_TXQ_H_
+#define _RWNX_TXQ_H_
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/ieee80211.h>
+
+#ifdef CONFIG_RWNX_FULLMAC
+/**
+ * Fullmac TXQ configuration:
+ *  - STA: 1 TXQ per TID (limited to 8)
+ *         1 TXQ for bufferable MGT frames
+ *  - VIF: 1 TXQ for Multi/Broadcast +
+ *         1 TXQ for MGT for unknown STAs or non-bufferable MGT frames
+ *  - 1 TXQ for offchannel transmissions
+ *
+ *
+ * Txq mapping looks like
+ * for NX_REMOTE_STA_MAX=10 and NX_VIRT_DEV_MAX=4
+ *
+ * | TXQ | NDEV_ID | VIF |   STA |  TID | HWQ |
+ * |-----+---------+-----+-------+------+-----|-
+ * |   0 |       0 |     |     0 |    0 |   1 | 9 TXQ per STA
+ * |   1 |       1 |     |     0 |    1 |   0 | (8 data + 1 mgmt)
+ * |   2 |       2 |     |     0 |    2 |   0 |
+ * |   3 |       3 |     |     0 |    3 |   1 |
+ * |   4 |       4 |     |     0 |    4 |   2 |
+ * |   5 |       5 |     |     0 |    5 |   2 |
+ * |   6 |       6 |     |     0 |    6 |   3 |
+ * |   7 |       7 |     |     0 |    7 |   3 |
+ * |   8 |     N/A |     |     0 | MGMT |   3 |
+ * |-----+---------+-----+-------+------+-----|-
+ * | ... |         |     |       |      |     | Same for all STAs
+ * |-----+---------+-----+-------+------+-----|-
+ * |  90 |      80 |   0 | BC/MC |    0 | 1/4 | 1 TXQ for BC/MC per VIF
+ * | ... |         |     |       |      |     |
+ * |  93 |      80 |   3 | BC/MC |    0 | 1/4 |
+ * |-----+---------+-----+-------+------+-----|-
+ * |  94 |     N/A |   0 |   N/A | MGMT |   3 | 1 TXQ for unknown STA per VIF
+ * | ... |         |     |       |      |     |
+ * |  97 |     N/A |   3 |   N/A | MGMT |   3 |
+ * |-----+---------+-----+-------+------+-----|-
+ * |  98 |     N/A |     |   N/A | MGMT |   3 | 1 TXQ for offchannel frame
+ */
+#define NX_NB_TID_PER_STA 8
+#define NX_NB_TXQ_PER_STA (NX_NB_TID_PER_STA + 1)
+#define NX_NB_TXQ_PER_VIF 2
+#define NX_NB_TXQ ((NX_NB_TXQ_PER_STA * NX_REMOTE_STA_MAX) +    \
+                   (NX_NB_TXQ_PER_VIF * NX_VIRT_DEV_MAX) + 1)
+
+#define NX_FIRST_VIF_TXQ_IDX (NX_REMOTE_STA_MAX * NX_NB_TXQ_PER_STA)
+#define NX_FIRST_BCMC_TXQ_IDX  NX_FIRST_VIF_TXQ_IDX
+#define NX_FIRST_UNK_TXQ_IDX  (NX_FIRST_BCMC_TXQ_IDX + NX_VIRT_DEV_MAX)
+
+#define NX_OFF_CHAN_TXQ_IDX (NX_FIRST_VIF_TXQ_IDX +                     \
+                             (NX_VIRT_DEV_MAX * NX_NB_TXQ_PER_VIF))
+#define NX_BCMC_TXQ_TYPE 0
+#define NX_UNK_TXQ_TYPE  1
+
+/**
+ * Each data TXQ is a netdev queue. TXQ to send MGT are not data TXQ as
+ * they did not recieved buffer from netdev interface.
+ * Need to allocate the maximum case.
+ * AP : all STAs + 1 BC/MC
+ */
+#define NX_NB_NDEV_TXQ ((NX_NB_TID_PER_STA * NX_REMOTE_STA_MAX) + 1 )
+#define NX_BCMC_TXQ_NDEV_IDX (NX_NB_TID_PER_STA * NX_REMOTE_STA_MAX)
+#define NX_STA_NDEV_IDX(tid, sta_idx) ((tid) + (sta_idx) * NX_NB_TID_PER_STA)
+#define NDEV_NO_TXQ 0xffff
+#if (NX_NB_NDEV_TXQ >= NDEV_NO_TXQ)
+#error("Need to increase struct rwnx_txq->ndev_idx size")
+#endif
+
+/* stop netdev queue when number of queued buffers if greater than this  */
+#define RWNX_NDEV_FLOW_CTRL_STOP    64
+/* restart netdev queue when number of queued buffers is lower than this */
+#define RWNX_NDEV_FLOW_CTRL_RESTART 64
+
+#endif /*  CONFIG_RWNX_FULLMAC */
+
+#define TXQ_INACTIVE 0xffff
+#if (NX_NB_TXQ >= TXQ_INACTIVE)
+#error("Need to increase struct rwnx_txq->idx size")
+#endif
+
+#define NX_TXQ_INITIAL_CREDITS 64
+
+/**
+ * TXQ tid sorted by decreasing priority
+ */
+extern const int nx_tid_prio[NX_NB_TID_PER_STA];
+
+/**
+ * struct rwnx_hwq - Structure used to save information relative to
+ *                   an AC TX queue (aka HW queue)
+ * @list: List of TXQ, that have buffers ready for this HWQ
+ * @credits: available credit for the queue (i.e. nb of buffers that
+ *           can be pushed to FW )
+ * @id Id of the HWQ among RWNX_HWQ_....
+ * @size size of the queue
+ * @need_processing Indicate if hwq should be processed
+ * @len number of packet ready to be pushed to fw for this HW queue
+ * @len_stop threshold to stop mac80211(i.e. netdev) queues. Stop queue when
+ *           driver has more than @len_stop packets ready.
+ * @len_start threshold to wake mac8011 queues. Wake queue when driver has
+ *            less than @len_start packets ready.
+ */
+struct rwnx_hwq {
+    struct list_head list;
+    u8 size;
+    u8 id;
+    bool need_processing;
+};
+
+/**
+ * enum rwnx_push_flags - Flags of pushed buffer
+ *
+ * @RWNX_PUSH_RETRY Pushing a buffer for retry
+ * @RWNX_PUSH_IMMEDIATE Pushing a buffer without queuing it first
+ */
+enum rwnx_push_flags {
+    RWNX_PUSH_RETRY  = BIT(0),
+    RWNX_PUSH_IMMEDIATE = BIT(1),
+};
+
+/**
+ * enum rwnx_txq_flags - TXQ status flag
+ *
+ * @RWNX_TXQ_IN_HWQ_LIST: The queue is scheduled for transmission
+ * @RWNX_TXQ_STOP_FULL: No more credits for the queue
+ * @RWNX_TXQ_STOP_CSA: CSA is in progress
+ * @RWNX_TXQ_STOP_STA_PS: Destiniation sta is currently in power save mode
+ * @RWNX_TXQ_STOP_VIF_PS: Vif owning this queue is currently in power save mode
+ * @RWNX_TXQ_STOP_CHAN: Channel of this queue is not the current active channel
+ * @RWNX_TXQ_STOP_MU_POS: TXQ is stopped waiting for all the buffers pushed to
+ *                       fw to be confirmed
+ * @RWNX_TXQ_STOP: All possible reason to have a txq stopped
+ * @RWNX_TXQ_NDEV_FLOW_CTRL: associated netdev queue is currently stopped.
+ *                          Note: when a TXQ is flowctrl it is NOT stopped
+ */
+enum rwnx_txq_flags {
+    RWNX_TXQ_IN_HWQ_LIST  = BIT(0),
+    RWNX_TXQ_STOP_FULL    = BIT(1),
+    RWNX_TXQ_STOP_CSA     = BIT(2),
+    RWNX_TXQ_STOP_STA_PS  = BIT(3),
+    RWNX_TXQ_STOP_VIF_PS  = BIT(4),
+    RWNX_TXQ_STOP_CHAN    = BIT(5),
+    RWNX_TXQ_STOP_MU_POS  = BIT(6),
+    RWNX_TXQ_STOP         = (RWNX_TXQ_STOP_FULL | RWNX_TXQ_STOP_CSA |
+                             RWNX_TXQ_STOP_STA_PS | RWNX_TXQ_STOP_VIF_PS |
+                             RWNX_TXQ_STOP_CHAN) ,
+    RWNX_TXQ_NDEV_FLOW_CTRL = BIT(7),
+};
+
+
+/**
+ * struct rwnx_txq - Structure used to save information relative to
+ *                   a RA/TID TX queue
+ *
+ * @idx: Unique txq idx. Set to TXQ_INACTIVE if txq is not used.
+ * @status: bitfield of @rwnx_txq_flags.
+ * @credits: available credit for the queue (i.e. nb of buffers that
+ *           can be pushed to FW).
+ * @pkt_sent: number of consecutive pkt sent without leaving HW queue list
+ * @pkt_pushed: number of pkt currently pending for transmission confirmation
+ * @sched_list: list node for HW queue schedule list (rwnx_hwq.list)
+ * @sk_list: list of buffers to push to fw
+ * @last_retry_skb: pointer on the last skb in @sk_list that is a retry.
+ *                  (retry skb are stored at the beginning of the list)
+ *                  NULL if no retry skb is queued in @sk_list
+ * @nb_retry: Number of retry packet queued.
+ * @hwq: Pointer on the associated HW queue.
+ * @push_limit: number of packet to push before removing the txq from hwq list.
+ *              (we always have push_limit < skb_queue_len(sk_list))
+ * @tid: TID
+ *
+ * FULLMAC specific
+ * @ps_id: Index to use for Power save mode (LEGACY or UAPSD)
+ * @ndev_idx: txq idx from netdev point of view (0xFF for non netdev queue)
+ * @ndev: pointer to ndev of the corresponding vif
+ * @amsdu: pointer to rwnx_sw_txhdr of the first subframe of the A-MSDU.
+ *         NULL if no A-MSDU frame is in construction
+ * @amsdu_len: Maximum size allowed for an A-MSDU. 0 means A-MSDU not allowed
+ */
+struct rwnx_txq {
+    u16 idx;
+    u8 status;
+    s8 credits;
+    u8 pkt_sent;
+    u8 pkt_pushed[CONFIG_USER_MAX];
+    struct list_head sched_list;
+    struct sk_buff_head sk_list;
+    struct sk_buff *last_retry_skb;
+    struct rwnx_hwq *hwq;
+    int nb_retry;
+    u8 push_limit;
+    u8 tid;
+#ifdef CONFIG_MAC80211_TXQ
+    unsigned long nb_ready_mac80211;
+#endif
+#ifdef CONFIG_RWNX_FULLMAC
+    struct rwnx_sta *sta;
+    u8 ps_id;
+    u16 ndev_idx;
+    struct net_device *ndev;
+#ifdef CONFIG_RWNX_AMSDUS_TX
+    struct rwnx_sw_txhdr *amsdu;
+    u16 amsdu_len;
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+#endif /* CONFIG_RWNX_FULLMAC */
+#ifdef CONFIG_RWNX_MUMIMO_TX
+    u8 mumimo_info;
+#endif
+};
+
+struct rwnx_sta;
+struct rwnx_vif;
+struct rwnx_hw;
+struct rwnx_sw_txhdr;
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+#define RWNX_TXQ_GROUP_ID(txq) ((txq)->mumimo_info & 0x3f)
+#define RWNX_TXQ_POS_ID(txq)   (((txq)->mumimo_info >> 6) & 0x3)
+#else
+#define RWNX_TXQ_GROUP_ID(txq) 0
+#define RWNX_TXQ_POS_ID(txq)   0
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+static inline bool rwnx_txq_is_stopped(struct rwnx_txq *txq)
+{
+    return (txq->status & RWNX_TXQ_STOP);
+}
+
+static inline bool rwnx_txq_is_full(struct rwnx_txq *txq)
+{
+    return (txq->status & RWNX_TXQ_STOP_FULL);
+}
+
+static inline bool rwnx_txq_is_scheduled(struct rwnx_txq *txq)
+{
+    return (txq->status & RWNX_TXQ_IN_HWQ_LIST);
+}
+
+/**
+ * foreach_sta_txq - Macro to iterate over all TXQ of a STA in increasing
+ *                   TID order
+ *
+ * @sta: pointer to rwnx_sta
+ * @txq: pointer to rwnx_txq updated with the next TXQ at each iteration
+ * @tid: int updated with the TXQ tid at each iteration
+ * @rwnx_hw: main driver data
+ */
+#ifdef CONFIG_MAC80211_TXQ
+#define foreach_sta_txq(sta, txq, tid, rwnx_hw)                         \
+    for (tid = 0, txq = rwnx_txq_sta_get(sta, 0);                       \
+         tid < NX_NB_TXQ_PER_STA;                                       \
+         tid++, txq = rwnx_txq_sta_get(sta, tid))
+
+#elif defined(CONFIG_RWNX_FULLMAC) /* CONFIG_RWNX_FULLMAC */
+#define foreach_sta_txq(sta, txq, tid, rwnx_hw)                          \
+    for (tid = 0, txq = rwnx_txq_sta_get(sta, 0, rwnx_hw);               \
+         tid < (is_multicast_sta(sta->sta_idx) ? 1 : NX_NB_TXQ_PER_STA); \
+         tid++, txq++)
+
+#endif
+
+/**
+ * foreach_sta_txq_prio - Macro to iterate over all TXQ of a STA in
+ *                        decreasing priority order
+ *
+ * @sta: pointer to rwnx_sta
+ * @txq: pointer to rwnx_txq updated with the next TXQ at each iteration
+ * @tid: int updated with the TXQ tid at each iteration
+ * @i: int updated with ieration count
+ * @rwnx_hw: main driver data
+ *
+ * Note: For fullmac txq for mgmt frame is skipped
+ */
+#ifdef CONFIG_RWNX_FULLMAC
+#define foreach_sta_txq_prio(sta, txq, tid, i, rwnx_hw)                          \
+    for (i = 0, tid = nx_tid_prio[0], txq = rwnx_txq_sta_get(sta, tid, rwnx_hw); \
+         i < NX_NB_TID_PER_STA;                                                  \
+         i++, tid = nx_tid_prio[i], txq = rwnx_txq_sta_get(sta, tid, rwnx_hw))
+#endif
+
+/**
+ * foreach_vif_txq - Macro to iterate over all TXQ of a VIF (in AC order)
+ *
+ * @vif: pointer to rwnx_vif
+ * @txq: pointer to rwnx_txq updated with the next TXQ at each iteration
+ * @ac:  int updated with the TXQ ac at each iteration
+ */
+#ifdef CONFIG_MAC80211_TXQ
+#define foreach_vif_txq(vif, txq, ac)                                   \
+    for (ac = RWNX_HWQ_BK, txq = rwnx_txq_vif_get(vif, ac);             \
+         ac < NX_NB_TXQ_PER_VIF;                                        \
+         ac++, txq = rwnx_txq_vif_get(vif, ac))
+
+#else
+#define foreach_vif_txq(vif, txq, ac)                                   \
+    for (ac = RWNX_HWQ_BK, txq = &vif->txqs[0];                         \
+         ac < NX_NB_TXQ_PER_VIF;                                        \
+         ac++, txq++)
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+struct rwnx_txq *rwnx_txq_sta_get(struct rwnx_sta *sta, u8 tid,
+                                  struct rwnx_hw * rwnx_hw);
+struct rwnx_txq *rwnx_txq_vif_get(struct rwnx_vif *vif, u8 type);
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/**
+ * rwnx_txq_vif_get_status - return status bits related to the vif
+ *
+ * @rwnx_vif: Pointer to vif structure
+ */
+static inline u8 rwnx_txq_vif_get_status(struct rwnx_vif *rwnx_vif)
+{
+    struct rwnx_txq *txq = rwnx_txq_vif_get(rwnx_vif, 0);
+    return (txq->status & (RWNX_TXQ_STOP_CHAN | RWNX_TXQ_STOP_VIF_PS));
+}
+
+void rwnx_txq_vif_init(struct rwnx_hw * rwnx_hw, struct rwnx_vif *vif,
+                       u8 status);
+void rwnx_txq_vif_deinit(struct rwnx_hw * rwnx_hw, struct rwnx_vif *vif);
+void rwnx_txq_sta_init(struct rwnx_hw * rwnx_hw, struct rwnx_sta *rwnx_sta,
+                       u8 status);
+void rwnx_txq_sta_deinit(struct rwnx_hw * rwnx_hw, struct rwnx_sta *rwnx_sta);
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_txq_unk_vif_init(struct rwnx_vif *rwnx_vif);
+void rwnx_txq_unk_vif_deinit(struct rwnx_vif *vif);
+void rwnx_txq_offchan_init(struct rwnx_vif *rwnx_vif);
+void rwnx_txq_offchan_deinit(struct rwnx_vif *rwnx_vif);
+void rwnx_txq_tdls_vif_init(struct rwnx_vif *rwnx_vif);
+void rwnx_txq_tdls_vif_deinit(struct rwnx_vif *vif);
+void rwnx_txq_tdls_sta_start(struct rwnx_vif *rwnx_vif, u16 reason,
+                             struct rwnx_hw *rwnx_hw);
+void rwnx_txq_tdls_sta_stop(struct rwnx_vif *rwnx_vif, u16 reason,
+                            struct rwnx_hw *rwnx_hw);
+#endif
+
+
+void rwnx_txq_add_to_hw_list(struct rwnx_txq *txq);
+void rwnx_txq_del_from_hw_list(struct rwnx_txq *txq);
+void rwnx_txq_stop(struct rwnx_txq *txq, u16 reason);
+void rwnx_txq_start(struct rwnx_txq *txq, u16 reason);
+void rwnx_txq_vif_start(struct rwnx_vif *vif, u16 reason,
+                        struct rwnx_hw *rwnx_hw);
+void rwnx_txq_vif_stop(struct rwnx_vif *vif, u16 reason,
+                       struct rwnx_hw *rwnx_hw);
+
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_txq_sta_start(struct rwnx_sta *sta, u16 reason,
+                        struct rwnx_hw *rwnx_hw);
+void rwnx_txq_sta_stop(struct rwnx_sta *sta, u16 reason,
+                       struct rwnx_hw *rwnx_hw);
+void rwnx_txq_offchan_start(struct rwnx_hw *rwnx_hw);
+void rwnx_txq_sta_switch_vif(struct rwnx_sta *sta, struct rwnx_vif *old_vif,
+                             struct rwnx_vif *new_vif);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+int rwnx_txq_queue_skb(struct sk_buff *skb, struct rwnx_txq *txq,
+                       struct rwnx_hw *rwnx_hw,  bool retry);
+void rwnx_txq_confirm_any(struct rwnx_hw *rwnx_hw, struct rwnx_txq *txq,
+                          struct rwnx_hwq *hwq, struct rwnx_sw_txhdr *sw_txhdr);
+void rwnx_txq_flush(struct rwnx_hw *rwnx_hw, struct rwnx_txq *txq);
+
+void rwnx_hwq_init(struct rwnx_hw *rwnx_hw);
+void rwnx_hwq_process(struct rwnx_hw *rwnx_hw, struct rwnx_hwq *hwq);
+void rwnx_hwq_process_all(struct rwnx_hw *rwnx_hw);
+
+#endif /* _RWNX_TXQ_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_utils.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_utils.c
new file mode 100755
index 0000000..b261330
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_utils.c
@@ -0,0 +1,29 @@
+/**
+ * rwnx_utils.c
+ *
+ * IPC utility function definitions
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ */
+#include "rwnx_utils.h"
+#include "rwnx_defs.h"
+#include "rwnx_rx.h"
+#include "rwnx_tx.h"
+#include "rwnx_msg_rx.h"
+#include "rwnx_debugfs.h"
+#include "rwnx_prof.h"
+#include "ipc_host.h"
+
+int rwnx_init_aic(struct rwnx_hw *rwnx_hw)
+{
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifdef AICWF_SDIO_SUPPORT
+	aicwf_sdio_host_init(&(rwnx_hw->sdio_env), NULL, NULL, rwnx_hw);
+#else
+	aicwf_usb_host_init(&(rwnx_hw->usb_env), NULL, NULL, rwnx_hw);
+#endif
+	rwnx_cmd_mgr_init(rwnx_hw->cmd_mgr);
+
+	return 0;
+}
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_utils.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_utils.h
new file mode 100755
index 0000000..d510bba
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_utils.h
@@ -0,0 +1,131 @@
+/**
+ * rwnx_ipc_utils.h
+ *
+ * IPC utility function declarations
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ */
+#ifndef _RWNX_IPC_UTILS_H_
+#define _RWNX_IPC_UTILS_H_
+
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/skbuff.h>
+
+#include "lmac_msg.h"
+
+#ifdef CONFIG_RWNX_DBG
+/*  #define RWNX_DBG(format, arg...) pr_warn(format, ## arg) */
+#define RWNX_DBG printk
+#else
+#define RWNX_DBG(a...) do {} while (0)
+#endif
+
+#define RWNX_FN_ENTRY_STR ">>> %s()\n", __func__
+
+enum rwnx_dev_flag {
+	RWNX_DEV_RESTARTING,
+	RWNX_DEV_STACK_RESTARTING,
+	RWNX_DEV_STARTED,
+};
+
+struct rwnx_hw;
+struct rwnx_sta;
+
+/**
+ * struct rwnx_ipc_elem - Generic IPC buffer of fixed size
+ *
+ * @addr: Host address of the buffer.
+ * @dma_addr: DMA address of the buffer.
+ */
+struct rwnx_ipc_elem {
+	void *addr;
+	dma_addr_t dma_addr;
+};
+
+/**
+ * struct rwnx_ipc_elem_pool - Generic pool of IPC buffers of fixed size
+ *
+ * @nb: Number of buffers currenlty allocated in the pool
+ * @buf: Array of buffers (size of array is @nb)
+ * @pool: DMA pool in which buffers have been allocated
+ */
+struct rwnx_ipc_elem_pool {
+	int nb;
+	struct rwnx_ipc_elem *buf;
+	struct dma_pool *pool;
+};
+
+/**
+ * struct rwnx_ipc_elem - Generic IPC buffer of variable size
+ *
+ * @addr: Host address of the buffer.
+ * @dma_addr: DMA address of the buffer.
+ * @size: Size, in bytes, of the buffer
+ */
+struct rwnx_ipc_elem_var {
+	void *addr;
+	dma_addr_t dma_addr;
+	size_t size;
+};
+
+/**
+ * struct rwnx_ipc_dbgdump_elem - IPC buffer for debug dump
+ *
+ * @mutex: Mutex to protect access to debug dump
+ * @buf: IPC buffer
+ */
+struct rwnx_ipc_dbgdump_elem {
+	struct mutex mutex;
+	struct rwnx_ipc_elem_var buf;
+};
+
+static const u32 rwnx_rxbuff_pattern = 0xCAFEFADE;
+
+/*
+ * Maximum Length of Radiotap header vendor specific data(in bytes)
+ */
+#define RADIOTAP_HDR_VEND_MAX_LEN   16
+
+/*
+ * Maximum Radiotap Header Length without vendor specific data (in bytes)
+ */
+#define RADIOTAP_HDR_MAX_LEN        80
+
+/*
+ * Unsupported HT Frame data length (in bytes)
+ */
+#define UNSUP_RX_VEC_DATA_LEN       2
+
+/**
+ * struct rwnx_ipc_skb_elem - IPC buffer for SKB element
+ *
+ * @skb: Pointer to the skb buffer allocated
+ * @dma_addr: DMA address of the data buffer fo skb
+ *
+ */
+struct rwnx_ipc_skb_elem {
+	struct sk_buff *skb;
+	dma_addr_t dma_addr;
+};
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+/* Maximum number of rx buffer the fw may use at the same time */
+#define RWNX_RXBUFF_MAX (64 * NX_REMOTE_STA_MAX)
+
+/**
+ * struct rwnx_ipc_rxbuf_elems - IPC buffers for RX
+ *
+ * @skb: Array of buffer push to FW.
+ * @idx: Index of the last pushed skb.(Use to find the next free entry quicker)
+ *
+ * Note: contrary to softmac version, dma_addr are stored inside skb->cb.
+ */
+struct rwnx_ipc_rxbuf_elems {
+	struct sk_buff *skb[RWNX_RXBUFF_MAX];
+	int idx;
+};
+
+#endif /* CONFIG_RWNX_FULLMAC */
+#endif /* _RWNX_IPC_UTILS_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_v7.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_v7.c
new file mode 100755
index 0000000..c8bcfe1
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_v7.c
@@ -0,0 +1,192 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_v7.c - Support for v7 platform
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#include "rwnx_v7.h"
+#include "rwnx_defs.h"
+#include "rwnx_irqs.h"
+#include "reg_access.h"
+#include "hal_desc.h"
+
+struct rwnx_v7
+{
+    u8 *pci_bar0_vaddr;
+    u8 *pci_bar1_vaddr;
+};
+
+static int rwnx_v7_platform_enable(struct rwnx_hw *rwnx_hw)
+{
+    int ret;
+
+    /* sched_setscheduler on ONESHOT threaded irq handler for BCNs ? */
+    ret = request_irq(rwnx_hw->plat->pci_dev->irq, rwnx_irq_hdlr, 0,
+                      "rwnx", rwnx_hw);
+    return ret;
+}
+
+static int rwnx_v7_platform_disable(struct rwnx_hw *rwnx_hw)
+{
+    free_irq(rwnx_hw->plat->pci_dev->irq, rwnx_hw);
+    return 0;
+}
+
+static void rwnx_v7_platform_deinit(struct rwnx_plat *rwnx_plat)
+{
+    #ifdef CONFIG_PCI
+    struct rwnx_v7 *rwnx_v7 = (struct rwnx_v7 *)rwnx_plat->priv;
+
+    pci_disable_device(rwnx_plat->pci_dev);
+    iounmap(rwnx_v7->pci_bar0_vaddr);
+    iounmap(rwnx_v7->pci_bar1_vaddr);
+    pci_release_regions(rwnx_plat->pci_dev);
+    pci_clear_master(rwnx_plat->pci_dev);
+    pci_disable_msi(rwnx_plat->pci_dev);
+    #endif
+    kfree(rwnx_plat);
+}
+
+static u8* rwnx_v7_get_address(struct rwnx_plat *rwnx_plat, int addr_name,
+                               unsigned int offset)
+{
+    struct rwnx_v7 *rwnx_v7 = (struct rwnx_v7 *)rwnx_plat->priv;
+
+    if (WARN(addr_name >= RWNX_ADDR_MAX, "Invalid address %d", addr_name))
+        return NULL;
+
+    if (addr_name == RWNX_ADDR_CPU)
+        return rwnx_v7->pci_bar0_vaddr + offset;
+    else
+        return rwnx_v7->pci_bar1_vaddr + offset;
+}
+
+static void rwnx_v7_ack_irq(struct rwnx_plat *rwnx_plat)
+{
+
+}
+
+static const u32 rwnx_v7_config_reg[] = {
+    NXMAC_DEBUG_PORT_SEL_ADDR,
+    SYSCTRL_DIAG_CONF_ADDR,
+    SYSCTRL_PHYDIAG_CONF_ADDR,
+    SYSCTRL_RIUDIAG_CONF_ADDR,
+    RF_V7_DIAGPORT_CONF1_ADDR,
+};
+
+static const u32 rwnx_v7_he_config_reg[] = {
+    SYSCTRL_DIAG_CONF0,
+    SYSCTRL_DIAG_CONF1,
+    SYSCTRL_DIAG_CONF2,
+    SYSCTRL_DIAG_CONF3,
+};
+
+static int rwnx_v7_get_config_reg(struct rwnx_plat *rwnx_plat, const u32 **list)
+{
+    u32 fpga_sign;
+
+    if (!list)
+        return 0;
+
+    fpga_sign = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+    if (__FPGA_TYPE(fpga_sign) == 0xc0ca) {
+        *list = rwnx_v7_he_config_reg;
+        return ARRAY_SIZE(rwnx_v7_he_config_reg);
+    } else {
+        *list = rwnx_v7_config_reg;
+        return ARRAY_SIZE(rwnx_v7_config_reg);
+    }
+}
+
+
+/**
+ * rwnx_v7_platform_init - Initialize the DINI platform
+ *
+ * @pci_dev PCI device
+ * @rwnx_plat Pointer on struct rwnx_stat * to be populated
+ *
+ * @return 0 on success, < 0 otherwise
+ *
+ * Allocate and initialize a rwnx_plat structure for the dini platform.
+ */
+int rwnx_v7_platform_init(struct pci_dev *pci_dev, struct rwnx_plat **rwnx_plat)
+{
+    struct rwnx_v7 *rwnx_v7;
+    u16 pci_cmd;
+    int ret = 0;
+
+    *rwnx_plat = kzalloc(sizeof(struct rwnx_plat) + sizeof(struct rwnx_v7),
+                        GFP_KERNEL);
+    if (!*rwnx_plat)
+        return -ENOMEM;
+
+    rwnx_v7 = (struct rwnx_v7 *)(*rwnx_plat)->priv;
+
+    /* Hotplug fixups */
+    pci_read_config_word(pci_dev, PCI_COMMAND, &pci_cmd);
+    pci_cmd |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
+    pci_write_config_word(pci_dev, PCI_COMMAND, pci_cmd);
+    pci_write_config_byte(pci_dev, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES >> 2);
+
+    if ((ret = pci_enable_device(pci_dev))) {
+        dev_err(&(pci_dev->dev), "pci_enable_device failed\n");
+        goto out_enable;
+    }
+
+    pci_set_master(pci_dev);
+
+    if ((ret = pci_request_regions(pci_dev, KBUILD_MODNAME))) {
+        dev_err(&(pci_dev->dev), "pci_request_regions failed\n");
+        goto out_request;
+    }
+
+    #ifdef CONFIG_PCI
+    if (pci_enable_msi(pci_dev))
+    {
+        dev_err(&(pci_dev->dev), "pci_enable_msi failed\n");
+        goto out_msi;
+
+    }
+    #endif
+
+    if (!(rwnx_v7->pci_bar0_vaddr = (u8 *)pci_ioremap_bar(pci_dev, 0))) {
+        dev_err(&(pci_dev->dev), "pci_ioremap_bar(%d) failed\n", 0);
+        ret = -ENOMEM;
+        goto out_bar0;
+    }
+    if (!(rwnx_v7->pci_bar1_vaddr = (u8 *)pci_ioremap_bar(pci_dev, 1))) {
+        dev_err(&(pci_dev->dev), "pci_ioremap_bar(%d) failed\n", 1);
+        ret = -ENOMEM;
+        goto out_bar1;
+    }
+
+    (*rwnx_plat)->enable = rwnx_v7_platform_enable;
+    (*rwnx_plat)->disable = rwnx_v7_platform_disable;
+    (*rwnx_plat)->deinit = rwnx_v7_platform_deinit;
+    (*rwnx_plat)->get_address = rwnx_v7_get_address;
+    (*rwnx_plat)->ack_irq = rwnx_v7_ack_irq;
+    (*rwnx_plat)->get_config_reg = rwnx_v7_get_config_reg;
+
+    return 0;
+
+  out_bar1:
+    iounmap(rwnx_v7->pci_bar0_vaddr);
+  out_bar0:
+#ifdef CONFIG_PCI
+    pci_disable_msi(pci_dev);
+  out_msi:
+#endif
+    pci_release_regions(pci_dev);
+  out_request:
+#ifdef CONFIG_PCI
+    pci_clear_master(pci_dev);
+#endif
+    pci_disable_device(pci_dev);
+  out_enable:
+    kfree(*rwnx_plat);
+    return ret;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_v7.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_v7.h
new file mode 100755
index 0000000..8bb99df
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_v7.h
@@ -0,0 +1,20 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_v7.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_V7_H_
+#define _RWNX_V7_H_
+
+#include <linux/pci.h>
+#include "rwnx_platform.h"
+
+int rwnx_v7_platform_init(struct pci_dev *pci_dev,
+                          struct rwnx_plat **rwnx_plat);
+
+#endif /* _RWNX_V7_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_version.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_version.h
new file mode 100755
index 0000000..0018478
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_version.h
@@ -0,0 +1,12 @@
+#ifndef _RWNX_VERSION_H_
+#define _RWNX_VERSION_H_
+
+#include "rwnx_version_gen.h"
+
+static inline void rwnx_print_version(void)
+{
+	printk(RWNX_VERS_BANNER"\n");
+	printk("RELEASE_DATE:%s \r\n", RELEASE_DATE);
+}
+
+#endif /* _RWNX_VERSION_H_ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_version_gen.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_version_gen.h
new file mode 100755
index 0000000..45b8850
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/rwnx_version_gen.h
@@ -0,0 +1,4 @@
+#define RWNX_VERS_REV "241c091M (master)"
+#define RWNX_VERS_MOD "6.4.3.0"
+#define RWNX_VERS_BANNER "rwnx v6.4.3.0 - - 241c091M (master)"
+#define RELEASE_DATE "2022_0622_1326"
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/sdio_host.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/sdio_host.c
new file mode 100755
index 0000000..6cac5ec
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/sdio_host.c
@@ -0,0 +1,146 @@
+/**
+ * sdio_host.c
+ *
+ * SDIO host function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+
+#include "sdio_host.h"
+//#include "ipc_compat.h"
+#include "rwnx_tx.h"
+#include "rwnx_platform.h"
+
+/**
+ ****************************************************************************************
+ */
+void aicwf_sdio_host_init(struct sdio_host_env_tag *env,
+                  void *cb,
+                  void *shared_env_ptr,
+                  void *pthis)
+{
+    // Reset the environments
+
+    // Reset the Host environment
+    memset(env, 0, sizeof(struct sdio_host_env_tag));
+    // Save the pointer to the register base
+    env->pthis = pthis;
+}
+
+/**
+ ****************************************************************************************
+ */
+volatile struct txdesc_host *aicwf_sdio_host_txdesc_get(struct sdio_host_env_tag *env, const int queue_idx)
+{
+ //   struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+    volatile struct txdesc_host *txdesc_free;
+    uint32_t used_idx = env->txdesc_used_idx[queue_idx];
+    uint32_t free_idx = env->txdesc_free_idx[queue_idx];
+
+   // ASSERT_ERR(queue_idx < SDIO_TXQUEUE_CNT);
+   // ASSERT_ERR((free_idx - used_idx) <= SDIO_TXDESC_CNT);
+
+    // Check if a free descriptor is available
+    if (free_idx != (used_idx + SDIO_TXDESC_CNT))
+    {
+        // Get the pointer to the first free descriptor
+    //    txdesc_free = shared_env_ptr->txdesc[queue_idx] + (free_idx % IPC_TXDESC_CNT);
+    }
+    else
+    {
+        txdesc_free = NULL;
+    }
+
+    return txdesc_free;
+}
+
+/**
+ ****************************************************************************************
+ */
+void aicwf_sdio_host_txdesc_push(struct sdio_host_env_tag *env, const int queue_idx, const uint64_t host_id)
+{
+    //printk("push, %d, %d, 0x%llx \r\n", queue_idx, env->txdesc_free_idx[queue_idx], host_id);
+    // Save the host id in the environment
+    env->tx_host_id[queue_idx][env->txdesc_free_idx[queue_idx] % SDIO_TXDESC_CNT] = host_id;
+
+    // Increment the index
+    env->txdesc_free_idx[queue_idx]++;
+    if(env->txdesc_free_idx[queue_idx]==0x40000000)
+        env->txdesc_free_idx[queue_idx] = 0;
+}
+
+/**
+ ****************************************************************************************
+ */
+void aicwf_sdio_host_tx_cfm_handler(struct sdio_host_env_tag *env, u32 *data)
+{
+    u32 queue_idx  = 0;// data[0];
+    //struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)env->pthis;
+    struct sk_buff *skb = NULL;
+    struct rwnx_txhdr *txhdr;
+
+    // TX confirmation descriptors have been received
+   // REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_TXCFM);
+    //while (1)
+    {
+        // Get the used index and increase it. We do the increase before knowing if the
+        // current buffer is confirmed because the callback function may call the
+        // ipc_host_txdesc_get() in case flow control was enabled and the index has to be
+        // already at the good value to ensure that the test of FIFO full is correct
+        //uint32_t used_idx = env->txdesc_used_idx[queue_idx]++;
+	    uint32_t used_idx = data[1];
+        uint64_t host_id = env->tx_host_id[queue_idx][used_idx % SDIO_TXDESC_CNT];
+
+        // Reset the host id in the array
+        env->tx_host_id[queue_idx][used_idx % SDIO_TXDESC_CNT] = 0;
+
+        // call the external function to indicate that a TX packet is freed
+        if (host_id == 0)
+        {
+            // No more confirmations, so put back the used index at its initial value
+            env->txdesc_used_idx[queue_idx] = used_idx;
+            printk("ERROR:No more confirmations\r\n");
+            //break;
+        }
+        // set the cfm status
+        skb = (struct sk_buff *)(uint64_t)host_id;
+        txhdr = (struct rwnx_txhdr *)skb->data;
+        txhdr->hw_hdr.cfm.status = (union rwnx_hw_txstatus)data[0];
+        printk("sdio_host_tx_cfm_handler:used_idx=%d, 0x%p, status=%x\r\n",used_idx, env->pthis, txhdr->hw_hdr.cfm.status.value);
+		if(txhdr->sw_hdr.is_dhcp == 1){
+			if(txhdr->hw_hdr.cfm.status.value)
+				printk("DHCP:%d\n", txhdr->hw_hdr.cfm.status.value);
+		}
+        //if (env->cb.send_data_cfm(env->pthis, host_id) != 0)
+        if (rwnx_txdatacfm(env->pthis, (void *)host_id) != 0)
+        {
+            // No more confirmations, so put back the used index at its initial value
+            env->txdesc_used_idx[queue_idx] = used_idx;
+            env->tx_host_id[queue_idx][used_idx % SDIO_TXDESC_CNT] = host_id;
+            // and exit the loop
+            printk("ERROR:rwnx_txdatacfm,\r\n");
+          //  break;
+        }
+
+    }
+}
+
+int aicwf_rwnx_sdio_platform_init(struct aic_sdio_dev *sdiodev)
+{
+    struct rwnx_plat *rwnx_plat = NULL;
+    void *drvdata;
+    int ret = -ENODEV;
+
+    rwnx_plat = kzalloc(sizeof(struct rwnx_plat), GFP_KERNEL);
+
+    if (!rwnx_plat) {
+        return -ENOMEM;
+    }
+
+	rwnx_plat->sdiodev = sdiodev;
+    ret = rwnx_platform_init(rwnx_plat, &drvdata);
+
+    return ret;
+}
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/sdio_host.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/sdio_host.h
new file mode 100755
index 0000000..2d6f0f8
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/sdio_host.h
@@ -0,0 +1,42 @@
+/**
+ * sdio_host.h
+ *
+ * SDIO host function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+
+#ifndef _SDIO_HOST_H_
+#define _SDIO_HOST_H_
+
+#include "lmac_types.h"
+#include "aicwf_sdio.h"
+
+#define SDIO_TXQUEUE_CNT     NX_TXQ_CNT
+#define SDIO_TXDESC_CNT      (4 * NX_TXDESC_CNT)
+
+
+/// Definition of the IPC Host environment structure.
+struct sdio_host_env_tag
+{
+    // Index used that points to the first free TX desc
+    uint32_t txdesc_free_idx[SDIO_TXQUEUE_CNT];
+    // Index used that points to the first used TX desc
+    uint32_t txdesc_used_idx[SDIO_TXQUEUE_CNT];
+    // Array storing the currently pushed host ids, per IPC queue
+    uint64_t tx_host_id[SDIO_TXQUEUE_CNT][SDIO_TXDESC_CNT];
+
+    /// Pointer to the attached object (used in callbacks and register accesses)
+    void *pthis;
+};
+
+extern void aicwf_sdio_host_init(struct sdio_host_env_tag *env,
+                  void *cb, void *shared_env_ptr, void *pthis);
+
+extern void aicwf_sdio_host_txdesc_push(struct sdio_host_env_tag *env, const int queue_idx, const uint64_t host_id);
+
+extern void aicwf_sdio_host_tx_cfm_handler(struct sdio_host_env_tag *env, u32 *data);
+extern int aicwf_rwnx_sdio_platform_init(struct aic_sdio_dev *sdiodev);
+
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/usb_host.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/usb_host.c
new file mode 100755
index 0000000..7560316
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/usb_host.c
@@ -0,0 +1,125 @@
+/**
+ * usb_host.c
+ *
+ * USB host function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+
+#include "usb_host.h"
+//#include "ipc_compat.h"
+#include "rwnx_tx.h"
+#include "rwnx_platform.h"
+
+/**
+ ****************************************************************************************
+ */
+void aicwf_usb_host_init(struct usb_host_env_tag *env,
+                  void *cb,
+                  void *shared_env_ptr,
+                  void *pthis)
+{
+    // Reset the environments
+
+    // Reset the Host environment
+    memset(env, 0, sizeof(struct usb_host_env_tag));
+    // Save the pointer to the register base
+    env->pthis = pthis;
+}
+
+/**
+ ****************************************************************************************
+ */
+void aicwf_usb_host_txdesc_push(struct usb_host_env_tag *env, const int queue_idx, const unsigned long host_id)
+{
+    //printk("push, %d, %d, 0x%llx \r\n", queue_idx, env->txdesc_free_idx[queue_idx], host_id);
+    // Save the host id in the environment
+    env->tx_host_id[queue_idx][env->txdesc_free_idx[queue_idx] % USB_TXDESC_CNT] = host_id;
+
+    // Increment the index
+    env->txdesc_free_idx[queue_idx]++;
+    if(env->txdesc_free_idx[queue_idx]==0x40000000)
+        env->txdesc_free_idx[queue_idx] = 0;
+}
+
+/**
+ ****************************************************************************************
+ */
+void aicwf_usb_host_tx_cfm_handler(struct usb_host_env_tag *env, u32 *data)
+{
+    u32 queue_idx  = 0;//data[0];
+    //struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)env->pthis;
+    struct sk_buff *skb = NULL;
+    struct rwnx_txhdr *txhdr;
+    printk("%s enter \n", __func__);
+    //printk("sdio_host_tx_cfm_handler, %d, 0x%08x\r\n", queue_idx, data[1]);
+    // TX confirmation descriptors have been received
+   // REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_TXCFM);
+    //while (1)
+    {
+        // Get the used index and increase it. We do the increase before knowing if the
+        // current buffer is confirmed because the callback function may call the
+        // ipc_host_txdesc_get() in case flow control was enabled and the index has to be
+        // already at the good value to ensure that the test of FIFO full is correct
+        //uint32_t used_idx = env->txdesc_used_idx[queue_idx]++;
+        uint32_t used_idx = data[1];
+        unsigned long host_id = env->tx_host_id[queue_idx][used_idx % USB_TXDESC_CNT];
+
+        // Reset the host id in the array
+        env->tx_host_id[queue_idx][used_idx % USB_TXDESC_CNT] = 0;
+
+        // call the external function to indicate that a TX packet is freed
+        if (host_id == 0)
+        {
+            // No more confirmations, so put back the used index at its initial value
+            env->txdesc_used_idx[queue_idx] = used_idx;
+            printk("ERROR:No more confirmations\r\n");
+            return;
+            //break;
+        }
+        printk("usb_host_tx_cfm_handler:used_idx=%d, hostid=%p, 0x%p, status=%x\r\n",used_idx, (void *)host_id, env->pthis, data[0]);
+
+        // set the cfm status
+        skb = (struct sk_buff *)host_id;
+        txhdr = (struct rwnx_txhdr *)skb->data;
+        txhdr->hw_hdr.cfm.status = (union rwnx_hw_txstatus)data[0];
+        //txhdr->hw_hdr.status = data[1];
+        //if (env->cb.send_data_cfm(env->pthis, host_id) != 0)
+        if (rwnx_txdatacfm(env->pthis, (void *)host_id) != 0)
+        {
+            // No more confirmations, so put back the used index at its initial value
+            env->txdesc_used_idx[queue_idx] = used_idx;
+            env->tx_host_id[queue_idx][used_idx % USB_TXDESC_CNT] = host_id;
+            // and exit the loop
+            printk("ERROR:rwnx_txdatacfm,\r\n");
+          //  break;
+        }
+
+    }
+}
+
+int aicwf_rwnx_usb_platform_init(struct aic_usb_dev *usbdev)
+{
+    struct rwnx_plat *rwnx_plat = NULL;
+    void *drvdata;
+    int ret = -ENODEV;
+
+    rwnx_plat = kzalloc(sizeof(struct rwnx_plat), GFP_KERNEL);
+
+    if (!rwnx_plat)
+        return -ENOMEM;
+
+//    rwnx_plat->pci_dev = pci_dev;
+	rwnx_plat->usbdev = usbdev;
+
+    ret = rwnx_platform_init(rwnx_plat, &drvdata);
+#if 0
+    pci_set_drvdata(pci_dev, drvdata);
+
+    if (ret)
+        rwnx_plat->deinit(rwnx_plat);
+#endif
+    return ret;
+}
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/usb_host.h b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/usb_host.h
new file mode 100755
index 0000000..a766fd9
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/usb_host.h
@@ -0,0 +1,42 @@
+/**
+ * usb_host.h
+ *
+ * USB host function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+
+#ifndef _USB_HOST_H_
+#define _USB_HOST_H_
+
+#include "lmac_types.h"
+#include "aicwf_usb.h"
+
+#define USB_TXQUEUE_CNT     NX_TXQ_CNT
+#define USB_TXDESC_CNT      NX_TXDESC_CNT
+
+
+/// Definition of the IPC Host environment structure.
+struct usb_host_env_tag
+{
+    // Index used that points to the first free TX desc
+    uint32_t txdesc_free_idx[USB_TXQUEUE_CNT];
+    // Index used that points to the first used TX desc
+    uint32_t txdesc_used_idx[USB_TXQUEUE_CNT];
+    // Array storing the currently pushed host ids, per IPC queue
+    unsigned long tx_host_id[USB_TXQUEUE_CNT][USB_TXDESC_CNT];
+
+    /// Pointer to the attached object (used in callbacks and register accesses)
+    void *pthis;
+};
+
+extern void aicwf_usb_host_init(struct usb_host_env_tag *env,
+                  void *cb, void *shared_env_ptr, void *pthis);
+
+extern void aicwf_usb_host_txdesc_push(struct usb_host_env_tag *env, const int queue_idx, const unsigned long host_id);
+
+extern void aicwf_usb_host_tx_cfm_handler(struct usb_host_env_tag *env, u32 *data);
+extern int aicwf_rwnx_usb_platform_init(struct aic_usb_dev *usbdev);
+
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/wifi_dev_aic88.c b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/wifi_dev_aic88.c
new file mode 100755
index 0000000..3e06c1d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/net/wireless/aic8800d80l/wifi_dev_aic88.c
@@ -0,0 +1,122 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+
+#define WIFI_IOCTL_STOP   0x0
+#define WIFI_IOCTL_START 0x1
+#define WIFI_IOCTL_START_TESTMODE 0x3
+extern int rwnx_mod_init(void);
+extern void rwnx_mod_exit(void);
+extern int testmode;
+extern void aic8800_wifi_disable(int bval);
+extern void aic8800_wifi_re_enable(int bval);
+void dw_mci_rescan_card(unsigned id, unsigned insert);
+struct wifi_dev {
+	uint32_t dev_state;
+};
+
+struct wifi_dev *wifidev = NULL;
+static const char wifi_shortname[] = "wifi_device";
+
+static int wifi_dev_open(struct inode *ip, struct file *fp);
+static int wifi_dev_release(struct inode *ip, struct file *fp);
+static long wifi_dev_ioctl(struct file *fp, unsigned int cmd, unsigned long arg);
+
+/* file operations for volte device /dev/volte_device */
+static const struct file_operations wifi_dev_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = wifi_dev_ioctl,
+	.open = wifi_dev_open,
+	.release = wifi_dev_open,
+};
+
+static struct miscdevice wifi_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = wifi_shortname,
+	.fops = &wifi_dev_fops,
+};
+
+static long wifi_dev_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+	int ret = 0;
+	void __user *argp = (void __user *) arg;
+
+	switch (cmd) {
+	case WIFI_IOCTL_STOP: {
+	      printk(KERN_INFO "@@@exit Wlan@@@\n");
+		rwnx_mod_exit();
+		aic8800_wifi_disable(1);
+		testmode = 0;
+		break;
+	}
+	case WIFI_IOCTL_START: {
+	      printk(KERN_INFO "@@@initWlan@@@\n");
+		aic8800_wifi_re_enable(1);
+                dw_mci_rescan_card(0, 1);
+             	ret = rwnx_mod_init();
+		break;	
+	}
+
+	case WIFI_IOCTL_START_TESTMODE: {
+	      printk(KERN_INFO "@@@initWlan Testmode@@@\n");
+		  	 testmode = 1;
+             ret = rwnx_mod_init();
+		break;	
+	}
+
+	default: {
+		printk(KERN_INFO "wifi_ioctl  invalid cmd!\n");
+		break;
+	}
+	}
+	
+	return ret;
+}
+
+static int wifi_dev_open(struct inode *ip, struct file *fp)
+{
+	if (wifidev == NULL) {
+		wifidev = kzalloc(sizeof(*wifidev), GFP_KERNEL);
+		if (!wifidev) {
+			pr_info("wifi dev malloc error\n");
+			return -ENOMEM;
+		}
+
+	}
+	return 0;
+}
+
+static int wifi_dev_release(struct inode *ip, struct file *fp)
+{
+	if (wifidev) {
+		kfree(wifidev);
+		wifidev = NULL;
+		printk("release wifi dev!\n");
+
+	}
+	return 0;
+}
+
+
+int  wifi_dev_init(void)
+{
+	int ret;
+       printk(KERN_ERR "@@@@@wifi dev register@@@@@\n");
+	ret = misc_register(&wifi_device);
+	if (ret) {
+		printk(KERN_ERR "wifi dev failed to initialize\n");
+		return -EFAULT;
+	}
+	printk("qqq wifi_dev_init.\n");
+	return 0;
+}
+
+void  wifi_dev_exit(void)
+{
+	misc_deregister(&wifi_device);
+}
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/soc/zte/efuse/efuse_zx.c b/ap/os/linux/linux-3.4.x/drivers/soc/zte/efuse/efuse_zx.c
old mode 100644
new mode 100755
index 3c4da88..6fbf602
--- a/ap/os/linux/linux-3.4.x/drivers/soc/zte/efuse/efuse_zx.c
+++ b/ap/os/linux/linux-3.4.x/drivers/soc/zte/efuse/efuse_zx.c
@@ -111,10 +111,13 @@
 {
 	s_EfuseRegister * pReg = (s_EfuseRegister *)(ZX_EFUSE_BASE);
 	int i = 0;
+	emsf_lock_id softLock = EFUSE_SFLOCK;
 
 	for(i = 0; i < EFUSE_PROGRAM_MAX_CNT; i++) {
 		/*********************write***********************/
 		/*step1, wait operation done*/
+		soft_spin_lock(softLock);
+		
 		while(pReg->ctrl & 0x1);/*bit0=1 ctl is busy*/
 
 		/*step2, program data & addr*/
@@ -138,6 +141,9 @@
 		pReg->ctrl = 1;
 		/*step8, wait cache valid*/
 		while(!(pReg->status & 0x2));/* bit0=1 ctl is busy*/
+		
+		soft_spin_unlock(softLock);
+
 		/********************compare**********************/
 		if(SECURE_EN_OFFSET == cache_offset) {
 			if(data == (pReg->cache[cache_offset] & 0xFF))
@@ -168,7 +174,11 @@
 {
 	s_EfuseRegister * pReg = (s_EfuseRegister *)(ZX_EFUSE_BASE);
 
+	emsf_lock_id	softLock = EFUSE_SFLOCK;
+
 	/*step1, wait operation done*/
+	soft_spin_lock(softLock);
+
 	while(pReg->ctrl & 0x1);/*bit0=1 ctl is busy*/
 
 	/*step2, select efuse0 read all*/
@@ -177,6 +187,8 @@
 	/*step3, wait cache valid*/
 	while(!(pReg->status & 0x2));/* bit0=1 ctl is busy*/
 
+	soft_spin_unlock(softLock);
+
 }
 
 /*******************************************************************************
diff --git a/ap/os/linux/linux-3.4.x/drivers/soc/zte/otp/otp_zx.c b/ap/os/linux/linux-3.4.x/drivers/soc/zte/otp/otp_zx.c
index fba3f50..9d1899e 100755
--- a/ap/os/linux/linux-3.4.x/drivers/soc/zte/otp/otp_zx.c
+++ b/ap/os/linux/linux-3.4.x/drivers/soc/zte/otp/otp_zx.c
@@ -584,7 +584,7 @@
 	  int ret;

 	  u32 page_offset, page_size, i;

 	  struct nor_info *info = spi_nor_flash;

-	  char temp_addr[4096] = {0};

+	  char temp_addr[256] = {0};

 	  unsigned long long start_time,end_time,delta_time = 0;

 

 	  soft_spin_lock(NAND_SFLOCK);

diff --git a/ap/os/linux/linux-3.4.x/include/net/SI/fast_common.h b/ap/os/linux/linux-3.4.x/include/net/SI/fast_common.h
index fa499d5..0061759 100755
--- a/ap/os/linux/linux-3.4.x/include/net/SI/fast_common.h
+++ b/ap/os/linux/linux-3.4.x/include/net/SI/fast_common.h
@@ -34,7 +34,7 @@
 #include <linux/module.h>
 
 #include <linux/proc_fs.h>
-
+#include <linux/interrupt.h>
 /* ******************************ºê¶¨Òå******************************* */
 /* ******************************ºê¶¨Òå******************************* */
 /* ******************************ºê¶¨Òå******************************* */
diff --git a/ap/os/linux/linux-3.4.x/lib/kobject.c b/ap/os/linux/linux-3.4.x/lib/kobject.c
old mode 100644
new mode 100755
index d90c692..d969d5b
--- a/ap/os/linux/linux-3.4.x/lib/kobject.c
+++ b/ap/os/linux/linux-3.4.x/lib/kobject.c
@@ -76,7 +76,7 @@
 	return length;
 }
 
-static void fill_kobj_path(struct kobject *kobj, char *path, int length)
+static int fill_kobj_path(struct kobject *kobj, char *path, int length)
 {
 	struct kobject *parent;
 
@@ -85,12 +85,15 @@
 		int cur = strlen(kobject_name(parent));
 		/* back up enough to print this name with '/' */
 		length -= cur;
+		if (length <= 0)
+			return -EINVAL;
 		strncpy(path + length, kobject_name(parent), cur);
 		*(path + --length) = '/';
 	}
 
 	pr_debug("kobject: '%s' (%p): %s: path = '%s'\n", kobject_name(kobj),
 		 kobj, __func__, path);
+	return 0;
 }
 
 /**
@@ -106,13 +109,17 @@
 	char *path;
 	int len;
 
+retry:
 	len = get_kobj_path_length(kobj);
 	if (len == 0)
 		return NULL;
 	path = kzalloc(len, gfp_mask);
 	if (!path)
 		return NULL;
-	fill_kobj_path(kobj, path, len);
+	if (fill_kobj_path(kobj, path, len)) {
+		kfree(path);
+		goto retry;
+	}
 
 	return path;
 }
diff --git a/ap/os/linux/linux-3.4.x/net/core/fastproc/fast6.c b/ap/os/linux/linux-3.4.x/net/core/fastproc/fast6.c
index 637d373..8d6a14e 100755
--- a/ap/os/linux/linux-3.4.x/net/core/fastproc/fast6.c
+++ b/ap/os/linux/linux-3.4.x/net/core/fastproc/fast6.c
@@ -36,7 +36,8 @@
 fast_list_t working_list6 = {0};
 struct hlist_nulls_head *working_hash6;
 extern int speedMode;
-
+extern struct sk_buff_head fast_txq;
+extern struct tasklet_struct fast_tx_bh;
 /* ******************************** º¯ÊýÉêÃ÷ ********************************/
 
 
@@ -482,6 +483,11 @@
 					kfree_skb(skb);
 				}
 			}
+			else if(skb->dev->type == ARPHRD_PPP)
+			{
+				skb_queue_tail(&fast_txq, skb);
+				tasklet_schedule(&fast_tx_bh);
+			}
 			else
 			{
 				dev_queue_xmit(skb);
diff --git a/ap/os/linux/linux-3.4.x/net/core/fastproc/fast_common.c b/ap/os/linux/linux-3.4.x/net/core/fastproc/fast_common.c
index 239a88f..2ba0485 100755
--- a/ap/os/linux/linux-3.4.x/net/core/fastproc/fast_common.c
+++ b/ap/os/linux/linux-3.4.x/net/core/fastproc/fast_common.c
@@ -57,6 +57,9 @@
 spinlock_t fast_del_spinlock;
 fast_list_t fast_del_list = {0};
 
+struct sk_buff_head fast_txq;
+struct tasklet_struct fast_tx_bh;
+
 /* 
 * 0: ¹Ø±Õfastnat£¬×ß±ê×¼linux£¬¿ÉÒÔÖжÏÔ­Á´½ÓÖØÐÂÁ´½Ó
 * 1: ×ßIP²ã±ê×¼fasnat£¬½ø¶øÒýÓÃÈíÖжϵ÷¶È£¬ÐÔÄÜÂԲ³¡¾°ÈçÁ÷¿Ø¹¦ÄÜ
@@ -416,6 +419,17 @@
 extern void net_dbg_perf_clear_last_item(struct sk_buff *skb);
 
 /* ******************************* º¯ÊýʵÏÖ ******************************* */
+static void fast_bh (unsigned long param)
+{
+	struct sk_buff_head *txq = (struct sk_buff_head *)param;
+	struct sk_buff *skb = (txq ? skb_dequeue(txq) : NULL);
+
+	while(skb != NULL) {
+		dev_queue_xmit(skb);
+		skb = skb_dequeue(txq);
+	}
+}
+
 static int fast_iphdr_check(struct sk_buff *skb, int proto)
 {
 	const struct iphdr *iph;
@@ -2668,7 +2682,10 @@
     set_fast_switch_cb(fast_switch);
     fast_conntrack_init_proc();
     net_adapter_init_proc();
-    
+    skb_queue_head_init(&fast_txq);
+    fast_tx_bh.func = fast_bh;
+    fast_tx_bh.data = (unsigned long)&fast_txq;
+	
     return 0;
 }
 
@@ -2685,6 +2702,7 @@
     tsp_fast6_cleanup();
     fast4_fw_cleanup();
     fast6_fw_cleanup();
+    tasklet_kill(&fast_tx_bh);
 }
 
 //module_init(fast_proc_init); 
diff --git a/ap/os/linux/linux-3.4.x/net/core/fastproc/fastnat.c b/ap/os/linux/linux-3.4.x/net/core/fastproc/fastnat.c
index 837c4c7..b6351db 100755
--- a/ap/os/linux/linux-3.4.x/net/core/fastproc/fastnat.c
+++ b/ap/os/linux/linux-3.4.x/net/core/fastproc/fastnat.c
@@ -38,7 +38,8 @@
 fast_list_t working_list = {0};
 struct hlist_nulls_head *working_hash;
 extern int speedMode;
-
+extern struct sk_buff_head fast_txq;
+extern struct tasklet_struct fast_tx_bh;
 /* **************************** º¯ÊýÉêÃ÷ ************************ */
 
 
@@ -494,6 +495,11 @@
 					kfree_skb(skb);
 				}
 			}
+			else if(skb->dev->type == ARPHRD_PPP)
+			{
+				skb_queue_tail(&fast_txq, skb);
+				tasklet_schedule(&fast_tx_bh);
+			}
 			else
 			{
 				dev_queue_xmit(skb);
diff --git a/ap/os/linux/linux-3.4.x/net/ipv4/igmp.c b/ap/os/linux/linux-3.4.x/net/ipv4/igmp.c
old mode 100644
new mode 100755
index 7824677..8abe6b0
--- a/ap/os/linux/linux-3.4.x/net/ipv4/igmp.c
+++ b/ap/os/linux/linux-3.4.x/net/ipv4/igmp.c
@@ -107,6 +107,7 @@
 
 #define IP_MAX_MEMBERSHIPS	20
 #define IP_MAX_MSF		10
+#define IP_MAX_MTU 0xFFFFU
 
 #ifdef CONFIG_IP_MULTICAST
 /* Parameter names and values are taken from igmp-v2-06 draft */
@@ -295,7 +296,7 @@
 
 #define igmp_skb_size(skb) (*(unsigned int *)((skb)->cb))
 
-static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
+static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int size)
 {
 	struct sk_buff *skb;
 	struct rtable *rt;
@@ -306,6 +307,9 @@
 	int hlen = LL_RESERVED_SPACE(dev);
 	int tlen = dev->needed_tailroom;
 
+	/* CVE-2023-42752 */
+	size = min(size, IP_MAX_MTU);
+
 	while (1) {
 		skb = alloc_skb(size + hlen + tlen,
 				GFP_ATOMIC | __GFP_NOWARN);
diff --git a/ap/os/linux/linux-3.4.x/net/sched/sch_hfsc.c b/ap/os/linux/linux-3.4.x/net/sched/sch_hfsc.c
old mode 100644
new mode 100755
index 9bdca2e..2e9b32d
--- a/ap/os/linux/linux-3.4.x/net/sched/sch_hfsc.c
+++ b/ap/os/linux/linux-3.4.x/net/sched/sch_hfsc.c
@@ -1050,6 +1050,10 @@
 			return -ENOENT;
 	}
 
+	/* CVE-2023-4623 */
+	if(!(parent->cl_flags & HFSC_FSC) && parent != &q->root)
+		return -EINVAL;
+
 	if (classid == 0 || TC_H_MAJ(classid ^ sch->handle) != 0)
 		return -EINVAL;
 	if (hfsc_find_class(classid, sch))
diff --git a/ap/os/linux/linux-3.4.x/net/wireless/db.txt b/ap/os/linux/linux-3.4.x/net/wireless/db.txt
index 77c0c63..ac32483 100755
--- a/ap/os/linux/linux-3.4.x/net/wireless/db.txt
+++ b/ap/os/linux/linux-3.4.x/net/wireless/db.txt
@@ -1,763 +1,1548 @@
+wmmrule ETSI:
+	vo_c: cw_min=3, cw_max=7, aifsn=2, cot=2
+	vi_c: cw_min=7, cw_max=15, aifsn=2, cot=4
+	be_c: cw_min=15, cw_max=1023, aifsn=3, cot=6
+	bk_c: cw_min=15, cw_max=1023, aifsn=7, cot=6
+	vo_ap: cw_min=3, cw_max=7, aifsn=1, cot=2
+	vi_ap: cw_min=7, cw_max=15, aifsn=1, cot=4
+	be_ap: cw_min=15, cw_max=63, aifsn=3, cot=6
+	bk_ap: cw_min=15, cw_max=1023, aifsn=7, cot=6
+
 # This is the world regulatory domain
 country 00:
-	(2402 - 2472 @ 40), (3, 20)
+	(2402 - 2472 @ 40), (20)
 	# Channel 12 - 13.
-	(2457 - 2482 @ 40), (3, 20), PASSIVE-SCAN, NO-IBSS
+	(2457 - 2482 @ 20), (20), NO-IR, AUTO-BW
 	# Channel 14. Only JP enables this and for 802.11b only
-	(2474 - 2494 @ 20), (3, 20), PASSIVE-SCAN, NO-IBSS, NO-OFDM
+	(2474 - 2494 @ 20), (20), NO-IR, NO-OFDM
 	# Channel 36 - 48
-	(5170 - 5250 @ 80), (3, 20), PASSIVE-SCAN, NO-IBSS
-	# NB: 5260 MHz - 5700 MHz requies DFS
+	(5170 - 5250 @ 80), (20), NO-IR, AUTO-BW
+	# Channel 52 - 64
+	(5250 - 5330 @ 80), (20), NO-IR, DFS, AUTO-BW
+	# Channel 100 - 144
+	(5490 - 5730 @ 160), (20), NO-IR, DFS
 	# Channel 149 - 165
-	(5735 - 5835 @ 80), (3, 20), PASSIVE-SCAN, NO-IBSS
+	(5735 - 5835 @ 80), (20), NO-IR
 	# IEEE 802.11ad (60GHz), channels 1..3
-	(57240 - 63720 @ 2160), (N/A, 0)
+	(57240 - 63720 @ 2160), (0)
 
+# AD as part of CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries: https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+country AD: DFS-ETSI
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4, ref: Etsi En 302 567
+	(57000 - 66000 @ 2160), (40)
 
-country AD:
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+country AE: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country AE:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country AF: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
 
-country AL:
-	(2402 - 2482 @ 40), (N/A, 20)
+# Source:
+# http://pucanguilla.org/Downloads/January2005-Anguilla%20Table%20of%20Allocations.pdf
+country AI: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW, wmmrule=ETSI
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW, wmmrule=ETSI
+	(5490 - 5710 @ 160), (27), DFS, wmmrule=ETSI
 
-country AM:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 20), (N/A, 18)
-	(5250 - 5330 @ 20), (N/A, 18), DFS
+# AL as part of CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+country AL: DFS-ETSI
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
 
-country AN:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
+country AM: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 20), (18)
+	(5250 - 5330 @ 20), (18), DFS
 
-country AR:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country AN: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
 
+country AR: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country AS: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+# AT as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# AT: https://www.rtr.at/en/tk/Spektrum5GHz/1997_bmvit-info-052010en.pdf
+# AT: acceptance https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2014_II_63/BGBLA_2014_II_63.pdfsig
 country AT: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country AU:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+# Source:
+# https://www.legislation.gov.au/Details/F2016C00432
+# Both DFS-ETSI and DFS-FCC are acceptable per AS/NZS 4268 Appendix B.
+# The EIRP for DFS bands can be increased by 3dB if TPC is implemented.
+# In order to allow 80MHz operation between 5650-5730MHz the upper boundary
+# of this more restrictive band has been shifted up by 5MHz from 5725MHz.
+country AU: DFS-ETSI
+	(2400 - 2483.5 @ 40), (36)
+	(5150 - 5250 @ 80), (23), NO-OUTDOOR, AUTO-BW
+	(5250 - 5350 @ 80), (20), NO-OUTDOOR, AUTO-BW, DFS
+	(5470 - 5600 @ 80), (27), DFS
+	(5650 - 5730 @ 80), (27), DFS
+	(5730 - 5850 @ 80), (36)
+	(57000 - 66000 @ 2160), (43), NO-OUTDOOR
 
-country AW:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
+country AW: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
 
-country AZ:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 18)
-	(5250 - 5330 @ 40), (N/A, 18), DFS
+country AZ: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (18), AUTO-BW
+	(5250 - 5330 @ 80), (18), DFS, AUTO-BW
 
+# BA as part of CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
 country BA: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4, ref: Etsi En 302 567
+	(57000 - 66000 @ 2160), (40)
 
-country BB:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (3, 23)
-	(5250 - 5330 @ 40), (3, 23), DFS
-	(5735 - 5835 @ 40), (3, 30)
+country BB: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (23), AUTO-BW
+	(5250 - 5330 @ 80), (23), DFS, AUTO-BW
+	(5735 - 5835 @ 80), (30)
 
-country BD:
-	(2402 - 2482 @ 40), (N/A, 20)
+country BD: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5735 - 5835 @ 80), (30)
 
+# BE as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# BE: https://www.ibpt.be/public/files/en/21760/B03-01_2.1_EN.pdf
+# BE: https://www.ibpt.be/public/files/en/21761/B03-02_2.1_EN.pdf
+# BE: https://www.ibpt.be/public/files/en/21762/B03-03_2.1_EN.pdf
+# BE: https://www.ibpt.be/public/files/en/22165/B01-28_3.1_EN.pdf
 country BE: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
+country BF: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+# Bulgarian rules as defined by the Communications Regulation Commission in the
+# following documents:
+#
+# Rules for carrying out electronic communications through radio equipment using
+# radio spectrum, which does not need to be individually assigned (the Rules):
+# http://www.crc.bg/files/_bg/Pravila_09_06_2015.pdf
+#
+# List of radio equipment that uses harmonized within the European Union bands
+# and electronic communications terminal equipment (the List):
+# http://www.crc.bg/files/_bg/Spisak_2015.pdf
+#
+# Note: The transmit power limits in the 5250-5350 MHz and 5470-5725 MHz bands
+# can be raised by 3 dBm if TPC is enabled. Refer to BDS EN 301 893 for details.
+#
+# BG as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# BG: https://crc.bg/files/_en/Electronic_Communications_Revised_EN1.pdf
+# BG: acceptance of 2006/771/EC https://crc.bg/files/Pravila_06_12_2018.pdf
 country BG: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 23)
-	(5250 - 5290 @ 40), (N/A, 23), DFS
-	(5490 - 5710 @ 40), (N/A, 30), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	# Wideband data transmission systems (WDTS) in the 2.4GHz ISM band, ref:
+	# I.22 of the List, BDS EN 300 328
+	(2400 - 2483.5 @ 40), (100 mW)
+	# 5 GHz Radio Local Area Networks (RLANs), ref:
+	# II.H01 of the List, BDS EN 301 893
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	# II.H01 of the List, I.54 from the List, BDS EN 301 893
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	# I.43 of the List, BDS EN 300 440-2, BDS EN 300 440-1
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	# II.H03 of the List, BDS EN 302 567-2
+	(57000 - 66000 @ 2160), (40)
 
-country BH:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 20), (N/A, 20)
-	(5250 - 5330 @ 20), (N/A, 20), DFS
-	(5735 - 5835 @ 20), (N/A, 20)
+country BH: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 20), (20)
+	(5250 - 5330 @ 20), (20), DFS
+	(5735 - 5835 @ 20), (20)
 
-country BL:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 18)
-	(5250 - 5330 @ 40), (N/A, 18), DFS
+country BL: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
 
-country BN:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5735 - 5835 @ 40), (N/A, 30)
+country BM: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country BO:
-	(2402 - 2482 @ 40), (N/A, 30)
-	(5735 - 5835 @ 40), (N/A, 30)
+country BN: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5735 - 5835 @ 80), (20)
 
-country BR:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country BO: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5250 - 5330 @ 80), (30), DFS
+	(5735 - 5835 @ 80), (30)
 
-country BY:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
+country BR: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country BZ:
-	(2402 - 2482 @ 40), (N/A, 30)
-	(5735 - 5835 @ 40), (N/A, 30)
+country BS: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country CA:
-	(2402 - 2472 @ 40), (3, 27)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+# Source:
+# http://www.bicma.gov.bt/paper/publication/nrrpart4.pdf
+country BT: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
 
+country BY: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
+
+country BZ: DFS-JP
+	(2402 - 2482 @ 40), (30)
+	(5735 - 5835 @ 80), (30)
+
+# Source:
+# https://www.ic.gc.ca/eic/site/smt-gst.nsf/vwapj/rss-247-i2-e.pdf/$file/rss-247-i2-e.pdf
+country CA: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5150 - 5250 @ 80), (23), NO-OUTDOOR, AUTO-BW
+	(5250 - 5350 @ 80), (24), DFS, AUTO-BW
+	(5470 - 5600 @ 80), (24), DFS
+	(5650 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+# Source:
+# http://www.art-rca.org
+country CF: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 40), (17)
+	(5250 - 5330 @ 40), (24), DFS
+	(5490 - 5730 @ 40), (24), DFS
+	(5735 - 5835 @ 40), (30)
+
+
+# CH as part of CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# CH: https://www.ofcomnet.ch/api/rir/1010/05
+# CH: https://www.ofcomnet.ch/api/rir/1010/04
+# CH: https://www.ofcomnet.ch/api/rir/1008/12
+# CH: https://www.ofcomnet.ch/#/fatTable
 country CH: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country CL:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5735 - 5835 @ 40), (N/A, 20)
+country CI: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country CN:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5735 - 5835 @ 80), (N/A, 30)
-	# 60 gHz band channels 1,4: 28dBm, channels 2,3: 44dBm
+country CL: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5735 - 5835 @ 80), (20)
+
+country CN: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (23), AUTO-BW
+	(5250 - 5330 @ 80), (23), DFS, AUTO-BW
+	(5735 - 5835 @ 80), (30)
+	# 60 GHz band channels 1,4: 28dBm, channels 2,3: 44dBm
 	# ref: http://www.miit.gov.cn/n11293472/n11505629/n11506593/n11960250/n11960606/n11960700/n12330791.files/n12330790.pdf
-	(57240 - 59400 @ 2160), (N/A, 28)
-	(59400 - 63720 @ 2160), (N/A, 44)
-	(63720 - 65880 @ 2160), (N/A, 28)
+	(57240 - 59400 @ 2160), (28)
+	(59400 - 63720 @ 2160), (44)
+	(63720 - 65880 @ 2160), (28)
 
-country CO:
-	(2402 - 2472 @ 40), (3, 27)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country CO: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country CR:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country CR: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 20), (17)
+	(5250 - 5330 @ 20), (24), DFS
+	(5490 - 5730 @ 20), (24), DFS
+	(5735 - 5835 @ 20), (30)
 
+# Source:
+# https://www.mincom.gob.cu/es/marco-legal
+# - Redes Informáticas
+# Resolución 127- 2011 Reglamento de Banda de frecuencias de 2,4 GHz.
+country CU: DFS-FCC
+	(2400 - 2483.5 @ 40), (200 mW)
+
+country CX: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+
+# CY as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# CY: http://www.mcw.gov.cy/mcw/dec/dec.nsf/all/292484CFC7013DD4C2256EBA0023D447/$file/Sxedio%20Radiosyxnothtwn%20ths%20Dhmokratias-3-8-2018-E2.2(English%20Unified%20Unofficial).pdf?openelement
 country CY: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-# Data from http://www.ctu.eu/164/download/VOR/VOR-12-08-2005-34.pdf
-# and http://www.ctu.eu/164/download/VOR/VOR-12-05-2007-6-AN.pdf
-# Power at 5250 - 5350 MHz and 5470 - 5725 MHz can be doubled if TPC is
-# implemented.
+# CZ as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# CZ: https://www.ctu.cz/cs/download/vseobecna-opravneni/archiv/vo-r_12-06_2010-09.pdf
+# CZ: https://www.ctu.cz/sites/default/files/obsah/ctu/vseobecne-opravneni-c.vo-r/10/12.2017-10/obrazky/vo-r10-122017-10.pdf
 country CZ: DFS-ETSI
-	(2400 - 2483.5 @ 40), (N/A, 100 mW)
-	(5150 - 5250 @ 80), (N/A, 200 mW), NO-OUTDOOR
-	(5250 - 5350 @ 80), (N/A, 100 mW), NO-OUTDOOR, DFS
-	(5470 - 5725 @ 80), (N/A, 500 mW), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-# Data from "Frequenznutzungsplan" (as published in April 2008), downloaded from
-# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38448/publicationFile/2659/Frequenznutzungsplan2008_Id17448pdf.pdf
-# For the 5GHz range also see
-# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38216/publicationFile/6579/WLAN5GHzVfg7_2010_28042010pdf.pdf
-# The values have been reduced by a factor of 2 (3db) for non TPC devices
-# (in other words: devices with TPC can use twice the tx power of this table).
-# Note that the docs do not require TPC for 5150--5250; the reduction to
-# 100mW thus is not strictly required -- however the conservative 100mW
-# limit is used here as the non-interference with radar and satellite
-# apps relies on the attenuation by the building walls only in the
-# absence of DFS; the neighbour countries have 100mW limit here as well.
-
+# DE as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+#
+# Allocation for the 2.4 GHz band (Vfg 10 / 2013, Allgemeinzuteilung von
+# Frequenzen für die Nutzung in lokalen Netzwerken; Wireless Local Area
+# Networks (WLAN-Funkanwendungen).
+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2013_10_WLAN_2,4GHz_pdf.pdf
+#
+# Allocation for the 5 GHz band (Vfg. 7 / 2010, Allgemeinzuteilung von
+# Frequenzen in den Bereichen 5150 MHz - 5350 MHz und 5470 MHz - 5725 MHz für
+# Funkanwendungen zur breitbandigen Datenübertragung, WAS/WLAN („Wireless
+# Access Systems including Wireless Local Area Networks“).
+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2010_07_WLAN_5GHz_pdf.pdf
+#
+# The ETSI EN 300 440-1 standard for short range devices in the 5 GHz band has
+# been implemented in Germany:
+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2014_69_SRD_pdf.pdf
+#
+# Allocation for the 60 GHz band (Allgemeinzuteilung von Frequenzen im
+# Bereich 57 GHz - 66 GHz für Funkanwendungen für weitbandige
+# Datenübertragungssysteme; „Multiple Gigabit WAS/RLAN Systems (MGWS)“).
+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2011_08_MGWS_pdf.pdf
 country DE: DFS-ETSI
-	# entries 279004 and 280006
-	(2400 - 2483.5 @ 40), (N/A, 100 mW)
-	# entry 303005
-	(5150 - 5250 @ 80), (N/A, 100 mW), NO-OUTDOOR
-	# entries 304002 and 305002
-	(5250 - 5350 @ 80), (N/A, 100 mW), NO-OUTDOOR, DFS
-	# entries 308002, 309001 and 310003
-	(5470 - 5725 @ 80), (N/A, 500 mW), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
+# DK as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# DK: https://ens.dk/sites/ens.dk/files/Tele/frekvensplan_0.pdf
+# 5GHz: https://erhvervsstyrelsen.dk/sites/default/files/007_interface-datanet_5-6_ghz.pdf.pdf
+# 60GHz: https://erhvervsstyrelsen.dk/sites/default/files/radiograenseflader-63.pdf
 country DK: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country DO:
-	(2402 - 2472 @ 40), (3, 27)
-	(5170 - 5250 @ 40), (3, 17)
-	(5250 - 5330 @ 40), (3, 23), DFS
-	(5735 - 5835 @ 40), (3, 30)
+# Source:
+# http://www.ntrcdom.org/index.php?option=com_content&view=category&layout=blog&id=10&Itemid=55
+country DM: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (23), DFS, AUTO-BW
+	(5735 - 5835 @ 80), (30)
 
-country DZ:
-	(2402 - 2482 @ 40), (N/A, 20)
+country DO: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (23), DFS, AUTO-BW
+	(5735 - 5835 @ 80), (30)
 
-country EC:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country DZ: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170.000 - 5250.000 @ 80.000), (23.00), AUTO-BW
+	(5250.000 - 5330.000 @ 80.000), (23.00), DFS, AUTO-BW
+	(5490.000 - 5670.000 @ 160.000), (23.00), DFS
 
+country EC: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 20), (17)
+	(5250 - 5330 @ 20), (24), DFS
+	(5490 - 5730 @ 20), (24), DFS
+	(5735 - 5835 @ 20), (30)
+
+# EE as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# EE: https://www.ttja.ee/et/ettevottele-organisatsioonile/sideteenused/raadioseadmed/wifi-seade
+# EE: https://www.itu.int/ITU-D/study_groups/SGP_1998-2002/JGRES09/pdf/estonia.pdf
 country EE: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country EG:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 20), (N/A, 20)
-	(5250 - 5330 @ 20), (N/A, 20), DFS
+country EG: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 40), (20)
+	(5250 - 5330 @ 40), (20), DFS
 
+# ES as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# ES: https://avancedigital.gob.es/espectro/Paginas/cnaf.aspx
 country ES: DFS-ETSI
-	(2400 - 2483.5 @ 40), (N/A, 100 mW)
-	(5150 - 5250 @ 80), (N/A, 100 mW), NO-OUTDOOR
-	(5250 - 5350 @ 80), (N/A, 100 mW), NO-OUTDOOR, DFS
-	(5470 - 5725 @ 80), (N/A, 500 mW), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
+country ET: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
+
+# FI as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
 country FI: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
+country FM: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+# FR as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
 country FR: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country GE:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 18)
-	(5250 - 5330 @ 40), (N/A, 18), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
-
+# GB as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# GB: https://www.ofcom.org.uk/__data/assets/pdf_file/0019/136009/Ofcom-Information-Sheet-5-GHz-RLANs.pdf
+# GB: https://www.ofcom.org.uk/__data/assets/pdf_file/0028/84970/ir-2030.pdf
+# GB: https://www.ofcom.org.uk/__data/assets/pdf_file/0013/126121/Statement_Implementing-Ofcoms-decision-on-the-57-71GHz-band.pdf
 country GB: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-6
+	(57000 - 71000 @ 2160), (40)
 
-country GD:
-	(2402 - 2472 @ 40), (3, 27)
-	(5170 - 5250 @ 40), (3, 17)
-	(5250 - 5330 @ 40), (3, 20), DFS
-	(5490 - 5710 @ 40), (3, 20), DFS
-	(5735 - 5835 @ 40), (3, 30)
+country GD: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country GR: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+country GE: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (18), AUTO-BW
+	(5250 - 5330 @ 80), (18), DFS, AUTO-BW
+	# 60 GHz band channels 1-4, ref: Etsi En 302 567
+	(57000 - 66000 @ 2160), (40)
+
+country GF: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW, wmmrule=ETSI
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW, wmmrule=ETSI
+	(5490 - 5710 @ 160), (27), DFS, wmmrule=ETSI
+
+country GH: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
 country GL: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 20), (N/A, 20)
-	(5250 - 5330 @ 20), (N/A, 20), DFS
-	(5490 - 5710 @ 20), (N/A, 27), DFS
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW, wmmrule=ETSI
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW, wmmrule=ETSI
+	(5490 - 5710 @ 160), (27), DFS, wmmrule=ETSI
 
-country GT:
-	(2402 - 2472 @ 40), (3, 27)
-	(5170 - 5250 @ 40), (3, 17)
-	(5250 - 5330 @ 40), (3, 23), DFS
-	(5735 - 5835 @ 40), (3, 30)
+country GP: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW, wmmrule=ETSI
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW, wmmrule=ETSI
+	(5490 - 5710 @ 160), (27), DFS, wmmrule=ETSI
 
-country GU:
-	(2402 - 2472 @ 40), (3, 27)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+# GR as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# GR: https://www.eett.gr/opencms/export/sites/default/EETT_EN/Electronic_Communications/Radio_Communications/TelecommunicationEquipment/Radio_equipment_interface_requirement_2012.pdf
+# GR: https://www.eett.gr/opencms/export/sites/default/EETT_EN/Electronic_Communications/Radio_Communications/TelecommunicationEquipment/Radio_equipment_interface_requirement_107.pdf
+country GR: DFS-ETSI
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country HN:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (3, 17)
-	(5250 - 5330 @ 40), (3, 20), DFS
-	(5490 - 5710 @ 40), (3, 20), DFS
-	(5735 - 5835 @ 40), (3, 30)
+country GT: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (23), DFS, AUTO-BW
+	(5735 - 5835 @ 80), (30)
 
-country HK:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country GU: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 20), (17)
+	(5250 - 5330 @ 20), (24), DFS
+	(5490 - 5730 @ 20), (24), DFS
+	(5735 - 5835 @ 20), (30)
+
+country GY:
+	(2402 - 2482 @ 40), (30)
+	(5735 - 5835 @ 80), (30)
+
+country HK: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country HN: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
 country HR: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+# HR as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# HR: http://tablice.hakom.hr:8080/vis?lang=en
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country HT:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
+country HT: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
+# HU as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# HU: http://stir.nmhh.hu/?oldal=dokumentumGeneralo&root_rendeletelem_id=3&hatalyos=1
+# HU: http://english.nmhh.hu/cikk/297/Eljarasi_tajekoztato_a_24_GHzes_es_az_5_GHzes_savban_mukodo_berendezesek_engedelyezeserol
+# HU: http://nmhh.hu/dokumentum/319/kis_hatotavolsagu_eszkozok_srdk.pdf
 country HU: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country ID:
-	# ref: http://www.postel.go.id/content/ID/regulasi/standardisasi/kepdir/bwa%205,8%20ghz.pdf
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5735 - 5815 @ 80), (N/A, 20)
+country ID: DFS-JP
+	# ref: https://jdih.kominfo.go.id/produk_hukum/view/id/676/t/peraturan+menteri+komunikasi+dan+informatika+nomor+1+tahun+2019+tanggal+24+april+2019
+	(2400 - 2483.5 @ 40), (500 mW), NO-OUTDOOR
+	(5150 - 5350 @ 80), (200 mW), NO-OUTDOOR
+	(5725 - 5825 @ 80), (200 mW), NO-OUTDOOR
 
+# IE as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# IE: https://www.comreg.ie/publication-download/interface-requirements-for-radio-services-in-ireland
+# IE: https://www.comreg.ie/publication-download/permitted-short-range-devices-ireland
 country IE: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country IL:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5150 - 5250 @ 80), (N/A, 200 mW), NO-OUTDOOR
-	(5250 - 5350 @ 80), (N/A, 200 mW), NO-OUTDOOR, DFS
+country IL: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW
+	(5250 - 5350 @ 80), (200 mW), NO-OUTDOOR, DFS, AUTO-BW
 
 country IN:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5735 - 5835 @ 40), (N/A, 20)
+	(2402 - 2482 @ 40), (20)
+	(5150 - 5250 @ 80), (30)
+	(5250 - 5350 @ 80), (24)
+	(5470 - 5725 @ 160), (24)
+	(5725 - 5875 @ 80), (30)
 
+country IR: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5735 - 5835 @ 80), (30)
+
+# IS as part of CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# IS: https://www.pfs.is/library/Skrar/Tidnir-og-taekni/MHZ_21022019.pdf
 country IS: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country IR:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5735 - 5835 @ 40), (N/A, 30)
-
+# IT as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
 country IT: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country JM:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (3, 17)
-	(5250 - 5330 @ 40), (3, 20), DFS
-	(5490 - 5710 @ 40), (3, 20), DFS
-	(5735 - 5835 @ 40), (3, 30)
+country JM: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country JP:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(2474 - 2494 @ 20), (N/A, 20), NO-OFDM
-	(4910 - 4990 @ 40), (N/A, 23)
-	(5030 - 5090 @ 40), (N/A, 23)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 160), (N/A, 23), DFS
+country JO: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (23)
+	(5735 - 5835 @ 80), (23)
 
-country JO:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 18)
+country JP: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(2474 - 2494 @ 20), (20), NO-OFDM
+	(4910 - 4990 @ 40), (23)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (23), DFS
+	# 60 GHz band channels 2-4 at 10mW,
+	# ref: http://www.arib.or.jp/english/html/overview/doc/1-STD-T74v1_1.pdf
+	(57000 - 66000 @ 2160), (10 mW)
 
-country KE:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5735 - 5835 @ 40), (N/A, 30)
+country KE: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (23)
+	(5490 - 5570 @ 80), (30), DFS
+	(5735 - 5775 @ 40), (23)
 
-country KH:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
+country KH: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
 
-country KP:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5330 @ 40), (3, 20)
-	(5160 - 5250 @ 40), (3, 20), DFS
-	(5490 - 5630 @ 40), (3, 30), DFS
-	(5735 - 5815 @ 40), (3, 30)
+# Source
+# http://ntrc.kn/?page_id=7
+country KN: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (30), DFS
+	(5735 - 5815 @ 80), (30)
 
-country KR:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 20)
-	(5250 - 5330 @ 80), (3, 20), DFS
-	(5490 - 5630 @ 80), (3, 30), DFS
-	(5735 - 5815 @ 80), (3, 30)
+country KP: DFS-JP
+	(2402 - 2482 @ 20), (20)
+	(5170 - 5250 @ 20), (20)
+	(5250 - 5330 @ 20), (20), DFS
+	(5490 - 5630 @ 20), (30), DFS
+	(5735 - 5815 @ 20), (30)
 
-country KW:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
+country KR: DFS-JP
+	# ref: https://www.rra.go.kr
+	(2400 - 2483.5 @ 40), (23)
+	(5150 - 5250 @ 80), (23), AUTO-BW
+	(5250 - 5350 @ 80), (20), DFS, AUTO-BW
+	(5470 - 5725 @ 160), (20), DFS
+	(5725 - 5835 @ 80), (23)
+	# 60 GHz band channels 1-4,
+	# ref: http://www.law.go.kr/%ED%96%89%EC%A0%95%EA%B7%9C%EC%B9%99/%EB%AC%B4%EC%84%A0%EC%84%A4%EB%B9%84%EA%B7%9C%EC%B9%99
+	(57000 - 66000 @ 2160), (43)
 
-country KZ:
-	(2402 - 2482 @ 40), (N/A, 20)
+country KW: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
 
-country LB:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5735 - 5835 @ 40), (N/A, 30)
+country KY: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
+# Source:
+# http://mic.gov.kz/sites/default/files/pages/pravila_prisvoeniya_polos_chastot_no34.pdf
+# http://adilet.zan.kz/rus/docs/P000001379_
+country KZ: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5150 - 5250 @ 80), (20), NO-OUTDOOR, AUTO-BW
+	(5250 - 5350 @ 80), (20), NO-OUTDOOR, DFS, AUTO-BW
+	(5470 - 5725 @ 80), (20), NO-OUTDOOR, DFS
+
+country LB: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+# Source:
+# http://www.ntrc.org.lc/operational_structures.htm
+country LC: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (30), DFS
+	(5735 - 5815 @ 80), (30)
+
+# LI as part of CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# LI: https://www.ofcomnet.ch/api/rir/1010/05
+# LI: https://www.ofcomnet.ch/api/rir/1010/04
+# LI: https://www.ofcomnet.ch/api/rir/1008/12
+# LI: https://www.ofcomnet.ch/#/fatTable
 country LI: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country LK:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 20), (3, 17)
-	(5250 - 5330 @ 20), (3, 20), DFS
-	(5490 - 5710 @ 20), (3, 20), DFS
-	(5735 - 5835 @ 20), (3, 30)
+country LK: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 20), (17)
+	(5250 - 5330 @ 20), (24), DFS
+	(5490 - 5730 @ 20), (24), DFS
+	(5735 - 5835 @ 20), (30)
 
+# Source:
+# http://lca.org.ls/images/documents/lesotho_national_frequency_allocation_plan.pdf
+country LS: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
+
+# LT as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# LT: https://www.rrt.lt/en/radio-spectrum/frequency-management/ or direct link:
+# LT: https://www.e-tar.lt/portal/lt/legalAct/6e718fd037a011e69101aaab2992cbcd/dGRioCBBHb
 country LT: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
+# LU as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# LU: https://assets.ilr.lu/frequences/Documents/ILRLU-1723895916-183.pdf#search=en%20300%20440
 country LU: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
+# LV as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# LV: http://likumi.lv/doc.php?id=198903
 country LV: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
+country MA: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+
+# MC as part of CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
 country MC: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 18)
-	(5250 - 5330 @ 40), (N/A, 18), DFS
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
 
-country MA:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 23)
-	(5735 - 5835 @ 80), (N/A, 23)
+# Source:
+# http://www.cnfr.md/index.php?pag=sec&id=117&l=en
+# MD as part of CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+country MD: DFS-ETSI
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
 
-country MO:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (3, 23)
-	(5250 - 5330 @ 40), (3, 23), DFS
-	(5735 - 5835 @ 40), (3, 30)
+# Source:
+# http://www.cept.org/files/1050/Tools%20and%20Services/EFIS%20-%20ECO%20Frequency%20Information%20System/National%20frequency%20tables/Montenegro%20NAFT%20-%202010.pdf
+# ME as part of CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+country ME: DFS-ETSI
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
 
+country MF: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW, wmmrule=ETSI
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW, wmmrule=ETSI
+	(5490 - 5710 @ 160), (27), DFS, wmmrule=ETSI
+
+country MH: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+# MK as part of CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
 country MK: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4, ref: Etsi En 302 567
+	(57000 - 66000 @ 2160), (40)
 
+country MN: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country MO: DFS-FCC
+	(2402 - 2482 @ 40), (23)
+	(5170 - 5250 @ 80), (23), AUTO-BW
+	(5250 - 5330 @ 80), (23), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (30), DFS
+	(5735 - 5835 @ 80), (30)
+
+country MP: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country MQ: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW, wmmrule=ETSI
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW, wmmrule=ETSI
+	(5490 - 5710 @ 160), (27), DFS, wmmrule=ETSI
+
+# Source:
+# http://www.are.mr/pdfs/telec_freq_TNAbf_2010.pdf
+country MR: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
+
+# MT as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# MT: https://www.mca.org.mt/sites/default/files/NFP_edition%206-1.pdf
 country MT: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country MY:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 17)
-	(5250 - 5330 @ 80), (N/A, 23), DFS
-	(5735 - 5835 @ 80), (N/A, 30)
+country MU: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country MX:
-	(2402 - 2472 @ 40), (3, 27)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+# Source:
+# http://www.cam.gov.mv/docs/tech_standards/TAM-TS-100-2004-WLAN.pdf
+country MV: DFS-ETSI
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), AUTO-BW
+	(5250 - 5350 @ 80), (100 mW), DFS, AUTO-BW
+	(5725 - 5850 @ 80), (100 mW)
 
+country MW: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
+
+country MX: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country MY: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5650 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (24)
+
+country NG: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5250 - 5330 @ 80), (30), DFS
+	(5735 - 5835 @ 80), (30)
+
+country NI: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+# NL as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# NL: http://wetten.overheid.nl/BWBR0036378/2015-03-05
 country NL: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20), NO-OUTDOOR
-	(5250 - 5330 @ 80), (N/A, 20), NO-OUTDOOR, DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
+# NO as part of CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# NO: https://eng.nkom.no/technical/temporary-licenses/mobile-videolink/wireless-cameras-mobile-video-links/_attachment/9947
+# NO: http://www.lovdata.no/dokument/SF/forskrift/2012-01-19-77
+# In addition to EU NO can use 5725–5795 MHz and 5815–5850 bands with limit of 4 W EIRP (with DFS and TPC)
 country NO: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country NP:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5735 - 5835 @ 40), (N/A, 30)
+country NP: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5735 - 5835 @ 80), (20)
 
-country NZ:
-	(2402 - 2482 @ 40), (N/A, 30)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country NZ: DFS-ETSI
+	(2402 - 2482 @ 40), (30)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country OM:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (3, 17)
-	(5250 - 5330 @ 40), (3, 20), DFS
-	(5490 - 5710 @ 40), (3, 20), DFS
-	(5735 - 5835 @ 40), (3, 30)
+country OM: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
 
-country PA:
-	(2402 - 2472 @ 40), (3, 27)
-	(5170 - 5250 @ 40), (3, 17)
-	(5250 - 5330 @ 40), (3, 23), DFS
-	(5735 - 5835 @ 40), (3, 30)
+# Source:
+# http://www.asep.gob.pa/images/telecomunicaciones/Anexos/PNAF-dic2015.pdf
+country PA: DFS-FCC
+	(2400 - 2483.5 @ 40), (36)
+	(5150 - 5250 @ 80), (36), AUTO-BW
+	(5250 - 5350 @ 80), (30), AUTO-BW
+	(5470 - 5725 @ 160), (30)
+	(5725 - 5850 @ 80), (36)
+	(57000 - 64000 @ 2160), (43)
 
-country PE:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country PE: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country PG:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (3, 17)
-	(5250 - 5330 @ 40), (3, 23), DFS
-	(5735 - 5835 @ 40), (3, 30)
+country PF: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW, wmmrule=ETSI
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW, wmmrule=ETSI
+	(5490 - 5710 @ 160), (27), DFS, wmmrule=ETSI
 
-country PH:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country PG: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country PK:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5735 - 5835 @ 40), (N/A, 30)
+country PH: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
+country PK: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5735 - 5835 @ 80), (30)
+
+# PL as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
 country PL: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
+country PM: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW, wmmrule=ETSI
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW, wmmrule=ETSI
+	(5490 - 5710 @ 160), (27), DFS, wmmrule=ETSI
+
+country PR: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+# PT as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# PT: https://www.anacom.pt/render.jsp?categoryId=336334
 country PT: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country PR:
-	(2402 - 2472 @ 40), (3, 27)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country PW: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country QA:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5735 - 5835 @ 40), (N/A, 30)
+country PY: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
+country QA: DFS-ETSI
+	(2400 - 2483.5 @ 40), (100 mW), NO-OUTDOOR
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW
+	(5250 - 5350 @ 80), (200 mW), NO-OUTDOOR, DFS, AUTO-BW
+	(5470 - 5725 @ 160), (100 mW), NO-OUTDOOR, DFS
+	(5725 - 5875 @ 80), (100 mW), NO-OUTDOOR, DFS
+	(57000 - 66000 @ 2160), (40), NO-OUTDOOR
+
+country RE: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW, wmmrule=ETSI
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW, wmmrule=ETSI
+	(5490 - 5710 @ 160), (27), DFS, wmmrule=ETSI
+
+# RO as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# RO: http://www.ancom.org.ro/en/uploads/links_files/ordin_262_2006.pdf
 country RO: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
-
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
 # Source:
 # http://www.ratel.rs/upload/documents/Plan_namene/Plan_namene-sl_glasnik.pdf
-country RS:
-	(2400 - 2483.5 @ 40), (N/A, 100 mW)
-	(5150 - 5350 @ 40), (N/A, 200 mW), NO-OUTDOOR
-	(5470 - 5725 @ 20), (3, 1000 mW), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+# RS as part of CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+country RS: DFS-ETSI
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4, ref: Etsi En 302 567
+	(57000 - 66000 @ 2160), (40)
 
 country RU:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5330 @ 40), (N/A, 20)
-	(5650 - 5710 @ 40), (N/A, 30)
-	(5735 - 5835 @ 40), (N/A, 30)
+	(2400 - 2483.5 @ 40), (20)
+	(5150 - 5350 @ 160), (20), NO-OUTDOOR
+	(5650 - 5850 @ 160), (20), NO-OUTDOOR
+	# 60 GHz band channels 1-4, ref: Changes to NLA 124_Order №129_22042015.pdf
+	(57000 - 66000 @ 2160), (40), NO-OUTDOOR
 
-country RW:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5735 - 5835 @ 40), (N/A, 30)
+country RW: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
-country SA:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country SA: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
 
+# SE as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# SE: https://pts.se/globalassets/startpage/dokument/legala-dokument/foreskrifter/radio/beslutade_ptsfs-2018-3-undantagsforeskrifter.pdf
 country SE: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country SG:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+# https://www.imda.gov.sg/-/media/Imda/Files/Regulation-Licensing-and-Consultations/ICT-Standards/Telecommunication-Standards/Radio-Comms/IMDATSSRD.pdf
+# IMDA TS SRD, Issue 1 Revision 1, April 2019, subsequently "IMDA TS SRD"
+# 2400 - 2483.5 MHz: IMDA TS SRD, Table 1 (25); ANSI C63.10-2013 and FCC Part 15 Section 15.247 or EN 300 328
+# 5150 - 5350 MHz: IMDA TS SRD, Table 1 (29); FCC Part 15 Section 15.407 (1) 5.15-5.25 GHz (2) 5.25-5.35 GHz; EN 301 893
+# 5470 - 5725 MHz: IMDA TS SRD, Table 1 (30); FCC Part 15 Section 15.407 (2) 5.47-5.725 GHz; EN 301 893
+# 5725 - 5850 MHz: IMDA TS SRD, Table 1 (27); FCC Part 15 Section 15.247; FCC Part 15 Section 15.407 (3) 5.725-5.85 GHz
+# 57000 - 66000 MHz: IMDA TS SRD, Table 1 (31); ETSI EN 302 567
+# Note: 27dBm for 5470-5725MHz bands is 3dBm reduction per FCC Part 15 Section 15.407 (2) 5.47-5.725 GHz; EN 301 893 as referenced by IMDA TS SRD
+#  AU and BG regulatory domains use the same interpretation of cited FCC and ETSI standards
+# Note: The transmit power for 5250-5350MHz bands can be raised by 3dBm when TPC is implemented: IMDA TS SRD Table 1 (29)
+# Note: The transmit power for 5470-5725MHz bands can be raised by 3dBm when TPC is implemented: IMDA TS SRD Table 1 (30)
 
+country SG: DFS-FCC
+	(2400 - 2483.5 @ 40), (23)
+	(5150 - 5250 @ 80), (23), AUTO-BW
+	(5250 - 5350 @ 80), (20), DFS, AUTO-BW
+	(5470 - 5725 @ 160), (27), DFS
+	(5725 - 5850 @ 80), (30)
+	(57000 - 66000 @ 2160), (40)
+
+# SI as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# SI: https://www.akos-rs.si/bwa
 country SI: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (N/A, 20)
-	(5250 - 5330 @ 40), (N/A, 20), DFS
-	(5490 - 5710 @ 40), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
+# SK as part of EU/CEPT accepted decisions 2005/513/EC (5GHz RLAN, EN 301 893)
+# and 2006/771/EC (amended by 2008/432/EC, Short-Range Devices, EN 300 440)
+#  EU decision 2005/513/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02005D0513-20070213
+#  EU decision 2006/771/EC: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02008D0432-20080611
+# Harmonized CEPT countries (July 2019): https://www.ecodocdb.dk/download/25c41779-cd6e/Rec7003e.pdf
+# SK: https://www.teleoff.gov.sk/data/files/25911.pdf
+# SK: https://www.teleoff.gov.sk/data/files/41072.pdf
+# SK: https://www.teleoff.gov.sk/data/files/49125_vpr-01_2018-rusi-vpr-10_2014a21_2012-nespecifik-srd_021018.pdf
 country SK: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW, wmmrule=ETSI
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW, wmmrule=ETSI
+	(5470 - 5725 @ 160), (500 mW), DFS, wmmrule=ETSI
+	# short range devices (ETSI EN 300 440-1)
+	(5725 - 5875 @ 80), (25 mW)
+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
+	(57000 - 66000 @ 2160), (40)
 
-country SV:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 20), (3, 17)
-	(5250 - 5330 @ 20), (3, 23), DFS
-	(5735 - 5835 @ 20), (3, 30)
+# Source:
+# Regulation N° 2004-005 ART/DG/DRC/D.Rég
+country SN: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country SR: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
+
+country SV: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 20), (17)
+	(5250 - 5330 @ 20), (23), DFS
+	(5735 - 5835 @ 20), (30)
 
 country SY:
-	(2402 - 2482 @ 40), (N/A, 20)
+	(2402 - 2482 @ 40), (20)
 
-country TW:
-	(2402 - 2472 @ 40), (3, 27)
-	(5270 - 5330 @ 40), (3, 17), DFS
-	(5490 - 5710 @ 80), (3, 30), DFS
-	(5735 - 5815 @ 80), (3, 30)
+# Source:
+# http://www.telecommission.tc/Spectrum-plan20110324-101210.html
+country TC: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (24), AUTO-BW, wmmrule=ETSI
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW, wmmrule=ETSI
+	(5490 - 5730 @ 160), (24), DFS, wmmrule=ETSI
+	(5735 - 5835 @ 80), (30)
 
-country TH:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country TD: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
 
-country TT:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (3, 17)
-	(5250 - 5330 @ 40), (3, 20), DFS
-	(5490 - 5710 @ 40), (3, 20), DFS
-	(5735 - 5835 @ 40), (3, 30)
+country TG: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 40), (20)
+	(5250 - 5330 @ 40), (20), DFS
+	(5490 - 5710 @ 40), (27), DFS
 
-country TN:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 20), (N/A, 20)
-	(5250 - 5330 @ 20), (N/A, 20), DFS
+country TH: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
 
+country TN: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+
+# Source:
+# By-Law on Short Range Radio Devices (SRD) Oct 2015
+# https://www.btk.gov.tr/File/?path=ROOT%2f1%2fDocuments%2fOrdinance%2fBY%2DLAW%20ON%20SHORT%20RANGE%20DEVICES.pdf
+# Article 8
 country TR: DFS-ETSI
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (N/A, 20)
-	(5250 - 5330 @ 80), (N/A, 20), DFS
-	(5490 - 5710 @ 80), (N/A, 27), DFS
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+	(2400 - 2483.5 @ 40), (20)
+	(5170 - 5250 @ 80), (23), NO-OUTDOOR, AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, NO-OUTDOOR, AUTO-BW
+	(5470 - 5725 @ 160), (27), DFS
+	# 60 GHz band channels 1-4, ref: Etsi En 302 567
+	(57000 - 66000 @ 2160), (40)
+
+country TT: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+# Source:
+# Table of Frequency Allocations of Republic of China (Taiwan) / Feb 2017:
+#   https://www.motc.gov.tw/websitedowndoc?file=post/201702221012200.doc& \
+#	filedisplay=Table%2Bof%2Bradio%2Bfrequency%2Ballocation.doc
+# LP0002 Low-power Radio-frequency Devices Technical Regulations / 23 Aug 2016:
+#   http://www.ncc.gov.tw/english/show_file.aspx?table_name=news&file_sn=681
+country TW: DFS-FCC
+	# 2.4g band, LP0002 section 3.10.1
+	(2400 - 2483.5 @ 40), (30)
+	# 5g U-NII band, LP0002 section 4.7
+	# 5.15 ~ 5.25 GHz: 30 dBm for master mode, 23 dBm for clients
+	(5150 - 5250 @ 80), (23), AUTO-BW
+	(5250 - 5350 @ 80), (23), DFS, AUTO-BW
+	# This range ends at 5725 MHz, but channel 144 extends to 5730 MHz.
+	# Since 5725 ~ 5730 MHz belongs to the next range which has looser
+	# requirements, we can extend the range by 5 MHz to make the kernel
+	# happy and be able to use channel 144.
+	(5470 - 5730 @ 160), (23), DFS
+	(5725 - 5850 @ 80), (30)
+	# 60g band, LP0002 section 3.13.1.1 (3)(C), EIRP=40dBm(43dBm peak)
+	(57000 - 66000 @ 2160), (40)
  
+country TZ:
+	(2402 - 2482 @ 40), (20)
+	(5735 - 5835 @ 80), (30)
+
 # Source:
 # #914 / 06 Sep 2007: http://www.ucrf.gov.ua/uk/doc/nkrz/1196068874
 # #1174 / 23 Oct 2008: http://www.nkrz.gov.ua/uk/activities/ruling/1225269361
@@ -765,59 +1550,124 @@
 # Listed 5GHz range is a lowest common denominator for all related
 # rules in the referenced laws. Such a range is used because of
 # disputable definitions there.
-country UA:
-	(2400 - 2483.5 @ 40), (N/A, 20), NO-OUTDOOR
-	(5150 - 5350 @ 40), (N/A, 20), NO-OUTDOOR
-	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+country UA: DFS-ETSI
+	(2400 - 2483.5 @ 40), (20), NO-OUTDOOR
+	(5150 - 5250 @ 80), (20), NO-OUTDOOR, AUTO-BW
+	(5250 - 5350 @ 80), (20), DFS, NO-OUTDOOR, AUTO-BW
+	(5490 - 5670 @ 160), (20), DFS
+	(5735 - 5835 @ 80), (20)
+	# 60 GHz band channels 1-4, ref: Etsi En 302 567
+	(57000 - 66000 @ 2160), (40)
 
+country UG: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+# https://www.ecfr.gov/cgi-bin/text-idx?SID=eed706a2c49fd9271106c3228b0615f3&mc=true&node=pt47.1.15&rgn=div5
+# Title 47 Part 15 - Radio Frequency Devices, April 2, 2020
 country US: DFS-FCC
-	(2402 - 2472 @ 40), (3, 27)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5600 @ 80), (3, 24), DFS
-	(5650 - 5710 @ 40), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+	(2400 - 2483.5 @ 40), (30)
+	# 5.15 ~ 5.25 GHz: 30 dBm for master mode, 23 dBm for clients
+	(5150 - 5250 @ 80), (23), AUTO-BW
+	(5250 - 5350 @ 80), (23), DFS, AUTO-BW
+	# This range ends at 5725 MHz, but channel 144 extends to 5730 MHz.
+	# Since 5725 ~ 5730 MHz belongs to the next range which has looser
+	# requirements, we can extend the range by 5 MHz to make the kernel
+	# happy and be able to use channel 144.
+	(5470 - 5730 @ 160), (23), DFS
+	(5730 - 5850 @ 80), (30)
 	# 60g band
-	# reference: http://cfr.regstoday.com/47cfr15.aspx#47_CFR_15p255
-	# channels 1,2,3, EIRP=40dBm(43dBm peak)
-	(57240 - 63720 @ 2160), (N/A, 40)
+	# reference: section IV-D https://docs.fcc.gov/public/attachments/FCC-16-89A1.pdf
+	# channels 1-6 EIRP=40dBm(43dBm peak)
+	(57240 - 71000 @ 2160), (40)
 
-country UY:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 40), (3, 17)
-	(5250 - 5330 @ 40), (3, 20), DFS
-	(5490 - 5710 @ 40), (3, 20), DFS
-	(5735 - 5835 @ 40), (3, 30)
+country UY: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (23), AUTO-BW
+	(5250 - 5330 @ 80), (23), DFS, AUTO-BW
+	(5735 - 5835 @ 80), (30)
 
-country UZ:
-	(2402 - 2472 @ 40), (3, 27)
-	(5170 - 5250 @ 40), (3, 17)
-	(5250 - 5330 @ 40), (3, 20), DFS
-	(5490 - 5710 @ 40), (3, 20), DFS
-	(5735 - 5835 @ 40), (3, 30)
+# Source:
+# http://cemc.uz/article/1976/
+country UZ: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
 
-country VE:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5735 - 5815 @ 40), (N/A, 23)
+# Source:
+# http://www.ntrc.vc/regulations/Jun_2006_Spectrum_Managment_Regulations.pdf
+country VC: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS
 
-country VN:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+# Source:
+# Official Gazette (Gaceta Oficial) concerning Unlicensed transmitter use
+# (10 June 2013)
+# http://www.conatel.gob.ve/
+country VE: DFS-FCC
+	(2402 - 2482 @ 40), (30)
+	(5170 - 5250 @ 80), (23), AUTO-BW
+	(5250 - 5330 @ 80), (23), DFS, AUTO-BW
+	(5735 - 5835 @ 80), (30)
+
+country VI: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (24), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country VN: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+# Source:
+# http://www.trr.vu/attachments/category/130/GURL_for_Short-range_Radiocommunication_Devices2.pdf
+country VU: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17), AUTO-BW
+	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
+	(5490 - 5730 @ 160), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country WF: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW, wmmrule=ETSI
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW, wmmrule=ETSI
+	(5490 - 5710 @ 160), (27), DFS, wmmrule=ETSI
+
+country WS: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 40), (20)
+	(5250 - 5330 @ 40), (20), DFS
+	(5490 - 5710 @ 40), (27), DFS
 
 country YE:
-	(2402 - 2482 @ 40), (N/A, 20)
+	(2402 - 2482 @ 40), (20)
 
-country ZA:
-	(2402 - 2482 @ 40), (N/A, 20)
-	(5170 - 5250 @ 80), (3, 17)
-	(5250 - 5330 @ 80), (3, 24), DFS
-	(5490 - 5710 @ 80), (3, 24), DFS
-	(5735 - 5835 @ 80), (3, 30)
+country YT: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW, wmmrule=ETSI
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW, wmmrule=ETSI
+	(5490 - 5710 @ 160), (27), DFS, wmmrule=ETSI
 
-country ZW:
-	(2402 - 2482 @ 40), (N/A, 20)
+country ZA: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (30)
+
+country ZW: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20), AUTO-BW
+	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (27), DFS