diff -ruw linux-3.11.10/arch/arm/include/asm/cacheflush.h linux-3.11.10-fbx/arch/arm/include/asm/cacheflush.h
--- linux-3.11.10/arch/arm/include/asm/cacheflush.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/include/asm/cacheflush.h	2015-09-29 11:15:04.817724647 +0200
@@ -212,6 +212,7 @@
 static inline void __flush_icache_all(void)
 {
 	__flush_icache_preferred();
+	dsb();
 }
 
 /*
diff -ruw linux-3.11.10/arch/arm/include/asm/checksum.h linux-3.11.10-fbx/arch/arm/include/asm/checksum.h
--- linux-3.11.10/arch/arm/include/asm/checksum.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/include/asm/checksum.h	2015-09-29 11:15:04.817724647 +0200
@@ -87,19 +87,34 @@
 csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
 		   unsigned short proto, __wsum sum)
 {
+	u32 lenprot = len | proto << 16;
+
+	if (__builtin_constant_p(sum) && sum == 0) {
 	__asm__(
-	"adds	%0, %1, %2		@ csum_tcpudp_nofold	\n\
-	adcs	%0, %0, %3					\n"
+		"adds	%0, %1, %2	@ csum_tcpudp_nofold0	\n\t"
 #ifdef __ARMEB__
-	"adcs	%0, %0, %4					\n"
+		"adcs	%0, %0, %3				\n\t"
 #else
-	"adcs	%0, %0, %4, lsl #8				\n"
+		"adcs	%0, %0, %3, ror #8			\n\t"
 #endif
-	"adcs	%0, %0, %5					\n\
-	adc	%0, %0, #0"
+		"adc	%0, %0, #0"
 	: "=&r"(sum)
-	: "r" (sum), "r" (daddr), "r" (saddr), "r" (len), "Ir" (htons(proto))
+		: "r" (daddr), "r" (saddr), "r" (lenprot)
 	: "cc");
+	} else {
+		__asm__(
+		"adds	%0, %1, %2	@ csum_tcpudp_nofold	\n\t"
+		"adcs	%0, %0, %3				\n\t"
+#ifdef __ARMEB__
+		"adcs	%0, %0, %4				\n\t"
+#else
+		"adcs	%0, %0, %4, ror #8			\n\t"
+#endif
+		"adc	%0, %0, #0"
+		: "=&r"(sum)
+		: "r" (sum), "r" (daddr), "r" (saddr), "r" (lenprot)
+		: "cc");
+	}
 	return sum;
 }	
 /*
diff -ruw linux-3.11.10/arch/arm/include/asm/div64.h linux-3.11.10-fbx/arch/arm/include/asm/div64.h
--- linux-3.11.10/arch/arm/include/asm/div64.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/include/asm/div64.h	2014-07-29 21:29:41.209848720 +0200
@@ -156,7 +156,7 @@
 		/* Select the best insn combination to perform the   */	\
 		/* actual __m * __n / (__p << 64) operation.         */	\
 		if (!__c) {						\
-			asm (	"umull	%Q0, %R0, %1, %Q2\n\t"		\
+			asm (	"umull	%Q0, %R0, %Q1, %Q2\n\t"		\
 				"mov	%Q0, #0"			\
 				: "=&r" (__res)				\
 				: "r" (__m), "r" (__n)			\
diff -ruw linux-3.11.10/arch/arm/include/asm/futex.h linux-3.11.10-fbx/arch/arm/include/asm/futex.h
--- linux-3.11.10/arch/arm/include/asm/futex.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/include/asm/futex.h	2015-09-29 11:15:04.817724647 +0200
@@ -3,11 +3,6 @@
 
 #ifdef __KERNEL__
 
-#if defined(CONFIG_CPU_USE_DOMAINS) && defined(CONFIG_SMP)
-/* ARM doesn't provide unprivileged exclusive memory accessors */
-#include <asm-generic/futex.h>
-#else
-
 #include <linux/futex.h>
 #include <linux/uaccess.h>
 #include <asm/errno.h>
@@ -164,6 +159,5 @@
 	return ret;
 }
 
-#endif /* !(CPU_USE_DOMAINS && SMP) */
 #endif /* __KERNEL__ */
 #endif /* _ASM_ARM_FUTEX_H */
diff -ruw linux-3.11.10/arch/arm/include/asm/memory.h linux-3.11.10-fbx/arch/arm/include/asm/memory.h
--- linux-3.11.10/arch/arm/include/asm/memory.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/include/asm/memory.h	2015-09-29 11:15:04.821724673 +0200
@@ -30,14 +30,15 @@
  */
 #define UL(x) _AC(x, UL)
 
+/* PAGE_OFFSET - the virtual address of the start of the kernel image */
+#define PAGE_OFFSET		UL(CONFIG_PAGE_OFFSET)
+
 #ifdef CONFIG_MMU
 
 /*
- * PAGE_OFFSET - the virtual address of the start of the kernel image
  * TASK_SIZE - the maximum size of a user space task.
  * TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area
  */
-#define PAGE_OFFSET		UL(CONFIG_PAGE_OFFSET)
 #define TASK_SIZE		(UL(CONFIG_PAGE_OFFSET) - UL(SZ_16M))
 #define TASK_UNMAPPED_BASE	ALIGN(TASK_SIZE / 3, SZ_16M)
 
@@ -108,10 +109,6 @@
 #define END_MEM     		(UL(CONFIG_DRAM_BASE) + CONFIG_DRAM_SIZE)
 #endif
 
-#ifndef PAGE_OFFSET
-#define PAGE_OFFSET		(PHYS_OFFSET)
-#endif
-
 /*
  * The module can be at any place in ram in nommu mode.
  */
diff -ruw linux-3.11.10/arch/arm/include/asm/outercache.h linux-3.11.10-fbx/arch/arm/include/asm/outercache.h
--- linux-3.11.10/arch/arm/include/asm/outercache.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/include/asm/outercache.h	2015-09-29 11:15:04.821724673 +0200
@@ -37,10 +37,10 @@
 	void (*resume)(void);
 };
 
-#ifdef CONFIG_OUTER_CACHE
-
 extern struct outer_cache_fns outer_cache;
 
+#ifdef CONFIG_OUTER_CACHE
+
 static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
 {
 	if (outer_cache.inv_range)
diff -ruw linux-3.11.10/arch/arm/include/asm/pgtable-2level.h linux-3.11.10-fbx/arch/arm/include/asm/pgtable-2level.h
--- linux-3.11.10/arch/arm/include/asm/pgtable-2level.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/include/asm/pgtable-2level.h	2015-09-29 11:15:04.821724673 +0200
@@ -140,6 +140,7 @@
 #define L_PTE_MT_DEV_NONSHARED	(_AT(pteval_t, 0x0c) << 2)	/* 1100 */
 #define L_PTE_MT_DEV_WC		(_AT(pteval_t, 0x09) << 2)	/* 1001 */
 #define L_PTE_MT_DEV_CACHED	(_AT(pteval_t, 0x0b) << 2)	/* 1011 */
+#define L_PTE_MT_VECTORS	(_AT(pteval_t, 0x0f) << 2)	/* 1111 */
 #define L_PTE_MT_MASK		(_AT(pteval_t, 0x0f) << 2)
 
 #ifndef __ASSEMBLY__
diff -ruw linux-3.11.10/arch/arm/include/asm/pgtable.h linux-3.11.10-fbx/arch/arm/include/asm/pgtable.h
--- linux-3.11.10/arch/arm/include/asm/pgtable.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/include/asm/pgtable.h	2015-09-29 11:15:04.821724673 +0200
@@ -61,7 +61,7 @@
  * mapping to be mapped at.  This is particularly important for
  * non-high vector CPUs.
  */
-#define FIRST_USER_ADDRESS	PAGE_SIZE
+#define FIRST_USER_ADDRESS	(PAGE_SIZE * 2)
 
 /*
  * Use TASK_SIZE as the ceiling argument for free_pgtables() and
diff -ruw linux-3.11.10/arch/arm/include/asm/uaccess.h linux-3.11.10-fbx/arch/arm/include/asm/uaccess.h
--- linux-3.11.10/arch/arm/include/asm/uaccess.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/include/asm/uaccess.h	2015-09-29 11:15:04.825724702 +0200
@@ -164,8 +164,9 @@
 #define __put_user_check(x,p)							\
 	({								\
 		unsigned long __limit = current_thread_info()->addr_limit - 1; \
+		const typeof(*(p)) __user *__tmp_p = (p);		\
 		register const typeof(*(p)) __r2 asm("r2") = (x);	\
-		register const typeof(*(p)) __user *__p asm("r0") = (p);\
+		register const typeof(*(p)) __user *__p asm("r0") = __tmp_p; \
 		register unsigned long __l asm("r1") = __limit;		\
 		register int __e asm("r0");				\
 		switch (sizeof(*(__p))) {				\
diff -ruw linux-3.11.10/arch/arm/include/asm/unistd.h linux-3.11.10-fbx/arch/arm/include/asm/unistd.h
--- linux-3.11.10/arch/arm/include/asm/unistd.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/include/asm/unistd.h	2015-09-29 11:15:04.825724702 +0200
@@ -48,6 +48,5 @@
  */
 #define __IGNORE_fadvise64_64
 #define __IGNORE_migrate_pages
-#define __IGNORE_kcmp
 
 #endif /* __ASM_ARM_UNISTD_H */
diff -ruw linux-3.11.10/arch/arm/include/uapi/asm/setup.h linux-3.11.10-fbx/arch/arm/include/uapi/asm/setup.h
--- linux-3.11.10/arch/arm/include/uapi/asm/setup.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/include/uapi/asm/setup.h	2015-09-29 11:15:04.825724702 +0200
@@ -143,6 +143,37 @@
 	__u32 fmemclk;
 };
 
+/*
+ * bootloader version
+ */
+#define ATAG_LOADER_VERSION	0x41000045
+struct tag_loader_version {
+	char version[1];
+};
+
+/*
+ * freebox serial info, make RMK cry a bit more :(
+ */
+#include <linux/fbxserial.h>
+#define ATAG_FBXSERIAL          0x41000044
+struct tag_fbxserial {
+        struct fbx_serial serial;
+};
+
+/*
+ * boot_info tag, used by bank0 in conjuction with fbxhwinfo to
+ * sortout whether:
+ *
+ * - user forced a bank0 boot
+ * - user forced a bank0 boot _and_ asked for nvram to be erased.
+ */
+#define ATAG_BOOT_INFO		0x41000046
+struct tag_boot_info {
+	u32 erase_nvram;		/* == 1 if user selected nvram erase */
+	u32 bank0_forced;		/* == 1 if bank0 boot was
+					   forced by user*/
+};
+
 struct tag {
 	struct tag_header hdr;
 	union {
@@ -165,6 +196,12 @@
 		 * DC21285 specific
 		 */
 		struct tag_memclk	memclk;
+		/*
+		 * Freebox specific
+		 */
+		struct tag_loader_version loader_version;
+		struct tag_fbxserial	fbxserial;
+		struct tag_boot_info	boot_info;
 	} u;
 };
 
diff -ruw linux-3.11.10/arch/arm/Kconfig linux-3.11.10-fbx/arch/arm/Kconfig
--- linux-3.11.10/arch/arm/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/Kconfig	2015-09-29 17:24:52.820409367 +0200
@@ -22,9 +22,10 @@
 	select HARDIRQS_SW_RESEND
 	select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL
 	select HAVE_ARCH_KGDB
-	select HAVE_ARCH_SECCOMP_FILTER
+	select HAVE_ARCH_SECCOMP_FILTER if (AEABI && !OABI_COMPAT)
 	select HAVE_ARCH_TRACEHOOK
 	select HAVE_BPF_JIT
+	select HAVE_SECCOMP_FILTER_JIT
 	select HAVE_C_RECORDMCOUNT
 	select HAVE_DEBUG_KMEMLEAK
 	select HAVE_DMA_API_DEBUG
@@ -890,7 +891,7 @@
 	bool "ARMv5 based platforms (ARM926T, XSCALE, PJ1, ...)"
 	depends on !ARCH_MULTI_V6_V7
 	select ARCH_MULTI_V4_V5
-	select CPU_ARM926T if (!CPU_ARM946E || CPU_ARM1020 || \
+	select CPU_ARM926T if !(CPU_ARM946E || CPU_ARM1020 || \
 		CPU_ARM1020E || CPU_ARM1022 || CPU_ARM1026 || \
 		CPU_XSCALE || CPU_XSC3 || CPU_MOHAWK || CPU_FEROCEON)
 
@@ -1435,6 +1436,8 @@
 source "drivers/pci/Kconfig"
 source "drivers/pci/pcie/Kconfig"
 
+source "drivers/pci/hotplug/Kconfig"
+
 source "drivers/pcmcia/Kconfig"
 
 endmenu
@@ -1540,6 +1543,7 @@
 
 choice
 	prompt "Memory split"
+	depends on MMU
 	default VMSPLIT_3G
 	help
 	  Select the desired split between kernel and user memory.
@@ -1557,6 +1561,7 @@
 
 config PAGE_OFFSET
 	hex
+	default PHYS_OFFSET if !MMU
 	default 0x40000000 if VMSPLIT_1G
 	default 0x80000000 if VMSPLIT_2G
 	default 0xC0000000
@@ -2206,6 +2211,13 @@
 config ARM_CPU_SUSPEND
 	def_bool PM_SLEEP
 
+config ARM_BPF_JIT_HARDEN
+	bool "ARM BPF JIT Hardening"
+	depends on BPF_JIT
+	help
+	  Harden BPF JIT generated code by xoring all 32bit constants
+	  in the litteral pool with a runtime random-selected value.
+
 endmenu
 
 source "net/Kconfig"
@@ -2223,3 +2235,4 @@
 source "lib/Kconfig"
 
 source "arch/arm/kvm/Kconfig"
+
diff -ruw linux-3.11.10/arch/arm/Kconfig.debug linux-3.11.10-fbx/arch/arm/Kconfig.debug
--- linux-3.11.10/arch/arm/Kconfig.debug	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/Kconfig.debug	2015-09-29 11:15:04.777724336 +0200
@@ -831,6 +831,14 @@
 	  kernel low-level debugging functions. Add earlyprintk to your
 	  kernel parameters to enable this console.
 
+config DEBUG_LL_UART1
+	bool "Use UART1 for low-level debugging"
+	depends on DEBUG_LL
+	help
+	  Say Y here to use the second UART when enabling DEBUG_LL, if
+	  your board does not have the first UART wired. This will
+	  enlarge the uncompressed kernel size by exactly four bytes.
+
 config OC_ETM
 	bool "On-chip ETM and ETB"
 	depends on ARM_AMBA
diff -ruw linux-3.11.10/arch/arm/kernel/bios32.c linux-3.11.10-fbx/arch/arm/kernel/bios32.c
--- linux-3.11.10/arch/arm/kernel/bios32.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/kernel/bios32.c	2015-09-29 11:15:04.829724739 +0200
@@ -670,3 +670,23 @@
 	pci_io_desc.pfn = pfn;
 	iotable_init(&pci_io_desc, 1);
 }
+
+#ifdef CONFIG_HOTPLUG_PCI
+/*
+ *	pci hotplug won't map the irq for new devices, so use a fixup
+ *	to work around this.
+ */
+void pci_fixup_hotplug_irq(struct pci_dev *dev)
+{
+	int irq;
+
+	printk("pci_fixup_hotplug_irq.\n");
+
+	irq = pcibios_map_irq(dev, PCI_SLOT(dev->devfn), dev->pin);
+	if (irq == -1)
+		irq = 0;
+	dev->irq = irq;
+	pcibios_update_irq(dev, dev->irq);
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_hotplug_irq);
+#endif
diff -ruw linux-3.11.10/arch/arm/kernel/entry-armv.S linux-3.11.10-fbx/arch/arm/kernel/entry-armv.S
--- linux-3.11.10/arch/arm/kernel/entry-armv.S	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/kernel/entry-armv.S	2015-09-29 11:15:04.829724739 +0200
@@ -192,6 +192,7 @@
 	svc_entry
 	mov	r2, sp
 	dabt_helper
+ THUMB(	ldr	r5, [sp, #S_PSR]	)	@ potentially updated CPSR
 	svc_exit r5				@ return from exception
  UNWIND(.fnend		)
 ENDPROC(__dabt_svc)
diff -ruw linux-3.11.10/arch/arm/kernel/entry-header.S linux-3.11.10-fbx/arch/arm/kernel/entry-header.S
--- linux-3.11.10/arch/arm/kernel/entry-header.S	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/kernel/entry-header.S	2015-09-29 11:15:04.829724739 +0200
@@ -132,6 +132,10 @@
 	orrne	r5, V7M_xPSR_FRAMEPTRALIGN
 	biceq	r5, V7M_xPSR_FRAMEPTRALIGN
 
+	@ ensure bit 0 is cleared in the PC, otherwise behaviour is
+	@ unpredictable
+	bic	r4, #1
+
 	@ write basic exception frame
 	stmdb	r2!, {r1, r3-r5}
 	ldmia	sp, {r1, r3-r5}
diff -ruw linux-3.11.10/arch/arm/kernel/process.c linux-3.11.10-fbx/arch/arm/kernel/process.c
--- linux-3.11.10/arch/arm/kernel/process.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/kernel/process.c	2015-09-29 11:15:04.833724764 +0200
@@ -404,6 +404,7 @@
 unsigned long get_wchan(struct task_struct *p)
 {
 	struct stackframe frame;
+	unsigned long stack_page;
 	int count = 0;
 	if (!p || p == current || p->state == TASK_RUNNING)
 		return 0;
@@ -412,9 +413,11 @@
 	frame.sp = thread_saved_sp(p);
 	frame.lr = 0;			/* recovered from the stack */
 	frame.pc = thread_saved_pc(p);
+	stack_page = (unsigned long)task_stack_page(p);
 	do {
-		int ret = unwind_frame(&frame);
-		if (ret < 0)
+		if (frame.sp < stack_page ||
+		    frame.sp >= stack_page + THREAD_SIZE ||
+		    unwind_frame(&frame) < 0)
 			return 0;
 		if (!in_sched_functions(frame.pc))
 			return frame.pc;
diff -ruw linux-3.11.10/arch/arm/kernel/setup.c linux-3.11.10-fbx/arch/arm/kernel/setup.c
--- linux-3.11.10/arch/arm/kernel/setup.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/kernel/setup.c	2015-09-29 11:15:04.833724764 +0200
@@ -622,6 +622,7 @@
 int __init arm_add_memory(phys_addr_t start, phys_addr_t size)
 {
 	struct membank *bank = &meminfo.bank[meminfo.nr_banks];
+	u64 aligned_start;
 
 	if (meminfo.nr_banks >= NR_BANKS) {
 		printk(KERN_CRIT "NR_BANKS too low, "
@@ -634,10 +635,16 @@
 	 * Size is appropriately rounded down, start is rounded up.
 	 */
 	size -= start & ~PAGE_MASK;
-	bank->start = PAGE_ALIGN(start);
+	aligned_start = PAGE_ALIGN(start);
 
-#ifndef CONFIG_ARM_LPAE
-	if (bank->start + size < bank->start) {
+#ifndef CONFIG_ARCH_PHYS_ADDR_T_64BIT
+	if (aligned_start > ULONG_MAX) {
+		printk(KERN_CRIT "Ignoring memory at 0x%08llx outside "
+		       "32-bit physical address space\n", (long long)start);
+		return -EINVAL;
+	}
+
+	if (aligned_start + size > ULONG_MAX) {
 		printk(KERN_CRIT "Truncating memory at 0x%08llx to fit in "
 			"32-bit physical address space\n", (long long)start);
 		/*
@@ -645,10 +652,25 @@
 		 * 32 bits, we use ULONG_MAX as the upper limit rather than 4GB.
 		 * This means we lose a page after masking.
 		 */
-		size = ULONG_MAX - bank->start;
+		size = ULONG_MAX - aligned_start;
 	}
 #endif
 
+	if (aligned_start < PHYS_OFFSET) {
+		if (aligned_start + size <= PHYS_OFFSET) {
+			pr_info("Ignoring memory below PHYS_OFFSET: 0x%08llx-0x%08llx\n",
+				aligned_start, aligned_start + size);
+			return -EINVAL;
+		}
+
+		pr_info("Ignoring memory below PHYS_OFFSET: 0x%08llx-0x%08llx\n",
+			aligned_start, (u64)PHYS_OFFSET);
+
+		size -= PHYS_OFFSET - aligned_start;
+		aligned_start = PHYS_OFFSET;
+	}
+
+	bank->start = aligned_start;
 	bank->size = size & ~(phys_addr_t)(PAGE_SIZE - 1);
 
 	/*
diff -ruw linux-3.11.10/arch/arm/kernel/stacktrace.c linux-3.11.10-fbx/arch/arm/kernel/stacktrace.c
--- linux-3.11.10/arch/arm/kernel/stacktrace.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/kernel/stacktrace.c	2015-09-29 11:15:04.833724764 +0200
@@ -31,7 +31,7 @@
 	high = ALIGN(low, THREAD_SIZE);
 
 	/* check current frame pointer is within bounds */
-	if (fp < (low + 12) || fp + 4 >= high)
+	if (fp < low + 12 || fp > high - 4)
 		return -EINVAL;
 
 	/* restore the registers from the stack frame */
@@ -83,13 +83,16 @@
 	return trace->nr_entries >= trace->max_entries;
 }
 
-void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+/* This must be noinline to so that our skip calculation works correctly */
+static noinline void __save_stack_trace(struct task_struct *tsk,
+	struct stack_trace *trace, unsigned int nosched)
 {
 	struct stack_trace_data data;
 	struct stackframe frame;
 
 	data.trace = trace;
 	data.skip = trace->skip;
+	data.no_sched_functions = nosched;
 
 	if (tsk != current) {
 #ifdef CONFIG_SMP
@@ -102,7 +105,6 @@
 			trace->entries[trace->nr_entries++] = ULONG_MAX;
 		return;
 #else
-		data.no_sched_functions = 1;
 		frame.fp = thread_saved_fp(tsk);
 		frame.sp = thread_saved_sp(tsk);
 		frame.lr = 0;		/* recovered from the stack */
@@ -111,11 +113,12 @@
 	} else {
 		register unsigned long current_sp asm ("sp");
 
-		data.no_sched_functions = 0;
+		/* We don't want this function nor the caller */
+		data.skip += 2;
 		frame.fp = (unsigned long)__builtin_frame_address(0);
 		frame.sp = current_sp;
 		frame.lr = (unsigned long)__builtin_return_address(0);
-		frame.pc = (unsigned long)save_stack_trace_tsk;
+		frame.pc = (unsigned long)__save_stack_trace;
 	}
 
 	walk_stackframe(&frame, save_trace, &data);
@@ -123,9 +126,14 @@
 		trace->entries[trace->nr_entries++] = ULONG_MAX;
 }
 
+void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+{
+	__save_stack_trace(tsk, trace, 1);
+}
+
 void save_stack_trace(struct stack_trace *trace)
 {
-	save_stack_trace_tsk(current, trace);
+	__save_stack_trace(current, trace, 0);
 }
 EXPORT_SYMBOL_GPL(save_stack_trace);
 #endif
diff -ruw linux-3.11.10/arch/arm/kernel/traps.c linux-3.11.10-fbx/arch/arm/kernel/traps.c
--- linux-3.11.10/arch/arm/kernel/traps.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/kernel/traps.c	2015-09-29 11:15:04.837724793 +0200
@@ -35,7 +35,13 @@
 #include <asm/tls.h>
 #include <asm/system_misc.h>
 
-static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" };
+static const char *handler[]= {
+	"prefetch abort",
+	"data abort",
+	"address exception",
+	"interrupt",
+	"undefined instruction",
+};
 
 void *vectors_page;
 
diff -ruw linux-3.11.10/arch/arm/mach-kirkwood/common.c linux-3.11.10-fbx/arch/arm/mach-kirkwood/common.c
--- linux-3.11.10/arch/arm/mach-kirkwood/common.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/common.c	2015-09-29 11:15:04.881725130 +0200
@@ -21,6 +21,7 @@
 #include <linux/timex.h>
 #include <linux/kexec.h>
 #include <linux/reboot.h>
+#include <linux/spi/orion_tdm_spi.h>
 #include <net/dsa.h>
 #include <asm/page.h>
 #include <asm/mach/map.h>
@@ -222,7 +223,7 @@
 
 void __init kirkwood_clk_init(void)
 {
-	struct clk *runit, *sata0, *sata1, *usb0, *sdio;
+	struct clk *runit, *sata0, *sata1, *usb0, *sdio, *tdm;
 	struct clk *crypto, *xor0, *xor1, *pex0, *pex1, *audio;
 
 	tclk = clk_register_fixed_rate(NULL, "tclk", NULL,
@@ -245,7 +246,7 @@
 	pex1 = kirkwood_register_gate_fn("pex1",   CGC_BIT_PEX1,
 					 NULL, disable_pcie1);
 	audio = kirkwood_register_gate("audio",  CGC_BIT_AUDIO);
-	kirkwood_register_gate("tdm",    CGC_BIT_TDM);
+	tdm = kirkwood_register_gate("tdm",    CGC_BIT_TDM);
 	kirkwood_register_gate("tsu",    CGC_BIT_TSU);
 
 	/* clkdev entries, mapping clks to devices */
@@ -254,9 +255,11 @@
 	orion_clkdev_add(NULL, MV643XX_ETH_NAME ".0", ge0);
 	orion_clkdev_add(NULL, MV643XX_ETH_NAME ".1", ge1);
 	orion_clkdev_add(NULL, "orion_wdt", tclk);
+	orion_clkdev_add(NULL, "orion_fbxwdt", tclk);
 	orion_clkdev_add("0", "sata_mv.0", sata0);
 	orion_clkdev_add("1", "sata_mv.0", sata1);
 	orion_clkdev_add(NULL, "orion-ehci.0", usb0);
+	orion_clkdev_add(NULL, "fsl-usb2-udc.0", usb0);
 	orion_clkdev_add(NULL, "orion_nand", runit);
 	orion_clkdev_add(NULL, "mvsdio", sdio);
 	orion_clkdev_add(NULL, "mv_crypto", crypto);
@@ -267,6 +270,7 @@
 	orion_clkdev_add(NULL, "kirkwood-i2s", audio);
 	orion_clkdev_add(NULL, MV64XXX_I2C_CTLR_NAME ".0", runit);
 	orion_clkdev_add(NULL, MV64XXX_I2C_CTLR_NAME ".1", runit);
+	orion_clkdev_add(NULL, "orion_tdm_spi.0", tdm);
 
 	/* Marvell says runit is used by SPI, UART, NAND, TWSI, ...,
 	 * so should never be gated.
@@ -279,7 +283,23 @@
  ****************************************************************************/
 void __init kirkwood_ehci_init(void)
 {
-	orion_ehci_init(USB_PHYS_BASE, IRQ_KIRKWOOD_USB, EHCI_PHY_NA);
+	u32 val;
+
+	/* Analog Group Configuration Register */
+	val = readl(DEV_BUS_VIRT_BASE + 0x7c);
+	val &= ~(0x3 << 3);
+	val |= (0x1 << 3);
+	writel(val, DEV_BUS_VIRT_BASE + 0x7c);
+
+	orion_ehci_init(USB_PHYS_BASE, IRQ_KIRKWOOD_USB, EHCI_PHY_KW);
+}
+
+/*****************************************************************************
+ * UDC
+ ****************************************************************************/
+void __init kirkwood_udc_init(void)
+{
+	orion_udc_init(USB_PHYS_BASE, IRQ_KIRKWOOD_USB);
 }
 
 
@@ -350,6 +370,7 @@
 	kirkwood_nand_data.parts = parts;
 	kirkwood_nand_data.nr_parts = nr_parts;
 	kirkwood_nand_data.chip_delay = chip_delay;
+	kirkwood_nand_data.ecc = NAND_ECC_SOFT;
 	platform_device_register(&kirkwood_nand_flash);
 }
 
@@ -362,6 +383,24 @@
 	platform_device_register(&kirkwood_nand_flash);
 }
 
+void __init kirkwood_nand_init_ecc(struct mtd_partition *parts, int nr_parts,
+				  int chip_delay, struct kirkwood_nand_ecc *ecc)
+{
+	if (!ecc) {
+		kirkwood_nand_data.ecc = NAND_ECC_SOFT;
+		kirkwood_nand_data.bch_ecc_size =
+			kirkwood_nand_data.bch_ecc_bytes = 0;
+	} else {
+		kirkwood_nand_data.ecc = ecc->ecc;
+		kirkwood_nand_data.bch_ecc_bytes = ecc->bch_ecc_bytes;
+		kirkwood_nand_data.bch_ecc_size = ecc->bch_ecc_size;
+	}
+	kirkwood_nand_data.parts = parts;
+	kirkwood_nand_data.nr_parts = nr_parts;
+	kirkwood_nand_data.chip_delay = chip_delay;
+	platform_device_register(&kirkwood_nand_flash);
+}
+
 /*****************************************************************************
  * SoC RTC
  ****************************************************************************/
@@ -431,15 +470,58 @@
 	orion_spi_init(SPI_PHYS_BASE);
 }
 
+/*****************************************************************************
+ * TDM SPI
+ ****************************************************************************/
+static struct orion_tdm_spi_info kirkwood_tdm_spi_plat_data = {
+};
+
+static struct resource kirkwood_tdm_spi_resources[] = {
+	{
+		.start	= TDM_SPI_PHYS_BASE,
+		.end	= TDM_SPI_PHYS_BASE + SZ_64K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device kirkwood_tdm_spi = {
+	.name		= "orion_tdm_spi",
+	.id		= 0,
+	.resource	= kirkwood_tdm_spi_resources,
+	.dev		= {
+		.platform_data	= &kirkwood_tdm_spi_plat_data,
+	},
+	.num_resources	= ARRAY_SIZE(kirkwood_tdm_spi_resources),
+};
+
+void __init kirkwood_tdm_spi_init(int *cs_gpio)
+{
+	memcpy(kirkwood_tdm_spi_plat_data.cs_use_gpio, cs_gpio,
+	       sizeof (kirkwood_tdm_spi_plat_data.cs_use_gpio));
+	platform_device_register(&kirkwood_tdm_spi);
+}
+
 
 /*****************************************************************************
  * I2C
  ****************************************************************************/
-void __init kirkwood_i2c_init(void)
+void __init kirkwood_i2c_init(unsigned int freq_m, unsigned int freq_n)
 {
-	orion_i2c_init(I2C_PHYS_BASE, IRQ_KIRKWOOD_TWSI, 8);
+	/*
+	 * FYI
+	 *
+	 * Fscl = Ftclk / (10 * (freq_m + 1) * 2 ^ (freq_n + 1))
+	 *
+	 * 0 <= M <= 15
+	 * 0 <= N <= 7
+	 */
+	orion_i2c_init(I2C_PHYS_BASE, IRQ_KIRKWOOD_TWSI, freq_m, freq_n);
 }
 
+void __init kirkwood_i2c1_init(unsigned int freq_m, unsigned int freq_n)
+{
+	orion_i2c_1_init(I2C1_PHYS_BASE, IRQ_KIRKWOOD_TWSI1, freq_m, freq_n);
+}
 
 /*****************************************************************************
  * UART0
@@ -532,6 +614,8 @@
 	mvebu_mbus_init("marvell,kirkwood-mbus",
 			BRIDGE_WINS_BASE, BRIDGE_WINS_SZ,
 			DDR_WINDOW_CPU_BASE, DDR_WINDOW_CPU_SZ);
+
+	init_dma_coherent_pool_size(SZ_1M);
 }
 
 int kirkwood_tclk;
@@ -621,6 +705,29 @@
 	platform_device_register(&kirkwood_cpufreq_device);
 }
 
+
+/*****************************************************************************
+ * Temperature sensor
+ ****************************************************************************/
+static struct resource kirkwood_coretemp_resources[] = {
+	[0] = {
+		.start	= TEMP_PHYS_BASE,
+		.end	= TEMP_PHYS_BASE + sizeof (u32) - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+struct platform_device kirkwood_coretemp_device = {
+	.name		= "kirkwood-coretemp",
+	.id		= -1,
+	.num_resources = ARRAY_SIZE(kirkwood_coretemp_resources),
+	.resource = kirkwood_coretemp_resources,
+};
+
+static void __init kirkwood_coretemp_init(void)
+{
+	platform_device_register(&kirkwood_coretemp_device);
+}
+
 /*****************************************************************************
  * General
  ****************************************************************************/
@@ -691,8 +798,26 @@
 #endif
 }
 
+/*
+ * implement workaround for FE-MISC-120 entry in errata document:
+ * "Miscellaneous Register Setting in an Undocumented Register"
+ */
+static void __init kirkwood_fe_misc_120(void)
+{
+	u32 reg = readl(BRIDGE_VIRT_BASE + 0x100e4);
+	reg |= (3 << 25);
+	writel(reg, BRIDGE_VIRT_BASE + 0x100e4);
+}
+
 void __init kirkwood_init(void)
 {
+	u32 dev, rev;
+
+	kirkwood_pcie_id(&dev, &rev);
+
+	if (dev == MV88F6282_DEV_ID)
+		kirkwood_fe_misc_120();
+
 	pr_info("Kirkwood: %s, TCLK=%d.\n", kirkwood_id(), kirkwood_tclk);
 
 	/*
@@ -716,6 +841,8 @@
 	kirkwood_xor0_init();
 	kirkwood_xor1_init();
 	kirkwood_crypto_init();
+	if (dev == MV88F6282_DEV_ID)
+		kirkwood_coretemp_init();
 
 	kirkwood_cpuidle_init();
 #ifdef CONFIG_KEXEC
diff -ruw linux-3.11.10/arch/arm/mach-kirkwood/common.h linux-3.11.10-fbx/arch/arm/mach-kirkwood/common.h
--- linux-3.11.10/arch/arm/mach-kirkwood/common.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/common.h	2015-09-29 11:15:04.881725130 +0200
@@ -24,6 +24,12 @@
 #define KW_PCIE0	(1 << 0)
 #define KW_PCIE1	(1 << 1)
 
+struct kirkwood_nand_ecc {
+	u8 ecc;
+	u16 bch_ecc_size;
+	u16 bch_ecc_bytes;
+};
+
 /*
  * Basic Kirkwood init functions used early by machine-setup.
  */
@@ -45,12 +51,16 @@
 void kirkwood_sata_init(struct mv_sata_platform_data *sata_data);
 void kirkwood_sdio_init(struct mvsdio_platform_data *mvsdio_data);
 void kirkwood_spi_init(void);
-void kirkwood_i2c_init(void);
+void kirkwood_tdm_spi_init(int cs_gpio[2]);
+void kirkwood_i2c_init(unsigned int freq_m, unsigned int freq_n);
+void kirkwood_i2c1_init(unsigned int freq_m, unsigned int freq_n);
 void kirkwood_uart0_init(void);
 void kirkwood_uart1_init(void);
 void kirkwood_nand_init(struct mtd_partition *parts, int nr_parts, int delay);
 void kirkwood_nand_init_rnb(struct mtd_partition *parts, int nr_parts,
 			    int (*dev_ready)(struct mtd_info *));
+void kirkwood_nand_init_ecc(struct mtd_partition *parts, int nr_parts,
+			    int delay, struct kirkwood_nand_ecc *ecc);
 void kirkwood_audio_init(void);
 void kirkwood_cpuidle_init(void);
 void kirkwood_cpufreq_init(void);
@@ -181,6 +191,7 @@
 void kirkwood_xor0_init(void);
 void kirkwood_xor1_init(void);
 void kirkwood_crypto_init(void);
+void kirkwood_udc_init(void);
 
 extern int kirkwood_tclk;
 extern void kirkwood_timer_init(void);
diff -ruw linux-3.11.10/arch/arm/mach-kirkwood/include/mach/bridge-regs.h linux-3.11.10-fbx/arch/arm/mach-kirkwood/include/mach/bridge-regs.h
--- linux-3.11.10/arch/arm/mach-kirkwood/include/mach/bridge-regs.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/include/mach/bridge-regs.h	2015-09-29 11:15:04.881725130 +0200
@@ -18,7 +18,9 @@
 
 #define CPU_CONTROL		(BRIDGE_VIRT_BASE + 0x0104)
 #define CPU_CONTROL_PHYS	(BRIDGE_PHYS_BASE + 0x0104)
+#define PCIE0_ENABLE		0x00000001
 #define CPU_RESET		0x00000002
+#define PCIE1_ENABLE		0x00000010
 
 #define RSTOUTn_MASK		(BRIDGE_VIRT_BASE + 0x0108)
 #define SOFT_RESET_OUT_EN	0x00000004
diff -ruw linux-3.11.10/arch/arm/mach-kirkwood/include/mach/irqs.h linux-3.11.10-fbx/arch/arm/mach-kirkwood/include/mach/irqs.h
--- linux-3.11.10/arch/arm/mach-kirkwood/include/mach/irqs.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/include/mach/irqs.h	2015-09-29 11:15:04.881725130 +0200
@@ -40,6 +40,7 @@
 /*
  * High Interrupt Controller
  */
+#define IRQ_KIRKWOOD_TWSI1	32
 #define IRQ_KIRKWOOD_UART_0	33
 #define IRQ_KIRKWOOD_UART_1	34
 #define IRQ_KIRKWOOD_GPIO_LOW_0_7	35
diff -ruw linux-3.11.10/arch/arm/mach-kirkwood/include/mach/kirkwood.h linux-3.11.10-fbx/arch/arm/mach-kirkwood/include/mach/kirkwood.h
--- linux-3.11.10/arch/arm/mach-kirkwood/include/mach/kirkwood.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/include/mach/kirkwood.h	2015-09-29 11:15:04.881725130 +0200
@@ -67,13 +67,20 @@
 
 #define DEV_BUS_PHYS_BASE	(KIRKWOOD_REGS_PHYS_BASE + 0x10000)
 #define DEV_BUS_VIRT_BASE	(KIRKWOOD_REGS_VIRT_BASE + 0x10000)
+#define  MPP_VIRT_BASE		(DEV_BUS_VIRT_BASE + 0x0000)
 #define  SAMPLE_AT_RESET	(DEV_BUS_VIRT_BASE + 0x0030)
 #define  DEVICE_ID		(DEV_BUS_VIRT_BASE + 0x0034)
 #define  GPIO_LOW_VIRT_BASE	(DEV_BUS_VIRT_BASE + 0x0100)
 #define  GPIO_HIGH_VIRT_BASE	(DEV_BUS_VIRT_BASE + 0x0140)
 #define  RTC_PHYS_BASE		(DEV_BUS_PHYS_BASE + 0x0300)
+#define  NAND_PHYS_BASE		(DEV_BUS_PHYS_BASE + 0x0400)
+#define  NAND_VIRT_BASE		(DEV_BUS_VIRT_BASE + 0x0400)
+#define   NAND_RD_PARAM_OFF	0x0018
+#define   NAND_WR_PARAM_OFF	0x001C
+#define   NAND_FLASH_CTL_OFF	0x0070
 #define  SPI_PHYS_BASE		(DEV_BUS_PHYS_BASE + 0x0600)
 #define  I2C_PHYS_BASE		(DEV_BUS_PHYS_BASE + 0x1000)
+#define  I2C1_PHYS_BASE		(DEV_BUS_PHYS_BASE + 0x1100)
 #define  UART0_PHYS_BASE	(DEV_BUS_PHYS_BASE + 0x2000)
 #define  UART0_VIRT_BASE	(DEV_BUS_VIRT_BASE + 0x2000)
 #define  UART1_PHYS_BASE	(DEV_BUS_PHYS_BASE + 0x2100)
@@ -119,6 +126,11 @@
 #define AUDIO_PHYS_BASE		(KIRKWOOD_REGS_PHYS_BASE + 0xA0000)
 #define AUDIO_VIRT_BASE		(KIRKWOOD_REGS_VIRT_BASE + 0xA0000)
 
+#define TDM_SPI_PHYS_BASE	(KIRKWOOD_REGS_PHYS_BASE + 0xd0000)
+#define TDM_SPI_VIRT_BASE	(KIRKWOOD_REGS_VIRT_BASE + 0xd0000)
+
+#define TEMP_PHYS_BASE		(KIRKWOOD_REGS_PHYS_BASE + 0x10078)
+
 /*
  * Supported devices and revisions.
  */
diff -ruw linux-3.11.10/arch/arm/mach-kirkwood/irq.c linux-3.11.10-fbx/arch/arm/mach-kirkwood/irq.c
--- linux-3.11.10/arch/arm/mach-kirkwood/irq.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/irq.c	2015-09-29 11:15:04.881725130 +0200
@@ -32,6 +32,13 @@
 
 void __init kirkwood_init_irq(void)
 {
+	u32 dev, rev;
+
+	kirkwood_pcie_id(&dev, &rev);
+	if (dev == MV88F6282_DEV_ID)
+		/* see FE-MISC-70 in errata document */
+		orion_gpio_mpp33_fix_needed();
+
 	orion_irq_init(0, IRQ_VIRT_BASE + IRQ_MASK_LOW_OFF);
 	orion_irq_init(32, IRQ_VIRT_BASE + IRQ_MASK_HIGH_OFF);
 
diff -ruw linux-3.11.10/arch/arm/mach-kirkwood/Kconfig linux-3.11.10-fbx/arch/arm/mach-kirkwood/Kconfig
--- linux-3.11.10/arch/arm/mach-kirkwood/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/Kconfig	2015-09-29 11:15:04.881725130 +0200
@@ -318,6 +318,79 @@
 	  or MV6282. If you have the wrong one, the buttons will not
 	  work.
 
+config MACH_FBXGW1R
+	bool "Freebox Gateway version 1"
+	select FBXSERIAL
+	select FBXGW_COMMON
+
+config MACH_FBXGW1R_ETHSW_CONFIG
+	bool "initialize ethernet switch"
+	depends on MACH_FBXGW1R
+
+config MACH_FBXGW1R_ETHSW_CONFIG_VLAN
+	bool "setup vlan"
+	depends on MACH_FBXGW1R_ETHSW_CONFIG
+
+config MACH_FBXGW1R_EXPANSION_BOARD_SETUP
+	bool "setup expansion board early"
+	depends on MACH_FBXGW1R
+
+config MACH_FBXGW2R
+	bool "Freebox Gateway version 2"
+	select FBXSERIAL
+	select FBXGW_COMMON
+
+config MACH_FBXGW2R_ETHSW_CONFIG
+	bool "initialise ethernet switch"
+	depends on MACH_FBXGW2R
+
+config MACH_FBXGW2R_ETHSW_CONFIG_VLAN
+	bool "setup vlan"
+	depends on MACH_FBXGW2R
+
+config MACH_FBXGW2R_EXPANSION_BOARD_SETUP
+	bool "setup expansion board early"
+	depends on MACH_FBXGW2R
+
+config FBXGW_COMMON
+	bool
+
+if FBXGW_COMMON
+
+config FBXGW_COMMON_PARTS_WRITE_ALL
+	bool "make all mtd partitions writeable"
+
+config FBXGW_COMMON_NAND_SAFE_READ_TIMINGS
+	bool "use very conservative timings for nand read"
+
+config FBXGW_COMMON_NAND_SAFE_WRITE_TIMINGS
+	bool "use very conservative timings for nand write"
+
+choice
+	prompt "i2c bus speed"
+	default FBXGW_COMMON_I2C_25K
+
+config FBXGW_COMMON_I2C_25K
+	bool "25KHz"
+
+config FBXGW_COMMON_I2C_50K
+	bool "50KHz"
+
+config FBXGW_COMMON_I2C_100K
+	bool "100KHz"
+
+endchoice
+
+endif
+
+
+config MACH_RD88F6282A
+	bool "Marvell RD-88F6282-A Reference Design"
+
+config MACH_DB88F6282A_BP
+	bool "Marvell DB-88F6282-A-BP Reference Design"
+
 endmenu
 
 endif
+
diff -ruw linux-3.11.10/arch/arm/mach-kirkwood/Makefile linux-3.11.10-fbx/arch/arm/mach-kirkwood/Makefile
--- linux-3.11.10/arch/arm/mach-kirkwood/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/Makefile	2015-09-29 11:15:04.881725130 +0200
@@ -17,6 +17,12 @@
 obj-$(CONFIG_MACH_T5325)		+= t5325-setup.o
 obj-$(CONFIG_MACH_TS219)		+= ts219-setup.o tsx1x-common.o
 obj-$(CONFIG_MACH_TS41X)		+= ts41x-setup.o tsx1x-common.o
+obj-$(CONFIG_MACH_FBXGW1R)		+= fbxgw1r-setup.o fbxgw-common.o \
+						fbxgw-switch.o
+obj-$(CONFIG_MACH_FBXGW2R)		+= fbxgw2r-setup.o fbxgw-common.o \
+						fbxgw-switch.o
+obj-$(CONFIG_MACH_RD88F6282A)		+= rd88f6282a-setup.o
+obj-$(CONFIG_MACH_DB88F6282A_BP)	+= db88f6282a-bp-setup.o
 
 obj-$(CONFIG_ARCH_KIRKWOOD_DT)		+= board-dt.o
 obj-$(CONFIG_MACH_CLOUDBOX_DT)		+= board-ns2.o
diff -ruw linux-3.11.10/arch/arm/mach-kirkwood/pcie.c linux-3.11.10-fbx/arch/arm/mach-kirkwood/pcie.c
--- linux-3.11.10/arch/arm/mach-kirkwood/pcie.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/pcie.c	2015-09-29 11:15:04.881725130 +0200
@@ -230,7 +230,16 @@
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup);
 
-static int __init kirkwood_pcie_map_irq(const struct pci_dev *dev, u8 slot,
+static void __init kirkwood_pcie_preinit(void)
+{
+	/*
+	 * clear PCI_REASSIGN_ALL_RSRC bit as it brings pain and
+	 * suffering during PCI rescan.
+	 */
+	pci_clear_flags(PCI_REASSIGN_ALL_RSRC);
+}
+
+static int kirkwood_pcie_map_irq(const struct pci_dev *dev, u8 slot,
 	u8 pin)
 {
 	struct pci_sys_data *sys = dev->sysdata;
@@ -243,6 +252,7 @@
 	.setup		= kirkwood_pcie_setup,
 	.map_irq	= kirkwood_pcie_map_irq,
 	.ops            = &pcie_ops,
+	.preinit	= kirkwood_pcie_preinit,
 };
 
 static void __init add_pcie_port(int index, void __iomem *base)
diff -ruw linux-3.11.10/arch/arm/mm/dma-mapping.c linux-3.11.10-fbx/arch/arm/mm/dma-mapping.c
--- linux-3.11.10/arch/arm/mm/dma-mapping.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mm/dma-mapping.c	2015-09-29 11:15:04.981725890 +0200
@@ -1325,7 +1325,7 @@
 	*handle = DMA_ERROR_CODE;
 	size = PAGE_ALIGN(size);
 
-	if (gfp & GFP_ATOMIC)
+	if (!(gfp & __GFP_WAIT))
 		return __iommu_alloc_atomic(dev, size, handle);
 
 	/*
diff -ruw linux-3.11.10/arch/arm/mm/extable.c linux-3.11.10-fbx/arch/arm/mm/extable.c
--- linux-3.11.10/arch/arm/mm/extable.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mm/extable.c	2014-07-29 21:29:41.397849653 +0200
@@ -9,8 +9,13 @@
 	const struct exception_table_entry *fixup;
 
 	fixup = search_exception_tables(instruction_pointer(regs));
-	if (fixup)
+	if (fixup) {
 		regs->ARM_pc = fixup->fixup;
+#ifdef CONFIG_THUMB2_KERNEL
+		/* Clear the IT state to avoid nasty surprises in the fixup */
+		regs->ARM_cpsr &= ~PSR_IT_MASK;
+#endif
+	}
 
 	return fixup != NULL;
 }
diff -ruw linux-3.11.10/arch/arm/mm/flush.c linux-3.11.10-fbx/arch/arm/mm/flush.c
--- linux-3.11.10/arch/arm/mm/flush.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mm/flush.c	2015-09-29 11:15:04.981725890 +0200
@@ -175,16 +175,16 @@
 		unsigned long i;
 		if (cache_is_vipt_nonaliasing()) {
 			for (i = 0; i < (1 << compound_order(page)); i++) {
-				void *addr = kmap_atomic(page);
+				void *addr = kmap_atomic(page + i);
 				__cpuc_flush_dcache_area(addr, PAGE_SIZE);
 				kunmap_atomic(addr);
 			}
 		} else {
 			for (i = 0; i < (1 << compound_order(page)); i++) {
-				void *addr = kmap_high_get(page);
+				void *addr = kmap_high_get(page + i);
 				if (addr) {
 					__cpuc_flush_dcache_area(addr, PAGE_SIZE);
-					kunmap_high(page);
+					kunmap_high(page + i);
 				}
 			}
 		}
diff -ruw linux-3.11.10/arch/arm/mm/Kconfig linux-3.11.10-fbx/arch/arm/mm/Kconfig
--- linux-3.11.10/arch/arm/mm/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mm/Kconfig	2015-09-29 11:15:04.977725861 +0200
@@ -446,7 +446,6 @@
 
 config CPU_32v6
 	bool
-	select CPU_USE_DOMAINS if CPU_V6 && MMU
 	select TLS_REG_EMUL if !CPU_32v6K && !MMU
 
 config CPU_32v6K
@@ -671,7 +670,7 @@
 
 config SWP_EMULATE
 	bool "Emulate SWP/SWPB instructions"
-	depends on !CPU_USE_DOMAINS && CPU_V7
+	depends on CPU_V7
 	default y if SMP
 	select HAVE_PROC_CPU if PROC_FS
 	help
diff -ruw linux-3.11.10/arch/arm/mm/mmap.c linux-3.11.10-fbx/arch/arm/mm/mmap.c
--- linux-3.11.10/arch/arm/mm/mmap.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mm/mmap.c	2015-09-29 11:15:04.981725890 +0200
@@ -146,7 +146,7 @@
 
 	info.flags = VM_UNMAPPED_AREA_TOPDOWN;
 	info.length = len;
-	info.low_limit = PAGE_SIZE;
+	info.low_limit = FIRST_USER_ADDRESS;
 	info.high_limit = mm->mmap_base;
 	info.align_mask = do_align ? (PAGE_MASK & (SHMLBA - 1)) : 0;
 	info.align_offset = pgoff << PAGE_SHIFT;
@@ -202,13 +202,11 @@
 }
 
 /*
- * We don't use supersection mappings for mmap() on /dev/mem, which
- * means that we can't map the memory area above the 4G barrier into
- * userspace.
+ * Do not allow /dev/mem mappings beyond the supported physical range.
  */
 int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
 {
-	return !(pfn + (size >> PAGE_SHIFT) > 0x00100000);
+	return (pfn + (size >> PAGE_SHIFT)) <= (1 + (PHYS_MASK >> PAGE_SHIFT));
 }
 
 #ifdef CONFIG_STRICT_DEVMEM
diff -ruw linux-3.11.10/arch/arm/mm/mm.h linux-3.11.10-fbx/arch/arm/mm/mm.h
--- linux-3.11.10/arch/arm/mm/mm.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mm/mm.h	2015-09-29 11:15:04.981725890 +0200
@@ -38,6 +38,7 @@
 
 struct mem_type {
 	pteval_t prot_pte;
+	pteval_t prot_pte_s2;
 	pmdval_t prot_l1;
 	pmdval_t prot_sect;
 	unsigned int domain;
diff -ruw linux-3.11.10/arch/arm/mm/mmu.c linux-3.11.10-fbx/arch/arm/mm/mmu.c
--- linux-3.11.10/arch/arm/mm/mmu.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mm/mmu.c	2015-09-29 11:15:04.981725890 +0200
@@ -229,12 +229,16 @@
 #endif /* ifdef CONFIG_CPU_CP15 / else */
 
 #define PROT_PTE_DEVICE		L_PTE_PRESENT|L_PTE_YOUNG|L_PTE_DIRTY|L_PTE_XN
+#define PROT_PTE_S2_DEVICE	PROT_PTE_DEVICE
 #define PROT_SECT_DEVICE	PMD_TYPE_SECT|PMD_SECT_AP_WRITE
 
 static struct mem_type mem_types[] = {
 	[MT_DEVICE] = {		  /* Strongly ordered / ARMv6 shared device */
 		.prot_pte	= PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED |
 				  L_PTE_SHARED,
+		.prot_pte_s2	= s2_policy(PROT_PTE_S2_DEVICE) |
+				  s2_policy(L_PTE_S2_MT_DEV_SHARED) |
+				  L_PTE_SHARED,
 		.prot_l1	= PMD_TYPE_TABLE,
 		.prot_sect	= PROT_SECT_DEVICE | PMD_SECT_S,
 		.domain		= DOMAIN_IO,
@@ -456,7 +460,18 @@
 	cp = &cache_policies[cachepolicy];
 	vecs_pgprot = kern_pgprot = user_pgprot = cp->pte;
 	s2_pgprot = cp->pte_s2;
-	hyp_device_pgprot = s2_device_pgprot = mem_types[MT_DEVICE].prot_pte;
+	hyp_device_pgprot = mem_types[MT_DEVICE].prot_pte;
+	s2_device_pgprot = mem_types[MT_DEVICE].prot_pte_s2;
+
+	/*
+	 * We don't use domains on ARMv6 (since this causes problems with
+	 * v6/v7 kernels), so we must use a separate memory type for user
+	 * r/o, kernel r/w to map the vectors page.
+	 */
+#ifndef CONFIG_ARM_LPAE
+	if (cpu_arch == CPU_ARCH_ARMv6)
+		vecs_pgprot |= L_PTE_MT_VECTORS;
+#endif
 
 	/*
 	 * ARMv6 and above have extended page tables.
diff -ruw linux-3.11.10/arch/arm/mm/pgd.c linux-3.11.10-fbx/arch/arm/mm/pgd.c
--- linux-3.11.10/arch/arm/mm/pgd.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mm/pgd.c	2015-09-29 11:15:04.981725890 +0200
@@ -87,7 +87,8 @@
 		init_pud = pud_offset(init_pgd, 0);
 		init_pmd = pmd_offset(init_pud, 0);
 		init_pte = pte_offset_map(init_pmd, 0);
-		set_pte_ext(new_pte, *init_pte, 0);
+		set_pte_ext(new_pte + 0, init_pte[0], 0);
+		set_pte_ext(new_pte + 1, init_pte[1], 0);
 		pte_unmap(init_pte);
 		pte_unmap(new_pte);
 	}
diff -ruw linux-3.11.10/arch/arm/mm/proc-macros.S linux-3.11.10-fbx/arch/arm/mm/proc-macros.S
--- linux-3.11.10/arch/arm/mm/proc-macros.S	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/mm/proc-macros.S	2015-09-29 11:15:04.985725927 +0200
@@ -112,13 +112,9 @@
  *  100x   1   0   1	r/o	no acc
  *  10x0   1   0   1	r/o	no acc
  *  1011   0   0   1	r/w	no acc
- *  110x   0   1   0	r/w	r/o
- *  11x0   0   1   0	r/w	r/o
- *  1111   0   1   1	r/w	r/w
- *
- * If !CONFIG_CPU_USE_DOMAINS, the following permissions are changed:
  *  110x   1   1   1	r/o	r/o
  *  11x0   1   1   1	r/o	r/o
+ *  1111   0   1   1	r/w	r/w
  */
 	.macro	armv6_mt_table pfx
 \pfx\()_mt_table:
@@ -137,7 +133,7 @@
 	.long	PTE_EXT_TEX(2)					@ L_PTE_MT_DEV_NONSHARED
 	.long	0x00						@ unused
 	.long	0x00						@ unused
-	.long	0x00						@ unused
+	.long	PTE_CACHEABLE | PTE_BUFFERABLE | PTE_EXT_APX	@ L_PTE_MT_VECTORS
 	.endm
 
 	.macro	armv6_set_pte_ext pfx
@@ -158,24 +154,21 @@
 
 	tst	r1, #L_PTE_USER
 	orrne	r3, r3, #PTE_EXT_AP1
-#ifdef CONFIG_CPU_USE_DOMAINS
-	@ allow kernel read/write access to read-only user pages
 	tstne	r3, #PTE_EXT_APX
-	bicne	r3, r3, #PTE_EXT_APX | PTE_EXT_AP0
-#endif
+
+	@ user read-only -> kernel read-only
+	bicne	r3, r3, #PTE_EXT_AP0
 
 	tst	r1, #L_PTE_XN
 	orrne	r3, r3, #PTE_EXT_XN
 
-	orr	r3, r3, r2
+	eor	r3, r3, r2
 
 	tst	r1, #L_PTE_YOUNG
 	tstne	r1, #L_PTE_PRESENT
 	moveq	r3, #0
-#ifndef CONFIG_CPU_USE_DOMAINS
 	tstne	r1, #L_PTE_NONE
 	movne	r3, #0
-#endif
 
 	str	r3, [r0]
 	mcr	p15, 0, r0, c7, c10, 1		@ flush_pte
diff -ruw linux-3.11.10/arch/arm/net/bpf_jit_32.c linux-3.11.10-fbx/arch/arm/net/bpf_jit_32.c
--- linux-3.11.10/arch/arm/net/bpf_jit_32.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/net/bpf_jit_32.c	2015-09-29 11:15:04.985725927 +0200
@@ -41,6 +41,7 @@
 #define r_skb		ARM_R6
 #define r_skb_data	ARM_R7
 #define r_skb_hl	ARM_R8
+#define r_scramble	ARM_R10
 
 #define SCRATCH_SP_OFFSET	0
 #define SCRATCH_OFF(k)		(SCRATCH_SP_OFFSET + 4 * (k))
@@ -51,11 +52,14 @@
 #define SEEN_CALL		(1 << (BPF_MEMWORDS + 1))
 #define SEEN_SKB		(1 << (BPF_MEMWORDS + 2))
 #define SEEN_DATA		(1 << (BPF_MEMWORDS + 3))
+#define SEEN_IMM		(1 << (BPF_MEMWORDS + 4))
 
 #define FLAG_NEED_X_RESET	(1 << 0)
+#define FLAG_IMM_OVERFLOW	(1 << 1)
 
 struct jit_ctx {
-	const struct sk_filter *skf;
+	unsigned short prog_len;
+	struct sock_filter *prog_insns;
 	unsigned idx;
 	unsigned prologue_bytes;
 	int ret0_fp_idx;
@@ -67,11 +71,16 @@
 	u16 epilogue_bytes;
 	u16 imm_count;
 	u32 *imms;
+#ifdef CONFIG_ARM_BPF_JIT_HARDEN
+	u32 scramble_key;
+#endif
 #endif
 };
 
 int bpf_jit_enable __read_mostly;
 
+static inline void emit_mov_i_no8m(int rd, u32 val, struct jit_ctx *ctx);
+
 static u64 jit_get_skb_b(struct sk_buff *skb, unsigned offset)
 {
 	u8 ret;
@@ -131,8 +140,8 @@
 {
 	u16 ret = 0;
 
-	if ((ctx->skf->len > 1) ||
-	    (ctx->skf->insns[0].code == BPF_S_RET_A))
+	if ((ctx->prog_len > 1) ||
+	    (ctx->prog_insns[0].code == BPF_S_RET_A))
 		ret |= 1 << r_A;
 
 #ifdef CONFIG_FRAME_POINTER
@@ -147,6 +156,10 @@
 		ret |= (1 << r_skb_data) | (1 << r_skb_hl);
 	if (ctx->seen & SEEN_X)
 		ret |= 1 << r_X;
+#if __LINUX_ARM_ARCH__ < 7 && defined(CONFIG_ARM_BPF_JIT_HARDEN)
+	if (ctx->seen & SEEN_IMM)
+		ret |= 1 << r_scramble;
+#endif
 
 	return ret;
 }
@@ -181,7 +194,7 @@
 static void build_prologue(struct jit_ctx *ctx)
 {
 	u16 reg_set = saved_regs(ctx);
-	u16 first_inst = ctx->skf->insns[0].code;
+	u16 first_inst = ctx->prog_insns[0].code;
 	u16 off;
 
 #ifdef CONFIG_FRAME_POINTER
@@ -217,6 +230,12 @@
 	/* stack space for the BPF_MEM words */
 	if (ctx->seen & SEEN_MEM)
 		emit(ARM_SUB_I(ARM_SP, ARM_SP, mem_words_used(ctx) * 4), ctx);
+
+#if __LINUX_ARM_ARCH__ < 7 && defined(CONFIG_ARM_BPF_JIT_HARDEN)
+	if (ctx->seen & SEEN_IMM)
+		/* load scramble key once and for all in r_scramble */
+		emit_mov_i_no8m(r_scramble, ctx->scramble_key, ctx);
+#endif
 }
 
 static void build_epilogue(struct jit_ctx *ctx)
@@ -279,7 +298,7 @@
 		ctx->imms[i] = k;
 
 	/* constants go just after the epilogue */
-	offset =  ctx->offsets[ctx->skf->len];
+	offset =  ctx->offsets[ctx->prog_len];
 	offset += ctx->prologue_bytes;
 	offset += ctx->epilogue_bytes;
 	offset += i * 4;
@@ -289,6 +308,15 @@
 	/* PC in ARM mode == address of the instruction + 8 */
 	imm = offset - (8 + ctx->idx * 4);
 
+	if (imm & ~0xfff) {
+		/*
+		 * literal pool is too far, signal it into flags. we
+		 * can only detect it on the second pass unfortunately.
+		 */
+		ctx->flags |= FLAG_IMM_OVERFLOW;
+		return 0;
+	}
+
 	return imm;
 }
 
@@ -314,8 +342,20 @@
 
 	if (imm12 >= 0)
 		emit(ARM_MOV_I(rd, imm12), ctx);
-	else
+	else {
+#if __LINUX_ARM_ARCH__ < 7 && defined(CONFIG_ARM_BPF_JIT_HARDEN)
+		/*
+		 * put the scrambled value in the literal pool, and
+		 * emit a xor to unscramble the value after the load
+		 * from the literal pool.
+		 */
+		ctx->seen |= SEEN_IMM;
+		emit_mov_i_no8m(rd, val ^ ctx->scramble_key, ctx);
+		emit(ARM_EOR_R(rd, rd, r_scramble), ctx);
+#else
 		emit_mov_i_no8m(rd, val, ctx);
+#endif
+	}
 }
 
 #if __LINUX_ARM_ARCH__ < 6
@@ -419,7 +459,7 @@
 		emit(ARM_MOV_R(ARM_R0, ARM_R0), ctx);
 	} else {
 		_emit(cond, ARM_MOV_I(ARM_R0, 0), ctx);
-		_emit(cond, ARM_B(b_imm(ctx->skf->len, ctx)), ctx);
+		_emit(cond, ARM_B(b_imm(ctx->prog_len, ctx)), ctx);
 	}
 }
 
@@ -445,10 +485,21 @@
 		return;
 	}
 #endif
-	if (rm != ARM_R0)
-		emit(ARM_MOV_R(ARM_R0, rm), ctx);
+
+	/*
+	 * For BPF_ALU | BPF_DIV | BPF_K instructions, rm is ARM_R4
+	 * (r_A) and rn is ARM_R0 (r_scratch) so load rn first into
+	 * ARM_R1 to avoid accidentally overwriting ARM_R0 with rm
+	 * before using it as a source for ARM_R1.
+	 *
+	 * For BPF_ALU | BPF_DIV | BPF_X rm is ARM_R4 (r_A) and rn is
+	 * ARM_R5 (r_X) so there is no particular register overlap
+	 * issues.
+	 */
 	if (rn != ARM_R1)
 		emit(ARM_MOV_R(ARM_R1, rn), ctx);
+	if (rm != ARM_R0)
+		emit(ARM_MOV_R(ARM_R0, rm), ctx);
 
 	ctx->seen |= SEEN_CALL;
 	emit_mov_i(ARM_R3, (u32)jit_udiv, ctx);
@@ -469,14 +520,13 @@
 static int build_body(struct jit_ctx *ctx)
 {
 	void *load_func[] = {jit_get_skb_b, jit_get_skb_h, jit_get_skb_w};
-	const struct sk_filter *prog = ctx->skf;
 	const struct sock_filter *inst;
 	unsigned i, load_order, off, condt;
 	int imm12;
 	u32 k;
 
-	for (i = 0; i < prog->len; i++) {
-		inst = &(prog->insns[i]);
+	for (i = 0; i < ctx->prog_len; i++) {
+		inst = &(ctx->prog_insns[i]);
 		/* K as an immediate value operand */
 		k = inst->k;
 
@@ -548,6 +598,15 @@
 			emit_err_ret(ARM_COND_NE, ctx);
 			emit(ARM_MOV_R(r_A, ARM_R0), ctx);
 			break;
+#ifdef CONFIG_SECCOMP_FILTER_JIT
+		case BPF_S_ANC_SECCOMP_LD_W:
+			ctx->seen |= SEEN_CALL;
+			emit_mov_i(ARM_R3, (u32)seccomp_bpf_load, ctx);
+			emit_mov_i(ARM_R0, k, ctx);
+			emit_blx_r(ARM_R3, ctx);
+			emit(ARM_MOV_R(r_A, ARM_R0), ctx);
+			break;
+#endif
 		case BPF_S_LD_W_IND:
 			load_order = 2;
 			goto load_ind;
@@ -637,10 +696,10 @@
 			emit(ARM_MUL(r_A, r_A, r_X), ctx);
 			break;
 		case BPF_S_ALU_DIV_K:
-			/* current k == reciprocal_value(userspace k) */
+			if (k == 1)
+				break;
 			emit_mov_i(r_scratch, k, ctx);
-			/* A = top 32 bits of the product */
-			emit(ARM_UMULL(r_scratch, r_A, r_A, r_scratch), ctx);
+			emit_udiv(r_A, r_A, r_scratch, ctx);
 			break;
 		case BPF_S_ALU_DIV_X:
 			update_on_xread(ctx);
@@ -769,8 +828,8 @@
 				ctx->ret0_fp_idx = i;
 			emit_mov_i(ARM_R0, k, ctx);
 b_epilogue:
-			if (i != ctx->skf->len - 1)
-				emit(ARM_B(b_imm(prog->len, ctx)), ctx);
+			if (i != ctx->prog_len - 1)
+				emit(ARM_B(b_imm(ctx->prog_len, ctx)), ctx);
 			break;
 		case BPF_S_MISC_TAX:
 			/* X = A */
@@ -848,6 +907,14 @@
 		default:
 			return -1;
 		}
+
+		if (ctx->flags & FLAG_IMM_OVERFLOW)
+			/*
+			 * this instruction generated an overflow when
+			 * trying to access the literal pool, so
+			 * delegate this filter to the kernel interpreter.
+			 */
+			return -1;
 	}
 
 	/* compute offsets only during the first pass */
@@ -858,7 +925,7 @@
 }
 
 
-void bpf_jit_compile(struct sk_filter *fp)
+static void __bpf_jit_compile(struct jit_ctx *out_ctx)
 {
 	struct jit_ctx ctx;
 	unsigned tmp_idx;
@@ -867,11 +934,14 @@
 	if (!bpf_jit_enable)
 		return;
 
-	memset(&ctx, 0, sizeof(ctx));
-	ctx.skf		= fp;
+	ctx = *out_ctx;
 	ctx.ret0_fp_idx = -1;
 
-	ctx.offsets = kzalloc(4 * (ctx.skf->len + 1), GFP_KERNEL);
+#if __LINUX_ARM_ARCH__ < 7 && defined(CONFIG_ARM_BPF_JIT_HARDEN)
+	ctx.scramble_key = get_random_int();
+#endif
+
+	ctx.offsets = kzalloc(4 * (ctx.prog_len + 1), GFP_KERNEL);
 	if (ctx.offsets == NULL)
 		return;
 
@@ -906,7 +976,15 @@
 
 	ctx.idx = 0;
 	build_prologue(&ctx);
-	build_body(&ctx);
+	if (build_body(&ctx) < 0) {
+#if __LINUX_ARM_ARCH__ < 7
+		if (ctx.imm_count)
+			kfree(ctx.imms);
+#endif
+		module_free(NULL, ctx.target);
+		ctx.target = NULL;
+		goto out;
+	}
 	build_epilogue(&ctx);
 
 	flush_icache_range((u32)ctx.target, (u32)(ctx.target + ctx.idx));
@@ -918,16 +996,50 @@
 
 	if (bpf_jit_enable > 1)
 		/* there are 2 passes here */
-		bpf_jit_dump(fp->len, alloc_size, 2, ctx.target);
-
-	fp->bpf_func = (void *)ctx.target;
+		bpf_jit_dump(ctx.prog_len, alloc_size, 2, ctx.target);
 out:
 	kfree(ctx.offsets);
+
+	*out_ctx = ctx;
 	return;
 }
 
+void bpf_jit_compile(struct sk_filter *fp)
+{
+	struct jit_ctx ctx;
+
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.prog_len = fp->len;
+	ctx.prog_insns = fp->insns;
+
+	__bpf_jit_compile(&ctx);
+	if (ctx.target)
+		fp->bpf_func = (void*)ctx.target;
+}
+
 void bpf_jit_free(struct sk_filter *fp)
 {
 	if (fp->bpf_func != sk_run_filter)
 		module_free(NULL, fp->bpf_func);
 }
+
+#ifdef CONFIG_SECCOMP_FILTER_JIT
+void seccomp_jit_compile(struct seccomp_filter *fp)
+{
+	struct jit_ctx ctx;
+
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.prog_len = fp->len;
+	ctx.prog_insns = fp->insns;
+
+	__bpf_jit_compile(&ctx);
+	if (ctx.target)
+		fp->bpf_func = (void*)ctx.target;
+}
+
+void seccomp_jit_free(struct seccomp_filter *fp)
+{
+	if (fp->bpf_func != sk_run_filter)
+		module_free(NULL, fp->bpf_func);
+}
+#endif
diff -ruw linux-3.11.10/arch/arm/plat-orion/common.c linux-3.11.10-fbx/arch/arm/plat-orion/common.c
--- linux-3.11.10/arch/arm/plat-orion/common.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/plat-orion/common.c	2015-09-29 11:15:04.985725927 +0200
@@ -18,10 +18,12 @@
 #include <linux/clkdev.h>
 #include <linux/mv643xx_eth.h>
 #include <linux/mv643xx_i2c.h>
+#include <linux/fsl_devices.h>
 #include <net/dsa.h>
 #include <linux/platform_data/dma-mv_xor.h>
 #include <linux/platform_data/usb-ehci-orion.h>
 #include <mach/bridge-regs.h>
+#include <asm/sizes.h>
 
 /* Create a clkdev entry for a given device/clk */
 void __init orion_clkdev_add(const char *con_id, const char *dev_id,
@@ -539,9 +541,11 @@
 
 void __init orion_i2c_init(unsigned long mapbase,
 			   unsigned long irq,
-			   unsigned long freq_m)
+			   unsigned long freq_m,
+			   unsigned long freq_n)
 {
 	orion_i2c_pdata.freq_m = freq_m;
+	orion_i2c_pdata.freq_n = freq_n;
 	fill_resources(&orion_i2c, orion_i2c_resources, mapbase,
 		       SZ_32 - 1, irq);
 	platform_device_register(&orion_i2c);
@@ -817,6 +821,38 @@
 	platform_device_register(&orion_ehci_2);
 }
 
+
+/*****************************************************************************
+ * UDC
+ ****************************************************************************/
+static struct resource orion_udc_resources[2];
+
+static struct fsl_usb2_platform_data orion_udc_data = {
+	.operating_mode = FSL_USB2_DR_DEVICE,
+	.phy_mode       = FSL_USB2_PHY_UTMI,
+};
+
+static u64 otg_dmamask = DMA_BIT_MASK(32);
+
+/* OTG gadget device */
+struct platform_device orion_udc = {
+	.name		= "fsl-usb2-udc",
+	.id		= -1,
+	.dev		= {
+		.dma_mask		= &otg_dmamask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+		.platform_data		= &orion_udc_data,
+	},
+};
+
+void __init orion_udc_init(unsigned long mapbase, unsigned long irq)
+{
+	fill_resources(&orion_udc, orion_udc_resources, mapbase, SZ_4K - 1,
+		       irq);
+	platform_device_register(&orion_udc);
+}
+
+
 /*****************************************************************************
  * SATA
  ****************************************************************************/
diff -ruw linux-3.11.10/arch/arm/plat-orion/gpio.c linux-3.11.10-fbx/arch/arm/plat-orion/gpio.c
--- linux-3.11.10/arch/arm/plat-orion/gpio.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/plat-orion/gpio.c	2015-09-29 11:15:04.989725952 +0200
@@ -91,6 +91,7 @@
 
 static struct orion_gpio_chip orion_gpio_chips[2];
 static int orion_gpio_chip_count;
+static int orion_gpio_mpp33_fix;
 
 static inline void
 __set_direction(struct orion_gpio_chip *ochip, unsigned pin, int input)
@@ -208,6 +209,13 @@
 	spin_lock_irqsave(&ochip->lock, flags);
 	__set_blinking(ochip, pin, 0);
 	__set_level(ochip, pin, value);
+	if (pin == 1 && ochip == &orion_gpio_chips[1] && orion_gpio_mpp33_fix) {
+		/*
+		 * MPP33 direction is controlled by MPP1 direction bit.
+		 * see FE-MISC-70 in the errata datasheet.
+		 */
+		__set_direction(&orion_gpio_chips[0], 1, 0);
+	} else
 	__set_direction(ochip, pin, 0);
 	spin_unlock_irqrestore(&ochip->lock, flags);
 
@@ -645,3 +653,8 @@
 		orion_gpio_of_init_one(np, irq_gpio_base);
 }
 #endif
+
+void orion_gpio_mpp33_fix_needed(void)
+{
+	orion_gpio_mpp33_fix = 1;
+}
diff -ruw linux-3.11.10/arch/arm/plat-orion/include/plat/common.h linux-3.11.10-fbx/arch/arm/plat-orion/include/plat/common.h
--- linux-3.11.10/arch/arm/plat-orion/include/plat/common.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/plat-orion/include/plat/common.h	2015-09-29 11:15:04.989725952 +0200
@@ -65,11 +65,13 @@
 
 void __init orion_i2c_init(unsigned long mapbase,
 			   unsigned long irq,
-			   unsigned long freq_m);
+			   unsigned long freq_m,
+			   unsigned long freq_n);
 
 void __init orion_i2c_1_init(unsigned long mapbase,
 			     unsigned long irq,
-			     unsigned long freq_m);
+			     unsigned long freq_m,
+			     unsigned long freq_n);
 
 void __init orion_spi_init(unsigned long mapbase);
 
@@ -91,6 +93,8 @@
 			    unsigned long irq,
 			    enum orion_ehci_phy_ver phy_version);
 
+void __init orion_udc_init(unsigned long mapbase, unsigned long irq);
+
 void __init orion_ehci_1_init(unsigned long mapbase,
 			      unsigned long irq);
 
diff -ruw linux-3.11.10/arch/arm/plat-orion/include/plat/orion-gpio.h linux-3.11.10-fbx/arch/arm/plat-orion/include/plat/orion-gpio.h
--- linux-3.11.10/arch/arm/plat-orion/include/plat/orion-gpio.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/plat-orion/include/plat/orion-gpio.h	2015-09-29 11:15:04.989725952 +0200
@@ -33,5 +33,13 @@
 			    int secondary_irq_base,
 			    int irq[4]);
 
+/* some CPUs require special handling for mpp33 direction */
+void __init orion_gpio_mpp33_fix_needed(void);
+
+/*
+ * GPIO interrupt handling.
+ */
+void orion_gpio_irq_handler(int irqoff);
+
 void __init orion_gpio_of_init(int irq_gpio_base);
 #endif
diff -ruw linux-3.11.10/arch/arm/plat-orion/include/plat/pcie.h linux-3.11.10-fbx/arch/arm/plat-orion/include/plat/pcie.h
--- linux-3.11.10/arch/arm/plat-orion/include/plat/pcie.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/plat-orion/include/plat/pcie.h	2015-09-29 11:15:04.989725952 +0200
@@ -16,6 +16,10 @@
 u32 orion_pcie_dev_id(void __iomem *base);
 u32 orion_pcie_rev(void __iomem *base);
 int orion_pcie_link_up(void __iomem *base);
+void orion_pcie_set_link_disable(void __iomem *base, int v);
+void orion_pcie_set_retrain_link(void __iomem *base, int v);
+int orion_pcie_get_link_training(void __iomem *base);
+int orion_pcie_get_link_disable(void __iomem *base);
 int orion_pcie_x4_mode(void __iomem *base);
 int orion_pcie_get_local_bus_nr(void __iomem *base);
 void orion_pcie_set_local_bus_nr(void __iomem *base, int nr);
diff -ruw linux-3.11.10/arch/arm/plat-orion/pcie.c linux-3.11.10-fbx/arch/arm/plat-orion/pcie.c
--- linux-3.11.10/arch/arm/plat-orion/pcie.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/plat-orion/pcie.c	2015-09-29 11:15:04.989725952 +0200
@@ -24,6 +24,10 @@
 #define PCIE_DEV_REV_OFF	0x0008
 #define PCIE_BAR_LO_OFF(n)	(0x0010 + ((n) << 3))
 #define PCIE_BAR_HI_OFF(n)	(0x0014 + ((n) << 3))
+#define PCIE_LINK_CTRL_OFF	0x0070
+#define  PCIE_LINK_CTRL_LINK_DIS	(1 << 4)
+#define  PCIE_LINK_CTRL_RETR_LINK	(1 << 5)
+#define  PCIE_LINK_CTRL_LINK_TRAINING	(1 << 27)
 #define PCIE_HEADER_LOG_4_OFF	0x0128
 #define PCIE_BAR_CTRL_OFF(n)	(0x1804 + ((n - 1) * 4))
 #define PCIE_WIN04_CTRL_OFF(n)	(0x1820 + ((n) << 4))
@@ -67,6 +71,49 @@
 	return !(readl(base + PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN);
 }
 
+void orion_pcie_set_link_disable(void __iomem *base, int v)
+{
+	u32 val;
+
+	val = readl(base + PCIE_LINK_CTRL_OFF);
+	if (v)
+		val |= PCIE_LINK_CTRL_LINK_DIS;
+	else
+		val &= ~PCIE_LINK_CTRL_LINK_DIS;
+	writel(val, base + PCIE_LINK_CTRL_OFF);
+}
+
+int orion_pcie_get_link_disable(void __iomem *base)
+{
+	u32 val;
+
+	val = readl(base + PCIE_LINK_CTRL_OFF);
+	if (val & PCIE_LINK_CTRL_LINK_DIS)
+		return 1;
+	return 0;
+}
+
+void orion_pcie_set_retrain_link(void __iomem *base, int v)
+{
+	u32 val;
+
+	val = readl(base + PCIE_LINK_CTRL_OFF);
+	if (v)
+		val |= PCIE_LINK_CTRL_RETR_LINK;
+	else
+		val &= ~PCIE_LINK_CTRL_RETR_LINK;
+	writel(val, base + PCIE_LINK_CTRL_OFF);
+}
+
+int orion_pcie_get_link_training(void __iomem *base)
+{
+	u32 val = readl(base + PCIE_LINK_CTRL_OFF);
+
+	if (val & PCIE_LINK_CTRL_LINK_TRAINING)
+		return 1;
+	return 0;
+}
+
 int __init orion_pcie_x4_mode(void __iomem *base)
 {
 	return !(readl(base + PCIE_CTRL_OFF) & PCIE_CTRL_X1_MODE);
diff -ruw linux-3.11.10/arch/arm/tools/mach-types linux-3.11.10-fbx/arch/arm/tools/mach-types
--- linux-3.11.10/arch/arm/tools/mach-types	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/arm/tools/mach-types	2015-09-29 11:15:04.993725981 +0200
@@ -118,6 +118,7 @@
 omap_osk		MACH_OMAP_OSK		OMAP_OSK		515
 tosa			MACH_TOSA		TOSA			520
 avila			MACH_AVILA		AVILA			526
+fbxgw1r			MACH_FBXGW1R		FBXGW1R			527
 edb9302			MACH_EDB9302		EDB9302			538
 husky			MACH_HUSKY		HUSKY			543
 shepherd		MACH_SHEPHERD		SHEPHERD		545
@@ -1007,3 +1008,7 @@
 eukrea_cpuimx28sd	MACH_EUKREA_CPUIMX28SD	EUKREA_CPUIMX28SD	4573
 domotab			MACH_DOMOTAB		DOMOTAB			4574
 pfla03			MACH_PFLA03		PFLA03			4575
+fbxgw2r			MACH_FBXGW2R		FBXGW2R			4242
+rd88f6282a		MACH_RD88F6282A		RD88F6282A		527
+# and another one ...
+db88f6282a_bp		MACH_DB88F6282A_BP	DB88F6282A_BP		527
diff -ruw linux-3.11.10/arch/Kconfig linux-3.11.10-fbx/arch/Kconfig
--- linux-3.11.10/arch/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/arch/Kconfig	2015-09-29 11:15:04.757724191 +0200
@@ -332,6 +332,10 @@
 	  - secure_computing return value is checked and a return value of -1
 	    results in the system call being skipped immediately.
 
+# Used by archs to tell that they support SECCOMP_FILTER_JIT
+config HAVE_SECCOMP_FILTER_JIT
+	bool
+
 config SECCOMP_FILTER
 	def_bool y
 	depends on HAVE_ARCH_SECCOMP_FILTER && SECCOMP && NET
@@ -368,6 +372,9 @@
 config HAVE_ARCH_SOFT_DIRTY
 	bool
 
+config ARCH_THP_MOVE_PMD_ALWAYS_WITHDRAW
+	bool
+
 config HAVE_MOD_ARCH_SPECIFIC
 	bool
 	help
@@ -439,4 +446,14 @@
 config COMPAT_OLD_SIGACTION
 	bool
 
+config SECCOMP_FILTER_JIT
+	bool "enable Seccomp filter Just In Time compiler"
+	depends on HAVE_SECCOMP_FILTER_JIT && BPF_JIT && SECCOMP_FILTER
+	help
+	  Seccomp syscall filtering capabilities are normally handled
+	  by an interpreter. This option allows kernel to generate a native
+	  code when filter is loaded in memory. This should speedup
+	  syscall filtering. Note : Admin should enable this feature
+	  changing /proc/sys/net/core/bpf_jit_enable
+
 source "kernel/gcov/Kconfig"
diff -ruw linux-3.11.10/block/blk-cgroup.h linux-3.11.10-fbx/block/blk-cgroup.h
--- linux-3.11.10/block/blk-cgroup.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/block/blk-cgroup.h	2015-09-29 11:15:05.541730156 +0200
@@ -18,6 +18,7 @@
 #include <linux/seq_file.h>
 #include <linux/radix-tree.h>
 #include <linux/blkdev.h>
+#include <linux/atomic.h>
 
 /* Max limits for throttle policy */
 #define THROTL_IOPS_MAX		UINT_MAX
@@ -104,7 +105,7 @@
 	struct request_list		rl;
 
 	/* reference count */
-	int				refcnt;
+	atomic_t			refcnt;
 
 	/* is this blkg online? protected by both blkcg and q locks */
 	bool				online;
@@ -257,13 +258,12 @@
  * blkg_get - get a blkg reference
  * @blkg: blkg to get
  *
- * The caller should be holding queue_lock and an existing reference.
+ * The caller should be holding an existing reference.
  */
 static inline void blkg_get(struct blkcg_gq *blkg)
 {
-	lockdep_assert_held(blkg->q->queue_lock);
-	WARN_ON_ONCE(!blkg->refcnt);
-	blkg->refcnt++;
+	WARN_ON_ONCE(atomic_read(&blkg->refcnt) <= 0);
+	atomic_inc(&blkg->refcnt);
 }
 
 void __blkg_release_rcu(struct rcu_head *rcu);
@@ -271,14 +271,11 @@
 /**
  * blkg_put - put a blkg reference
  * @blkg: blkg to put
- *
- * The caller should be holding queue_lock.
  */
 static inline void blkg_put(struct blkcg_gq *blkg)
 {
-	lockdep_assert_held(blkg->q->queue_lock);
-	WARN_ON_ONCE(blkg->refcnt <= 0);
-	if (!--blkg->refcnt)
+	WARN_ON_ONCE(atomic_read(&blkg->refcnt) <= 0);
+	if (atomic_dec_and_test(&blkg->refcnt))
 		call_rcu(&blkg->rcu_head, __blkg_release_rcu);
 }
 
@@ -433,9 +430,9 @@
 	uint64_t v;
 
 	do {
-		start = u64_stats_fetch_begin(&stat->syncp);
+		start = u64_stats_fetch_begin_bh(&stat->syncp);
 		v = stat->cnt;
-	} while (u64_stats_fetch_retry(&stat->syncp, start));
+	} while (u64_stats_fetch_retry_bh(&stat->syncp, start));
 
 	return v;
 }
@@ -501,9 +498,9 @@
 	struct blkg_rwstat tmp;
 
 	do {
-		start = u64_stats_fetch_begin(&rwstat->syncp);
+		start = u64_stats_fetch_begin_bh(&rwstat->syncp);
 		tmp = *rwstat;
-	} while (u64_stats_fetch_retry(&rwstat->syncp, start));
+	} while (u64_stats_fetch_retry_bh(&rwstat->syncp, start));
 
 	return tmp;
 }
diff -ruw linux-3.11.10/block/blk-core.c linux-3.11.10-fbx/block/blk-core.c
--- linux-3.11.10/block/blk-core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/block/blk-core.c	2015-09-29 11:15:05.541730156 +0200
@@ -645,10 +645,12 @@
 	__set_bit(QUEUE_FLAG_BYPASS, &q->queue_flags);
 
 	if (blkcg_init_queue(q))
-		goto fail_id;
+		goto fail_bdi;
 
 	return q;
 
+fail_bdi:
+	bdi_destroy(&q->backing_dev_info);
 fail_id:
 	ida_simple_remove(&blk_queue_ida, q->id);
 fail_q:
@@ -739,9 +741,17 @@
 
 	q->sg_reserved_size = INT_MAX;
 
+	/* Protect q->elevator from elevator_change */
+	mutex_lock(&q->sysfs_lock);
+
 	/* init elevator */
-	if (elevator_init(q, NULL))
+	if (elevator_init(q, NULL)) {
+		mutex_unlock(&q->sysfs_lock);
 		return NULL;
+	}
+
+	mutex_unlock(&q->sysfs_lock);
+
 	return q;
 }
 EXPORT_SYMBOL(blk_init_allocated_queue);
@@ -2030,7 +2040,7 @@
 	}
 }
 
-static void blk_account_io_done(struct request *req)
+static void blk_account_io_done(struct request *req, int error)
 {
 	/*
 	 * Account IO completion.  flush_rq isn't accounted as a
@@ -2046,6 +2056,8 @@
 		cpu = part_stat_lock();
 		part = req->part;
 
+		if (error < 0)
+			part_stat_inc(cpu, part, io_errors[rw]);
 		part_stat_inc(cpu, part, ios[rw]);
 		part_stat_add(cpu, part, ticks[rw], duration);
 		part_round_stats(cpu, part);
@@ -2229,6 +2241,7 @@
 	if (unlikely(blk_bidi_rq(req)))
 		req->next_rq->resid_len = blk_rq_bytes(req->next_rq);
 
+	BUG_ON(test_bit(REQ_ATOM_COMPLETE, &req->atomic_flags));
 	blk_add_timer(req);
 }
 EXPORT_SYMBOL(blk_start_request);
@@ -2288,7 +2301,7 @@
 	if (!req->bio)
 		return false;
 
-	trace_block_rq_complete(req->q, req);
+	trace_block_rq_complete(req->q, req, nr_bytes);
 
 	/*
 	 * For fs requests, rq is just carrier of independent bio's
@@ -2448,7 +2461,7 @@
 		blk_unprep_request(req);
 
 
-	blk_account_io_done(req);
+	blk_account_io_done(req, error);
 
 	if (req->end_io)
 		req->end_io(req, error);
diff -ruw linux-3.11.10/block/blk.h linux-3.11.10-fbx/block/blk.h
--- linux-3.11.10/block/blk.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/block/blk.h	2015-09-29 11:15:05.545730185 +0200
@@ -96,7 +96,7 @@
 			q->flush_queue_delayed = 1;
 			return NULL;
 		}
-		if (unlikely(blk_queue_dying(q)) ||
+		if (unlikely(blk_queue_bypass(q)) ||
 		    !q->elevator->type->ops.elevator_dispatch_fn(q, 0))
 			return NULL;
 	}
diff -ruw linux-3.11.10/block/blk-lib.c linux-3.11.10-fbx/block/blk-lib.c
--- linux-3.11.10/block/blk-lib.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/block/blk-lib.c	2015-09-29 11:15:05.545730185 +0200
@@ -121,6 +121,14 @@
 
 		atomic_inc(&bb.done);
 		submit_bio(type, bio);
+
+		/*
+		 * We can loop for a long time in here, if someone does
+		 * full device discards (like mkfs). Be nice and allow
+		 * us to schedule out to avoid softlocking if preempt
+		 * is disabled.
+		 */
+		cond_resched();
 	}
 	blk_finish_plug(&plug);
 
diff -ruw linux-3.11.10/block/blk-settings.c linux-3.11.10-fbx/block/blk-settings.c
--- linux-3.11.10/block/blk-settings.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/block/blk-settings.c	2015-09-29 11:15:05.545730185 +0200
@@ -144,6 +144,7 @@
 	lim->discard_zeroes_data = 1;
 	lim->max_segments = USHRT_MAX;
 	lim->max_hw_sectors = UINT_MAX;
+	lim->max_segment_size = UINT_MAX;
 	lim->max_sectors = UINT_MAX;
 	lim->max_write_same_sectors = UINT_MAX;
 }
diff -ruw linux-3.11.10/block/blk-timeout.c linux-3.11.10-fbx/block/blk-timeout.c
--- linux-3.11.10/block/blk-timeout.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/block/blk-timeout.c	2015-09-29 11:15:05.545730185 +0200
@@ -91,8 +91,8 @@
 		__blk_complete_request(req);
 		break;
 	case BLK_EH_RESET_TIMER:
-		blk_clear_rq_complete(req);
 		blk_add_timer(req);
+		blk_clear_rq_complete(req);
 		break;
 	case BLK_EH_NOT_HANDLED:
 		/*
@@ -174,7 +174,6 @@
 		return;
 
 	BUG_ON(!list_empty(&req->timeout_list));
-	BUG_ON(test_bit(REQ_ATOM_COMPLETE, &req->atomic_flags));
 
 	/*
 	 * Some LLDs, like scsi, peek at the timeout to prevent a
diff -ruw linux-3.11.10/block/elevator.c linux-3.11.10-fbx/block/elevator.c
--- linux-3.11.10/block/elevator.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/block/elevator.c	2015-09-29 11:15:05.549730222 +0200
@@ -186,6 +186,12 @@
 	struct elevator_type *e = NULL;
 	int err;
 
+	/*
+	 * q->sysfs_lock must be held to provide mutual exclusion between
+	 * elevator_switch() and here.
+	 */
+	lockdep_assert_held(&q->sysfs_lock);
+
 	if (unlikely(q->elevator))
 		return 0;
 
@@ -959,7 +965,7 @@
 /*
  * Switch this queue to the given IO scheduler.
  */
-int elevator_change(struct request_queue *q, const char *name)
+static int __elevator_change(struct request_queue *q, const char *name)
 {
 	char elevator_name[ELV_NAME_MAX];
 	struct elevator_type *e;
@@ -981,6 +987,18 @@
 
 	return elevator_switch(q, e);
 }
+
+int elevator_change(struct request_queue *q, const char *name)
+{
+	int ret;
+
+	/* Protect q->elevator from elevator_init() */
+	mutex_lock(&q->sysfs_lock);
+	ret = __elevator_change(q, name);
+	mutex_unlock(&q->sysfs_lock);
+
+	return ret;
+}
 EXPORT_SYMBOL(elevator_change);
 
 ssize_t elv_iosched_store(struct request_queue *q, const char *name,
@@ -991,7 +1009,7 @@
 	if (!q->elevator)
 		return count;
 
-	ret = elevator_change(q, name);
+	ret = __elevator_change(q, name);
 	if (!ret)
 		return count;
 
diff -ruw linux-3.11.10/block/partition-generic.c linux-3.11.10-fbx/block/partition-generic.c
--- linux-3.11.10/block/partition-generic.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/block/partition-generic.c	2015-09-29 11:15:05.549730222 +0200
@@ -120,7 +120,7 @@
 	return sprintf(buf,
 		"%8lu %8lu %8llu %8u "
 		"%8lu %8lu %8llu %8u "
-		"%8u %8u %8u"
+		"%8u %8u %8u %8lu %lu"
 		"\n",
 		part_stat_read(p, ios[READ]),
 		part_stat_read(p, merges[READ]),
@@ -132,7 +132,9 @@
 		jiffies_to_msecs(part_stat_read(p, ticks[WRITE])),
 		part_in_flight(p),
 		jiffies_to_msecs(part_stat_read(p, io_ticks)),
-		jiffies_to_msecs(part_stat_read(p, time_in_queue)));
+		jiffies_to_msecs(part_stat_read(p, time_in_queue)),
+		part_stat_read(p, io_errors[READ]),
+		part_stat_read(p, io_errors[WRITE]));
 }
 
 ssize_t part_inflight_show(struct device *dev,
diff -ruw linux-3.11.10/crypto/algboss.c linux-3.11.10-fbx/crypto/algboss.c
--- linux-3.11.10/crypto/algboss.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/crypto/algboss.c	2015-06-12 14:02:21.462951726 +0200
@@ -221,7 +221,9 @@
 	if (type & CRYPTO_ALG_TESTED)
 		goto skiptest;
 
+#ifdef CONFIG_CRYPTO_BUILTIN_TEST
 	err = alg_test(param->driver, param->alg, type, CRYPTO_ALG_TESTED);
+#endif
 
 skiptest:
 	crypto_alg_tested(param->driver, err);
diff -ruw linux-3.11.10/crypto/authenc.c linux-3.11.10-fbx/crypto/authenc.c
--- linux-3.11.10/crypto/authenc.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/crypto/authenc.c	2015-09-29 11:15:05.553730248 +0200
@@ -368,9 +368,10 @@
 	if (!err) {
 		struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
 		struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
-		struct ablkcipher_request *abreq = aead_request_ctx(areq);
-		u8 *iv = (u8 *)(abreq + 1) +
-			 crypto_ablkcipher_reqsize(ctx->enc);
+		struct authenc_request_ctx *areq_ctx = aead_request_ctx(areq);
+		struct ablkcipher_request *abreq = (void *)(areq_ctx->tail
+							    + ctx->reqoff);
+		u8 *iv = (u8 *)abreq - crypto_ablkcipher_ivsize(ctx->enc);
 
 		err = crypto_authenc_genicv(areq, iv, 0);
 	}
diff -ruw linux-3.11.10/crypto/ccm.c linux-3.11.10-fbx/crypto/ccm.c
--- linux-3.11.10/crypto/ccm.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/crypto/ccm.c	2015-09-29 11:15:05.557730277 +0200
@@ -271,6 +271,7 @@
 	}
 
 	/* compute plaintext into mac */
+	if (cryptlen)
 	get_data_to_compute(cipher, pctx, plain, cryptlen);
 
 out:
diff -ruw linux-3.11.10/crypto/crypto_wq.c linux-3.11.10-fbx/crypto/crypto_wq.c
--- linux-3.11.10/crypto/crypto_wq.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/crypto/crypto_wq.c	2014-07-29 21:29:41.885852072 +0200
@@ -33,7 +33,7 @@
 	destroy_workqueue(kcrypto_wq);
 }
 
-module_init(crypto_wq_init);
+subsys_initcall(crypto_wq_init);
 module_exit(crypto_wq_exit);
 
 MODULE_LICENSE("GPL");
diff -ruw linux-3.11.10/crypto/Kconfig linux-3.11.10-fbx/crypto/Kconfig
--- linux-3.11.10/crypto/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/crypto/Kconfig	2015-09-29 11:15:05.549730222 +0200
@@ -86,6 +86,10 @@
 	tristate
 	select CRYPTO_ALGAPI2
 
+config CRYPTO_BUILTIN_TEST
+	bool "Include builtin tests"
+	default y
+
 config CRYPTO_MANAGER
 	tristate "Cryptographic algorithm manager"
 	select CRYPTO_MANAGER2
diff -ruw linux-3.11.10/crypto/Makefile linux-3.11.10-fbx/crypto/Makefile
--- linux-3.11.10/crypto/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/crypto/Makefile	2015-09-29 11:15:05.549730222 +0200
@@ -28,7 +28,10 @@
 
 obj-$(CONFIG_CRYPTO_PCOMP2) += pcompress.o
 
-cryptomgr-y := algboss.o testmgr.o
+cryptomgr-y := algboss.o
+ifeq ($(CONFIG_CRYPTO_BUILTIN_TEST),y)
+cryptomgr-y += testmgr.o
+endif
 
 obj-$(CONFIG_CRYPTO_MANAGER2) += cryptomgr.o
 obj-$(CONFIG_CRYPTO_USER) += crypto_user.o
diff -ruw linux-3.11.10/drivers/ata/libata-core.c linux-3.11.10-fbx/drivers/ata/libata-core.c
--- linux-3.11.10/drivers/ata/libata-core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/ata/libata-core.c	2015-09-29 11:15:05.633730862 +0200
@@ -2199,6 +2199,16 @@
 	if (rc)
 		return rc;
 
+	/* some WD SATA-1 drives have issues with LPM, turn on NOLPM for them */
+	if ((dev->horkage & ATA_HORKAGE_WD_BROKEN_LPM) &&
+	    (id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2)
+		dev->horkage |= ATA_HORKAGE_NOLPM;
+
+	if (dev->horkage & ATA_HORKAGE_NOLPM) {
+		ata_dev_warn(dev, "LPM support broken, forcing max_power\n");
+		dev->link->ap->target_lpm_policy = ATA_LPM_MAX_POWER;
+	}
+
 	/* let ACPI work its magic */
 	rc = ata_acpi_on_devcfg(dev);
 	if (rc)
@@ -4110,6 +4120,7 @@
 	{ "TORiSAN DVD-ROM DRD-N216", NULL,	ATA_HORKAGE_MAX_SEC_128 },
 	{ "QUANTUM DAT    DAT72-000", NULL,	ATA_HORKAGE_ATAPI_MOD16_DMA },
 	{ "Slimtype DVD A  DS8A8SH", NULL,	ATA_HORKAGE_MAX_SEC_LBA48 },
+	{ "Slimtype DVD A  DS8A9SH", NULL,	ATA_HORKAGE_MAX_SEC_LBA48 },
 
 	/* Devices we expect to fail diagnostics */
 
@@ -4139,6 +4150,10 @@
 	{ "ST3320[68]13AS",	"SD1[5-9]",	ATA_HORKAGE_NONCQ |
 						ATA_HORKAGE_FIRMWARE_WARN },
 
+	/* Seagate Momentus SpinPoint M8 seem to have FPMDA_AA issues */
+	{ "ST1000LM024 HN-M101MBB", "2AR10001",	ATA_HORKAGE_BROKEN_FPDMA_AA },
+	{ "ST1000LM024 HN-M101MBB", "2BA30001",	ATA_HORKAGE_BROKEN_FPDMA_AA },
+
 	/* Blacklist entries taken from Silicon Image 3124/3132
 	   Windows driver .inf file - also several Linux problem reports */
 	{ "HTS541060G9SA00",    "MB3OC60D",     ATA_HORKAGE_NONCQ, },
@@ -4148,6 +4163,8 @@
 	/* https://bugzilla.kernel.org/show_bug.cgi?id=15573 */
 	{ "C300-CTFDDAC128MAG",	"0001",		ATA_HORKAGE_NONCQ, },
 
+	{ "Boot ROM", 		NULL,		ATA_HORKAGE_NODMA  },
+
 	/* devices which puke on READ_NATIVE_MAX */
 	{ "HDS724040KLSA80",	"KFAOA20N",	ATA_HORKAGE_BROKEN_HPA, },
 	{ "WDC WD3200JD-00KLB0", "WD-WCAMR1130137", ATA_HORKAGE_BROKEN_HPA },
@@ -4185,6 +4202,23 @@
 	{ "PIONEER DVD-RW  DVR-212D",	NULL,	ATA_HORKAGE_NOSETXFER },
 	{ "PIONEER DVD-RW  DVR-216D",	NULL,	ATA_HORKAGE_NOSETXFER },
 
+	/*
+	 * Some WD SATA-I drives spin up and down erratically when the link
+	 * is put into the slumber mode.  We don't have full list of the
+	 * affected devices.  Disable LPM if the device matches one of the
+	 * known prefixes and is SATA-1.  As a side effect LPM partial is
+	 * lost too.
+	 *
+	 * https://bugzilla.kernel.org/show_bug.cgi?id=57211
+	 */
+	{ "WDC WD800JD-*",		NULL,	ATA_HORKAGE_WD_BROKEN_LPM },
+	{ "WDC WD1200JD-*",		NULL,	ATA_HORKAGE_WD_BROKEN_LPM },
+	{ "WDC WD1600JD-*",		NULL,	ATA_HORKAGE_WD_BROKEN_LPM },
+	{ "WDC WD2000JD-*",		NULL,	ATA_HORKAGE_WD_BROKEN_LPM },
+	{ "WDC WD2500JD-*",		NULL,	ATA_HORKAGE_WD_BROKEN_LPM },
+	{ "WDC WD3000JD-*",		NULL,	ATA_HORKAGE_WD_BROKEN_LPM },
+	{ "WDC WD3200JD-*",		NULL,	ATA_HORKAGE_WD_BROKEN_LPM },
+
 	/* End Marker */
 	{ }
 };
@@ -4733,21 +4767,26 @@
 static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap)
 {
 	struct ata_queued_cmd *qc = NULL;
-	unsigned int i;
+	unsigned int i, tag;
 
 	/* no command while frozen */
 	if (unlikely(ap->pflags & ATA_PFLAG_FROZEN))
 		return NULL;
 
+	for (i = 0; i < ATA_MAX_QUEUE; i++) {
+		tag = (i + ap->last_tag + 1) % ATA_MAX_QUEUE;
+
 	/* the last tag is reserved for internal command. */
-	for (i = 0; i < ATA_MAX_QUEUE - 1; i++)
-		if (!test_and_set_bit(i, &ap->qc_allocated)) {
-			qc = __ata_qc_from_tag(ap, i);
+		if (tag == ATA_TAG_INTERNAL)
+			continue;
+
+		if (!test_and_set_bit(tag, &ap->qc_allocated)) {
+			qc = __ata_qc_from_tag(ap, tag);
+			qc->tag = tag;
+			ap->last_tag = tag;
 			break;
 		}
-
-	if (qc)
-		qc->tag = i;
+	}
 
 	return qc;
 }
@@ -6265,6 +6304,8 @@
 static void ata_port_detach(struct ata_port *ap)
 {
 	unsigned long flags;
+	struct ata_link *link;
+	struct ata_device *dev;
 
 	if (!ap->ops->error_handler)
 		goto skip_eh;
@@ -6284,6 +6325,13 @@
 	cancel_delayed_work_sync(&ap->hotplug_task);
 
  skip_eh:
+	/* clean up zpodd on port removal */
+	ata_for_each_link(link, ap, HOST_FIRST) {
+		ata_for_each_dev(dev, link, ALL) {
+			if (zpodd_dev_enabled(dev))
+				zpodd_exit(dev);
+		}
+	}
 	if (ap->pmp_link) {
 		int i;
 		for (i = 0; i < SATA_PMP_MAX_PORTS; i++)
@@ -6505,6 +6553,7 @@
 		{ "norst",	.lflags		= ATA_LFLAG_NO_HRST | ATA_LFLAG_NO_SRST },
 		{ "rstonce",	.lflags		= ATA_LFLAG_RST_ONCE },
 		{ "atapi_dmadir", .horkage_on	= ATA_HORKAGE_ATAPI_DMADIR },
+		{ "disable",	.horkage_on	= ATA_HORKAGE_DISABLE },
 	};
 	char *start = *cur, *p = *cur;
 	char *id, *val, *endp;
diff -ruw linux-3.11.10/drivers/ata/libata-pmp.c linux-3.11.10-fbx/drivers/ata/libata-pmp.c
--- linux-3.11.10/drivers/ata/libata-pmp.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/ata/libata-pmp.c	2015-09-29 11:15:05.637730887 +0200
@@ -447,8 +447,11 @@
 		 * otherwise.  Don't try hard to recover it.
 		 */
 		ap->pmp_link[ap->nr_pmp_links - 1].flags |= ATA_LFLAG_NO_RETRY;
-	} else if (vendor == 0x197b && devid == 0x2352) {
-		/* chip found in Thermaltake BlackX Duet, jmicron JMB350? */
+	} else if (vendor == 0x197b && (devid == 0x2352 || devid == 0x0325)) {
+		/*
+		 * 0x2352: found in Thermaltake BlackX Duet, jmicron JMB350?
+		 * 0x0325: jmicron JMB394.
+		 */
 		ata_for_each_link(link, ap, EDGE) {
 			/* SRST breaks detection and disks get misclassified
 			 * LPM disabled to avoid potential problems
diff -ruw linux-3.11.10/drivers/ata/libata-scsi.c linux-3.11.10-fbx/drivers/ata/libata-scsi.c
--- linux-3.11.10/drivers/ata/libata-scsi.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/ata/libata-scsi.c	2015-09-29 11:15:05.637730887 +0200
@@ -112,12 +112,14 @@
 	[ATA_LPM_MIN_POWER]	= "min_power",
 };
 
-static ssize_t ata_scsi_lpm_store(struct device *dev,
+static ssize_t ata_scsi_lpm_store(struct device *device,
 				  struct device_attribute *attr,
 				  const char *buf, size_t count)
 {
-	struct Scsi_Host *shost = class_to_shost(dev);
+	struct Scsi_Host *shost = class_to_shost(device);
 	struct ata_port *ap = ata_shost_to_port(shost);
+	struct ata_link *link;
+	struct ata_device *dev;
 	enum ata_lpm_policy policy;
 	unsigned long flags;
 
@@ -133,10 +135,20 @@
 		return -EINVAL;
 
 	spin_lock_irqsave(ap->lock, flags);
+
+	ata_for_each_link(link, ap, EDGE) {
+		ata_for_each_dev(dev, &ap->link, ENABLED) {
+			if (dev->horkage & ATA_HORKAGE_NOLPM) {
+				count = -EOPNOTSUPP;
+				goto out_unlock;
+			}
+		}
+	}
+
 	ap->target_lpm_policy = policy;
 	ata_port_schedule_eh(ap);
+out_unlock:
 	spin_unlock_irqrestore(ap->lock, flags);
-
 	return count;
 }
 
@@ -3613,6 +3625,7 @@
 		shost->max_lun = 1;
 		shost->max_channel = 1;
 		shost->max_cmd_len = 16;
+		shost->no_write_same = 1;
 
 		/* Schedule policy is determined by ->qc_defer()
 		 * callback and it needs to see every deferred qc.
@@ -3622,7 +3635,7 @@
 		shost->max_host_blocked = 1;
 
 		rc = scsi_add_host_with_dma(ap->scsi_host,
-						&ap->tdev, ap->host->dev);
+					    host->dev, host->dev);
 		if (rc)
 			goto err_add;
 	}
@@ -3862,6 +3875,27 @@
 		return;
 	}
 
+	/*
+	 * XXX - UGLY HACK
+	 *
+	 * The block layer suspend/resume path is fundamentally broken due
+	 * to freezable kthreads and workqueue and may deadlock if a block
+	 * device gets removed while resume is in progress.  I don't know
+	 * what the solution is short of removing freezable kthreads and
+	 * workqueues altogether.
+	 *
+	 * The following is an ugly hack to avoid kicking off device
+	 * removal while freezer is active.  This is a joke but does avoid
+	 * this particular deadlock scenario.
+	 *
+	 * https://bugzilla.kernel.org/show_bug.cgi?id=62801
+	 * http://marc.info/?l=linux-kernel&m=138695698516487
+	 */
+#ifdef CONFIG_FREEZER
+	while (pm_freezing)
+		msleep(10);
+#endif
+
 	DPRINTK("ENTER\n");
 	mutex_lock(&ap->scsi_scan_mutex);
 
diff -ruw linux-3.11.10/drivers/ata/libata-transport.c linux-3.11.10-fbx/drivers/ata/libata-transport.c
--- linux-3.11.10/drivers/ata/libata-transport.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/ata/libata-transport.c	2015-09-29 11:15:05.637730887 +0200
@@ -320,25 +320,25 @@
 /*
  * ATA link attributes
  */
+static int noop(int x) { return x; }
 
-
-#define ata_link_show_linkspeed(field)					\
+#define ata_link_show_linkspeed(field, format)			        \
 static ssize_t								\
 show_ata_link_##field(struct device *dev,				\
 		      struct device_attribute *attr, char *buf)		\
 {									\
 	struct ata_link *link = transport_class_to_link(dev);		\
 									\
-	return sprintf(buf,"%s\n", sata_spd_string(fls(link->field)));	\
+	return sprintf(buf, "%s\n", sata_spd_string(format(link->field))); \
 }
 
-#define ata_link_linkspeed_attr(field)					\
-	ata_link_show_linkspeed(field)					\
+#define ata_link_linkspeed_attr(field, format)				\
+	ata_link_show_linkspeed(field, format)				\
 static DEVICE_ATTR(field, S_IRUGO, show_ata_link_##field, NULL)
 
-ata_link_linkspeed_attr(hw_sata_spd_limit);
-ata_link_linkspeed_attr(sata_spd_limit);
-ata_link_linkspeed_attr(sata_spd);
+ata_link_linkspeed_attr(hw_sata_spd_limit, fls);
+ata_link_linkspeed_attr(sata_spd_limit, fls);
+ata_link_linkspeed_attr(sata_spd, noop);
 
 
 static DECLARE_TRANSPORT_CLASS(ata_link_class,
diff -ruw linux-3.11.10/drivers/ata/sata_mv.c linux-3.11.10-fbx/drivers/ata/sata_mv.c
--- linux-3.11.10/drivers/ata/sata_mv.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/ata/sata_mv.c	2015-09-29 11:15:05.653731008 +0200
@@ -304,6 +304,7 @@
 	MV5_LTMODE		= 0x30,
 	MV5_PHY_CTL		= 0x0C,
 	SATA_IFCFG		= 0x050,
+	LP_PHY_CTL		= 0x058,
 
 	MV_M2_PREAMP_MASK	= 0x7e0,
 
@@ -431,6 +432,7 @@
 	MV_HP_CUT_THROUGH	= (1 << 10),	/* can use EDMA cut-through */
 	MV_HP_FLAG_SOC		= (1 << 11),	/* SystemOnChip, no PCI */
 	MV_HP_QUIRK_LED_BLINK_EN = (1 << 12),	/* is led blinking enabled? */
+	MV_HP_FIX_LP_PHY_CTL	= (1 << 13),	/* fix speed in LP_PHY_CTL ? */
 
 	/* Port private flags (pp_flags) */
 	MV_PP_FLAG_EDMA_EN	= (1 << 0),	/* is EDMA engine enabled? */
@@ -1353,6 +1355,7 @@
 
 	if (ofs != 0xffffffffU) {
 		void __iomem *addr = mv_ap_base(link->ap) + ofs;
+		struct mv_host_priv *hpriv = link->ap->host->private_data;
 		if (sc_reg_in == SCR_CONTROL) {
 			/*
 			 * Workaround for 88SX60x1 FEr SATA#26:
@@ -1369,6 +1372,18 @@
 			 */
 			if ((val & 0xf) == 1 || (readl(addr) & 0xf) == 1)
 				val |= 0xf000;
+
+			if (hpriv->hp_flags & MV_HP_FIX_LP_PHY_CTL) {
+				void __iomem *lp_phy_addr =
+					mv_ap_base(link->ap) + LP_PHY_CTL;
+				/*
+				 * Set PHY speed according to SControl speed.
+				 */
+				if ((val & 0xf0) == 0x10)
+					writelfl(0x7, lp_phy_addr);
+				else
+					writelfl(0x227, lp_phy_addr);
+			}
 		}
 		writelfl(val, addr);
 		return 0;
@@ -4111,6 +4126,15 @@
 	if (rc)
 		goto err;
 
+	/*
+	 * To allow disk hotplug on Armada 370/XP SoCs, the PHY speed must be
+	 * updated in the LP_PHY_CTL register.
+	 */
+	if (pdev->dev.of_node &&
+		of_device_is_compatible(pdev->dev.of_node,
+					"marvell,armada-370-sata"))
+		hpriv->hp_flags |= MV_HP_FIX_LP_PHY_CTL;
+
 	/* initialize adapter */
 	rc = mv_init_host(host);
 	if (rc)
@@ -4216,6 +4240,7 @@
 
 #ifdef CONFIG_OF
 static struct of_device_id mv_sata_dt_ids[] = {
+	{ .compatible = "marvell,armada-370-sata", },
 	{ .compatible = "marvell,orion-sata", },
 	{},
 };
diff -ruw linux-3.11.10/drivers/base/dd.c linux-3.11.10-fbx/drivers/base/dd.c
--- linux-3.11.10/drivers/base/dd.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/base/dd.c	2015-09-29 11:15:05.665731099 +0200
@@ -52,6 +52,7 @@
 static LIST_HEAD(deferred_probe_pending_list);
 static LIST_HEAD(deferred_probe_active_list);
 static struct workqueue_struct *deferred_wq;
+static atomic_t deferred_trigger_count = ATOMIC_INIT(0);
 
 /**
  * deferred_probe_work_func() - Retry probing devices in the active list.
@@ -135,6 +136,17 @@
  * This functions moves all devices from the pending list to the active
  * list and schedules the deferred probe workqueue to process them.  It
  * should be called anytime a driver is successfully bound to a device.
+ *
+ * Note, there is a race condition in multi-threaded probe. In the case where
+ * more than one device is probing at the same time, it is possible for one
+ * probe to complete successfully while another is about to defer. If the second
+ * depends on the first, then it will get put on the pending list after the
+ * trigger event has already occured and will be stuck there.
+ *
+ * The atomic 'deferred_trigger_count' is used to determine if a successful
+ * trigger has occurred in the midst of probing a driver. If the trigger count
+ * changes in the midst of a probe, then deferred processing should be triggered
+ * again.
  */
 static void driver_deferred_probe_trigger(void)
 {
@@ -147,6 +159,7 @@
 	 * into the active list so they can be retried by the workqueue
 	 */
 	mutex_lock(&deferred_probe_mutex);
+	atomic_inc(&deferred_trigger_count);
 	list_splice_tail_init(&deferred_probe_pending_list,
 			      &deferred_probe_active_list);
 	mutex_unlock(&deferred_probe_mutex);
@@ -265,6 +278,7 @@
 static int really_probe(struct device *dev, struct device_driver *drv)
 {
 	int ret = 0;
+	int local_trigger_count = atomic_read(&deferred_trigger_count);
 
 	atomic_inc(&probe_count);
 	pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
@@ -310,6 +324,9 @@
 		/* Driver requested deferred probing */
 		dev_info(dev, "Driver %s requests probe deferral\n", drv->name);
 		driver_deferred_probe_add(dev);
+		/* Did a trigger occur while probing? Need to re-trigger if yes */
+		if (local_trigger_count != atomic_read(&deferred_trigger_count))
+			driver_deferred_probe_trigger();
 	} else if (ret != -ENODEV && ret != -ENXIO) {
 		/* driver matched but the probe failed */
 		printk(KERN_WARNING
@@ -499,7 +516,7 @@
 						     BUS_NOTIFY_UNBIND_DRIVER,
 						     dev);
 
-		pm_runtime_put(dev);
+		pm_runtime_put_sync(dev);
 
 		if (dev->bus && dev->bus->remove)
 			dev->bus->remove(dev);
diff -ruw linux-3.11.10/drivers/base/firmware_class.c linux-3.11.10-fbx/drivers/base/firmware_class.c
--- linux-3.11.10/drivers/base/firmware_class.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/base/firmware_class.c	2015-09-29 11:15:05.665731099 +0200
@@ -258,7 +258,8 @@
 	"/lib/firmware/updates/" UTS_RELEASE,
 	"/lib/firmware/updates",
 	"/lib/firmware/" UTS_RELEASE,
-	"/lib/firmware"
+	"/lib/firmware",
+	"/usr/lib/hotplug/firmware"
 };
 
 /*
@@ -1532,6 +1533,7 @@
 	switch (mode) {
 	case PM_HIBERNATION_PREPARE:
 	case PM_SUSPEND_PREPARE:
+	case PM_RESTORE_PREPARE:
 		kill_requests_without_uevent();
 		device_cache_fw_images();
 		break;
diff -ruw linux-3.11.10/drivers/base/topology.c linux-3.11.10-fbx/drivers/base/topology.c
--- linux-3.11.10/drivers/base/topology.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/base/topology.c	2015-09-29 11:15:05.673731162 +0200
@@ -40,8 +40,7 @@
 static ssize_t show_##name(struct device *dev,			\
 		struct device_attribute *attr, char *buf)	\
 {								\
-	unsigned int cpu = dev->id;				\
-	return sprintf(buf, "%d\n", topology_##name(cpu));	\
+	return sprintf(buf, "%d\n", topology_##name(dev->id));	\
 }
 
 #if defined(topology_thread_cpumask) || defined(topology_core_cpumask) || \
diff -ruw linux-3.11.10/drivers/block/brd.c linux-3.11.10-fbx/drivers/block/brd.c
--- linux-3.11.10/drivers/block/brd.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/block/brd.c	2015-09-29 11:15:05.677731191 +0200
@@ -545,7 +545,7 @@
 
 	mutex_lock(&brd_devices_mutex);
 	brd = brd_init_one(MINOR(dev) >> part_shift);
-	kobj = brd ? get_disk(brd->brd_disk) : ERR_PTR(-ENOMEM);
+	kobj = brd ? get_disk(brd->brd_disk) : NULL;
 	mutex_unlock(&brd_devices_mutex);
 
 	*part = 0;
diff -ruw linux-3.11.10/drivers/block/loop.c linux-3.11.10-fbx/drivers/block/loop.c
--- linux-3.11.10/drivers/block/loop.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/block/loop.c	2015-09-29 11:15:05.689731282 +0200
@@ -894,13 +894,6 @@
 
 	bio_list_init(&lo->lo_bio_list);
 
-	/*
-	 * set queue make_request_fn, and add limits based on lower level
-	 * device
-	 */
-	blk_queue_make_request(lo->lo_queue, loop_make_request);
-	lo->lo_queue->queuedata = lo;
-
 	if (!(lo_flags & LO_FLAGS_READ_ONLY) && file->f_op->fsync)
 		blk_queue_flush(lo->lo_queue, REQ_FLUSH);
 
@@ -1618,6 +1611,8 @@
 	if (!lo)
 		goto out;
 
+	lo->lo_state = Lo_unbound;
+
 	/* allocate id, if @id >= 0, we're requesting that specific id */
 	if (i >= 0) {
 		err = idr_alloc(&loop_index_idr, lo, i, i + 1, GFP_KERNEL);
@@ -1633,7 +1628,13 @@
 	err = -ENOMEM;
 	lo->lo_queue = blk_alloc_queue(GFP_KERNEL);
 	if (!lo->lo_queue)
-		goto out_free_dev;
+		goto out_free_idr;
+
+	/*
+	 * set queue make_request_fn
+	 */
+	blk_queue_make_request(lo->lo_queue, loop_make_request);
+	lo->lo_queue->queuedata = lo;
 
 	disk = lo->lo_disk = alloc_disk(1 << part_shift);
 	if (!disk)
@@ -1678,6 +1679,8 @@
 
 out_free_queue:
 	blk_cleanup_queue(lo->lo_queue);
+out_free_idr:
+	idr_remove(&loop_index_idr, i);
 out_free_dev:
 	kfree(lo);
 out:
@@ -1741,7 +1744,7 @@
 	if (err < 0)
 		err = loop_add(&lo, MINOR(dev) >> part_shift);
 	if (err < 0)
-		kobj = ERR_PTR(err);
+		kobj = NULL;
 	else
 		kobj = get_disk(lo->lo_disk);
 	mutex_unlock(&loop_index_mutex);
diff -ruw linux-3.11.10/drivers/bluetooth/ath3k.c linux-3.11.10-fbx/drivers/bluetooth/ath3k.c
--- linux-3.11.10/drivers/bluetooth/ath3k.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/bluetooth/ath3k.c	2015-09-29 11:15:05.701731373 +0200
@@ -82,6 +82,7 @@
 	{ USB_DEVICE(0x04CA, 0x3004) },
 	{ USB_DEVICE(0x04CA, 0x3005) },
 	{ USB_DEVICE(0x04CA, 0x3006) },
+	{ USB_DEVICE(0x04CA, 0x3007) },
 	{ USB_DEVICE(0x04CA, 0x3008) },
 	{ USB_DEVICE(0x13d3, 0x3362) },
 	{ USB_DEVICE(0x0CF3, 0xE004) },
@@ -124,6 +125,7 @@
 	{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
diff -ruw linux-3.11.10/drivers/bluetooth/btusb.c linux-3.11.10-fbx/drivers/bluetooth/btusb.c
--- linux-3.11.10/drivers/bluetooth/btusb.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/bluetooth/btusb.c	2015-09-29 11:15:05.705731410 +0200
@@ -146,6 +146,7 @@
 	{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
@@ -220,6 +221,7 @@
 
 	/* Intel Bluetooth device */
 	{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
+	{ USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
 
 	{ }	/* Terminating entry */
 };
diff -ruw linux-3.11.10/drivers/bus/mvebu-mbus.c linux-3.11.10-fbx/drivers/bus/mvebu-mbus.c
--- linux-3.11.10/drivers/bus/mvebu-mbus.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/bus/mvebu-mbus.c	2015-09-29 11:15:05.705731410 +0200
@@ -252,12 +252,6 @@
 		 */
 		if ((u64)base < wend && end > wbase)
 			return 0;
-
-		/*
-		 * Check if target/attribute conflicts
-		 */
-		if (target == wtarget && attr == wattr)
-			return 0;
 	}
 
 	return 1;
diff -ruw linux-3.11.10/drivers/char/Kconfig linux-3.11.10-fbx/drivers/char/Kconfig
--- linux-3.11.10/drivers/char/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/char/Kconfig	2015-09-29 11:15:05.709731436 +0200
@@ -15,6 +15,15 @@
 	  kind of kernel debugging operations.
 	  When in doubt, say "N".
 
+config DEVPHYSMEM
+	bool "/dev/physmem virtual device support"
+	default n
+	help
+	  Say Y here if you want to support the /dev/physmem device. The
+	  /dev/physmem device allows unprivileged access to physical memory
+	  unused by the kernel.
+	  When in doubt, say "N".
+
 config SGI_SNSC
 	bool "SGI Altix system controller communication support"
 	depends on (IA64_SGI_SN2 || IA64_GENERIC)
diff -ruw linux-3.11.10/drivers/char/mem.c linux-3.11.10-fbx/drivers/char/mem.c
--- linux-3.11.10/drivers/char/mem.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/char/mem.c	2015-09-29 11:15:05.721731527 +0200
@@ -356,6 +356,16 @@
 }
 #endif
 
+#ifdef CONFIG_DEVPHYSMEM
+static int mmap_physmem(struct file * file, struct vm_area_struct * vma)
+{
+	if (vma->vm_pgoff < max_pfn && !capable(CAP_SYS_RAWIO))
+		return -EPERM;
+
+	return mmap_mem(file, vma);
+}
+#endif
+
 #ifdef CONFIG_DEVKMEM
 /*
  * This function reads the *virtual* memory as seen by the kernel.
@@ -730,6 +740,13 @@
 	return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
 }
 
+#ifdef CONFIG_DEVPHYSMEM
+static int open_physmem(struct inode * inode, struct file * filp)
+{
+	return 0;
+}
+#endif
+
 #define zero_lseek	null_lseek
 #define full_lseek      null_lseek
 #define write_zero	write_null
@@ -801,6 +818,14 @@
 	.write		= write_full,
 };
 
+#ifdef CONFIG_DEVPHYSMEM
+static const struct file_operations physmem_fops = {
+	.mmap		= mmap_physmem,
+	.open		= open_physmem,
+	.get_unmapped_area = get_unmapped_area_mem,
+};
+#endif
+
 static const struct memdev {
 	const char *name;
 	umode_t mode;
@@ -822,6 +847,9 @@
 #ifdef CONFIG_PRINTK
 	[11] = { "kmsg", 0644, &kmsg_fops, NULL },
 #endif
+#ifdef CONFIG_DEVPHYSMEM
+	[16] = { "physmem", 0, &physmem_fops, &directly_mappable_cdev_bdi },
+#endif
 };
 
 static int memory_open(struct inode *inode, struct file *filp)
diff -ruw linux-3.11.10/drivers/clk/clk-divider.c linux-3.11.10-fbx/drivers/clk/clk-divider.c
--- linux-3.11.10/drivers/clk/clk-divider.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/clk/clk-divider.c	2015-09-29 11:15:05.729731593 +0200
@@ -87,7 +87,7 @@
 	return 0;
 }
 
-static unsigned int _get_val(struct clk_divider *divider, u8 div)
+static unsigned int _get_val(struct clk_divider *divider, unsigned int div)
 {
 	if (divider->flags & CLK_DIVIDER_ONE_BASED)
 		return div;
diff -ruw linux-3.11.10/drivers/cpufreq/Makefile linux-3.11.10-fbx/drivers/cpufreq/Makefile
--- linux-3.11.10/drivers/cpufreq/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/cpufreq/Makefile	2015-09-29 11:15:05.745731710 +0200
@@ -50,7 +50,7 @@
 # LITTLE drivers, so that it is probed last.
 obj-$(CONFIG_ARM_DT_BL_CPUFREQ)		+= arm_big_little_dt.o
 
-obj-$(CONFIG_ARCH_DAVINCI_DA850)	+= davinci-cpufreq.o
+obj-$(CONFIG_ARCH_DAVINCI)		+= davinci-cpufreq.o
 obj-$(CONFIG_UX500_SOC_DB8500)		+= dbx500-cpufreq.o
 obj-$(CONFIG_ARM_EXYNOS_CPUFREQ)	+= exynos-cpufreq.o
 obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ)	+= exynos4210-cpufreq.o
diff -ruw linux-3.11.10/drivers/dma/Kconfig linux-3.11.10-fbx/drivers/dma/Kconfig
--- linux-3.11.10/drivers/dma/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/dma/Kconfig	2015-09-29 11:15:05.777731959 +0200
@@ -321,6 +321,7 @@
 	bool "Network: TCP receive copy offload"
 	depends on DMA_ENGINE && NET
 	default (INTEL_IOATDMA || FSL_DMA)
+	depends on BROKEN
 	help
 	  This enables the use of DMA engines in the network stack to
 	  offload receive copy-to-user operations, freeing CPU cycles.
diff -ruw linux-3.11.10/drivers/gpu/drm/i915/i915_reg.h linux-3.11.10-fbx/drivers/gpu/drm/i915/i915_reg.h
--- linux-3.11.10/drivers/gpu/drm/i915/i915_reg.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/gpu/drm/i915/i915_reg.h	2015-09-29 11:15:05.861732598 +0200
@@ -1865,9 +1865,13 @@
  * Please check the detailed lore in the commit message for for experimental
  * evidence.
  */
-#define   PORTD_HOTPLUG_LIVE_STATUS               (1 << 29)
-#define   PORTC_HOTPLUG_LIVE_STATUS               (1 << 28)
-#define   PORTB_HOTPLUG_LIVE_STATUS               (1 << 27)
+#define   PORTD_HOTPLUG_LIVE_STATUS_G4X		(1 << 29)
+#define   PORTC_HOTPLUG_LIVE_STATUS_G4X		(1 << 28)
+#define   PORTB_HOTPLUG_LIVE_STATUS_G4X		(1 << 27)
+/* VLV DP/HDMI bits again match Bspec */
+#define   PORTD_HOTPLUG_LIVE_STATUS_VLV		(1 << 27)
+#define   PORTC_HOTPLUG_LIVE_STATUS_VLV		(1 << 28)
+#define   PORTB_HOTPLUG_LIVE_STATUS_VLV		(1 << 29)
 #define   PORTD_HOTPLUG_INT_STATUS		(3 << 21)
 #define   PORTC_HOTPLUG_INT_STATUS		(3 << 19)
 #define   PORTB_HOTPLUG_INT_STATUS		(3 << 17)
diff -ruw linux-3.11.10/drivers/gpu/drm/shmobile/Kconfig linux-3.11.10-fbx/drivers/gpu/drm/shmobile/Kconfig
--- linux-3.11.10/drivers/gpu/drm/shmobile/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/gpu/drm/shmobile/Kconfig	2015-09-29 11:15:05.973733446 +0200
@@ -1,6 +1,7 @@
 config DRM_SHMOBILE
 	tristate "DRM Support for SH Mobile"
 	depends on DRM && (ARM || SUPERH)
+	select BACKLIGHT_CLASS_DEVICE
 	select DRM_KMS_HELPER
 	select DRM_KMS_CMA_HELPER
 	select DRM_GEM_CMA_HELPER
diff -ruw linux-3.11.10/drivers/hid/hid-core.c linux-3.11.10-fbx/drivers/hid/hid-core.c
--- linux-3.11.10/drivers/hid/hid-core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/hid/hid-core.c	2015-09-29 11:15:05.993733604 +0200
@@ -796,7 +796,17 @@
 	 * ->numbered being checked, which may not always be the case when
 	 * drivers go to access report values.
 	 */
+	if (id == 0) {
+		/*
+		 * Validating on id 0 means we should examine the first
+		 * report in the list.
+		 */
+		report = list_entry(
+				hid->report_enum[type].report_list.next,
+				struct hid_report, list);
+	} else {
 	report = hid->report_enum[type].report_id_hash[id];
+	}
 	if (!report) {
 		hid_err(hid, "missing %s %u\n", hid_report_names[type], id);
 		return NULL;
@@ -1562,6 +1572,7 @@
 	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0xf705) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) },
@@ -1673,6 +1684,7 @@
 	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) },
@@ -1680,6 +1692,7 @@
 	{ HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_MANTICORE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GX_IMPERATOR) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) },
@@ -1711,6 +1724,7 @@
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) },
@@ -1758,12 +1772,14 @@
 	{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
 #if IS_ENABLED(CONFIG_HID_ROCCAT)
-	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKUFX) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEXTD) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
@@ -1773,6 +1789,8 @@
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SIS2_TOUCH, USB_DEVICE_ID_SIS9200_TOUCH) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SIS2_TOUCH, USB_DEVICE_ID_SIS817_TOUCH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
@@ -1829,6 +1847,7 @@
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO2, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
+	{ HID_BLUETOOTH_DEVICE(0x10eb, 0x0023) },
 	{ }
 };
 
@@ -2330,15 +2349,6 @@
 				hdev->type == HID_TYPE_USBNONE)
 			return true;
 		break;
-	case USB_VENDOR_ID_DWAV:
-		/* These are handled by usbtouchscreen. hdev->type is probably
-		 * HID_TYPE_USBNONE, but we say !HID_TYPE_USBMOUSE to match
-		 * usbtouchscreen. */
-		if ((hdev->product == USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER ||
-		     hdev->product == USB_DEVICE_ID_DWAV_TOUCHCONTROLLER) &&
-		    hdev->type != HID_TYPE_USBMOUSE)
-			return true;
-		break;
 	case USB_VENDOR_ID_VELLEMAN:
 		/* These are not HID devices.  They are handled by comedi. */
 		if ((hdev->product >= USB_DEVICE_ID_VELLEMAN_K8055_FIRST &&
diff -ruw linux-3.11.10/drivers/hid/hid-ids.h linux-3.11.10-fbx/drivers/hid/hid-ids.h
--- linux-3.11.10/drivers/hid/hid-ids.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/hid/hid-ids.h	2015-09-29 11:15:05.993733604 +0200
@@ -332,6 +332,11 @@
 #define USB_VENDOR_ID_GENERAL_TOUCH	0x0dfc
 #define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003
 #define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0101 0x0101
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0102 0x0102
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0106 0x0106
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A 0x010a
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100
 
 #define USB_VENDOR_ID_GLAB		0x06c2
 #define USB_DEVICE_ID_4_PHIDGETSERVO_30	0x0038
@@ -448,8 +453,9 @@
 
 #define USB_VENDOR_ID_HOLTEK_ALT		0x04d9
 #define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD	0xa055
-#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067	0xa067
 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A	0xa04a
+#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067	0xa067
+#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072	0xa072
 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081	0xa081
 
 #define USB_VENDOR_ID_IMATION		0x0718
@@ -483,6 +489,7 @@
 #define USB_VENDOR_ID_KYE		0x0458
 #define USB_DEVICE_ID_KYE_ERGO_525V	0x0087
 #define USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE	0x0138
+#define USB_DEVICE_ID_GENIUS_MANTICORE	0x0153
 #define USB_DEVICE_ID_GENIUS_GX_IMPERATOR	0x4018
 #define USB_DEVICE_ID_KYE_GPEN_560	0x5003
 #define USB_DEVICE_ID_KYE_EASYPEN_I405X	0x5010
@@ -571,6 +578,7 @@
 #define USB_DEVICE_ID_DINOVO_EDGE	0xc714
 #define USB_DEVICE_ID_DINOVO_MINI	0xc71f
 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2	0xca03
+#define USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL	0xca04
 
 #define USB_VENDOR_ID_LUMIO		0x202e
 #define USB_DEVICE_ID_CRYSTALTOUCH	0x0006
@@ -745,6 +753,10 @@
 #define USB_VENDOR_ID_SIGMATEL		0x066F
 #define USB_DEVICE_ID_SIGMATEL_STMP3780	0x3780
 
+#define USB_VENDOR_ID_SIS2_TOUCH	0x0457
+#define USB_DEVICE_ID_SIS9200_TOUCH	0x9200
+#define USB_DEVICE_ID_SIS817_TOUCH	0x0817
+
 #define USB_VENDOR_ID_SKYCABLE			0x1223
 #define	USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER	0x3F07
 
@@ -793,6 +805,11 @@
 #define USB_DEVICE_ID_SYNAPTICS_COMP_TP	0x0009
 #define USB_DEVICE_ID_SYNAPTICS_WTP	0x0010
 #define USB_DEVICE_ID_SYNAPTICS_DPAD	0x0013
+#define USB_DEVICE_ID_SYNAPTICS_LTS1	0x0af8
+#define USB_DEVICE_ID_SYNAPTICS_LTS2	0x1d10
+#define USB_DEVICE_ID_SYNAPTICS_HD	0x0ac3
+#define USB_DEVICE_ID_SYNAPTICS_QUAD_HD	0x1ac3
+#define USB_DEVICE_ID_SYNAPTICS_TP_V103	0x5710
 
 #define USB_VENDOR_ID_THINGM		0x27b8
 #define USB_DEVICE_ID_BLINK1		0x01ed
@@ -886,6 +903,9 @@
 #define USB_DEVICE_ID_SUPER_DUAL_BOX_PRO 0x8802
 #define USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO 0x8804
 
+#define USB_VENDOR_ID_WISTRON		0x0fb8
+#define USB_DEVICE_ID_WISTRON_OPTICAL_TOUCH		0x1109
+
 #define USB_VENDOR_ID_X_TENSIONS               0x1ae7
 #define USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE    0x9001
 
@@ -917,4 +937,7 @@
 #define USB_VENDOR_ID_PRIMAX	0x0461
 #define USB_DEVICE_ID_PRIMAX_KEYBOARD	0x4e05
 
+#define USB_VENDOR_ID_SIS	0x0457
+#define USB_DEVICE_ID_SIS_TS	0x1013
+
 #endif
diff -ruw linux-3.11.10/drivers/hid/Kconfig linux-3.11.10-fbx/drivers/hid/Kconfig
--- linux-3.11.10/drivers/hid/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/hid/Kconfig	2015-09-29 11:15:05.993733604 +0200
@@ -242,6 +242,7 @@
 	  - Tracer Sniper TRM-503 / NOVA Gaming Slider X200 /
 	    Zalman ZM-GM1
 	  - SHARKOON DarkGlider Gaming mouse
+	  - LEETGION Hellion Gaming Mouse
 
 config HOLTEK_FF
 	bool "Holtek On Line Grip force feedback support"
@@ -287,6 +288,10 @@
 	---help---
 	Support for Waltop tablets.
 
+config HID_FBX_REMOTE_AUDIO
+	tristate "Freebox BLE remote audio driver"
+	depends on HID && SND_PCM
+
 config HID_GYRATION
 	tristate "Gyration remote control"
 	depends on HID
@@ -369,12 +374,14 @@
 	  force feedback.
 
 config LOGIRUMBLEPAD2_FF
-	bool "Logitech RumblePad/Rumblepad 2 force feedback support"
+	bool "Logitech force feedback support (variant 2)"
 	depends on HID_LOGITECH
 	select INPUT_FF_MEMLESS
 	help
-	  Say Y here if you want to enable force feedback support for Logitech
-	  RumblePad and Rumblepad 2 devices.
+	  Say Y here if you want to enable force feedback support for:
+	  - Logitech RumblePad
+	  - Logitech Rumblepad 2
+	  - Logitech Formula Vibration Feedback Wheel
 
 config LOGIG940_FF
 	bool "Logitech Flight System G940 force feedback support"
@@ -453,9 +460,11 @@
 	  - Pixcir dual touch panels
 	  - Quanta panels
 	  - eGalax dual-touch panels, including the Joojoo and Wetab tablets
+	  - SiS multitouch panels
 	  - Stantum multitouch panels
 	  - Touch International Panels
 	  - Unitec Panels
+	  - Wistron optical touch panels
 	  - XAT optical touch panels
 	  - Xiroku optical touch panels
 	  - Zytronic touch panels
diff -ruw linux-3.11.10/drivers/hid/Makefile linux-3.11.10-fbx/drivers/hid/Makefile
--- linux-3.11.10/drivers/hid/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/hid/Makefile	2015-09-29 11:15:05.993733604 +0200
@@ -47,6 +47,7 @@
 obj-$(CONFIG_HID_ELECOM)	+= hid-elecom.o
 obj-$(CONFIG_HID_ELO)		+= hid-elo.o
 obj-$(CONFIG_HID_EZKEY)		+= hid-ezkey.o
+obj-$(CONFIG_HID_FBX_REMOTE_AUDIO)	+= hid-fbx-remote-audio.o
 obj-$(CONFIG_HID_GYRATION)	+= hid-gyration.o
 obj-$(CONFIG_HID_HOLTEK)	+= hid-holtek-kbd.o
 obj-$(CONFIG_HID_HOLTEK)	+= hid-holtek-mouse.o
diff -ruw linux-3.11.10/drivers/hwmon/adt7475.c linux-3.11.10-fbx/drivers/hwmon/adt7475.c
--- linux-3.11.10/drivers/hwmon/adt7475.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/hwmon/adt7475.c	2015-09-29 11:15:06.013733750 +0200
@@ -124,7 +124,19 @@
 
 /* Macro to read the registers */
 
-#define adt7475_read(reg) i2c_smbus_read_byte_data(client, (reg))
+static inline s32 __adt7475_read(const struct i2c_client *client, u8 cmd)
+{
+	s32 ret;
+
+	ret = i2c_smbus_read_byte_data(client, cmd);
+	if (ret < 0) {
+		printk("__adt7475_read error: %d\n", ret);
+		return 0;
+	}
+	return ret;
+}
+
+#define adt7475_read(reg) __adt7475_read(client, (reg))
 
 /* Macros to easily index the registers */
 
@@ -278,6 +290,9 @@
 {
 	u16 val;
 
+	if (need_resched())
+		yield();
+
 	val = i2c_smbus_read_byte_data(client, reg);
 	val |= (i2c_smbus_read_byte_data(client, reg + 1) << 8);
 
@@ -846,7 +861,7 @@
 
 	data->range[sattr->index] =
 		adt7475_read(TEMP_TRANGE_REG(sattr->index));
-	data->range[sattr->index] &= ~7;
+	data->range[sattr->index] &= ~0xf;
 	data->range[sattr->index] |= out;
 
 	i2c_smbus_write_byte_data(client, TEMP_TRANGE_REG(sattr->index),
diff -ruw linux-3.11.10/drivers/hwmon/Kconfig linux-3.11.10-fbx/drivers/hwmon/Kconfig
--- linux-3.11.10/drivers/hwmon/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/hwmon/Kconfig	2015-09-29 11:15:06.009733721 +0200
@@ -959,7 +959,7 @@
 
 config SENSORS_NTC_THERMISTOR
 	tristate "NTC thermistor support"
-	depends on (!OF && !IIO) || (OF && IIO)
+	depends on !OF || IIO=n || IIO
 	help
 	  This driver supports NTC thermistors sensor reading and its
 	  interpretation. The driver can also monitor the temperature and
@@ -1543,6 +1543,10 @@
         help
           Support for the A/D converter on MC13783 and MC13892 PMIC.
 
+config SENSORS_KIRKWOOD_CORETEMP
+	tristate "Kirkwood core temperature censor"
+	depends on ARCH_KIRKWOOD
+
 if ACPI
 
 comment "ACPI drivers"
diff -ruw linux-3.11.10/drivers/hwmon/lm85.c linux-3.11.10-fbx/drivers/hwmon/lm85.c
--- linux-3.11.10/drivers/hwmon/lm85.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/hwmon/lm85.c	2015-09-29 11:15:06.025733841 +0200
@@ -207,11 +207,11 @@
 #define RANGE_FROM_REG(val)	lm85_range_map[(val) & 0x0f]
 
 /* These are the PWM frequency encodings */
-static const int lm85_freq_map[8] = { /* 1 Hz */
-	10, 15, 23, 30, 38, 47, 61, 94
+static const int lm85_freq_map[11] = { /* 1 Hz */
+	10, 15, 23, 30, 38, 47, 61, 94, 94, 94, 94
 };
-static const int adm1027_freq_map[8] = { /* 1 Hz */
-	11, 15, 22, 29, 35, 44, 59, 88
+static const int adm1027_freq_map[11] = { /* 1 Hz */
+	11, 15, 22, 29, 35, 44, 59, 88, 88, 88, 25000
 };
 
 static int FREQ_TO_REG(const int *map, int freq)
@@ -219,7 +219,7 @@
 	int i;
 
 	/* Find the closest match */
-	for (i = 0; i < 7; ++i)
+	for (i = 0; i < 10; ++i)
 		if (freq <= (map[i] + map[i + 1]) / 2)
 			break;
 	return i;
@@ -227,7 +227,7 @@
 
 static int FREQ_FROM_REG(const int *map, u8 reg)
 {
-	return map[reg & 0x07];
+	return map[reg & 0x0f];
 }
 
 /*
@@ -1013,7 +1013,7 @@
 		TEMP_FROM_REG(data->zone[nr].limit));
 	lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
 		((data->zone[nr].range & 0x0f) << 4)
-		| (data->pwm_freq[nr] & 0x07));
+		| (data->pwm_freq[nr] & 0x0f));
 
 	mutex_unlock(&data->update_lock);
 	return count;
@@ -1049,7 +1049,7 @@
 		val - min);
 	lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
 		((data->zone[nr].range & 0x0f) << 4)
-		| (data->pwm_freq[nr] & 0x07));
+		| (data->pwm_freq[nr] & 0x0f));
 	mutex_unlock(&data->update_lock);
 	return count;
 }
@@ -1240,8 +1240,28 @@
 
 static void lm85_init_client(struct i2c_client *client)
 {
+	struct lm85_data *data = i2c_get_clientdata(client);
 	int value;
 
+	/* workaround for emc2300 (emc6d103s), when auto temp min is
+	 * the default value, pwm can never be controlled manually, so
+	 * change this */
+	if (data->type == emc6d103s) {
+		int nr;
+
+		for (nr = 0; nr < 3; nr++) {
+			data->zone[nr].limit = TEMP_TO_REG(-127000);
+			lm85_write_value(client, LM85_REG_AFAN_LIMIT(nr),
+					 data->zone[nr].limit);
+
+			/* also force high frequency */
+			data->pwm_freq[nr] = 0xa;
+			lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
+					 data->pwm_freq[nr]);
+		}
+	}
+
+
 	/* Start monitoring if needed */
 	value = lm85_read_value(client, LM85_REG_CONFIG);
 	if (!(value & 0x01)) {
@@ -1667,7 +1687,7 @@
 			data->autofan[i].config =
 			    lm85_read_value(client, LM85_REG_AFAN_CONFIG(i));
 			val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i));
-			data->pwm_freq[i] = val & 0x07;
+			data->pwm_freq[i] = val & 0x0f;
 			data->zone[i].range = val >> 4;
 			data->autofan[i].min_pwm =
 			    lm85_read_value(client, LM85_REG_AFAN_MINPWM(i));
diff -ruw linux-3.11.10/drivers/hwmon/Makefile linux-3.11.10-fbx/drivers/hwmon/Makefile
--- linux-3.11.10/drivers/hwmon/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/hwmon/Makefile	2015-09-29 11:15:06.009733721 +0200
@@ -141,6 +141,7 @@
 obj-$(CONFIG_SENSORS_W83L786NG)	+= w83l786ng.o
 obj-$(CONFIG_SENSORS_WM831X)	+= wm831x-hwmon.o
 obj-$(CONFIG_SENSORS_WM8350)	+= wm8350-hwmon.o
+obj-$(CONFIG_SENSORS_KIRKWOOD_CORETEMP)+= kirkwood-coretemp.o
 
 obj-$(CONFIG_PMBUS)		+= pmbus/
 
diff -ruw linux-3.11.10/drivers/i2c/busses/i2c-mv64xxx.c linux-3.11.10-fbx/drivers/i2c/busses/i2c-mv64xxx.c
--- linux-3.11.10/drivers/i2c/busses/i2c-mv64xxx.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/i2c/busses/i2c-mv64xxx.c	2015-09-29 11:15:06.053734061 +0200
@@ -24,6 +24,7 @@
 #include <linux/of_i2c.h>
 #include <linux/clk.h>
 #include <linux/err.h>
+#include <linux/delay.h>
 
 #define MV64XXX_I2C_ADDR_ADDR(val)			((val & 0x7f) << 1)
 #define MV64XXX_I2C_BAUD_DIV_N(val)			(val & 0x7)
@@ -278,6 +279,11 @@
 	case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */
 	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */
 		/* Doesn't seem to be a device at other end */
+		dev_dbg(&drv_data->adapter.dev,
+			"mv64xxx_i2c_fsm: got no ack -- state: 0x%x, "
+			"status: 0x%x, addr: 0x%x, flags: 0x%x\n",
+			 drv_data->state, status, drv_data->msg->addr,
+			 drv_data->msg->flags);
 		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
 		drv_data->state = MV64XXX_I2C_STATE_IDLE;
 		drv_data->rc = -ENXIO;
@@ -457,6 +463,34 @@
 				int is_last)
 {
 	unsigned long	flags;
+	int limit;
+
+	/*
+	 * wait for (re)start/stop condition to clear from last
+	 * transfer if any
+	 */
+	limit = 1000;
+	do {
+		u32 val;
+
+		val = readl(drv_data->reg_base + drv_data->reg_offsets.control);
+		if (!(val & (MV64XXX_I2C_REG_CONTROL_START |
+			     MV64XXX_I2C_REG_CONTROL_STOP)))
+			break;
+
+		udelay(1);
+	} while (limit-- > 0);
+
+	if (limit < 0) {
+		dev_err(&drv_data->adapter.dev,
+			"mv64xxx: start/stop bit won't clear\n");
+	}
+
+	/* according to datasheet, controller is buggy when you do
+	 * register polling and it says to always wait for an IRQ
+	 * (clock domain related), since we busywait for START/STOP
+	 * clear, add a small delay */
+	udelay(5);
 
 	spin_lock_irqsave(&drv_data->lock, flags);
 	mv64xxx_i2c_prepare_for_io(drv_data, msg);
@@ -466,6 +500,7 @@
 
 	drv_data->send_stop = is_last;
 	drv_data->block = 1;
+
 	mv64xxx_i2c_do_action(drv_data);
 	spin_unlock_irqrestore(&drv_data->lock, flags);
 
diff -ruw linux-3.11.10/drivers/i2c/busses/Kconfig linux-3.11.10-fbx/drivers/i2c/busses/Kconfig
--- linux-3.11.10/drivers/i2c/busses/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/i2c/busses/Kconfig	2015-09-29 11:15:06.045733995 +0200
@@ -109,6 +109,8 @@
 	    Avoton (SOC)
 	    Wellsburg (PCH)
 	    Coleto Creek (PCH)
+	    Wildcat Point-LP (PCH)
+	    BayTrail (SOC)
 
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-i801.
@@ -375,7 +377,7 @@
 
 config I2C_CPM
 	tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)"
-	depends on (CPM1 || CPM2) && OF_I2C
+	depends on CPM1 || CPM2
 	help
 	  This supports the use of the I2C interface on Freescale
 	  processors with CPM1 or CPM2.
@@ -922,6 +924,10 @@
 	help
 	  Supports the SiByte SOC on-chip I2C interfaces (2 channels).
 
+config I2C_WP3
+	tristate "Wintegra WP3 I2C controll"
+	depends on WINTEGRA_WINPATH3
+
 config SCx200_I2C
 	tristate "NatSemi SCx200 I2C using GPIO pins (DEPRECATED)"
 	depends on SCx200_GPIO
diff -ruw linux-3.11.10/drivers/i2c/busses/Makefile linux-3.11.10-fbx/drivers/i2c/busses/Makefile
--- linux-3.11.10/drivers/i2c/busses/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/i2c/busses/Makefile	2015-09-29 11:15:06.045733995 +0200
@@ -76,6 +76,7 @@
 obj-$(CONFIG_I2C_XILINX)	+= i2c-xiic.o
 obj-$(CONFIG_I2C_XLR)		+= i2c-xlr.o
 obj-$(CONFIG_I2C_RCAR)		+= i2c-rcar.o
+obj-$(CONFIG_I2C_WP3)		+= i2c-wp3.o
 
 # External I2C/SMBus adapter drivers
 obj-$(CONFIG_I2C_DIOLAN_U2C)	+= i2c-diolan-u2c.o
diff -ruw linux-3.11.10/drivers/ide/Kconfig linux-3.11.10-fbx/drivers/ide/Kconfig
--- linux-3.11.10/drivers/ide/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/ide/Kconfig	2015-09-29 11:15:06.061734115 +0200
@@ -690,6 +690,11 @@
        depends on BLK_DEV_IDE_AU1XXX
 endchoice
 
+config BLK_DEV_IDE_TANGO2
+	tristate "IDE for Tango2"
+	depends on TANGO2
+	select BLK_DEV_IDEDMA_SFF
+
 config BLK_DEV_IDE_TX4938
 	tristate "TX4938 internal IDE support"
 	depends on SOC_TX4938
diff -ruw linux-3.11.10/drivers/ide/Makefile linux-3.11.10-fbx/drivers/ide/Makefile
--- linux-3.11.10/drivers/ide/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/ide/Makefile	2015-09-29 11:15:06.065734152 +0200
@@ -116,3 +116,4 @@
 
 obj-$(CONFIG_BLK_DEV_IDE_TX4938)	+= tx4938ide.o
 obj-$(CONFIG_BLK_DEV_IDE_TX4939)	+= tx4939ide.o
+obj-$(CONFIG_BLK_DEV_IDE_TANGO2)	+= tango2ide.o
diff -ruw linux-3.11.10/drivers/iio/gyro/Kconfig linux-3.11.10-fbx/drivers/iio/gyro/Kconfig
--- linux-3.11.10/drivers/iio/gyro/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/iio/gyro/Kconfig	2015-09-29 11:15:06.081734269 +0200
@@ -56,7 +56,7 @@
 	select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
 	help
 	  Say yes here to build support for STMicroelectronics gyroscopes:
-	  L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330DLC, L3G4IS, LSM330.
+	  L3G4200D, LSM330DL, L3GD20, LSM330DLC, L3G4IS, LSM330.
 
 	  This driver can also be built as a module. If so, will be created
 	  these modules:
diff -ruw linux-3.11.10/drivers/input/evdev.c linux-3.11.10-fbx/drivers/input/evdev.c
--- linux-3.11.10/drivers/input/evdev.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/input/evdev.c	2015-09-29 11:15:06.145734755 +0200
@@ -18,6 +18,8 @@
 #include <linux/poll.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/input/mt.h>
@@ -365,6 +367,10 @@
 	mutex_unlock(&evdev->mutex);
 
 	evdev_detach_client(evdev, client);
+
+	if (is_vmalloc_addr(client))
+		vfree(client);
+	else
 	kfree(client);
 
 	evdev_close_device(evdev);
@@ -385,12 +391,14 @@
 {
 	struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
 	unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
+	unsigned int size = sizeof(struct evdev_client) +
+					bufsize * sizeof(struct input_event);
 	struct evdev_client *client;
 	int error;
 
-	client = kzalloc(sizeof(struct evdev_client) +
-				bufsize * sizeof(struct input_event),
-			 GFP_KERNEL);
+	client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
+	if (!client)
+		client = vzalloc(size);
 	if (!client)
 		return -ENOMEM;
 
diff -ruw linux-3.11.10/drivers/input/input.c linux-3.11.10-fbx/drivers/input/input.c
--- linux-3.11.10/drivers/input/input.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/input/input.c	2015-09-29 11:15:06.149734792 +0200
@@ -1866,6 +1866,10 @@
 		break;
 
 	case EV_ABS:
+		input_alloc_absinfo(dev);
+		if (!dev->absinfo)
+			return;
+
 		__set_bit(code, dev->absbit);
 		break;
 
diff -ruw linux-3.11.10/drivers/input/Kconfig linux-3.11.10-fbx/drivers/input/Kconfig
--- linux-3.11.10/drivers/input/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/input/Kconfig	2015-09-29 11:15:06.145734755 +0200
@@ -80,7 +80,7 @@
 comment "Userland interfaces"
 
 config INPUT_MOUSEDEV
-	tristate "Mouse interface" if EXPERT
+	tristate "Mouse interface"
 	default y
 	help
 	  Say Y here if you want your mouse to be accessible as char devices
diff -ruw linux-3.11.10/drivers/input/keyboard/Kconfig linux-3.11.10-fbx/drivers/input/keyboard/Kconfig
--- linux-3.11.10/drivers/input/keyboard/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/input/keyboard/Kconfig	2015-09-29 11:15:06.153734817 +0200
@@ -2,7 +2,7 @@
 # Input core configuration
 #
 menuconfig INPUT_KEYBOARD
-	bool "Keyboards" if EXPERT || !X86
+	bool "Keyboards"
 	default y
 	help
 	  Say Y here, and a list of supported keyboards will be displayed.
@@ -67,7 +67,7 @@
 	  module will be called atakbd.
 
 config KEYBOARD_ATKBD
-	tristate "AT keyboard" if EXPERT || !X86
+	tristate "AT keyboard"
 	default y
 	select SERIO
 	select SERIO_LIBPS2
diff -ruw linux-3.11.10/drivers/input/misc/Kconfig linux-3.11.10-fbx/drivers/input/misc/Kconfig
--- linux-3.11.10/drivers/input/misc/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/input/misc/Kconfig	2015-09-29 11:15:06.161734883 +0200
@@ -647,4 +647,9 @@
 
 	  If unsure, say N.
 
+config INPUT_SMSC_CAP1066
+	tristate "SMSC CAP1066 capacitive sensor driver"
+	select I2C
+	select INPUT_POLLDEV
+
 endif
diff -ruw linux-3.11.10/drivers/input/misc/Makefile linux-3.11.10-fbx/drivers/input/misc/Makefile
--- linux-3.11.10/drivers/input/misc/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/input/misc/Makefile	2015-09-29 11:15:06.161734883 +0200
@@ -61,3 +61,4 @@
 obj-$(CONFIG_INPUT_WM831X_ON)		+= wm831x-on.o
 obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
+obj-$(CONFIG_INPUT_SMSC_CAP1066)	+= smsc_cap1066.o
diff -ruw linux-3.11.10/drivers/input/serio/Kconfig linux-3.11.10-fbx/drivers/input/serio/Kconfig
--- linux-3.11.10/drivers/input/serio/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/input/serio/Kconfig	2015-09-29 11:15:06.173734975 +0200
@@ -2,7 +2,7 @@
 # Input core configuration
 #
 config SERIO
-	tristate "Serial I/O support" if EXPERT || !X86
+	tristate "Serial I/O support"
 	default y
 	help
 	  Say Yes here if you have any input device that uses serial I/O to
@@ -19,7 +19,7 @@
 if SERIO
 
 config SERIO_I8042
-	tristate "i8042 PC Keyboard controller" if EXPERT || !X86
+	tristate "i8042 PC Keyboard controller"
 	default y
 	depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
 		   (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !S390 && \
@@ -170,7 +170,7 @@
 	  module will be called maceps2.
 
 config SERIO_LIBPS2
-	tristate "PS/2 driver library" if EXPERT
+	tristate "PS/2 driver library"
 	depends on SERIO_I8042 || SERIO_I8042=n
 	help
 	  Say Y here if you are using a driver for device connected
diff -ruw linux-3.11.10/drivers/Kconfig linux-3.11.10-fbx/drivers/Kconfig
--- linux-3.11.10/drivers/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/Kconfig	2015-09-29 11:15:05.585730496 +0200
@@ -6,8 +6,12 @@
 
 source "drivers/connector/Kconfig"
 
+source "drivers/fbxprocfs/Kconfig"
+
 source "drivers/mtd/Kconfig"
 
+source "drivers/fbxmtd/Kconfig"
+
 source "drivers/of/Kconfig"
 
 source "drivers/parport/Kconfig"
@@ -62,6 +66,10 @@
 
 source "drivers/gpio/Kconfig"
 
+source "drivers/fbxgpio/Kconfig"
+
+source "drivers/fbxjtag/Kconfig"
+
 source "drivers/w1/Kconfig"
 
 source "drivers/power/Kconfig"
@@ -70,6 +78,8 @@
 
 source "drivers/thermal/Kconfig"
 
+source "drivers/fbxwatchdog/Kconfig"
+
 source "drivers/watchdog/Kconfig"
 
 source "drivers/ssb/Kconfig"
@@ -98,6 +108,8 @@
 
 source "drivers/leds/Kconfig"
 
+source "drivers/fbxpanel/Kconfig"
+
 source "drivers/accessibility/Kconfig"
 
 source "drivers/infiniband/Kconfig"
diff -ruw linux-3.11.10/drivers/Makefile linux-3.11.10-fbx/drivers/Makefile
--- linux-3.11.10/drivers/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/Makefile	2015-09-29 11:15:05.585730496 +0200
@@ -12,6 +12,8 @@
 obj-y				+= pinctrl/
 obj-y				+= gpio/
 obj-y				+= pwm/
+obj-$(CONFIG_FREEBOX_GPIO)	+= fbxgpio/
+obj-$(CONFIG_FREEBOX_JTAG)	+= fbxjtag/
 obj-$(CONFIG_PCI)		+= pci/
 obj-$(CONFIG_PARISC)		+= parisc/
 obj-$(CONFIG_RAPIDIO)		+= rapidio/
@@ -73,6 +75,9 @@
 obj-$(CONFIG_VFIO)		+= vfio/
 obj-y				+= cdrom/
 obj-y				+= auxdisplay/
+
+obj-$(CONFIG_FREEBOX_PROCFS)	+= fbxprocfs/
+obj-$(CONFIG_FREEBOX_MTD)	+= fbxmtd/
 obj-$(CONFIG_PCCARD)		+= pcmcia/
 obj-$(CONFIG_DIO)		+= dio/
 obj-$(CONFIG_SBUS)		+= sbus/
@@ -98,6 +103,7 @@
 obj-$(CONFIG_POWER_SUPPLY)	+= power/
 obj-$(CONFIG_HWMON)		+= hwmon/
 obj-$(CONFIG_THERMAL)		+= thermal/
+obj-$(CONFIG_FREEBOX_WATCHDOG)	+= fbxwatchdog/
 obj-$(CONFIG_WATCHDOG)		+= watchdog/
 obj-$(CONFIG_MD)		+= md/
 obj-$(CONFIG_BT)		+= bluetooth/
@@ -111,6 +117,7 @@
 obj-y				+= mmc/
 obj-$(CONFIG_MEMSTICK)		+= memstick/
 obj-y				+= leds/
+obj-$(CONFIG_FREEBOX_PANEL)	+= fbxpanel/
 obj-$(CONFIG_INFINIBAND)	+= infiniband/
 obj-$(CONFIG_SGI_SN)		+= sn/
 obj-y				+= firmware/
diff -ruw linux-3.11.10/drivers/md/dm.c linux-3.11.10-fbx/drivers/md/dm.c
--- linux-3.11.10/drivers/md/dm.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/md/dm.c	2015-09-29 11:15:06.257735614 +0200
@@ -193,8 +193,8 @@
 	/* forced geometry settings */
 	struct hd_geometry geometry;
 
-	/* sysfs handle */
-	struct kobject kobj;
+	/* kobject and completion */
+	struct dm_kobject_holder kobj_holder;
 
 	/* zero-length flush that will be cloned and submitted to targets */
 	struct bio flush_bio;
@@ -1936,6 +1936,7 @@
 	init_waitqueue_head(&md->wait);
 	INIT_WORK(&md->work, dm_wq_work);
 	init_waitqueue_head(&md->eventq);
+	init_completion(&md->kobj_holder.completion);
 
 	md->disk->major = _major;
 	md->disk->first_minor = minor;
@@ -2761,20 +2762,14 @@
 
 struct kobject *dm_kobject(struct mapped_device *md)
 {
-	return &md->kobj;
+	return &md->kobj_holder.kobj;
 }
 
-/*
- * struct mapped_device should not be exported outside of dm.c
- * so use this check to verify that kobj is part of md structure
- */
 struct mapped_device *dm_get_from_kobject(struct kobject *kobj)
 {
 	struct mapped_device *md;
 
-	md = container_of(kobj, struct mapped_device, kobj);
-	if (&md->kobj != kobj)
-		return NULL;
+	md = container_of(kobj, struct mapped_device, kobj_holder.kobj);
 
 	if (test_bit(DMF_FREEING, &md->flags) ||
 	    dm_deleting_md(md))
diff -ruw linux-3.11.10/drivers/md/dm-crypt.c linux-3.11.10-fbx/drivers/md/dm-crypt.c
--- linux-3.11.10/drivers/md/dm-crypt.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/md/dm-crypt.c	2015-09-29 11:15:06.249735548 +0200
@@ -18,7 +18,6 @@
 #include <linux/crypto.h>
 #include <linux/workqueue.h>
 #include <linux/backing-dev.h>
-#include <linux/percpu.h>
 #include <linux/atomic.h>
 #include <linux/scatterlist.h>
 #include <asm/page.h>
@@ -44,6 +43,7 @@
 	unsigned int idx_out;
 	sector_t cc_sector;
 	atomic_t cc_pending;
+	struct ablkcipher_request *req;
 };
 
 /*
@@ -105,15 +105,7 @@
 enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID };
 
 /*
- * Duplicated per-CPU state for cipher.
- */
-struct crypt_cpu {
-	struct ablkcipher_request *req;
-};
-
-/*
- * The fields in here must be read only after initialization,
- * changing state should be in crypt_cpu.
+ * The fields in here must be read only after initialization.
  */
 struct crypt_config {
 	struct dm_dev *dev;
@@ -143,12 +135,6 @@
 	sector_t iv_offset;
 	unsigned int iv_size;
 
-	/*
-	 * Duplicated per cpu state. Access through
-	 * per_cpu_ptr() only.
-	 */
-	struct crypt_cpu __percpu *cpu;
-
 	/* ESSIV: struct crypto_cipher *essiv_tfm */
 	void *iv_private;
 	struct crypto_ablkcipher **tfms;
@@ -184,11 +170,6 @@
 static void kcryptd_queue_crypt(struct dm_crypt_io *io);
 static u8 *iv_of_dmreq(struct crypt_config *cc, struct dm_crypt_request *dmreq);
 
-static struct crypt_cpu *this_crypt_config(struct crypt_config *cc)
-{
-	return this_cpu_ptr(cc->cpu);
-}
-
 /*
  * Use this to access cipher attributes that are the same for each CPU.
  */
@@ -738,16 +719,15 @@
 static void crypt_alloc_req(struct crypt_config *cc,
 			    struct convert_context *ctx)
 {
-	struct crypt_cpu *this_cc = this_crypt_config(cc);
 	unsigned key_index = ctx->cc_sector & (cc->tfms_count - 1);
 
-	if (!this_cc->req)
-		this_cc->req = mempool_alloc(cc->req_pool, GFP_NOIO);
+	if (!ctx->req)
+		ctx->req = mempool_alloc(cc->req_pool, GFP_NOIO);
 
-	ablkcipher_request_set_tfm(this_cc->req, cc->tfms[key_index]);
-	ablkcipher_request_set_callback(this_cc->req,
+	ablkcipher_request_set_tfm(ctx->req, cc->tfms[key_index]);
+	ablkcipher_request_set_callback(ctx->req,
 	    CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
-	    kcryptd_async_done, dmreq_of_req(cc, this_cc->req));
+	    kcryptd_async_done, dmreq_of_req(cc, ctx->req));
 }
 
 /*
@@ -756,7 +736,6 @@
 static int crypt_convert(struct crypt_config *cc,
 			 struct convert_context *ctx)
 {
-	struct crypt_cpu *this_cc = this_crypt_config(cc);
 	int r;
 
 	atomic_set(&ctx->cc_pending, 1);
@@ -768,7 +747,7 @@
 
 		atomic_inc(&ctx->cc_pending);
 
-		r = crypt_convert_block(cc, ctx, this_cc->req);
+		r = crypt_convert_block(cc, ctx, ctx->req);
 
 		switch (r) {
 		/* async */
@@ -777,7 +756,7 @@
 			INIT_COMPLETION(ctx->restart);
 			/* fall through*/
 		case -EINPROGRESS:
-			this_cc->req = NULL;
+			ctx->req = NULL;
 			ctx->cc_sector++;
 			continue;
 
@@ -876,6 +855,7 @@
 	io->sector = sector;
 	io->error = 0;
 	io->base_io = NULL;
+	io->ctx.req = NULL;
 	atomic_set(&io->io_pending, 0);
 
 	return io;
@@ -901,6 +881,8 @@
 	if (!atomic_dec_and_test(&io->io_pending))
 		return;
 
+	if (io->ctx.req)
+		mempool_free(io->ctx.req, cc->req_pool);
 	mempool_free(io, cc->io_pool);
 
 	if (likely(!base_io))
@@ -1326,8 +1308,6 @@
 static void crypt_dtr(struct dm_target *ti)
 {
 	struct crypt_config *cc = ti->private;
-	struct crypt_cpu *cpu_cc;
-	int cpu;
 
 	ti->private = NULL;
 
@@ -1339,13 +1319,6 @@
 	if (cc->crypt_queue)
 		destroy_workqueue(cc->crypt_queue);
 
-	if (cc->cpu)
-		for_each_possible_cpu(cpu) {
-			cpu_cc = per_cpu_ptr(cc->cpu, cpu);
-			if (cpu_cc->req)
-				mempool_free(cpu_cc->req, cc->req_pool);
-		}
-
 	crypt_free_tfms(cc);
 
 	if (cc->bs)
@@ -1364,9 +1337,6 @@
 	if (cc->dev)
 		dm_put_device(ti, cc->dev);
 
-	if (cc->cpu)
-		free_percpu(cc->cpu);
-
 	kzfree(cc->cipher);
 	kzfree(cc->cipher_string);
 
@@ -1421,13 +1391,6 @@
 	if (tmp)
 		DMWARN("Ignoring unexpected additional cipher options");
 
-	cc->cpu = __alloc_percpu(sizeof(*(cc->cpu)),
-				 __alignof__(struct crypt_cpu));
-	if (!cc->cpu) {
-		ti->error = "Cannot allocate per cpu state";
-		goto bad_mem;
-	}
-
 	/*
 	 * For compatibility with the original dm-crypt mapping format, if
 	 * only the cipher name is supplied, use cbc-plain.
diff -ruw linux-3.11.10/drivers/md/dm.h linux-3.11.10-fbx/drivers/md/dm.h
--- linux-3.11.10/drivers/md/dm.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/md/dm.h	2015-09-29 11:15:06.257735614 +0200
@@ -15,6 +15,8 @@
 #include <linux/list.h>
 #include <linux/blkdev.h>
 #include <linux/hdreg.h>
+#include <linux/completion.h>
+#include <linux/kobject.h>
 
 /*
  * Suspend feature flags
@@ -125,12 +127,27 @@
 /*
  * sysfs interface
  */
+struct dm_kobject_holder {
+	struct kobject kobj;
+	struct completion completion;
+};
+
+static inline struct completion *dm_get_completion_from_kobject(struct kobject *kobj)
+{
+	return &container_of(kobj, struct dm_kobject_holder, kobj)->completion;
+}
+
 int dm_sysfs_init(struct mapped_device *md);
 void dm_sysfs_exit(struct mapped_device *md);
 struct kobject *dm_kobject(struct mapped_device *md);
 struct mapped_device *dm_get_from_kobject(struct kobject *kobj);
 
 /*
+ * The kobject helper
+ */
+void dm_kobject_release(struct kobject *kobj);
+
+/*
  * Targets for linear and striped mappings
  */
 int dm_linear_init(void);
diff -ruw linux-3.11.10/drivers/md/dm-io.c linux-3.11.10-fbx/drivers/md/dm-io.c
--- linux-3.11.10/drivers/md/dm-io.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/md/dm-io.c	2015-09-29 11:15:06.249735548 +0200
@@ -10,6 +10,7 @@
 #include <linux/device-mapper.h>
 
 #include <linux/bio.h>
+#include <linux/completion.h>
 #include <linux/mempool.h>
 #include <linux/module.h>
 #include <linux/sched.h>
@@ -34,7 +35,7 @@
 struct io {
 	unsigned long error_bits;
 	atomic_t count;
-	struct task_struct *sleeper;
+	struct completion *wait;
 	struct dm_io_client *client;
 	io_notify_fn callback;
 	void *context;
@@ -122,8 +123,8 @@
 			invalidate_kernel_vmap_range(io->vma_invalidate_address,
 						     io->vma_invalidate_size);
 
-		if (io->sleeper)
-			wake_up_process(io->sleeper);
+		if (io->wait)
+			complete(io->wait);
 
 		else {
 			unsigned long r = io->error_bits;
@@ -386,6 +387,7 @@
 	 */
 	volatile char io_[sizeof(struct io) + __alignof__(struct io) - 1];
 	struct io *io = (struct io *)PTR_ALIGN(&io_, __alignof__(struct io));
+	DECLARE_COMPLETION_ONSTACK(wait);
 
 	if (num_regions > 1 && (rw & RW_MASK) != WRITE) {
 		WARN_ON(1);
@@ -394,7 +396,7 @@
 
 	io->error_bits = 0;
 	atomic_set(&io->count, 1); /* see dispatch_io() */
-	io->sleeper = current;
+	io->wait = &wait;
 	io->client = client;
 
 	io->vma_invalidate_address = dp->vma_invalidate_address;
@@ -402,15 +404,7 @@
 
 	dispatch_io(rw, num_regions, where, dp, io, 1);
 
-	while (1) {
-		set_current_state(TASK_UNINTERRUPTIBLE);
-
-		if (!atomic_read(&io->count))
-			break;
-
-		io_schedule();
-	}
-	set_current_state(TASK_RUNNING);
+	wait_for_completion_io(&wait);
 
 	if (error_bits)
 		*error_bits = io->error_bits;
@@ -433,7 +427,7 @@
 	io = mempool_alloc(client->pool, GFP_NOIO);
 	io->error_bits = 0;
 	atomic_set(&io->count, 1); /* see dispatch_io() */
-	io->sleeper = NULL;
+	io->wait = NULL;
 	io->client = client;
 	io->callback = fn;
 	io->context = context;
diff -ruw linux-3.11.10/drivers/md/dm-sysfs.c linux-3.11.10-fbx/drivers/md/dm-sysfs.c
--- linux-3.11.10/drivers/md/dm-sysfs.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/md/dm-sysfs.c	2015-09-29 11:15:06.253735577 +0200
@@ -86,6 +86,7 @@
 static struct kobj_type dm_ktype = {
 	.sysfs_ops	= &dm_sysfs_ops,
 	.default_attrs	= dm_attrs,
+	.release	= dm_kobject_release,
 };
 
 /*
@@ -104,5 +105,7 @@
  */
 void dm_sysfs_exit(struct mapped_device *md)
 {
-	kobject_put(dm_kobject(md));
+	struct kobject *kobj = dm_kobject(md);
+	kobject_put(kobj);
+	wait_for_completion(dm_get_completion_from_kobject(kobj));
 }
diff -ruw linux-3.11.10/drivers/md/dm-table.c linux-3.11.10-fbx/drivers/md/dm-table.c
--- linux-3.11.10/drivers/md/dm-table.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/md/dm-table.c	2015-09-29 11:15:06.253735577 +0200
@@ -200,6 +200,11 @@
 
 	num_targets = dm_round_up(num_targets, KEYS_PER_NODE);
 
+	if (!num_targets) {
+		kfree(t);
+		return -ENOMEM;
+	}
+
 	if (alloc_targets(t, num_targets)) {
 		kfree(t);
 		return -ENOMEM;
@@ -545,14 +550,28 @@
 
 /*
  * Used to dynamically allocate the arg array.
+ *
+ * We do first allocation with GFP_NOIO because dm-mpath and dm-thin must
+ * process messages even if some device is suspended. These messages have a
+ * small fixed number of arguments.
+ *
+ * On the other hand, dm-switch needs to process bulk data using messages and
+ * excessive use of GFP_NOIO could cause trouble.
  */
 static char **realloc_argv(unsigned *array_size, char **old_argv)
 {
 	char **argv;
 	unsigned new_size;
+	gfp_t gfp;
 
-	new_size = *array_size ? *array_size * 2 : 64;
-	argv = kmalloc(new_size * sizeof(*argv), GFP_KERNEL);
+	if (*array_size) {
+		new_size = *array_size * 2;
+		gfp = GFP_KERNEL;
+	} else {
+		new_size = 8;
+		gfp = GFP_NOIO;
+	}
+	argv = kmalloc(new_size * sizeof(*argv), gfp);
 	if (argv) {
 		memcpy(argv, old_argv, *array_size * sizeof(*argv));
 		*array_size = new_size;
diff -ruw linux-3.11.10/drivers/md/Kconfig linux-3.11.10-fbx/drivers/md/Kconfig
--- linux-3.11.10/drivers/md/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/md/Kconfig	2015-09-29 11:15:06.241735486 +0200
@@ -176,8 +176,12 @@
 
 source "drivers/md/bcache/Kconfig"
 
+config BLK_DEV_DM_BUILTIN
+	boolean
+
 config BLK_DEV_DM
 	tristate "Device mapper support"
+	select BLK_DEV_DM_BUILTIN
 	---help---
 	  Device-mapper is a low level volume manager.  It works by allowing
 	  people to specify mappings for ranges of logical sectors.  Various
diff -ruw linux-3.11.10/drivers/md/Makefile linux-3.11.10-fbx/drivers/md/Makefile
--- linux-3.11.10/drivers/md/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/md/Makefile	2015-09-29 11:15:06.241735486 +0200
@@ -32,6 +32,7 @@
 obj-$(CONFIG_BCACHE)		+= bcache/
 obj-$(CONFIG_BLK_DEV_MD)	+= md-mod.o
 obj-$(CONFIG_BLK_DEV_DM)	+= dm-mod.o
+obj-$(CONFIG_BLK_DEV_DM_BUILTIN) += dm-builtin.o
 obj-$(CONFIG_DM_BUFIO)		+= dm-bufio.o
 obj-$(CONFIG_DM_BIO_PRISON)	+= dm-bio-prison.o
 obj-$(CONFIG_DM_CRYPT)		+= dm-crypt.o
diff -ruw linux-3.11.10/drivers/media/dvb-core/dvb_frontend.c linux-3.11.10-fbx/drivers/media/dvb-core/dvb_frontend.c
--- linux-3.11.10/drivers/media/dvb-core/dvb_frontend.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/dvb-core/dvb_frontend.c	2015-09-29 11:15:06.269735706 +0200
@@ -758,6 +758,7 @@
 
 	fepriv->exit = DVB_FE_NORMAL_EXIT;
 	mb();
+	wake_up_all(&fepriv->events.wait_queue);
 
 	if (!fepriv->thread)
 		return;
@@ -2407,6 +2408,9 @@
 
 	poll_wait (file, &fepriv->events.wait_queue, wait);
 
+	if (fepriv->exit)
+		return POLLERR | POLLHUP;
+
 	if (fepriv->events.eventw != fepriv->events.eventr)
 		return (POLLIN | POLLRDNORM | POLLPRI);
 
diff -ruw linux-3.11.10/drivers/media/dvb-core/dvb-usb-ids.h linux-3.11.10-fbx/drivers/media/dvb-core/dvb-usb-ids.h
--- linux-3.11.10/drivers/media/dvb-core/dvb-usb-ids.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/dvb-core/dvb-usb-ids.h	2015-09-29 11:15:06.269735706 +0200
@@ -105,6 +105,7 @@
 #define USB_PID_DELOCK_USB2_DVBT			0xb803
 #define USB_PID_DIBCOM_HOOK_DEFAULT			0x0064
 #define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM		0x0065
+#define USB_PID_DIBCOM_HOOK_DEFAULT_STK7770P		0x0066
 #define USB_PID_DIBCOM_MOD3000_COLD			0x0bb8
 #define USB_PID_DIBCOM_MOD3000_WARM			0x0bb9
 #define USB_PID_DIBCOM_MOD3001_COLD			0x0bc6
@@ -239,6 +240,7 @@
 #define USB_PID_AVERMEDIA_A835B_4835			0x4835
 #define USB_PID_AVERMEDIA_1867				0x1867
 #define USB_PID_AVERMEDIA_A867				0xa867
+#define USB_PID_AVERMEDIA_H335				0x0335
 #define USB_PID_AVERMEDIA_TWINSTAR			0x0825
 #define USB_PID_TECHNOTREND_CONNECT_S2400               0x3006
 #define USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM	0x3009
@@ -256,6 +258,7 @@
 #define USB_PID_TERRATEC_T5				0x10a1
 #define USB_PID_NOXON_DAB_STICK				0x00b3
 #define USB_PID_NOXON_DAB_STICK_REV2			0x00e0
+#define USB_PID_NOXON_DAB_STICK_REV3			0x00b4
 #define USB_PID_PINNACLE_EXPRESSCARD_320CX		0x022e
 #define USB_PID_PINNACLE_PCTV2000E			0x022c
 #define USB_PID_PINNACLE_PCTV_DVB_T_FLASH		0x0228
@@ -317,6 +320,7 @@
 #define USB_PID_WINFAST_DTV_DONGLE_H			0x60f6
 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2		0x6f01
 #define USB_PID_WINFAST_DTV_DONGLE_GOLD			0x6029
+#define USB_PID_WINFAST_DTV_DONGLE_MINID		0x6f0f
 #define USB_PID_GENPIX_8PSK_REV_1_COLD			0x0200
 #define USB_PID_GENPIX_8PSK_REV_1_WARM			0x0201
 #define USB_PID_GENPIX_8PSK_REV_2			0x0202
@@ -358,6 +362,7 @@
 #define USB_PID_FRIIO_WHITE				0x0001
 #define USB_PID_TVWAY_PLUS				0x0002
 #define USB_PID_SVEON_STV20				0xe39d
+#define USB_PID_SVEON_STV20_RTL2832U			0xd39d
 #define USB_PID_SVEON_STV22				0xe401
 #define USB_PID_SVEON_STV22_IT9137			0xe411
 #define USB_PID_AZUREWAVE_AZ6027			0x3275
@@ -369,4 +374,7 @@
 #define USB_PID_TECHNISAT_USB2_DVB_S2			0x0500
 #define USB_PID_CPYTO_REDI_PC50A			0xa803
 #define USB_PID_CTVDIGDUAL_V2				0xe410
+#define USB_PID_PCTV_2002E                              0x025c
+#define USB_PID_PCTV_2002E_SE                           0x025d
+#define USB_PID_SVEON_STV27                             0xd3af
 #endif
diff -ruw linux-3.11.10/drivers/media/dvb-frontends/af9033.c linux-3.11.10-fbx/drivers/media/dvb-frontends/af9033.c
--- linux-3.11.10/drivers/media/dvb-frontends/af9033.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/dvb-frontends/af9033.c	2015-09-29 11:15:06.273735731 +0200
@@ -21,6 +21,9 @@
 
 #include "af9033_priv.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 struct af9033_state {
 	struct i2c_adapter *i2c;
 	struct dvb_frontend fe;
@@ -40,16 +43,23 @@
 		int len)
 {
 	int ret;
-	u8 buf[3 + len];
+	u8 buf[MAX_XFER_SIZE];
 	struct i2c_msg msg[1] = {
 		{
 			.addr = state->cfg.i2c_addr,
 			.flags = 0,
-			.len = sizeof(buf),
+			.len = 3 + len,
 			.buf = buf,
 		}
 	};
 
+	if (3 + len > sizeof(buf)) {
+		dev_warn(&state->i2c->dev,
+			 "%s: i2c wr reg=%04x: len=%d is too big!\n",
+			 KBUILD_MODNAME, reg, len);
+		return -EINVAL;
+	}
+
 	buf[0] = (reg >> 16) & 0xff;
 	buf[1] = (reg >>  8) & 0xff;
 	buf[2] = (reg >>  0) & 0xff;
@@ -160,11 +170,18 @@
 static int af9033_wr_reg_val_tab(struct af9033_state *state,
 		const struct reg_val *tab, int tab_len)
 {
+#define MAX_TAB_LEN 212
 	int ret, i, j;
-	u8 buf[tab_len];
+	u8 buf[1 + MAX_TAB_LEN];
 
 	dev_dbg(&state->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len);
 
+	if (tab_len > sizeof(buf)) {
+		dev_warn(&state->i2c->dev, "%s: tab len %d is too big\n",
+				KBUILD_MODNAME, tab_len);
+		return -EINVAL;
+	}
+
 	for (i = 0, j = 0; i < tab_len; i++) {
 		buf[j] = tab[i].val;
 
@@ -257,6 +274,22 @@
 		{ 0x800045, state->cfg.adc_multiplier, 0xff },
 	};
 
+	/* power up tuner - for performance */
+	switch (state->cfg.tuner) {
+	case AF9033_TUNER_IT9135_38:
+	case AF9033_TUNER_IT9135_51:
+	case AF9033_TUNER_IT9135_52:
+	case AF9033_TUNER_IT9135_60:
+	case AF9033_TUNER_IT9135_61:
+	case AF9033_TUNER_IT9135_62:
+		ret = af9033_wr_reg(state, 0x80ec40, 0x1);
+		ret |= af9033_wr_reg(state, 0x80fba8, 0x0);
+		ret |= af9033_wr_reg(state, 0x80ec57, 0x0);
+		ret |= af9033_wr_reg(state, 0x80ec58, 0x0);
+		if (ret < 0)
+			goto err;
+	}
+
 	/* program clock control */
 	clock_cw = af9033_div(state, state->cfg.clock, 1000000ul, 19ul);
 	buf[0] = (clock_cw >>  0) & 0xff;
@@ -423,6 +456,8 @@
 	case AF9033_TUNER_IT9135_61:
 	case AF9033_TUNER_IT9135_62:
 		ret = af9033_wr_reg(state, 0x800000, 0x01);
+		ret |= af9033_wr_reg(state, 0x00d827, 0x00);
+		ret |= af9033_wr_reg(state, 0x00d829, 0x00);
 		if (ret < 0)
 			goto err;
 	}
diff -ruw linux-3.11.10/drivers/media/dvb-frontends/af9033_priv.h linux-3.11.10-fbx/drivers/media/dvb-frontends/af9033_priv.h
--- linux-3.11.10/drivers/media/dvb-frontends/af9033_priv.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/dvb-frontends/af9033_priv.h	2015-09-29 11:15:06.273735731 +0200
@@ -1418,7 +1418,7 @@
 	{ 0x800068, 0x0a },
 	{ 0x80006a, 0x03 },
 	{ 0x800070, 0x0a },
-	{ 0x800071, 0x05 },
+	{ 0x800071, 0x0a },
 	{ 0x800072, 0x02 },
 	{ 0x800075, 0x8c },
 	{ 0x800076, 0x8c },
@@ -1484,7 +1484,6 @@
 	{ 0x800104, 0x02 },
 	{ 0x800105, 0xbe },
 	{ 0x800106, 0x00 },
-	{ 0x800109, 0x02 },
 	{ 0x800115, 0x0a },
 	{ 0x800116, 0x03 },
 	{ 0x80011a, 0xbe },
@@ -1510,7 +1509,6 @@
 	{ 0x80014b, 0x8c },
 	{ 0x80014d, 0xac },
 	{ 0x80014e, 0xc6 },
-	{ 0x80014f, 0x03 },
 	{ 0x800151, 0x1e },
 	{ 0x800153, 0xbc },
 	{ 0x800178, 0x09 },
@@ -1522,9 +1520,10 @@
 	{ 0x80018d, 0x5f },
 	{ 0x80018f, 0xa0 },
 	{ 0x800190, 0x5a },
-	{ 0x80ed02, 0xff },
-	{ 0x80ee42, 0xff },
-	{ 0x80ee82, 0xff },
+	{ 0x800191, 0x00 },
+	{ 0x80ed02, 0x40 },
+	{ 0x80ee42, 0x40 },
+	{ 0x80ee82, 0x40 },
 	{ 0x80f000, 0x0f },
 	{ 0x80f01f, 0x8c },
 	{ 0x80f020, 0x00 },
@@ -1699,7 +1698,6 @@
 	{ 0x800104, 0x02 },
 	{ 0x800105, 0xc8 },
 	{ 0x800106, 0x00 },
-	{ 0x800109, 0x02 },
 	{ 0x800115, 0x0a },
 	{ 0x800116, 0x03 },
 	{ 0x80011a, 0xc6 },
@@ -1725,7 +1723,6 @@
 	{ 0x80014b, 0x8c },
 	{ 0x80014d, 0xa8 },
 	{ 0x80014e, 0xc6 },
-	{ 0x80014f, 0x03 },
 	{ 0x800151, 0x28 },
 	{ 0x800153, 0xcc },
 	{ 0x800178, 0x09 },
@@ -1737,9 +1734,10 @@
 	{ 0x80018d, 0x5f },
 	{ 0x80018f, 0xfb },
 	{ 0x800190, 0x5c },
-	{ 0x80ed02, 0xff },
-	{ 0x80ee42, 0xff },
-	{ 0x80ee82, 0xff },
+	{ 0x800191, 0x00 },
+	{ 0x80ed02, 0x40 },
+	{ 0x80ee42, 0x40 },
+	{ 0x80ee82, 0x40 },
 	{ 0x80f000, 0x0f },
 	{ 0x80f01f, 0x8c },
 	{ 0x80f020, 0x00 },
diff -ruw linux-3.11.10/drivers/media/platform/Kconfig linux-3.11.10-fbx/drivers/media/platform/Kconfig
--- linux-3.11.10/drivers/media/platform/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/platform/Kconfig	2015-09-29 11:15:06.369736462 +0200
@@ -124,6 +124,7 @@
 source "drivers/media/platform/soc_camera/Kconfig"
 source "drivers/media/platform/exynos4-is/Kconfig"
 source "drivers/media/platform/s5p-tv/Kconfig"
+source "drivers/media/platform/tango2/Kconfig"
 
 endif # V4L_PLATFORM_DRIVERS
 
diff -ruw linux-3.11.10/drivers/media/platform/Makefile linux-3.11.10-fbx/drivers/media/platform/Makefile
--- linux-3.11.10/drivers/media/platform/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/platform/Makefile	2015-09-29 11:15:06.369736462 +0200
@@ -46,6 +46,8 @@
 
 obj-$(CONFIG_SOC_CAMERA)		+= soc_camera/
 
+obj-$(CONFIG_DVB_TANGO2)		+= tango2/
+
 obj-y	+= davinci/
 
 obj-$(CONFIG_ARCH_OMAP)	+= omap/
diff -ruw linux-3.11.10/drivers/media/rc/keymaps/Makefile linux-3.11.10-fbx/drivers/media/rc/keymaps/Makefile
--- linux-3.11.10/drivers/media/rc/keymaps/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/rc/keymaps/Makefile	2015-09-29 11:15:06.413736802 +0200
@@ -78,6 +78,7 @@
 			rc-pv951.o \
 			rc-hauppauge.o \
 			rc-rc6-mce.o \
+			rc-rc6-freebox.o \
 			rc-real-audio-220-32-keys.o \
 			rc-reddo.o \
 			rc-snapstream-firefly.o \
diff -ruw linux-3.11.10/drivers/media/tuners/tuner_it913x.c linux-3.11.10-fbx/drivers/media/tuners/tuner_it913x.c
--- linux-3.11.10/drivers/media/tuners/tuner_it913x.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/tuners/tuner_it913x.c	2015-09-29 11:15:06.429736919 +0200
@@ -200,12 +200,6 @@
 		}
 	}
 
-	/* Power Up Tuner - common all versions */
-	ret = it913x_wr_reg(state, PRO_DMOD, 0xec40, 0x1);
-	ret |= it913x_wr_reg(state, PRO_DMOD, 0xfba8, 0x0);
-	ret |= it913x_wr_reg(state, PRO_DMOD, 0xec57, 0x0);
-	ret |= it913x_wr_reg(state, PRO_DMOD, 0xec58, 0x0);
-
 	return it913x_wr_reg(state, PRO_DMOD, 0xed81, val);
 }
 
diff -ruw linux-3.11.10/drivers/media/usb/dvb-usb/dib0700_devices.c linux-3.11.10-fbx/drivers/media/usb/dvb-usb/dib0700_devices.c
--- linux-3.11.10/drivers/media/usb/dvb-usb/dib0700_devices.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/usb/dvb-usb/dib0700_devices.c	2015-09-29 11:15:06.445737040 +0200
@@ -3589,6 +3589,9 @@
 	{ USB_DEVICE(USB_VID_DIBCOM,    USB_PID_DIBCOM_TFE7790P) },
 	{ USB_DEVICE(USB_VID_DIBCOM,    USB_PID_DIBCOM_TFE8096P) },
 /* 80 */{ USB_DEVICE(USB_VID_ELGATO,	USB_PID_ELGATO_EYETV_DTT_2) },
+	{ USB_DEVICE(USB_VID_PCTV,      USB_PID_PCTV_2002E) },
+	{ USB_DEVICE(USB_VID_PCTV,      USB_PID_PCTV_2002E_SE) },
+	{ USB_DEVICE(USB_VID_DIBCOM,	USB_PID_DIBCOM_HOOK_DEFAULT_STK7770P) },
 	{ 0 }		/* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -3993,12 +3996,20 @@
 			}
 		},
 
-		.num_device_descs = 1,
+		.num_device_descs = 3,
 		.devices = {
 			{   "Hauppauge Nova-TD Stick (52009)",
 				{ &dib0700_usb_id_table[35], NULL },
 				{ NULL },
 			},
+			{   "PCTV 2002e",
+				{ &dib0700_usb_id_table[81], NULL },
+				{ NULL },
+			},
+			{   "PCTV 2002e SE",
+				{ &dib0700_usb_id_table[82], NULL },
+				{ NULL },
+			},
 		},
 
 		.rc.core = {
@@ -4320,6 +4331,35 @@
 			.change_protocol  = dib0700_change_protocol,
 		},
 	}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+
+		.num_adapters = 1,
+		.adapter = {
+			{
+			.num_frontends = 1,
+			.fe = {{
+				.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+				.pid_filter_count = 32,
+				.pid_filter       = stk70x0p_pid_filter,
+				.pid_filter_ctrl  = stk70x0p_pid_filter_ctrl,
+				.frontend_attach  = stk7770p_frontend_attach,
+				.tuner_attach     = dib7770p_tuner_attach,
+
+				DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+			}},
+				.size_of_priv =
+					sizeof(struct dib0700_adapter_state),
+			},
+		},
+
+		.num_device_descs = 1,
+		.devices = {
+			{   "DiBcom STK7770P reference design no IR",
+				{ &dib0700_usb_id_table[83], NULL },
+				{ NULL },
+			},
+		},
+
+	}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
 		.num_adapters = 1,
 		.adapter = {
 			{
diff -ruw linux-3.11.10/drivers/media/usb/dvb-usb/dvb-usb-dvb.c linux-3.11.10-fbx/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
--- linux-3.11.10/drivers/media/usb/dvb-usb/dvb-usb-dvb.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/usb/dvb-usb/dvb-usb-dvb.c	2015-09-29 11:15:06.445737040 +0200
@@ -249,6 +249,10 @@
 		adap->fe_adap[i].fe_sleep = adap->fe_adap[i].fe->ops.sleep;
 		adap->fe_adap[i].fe->ops.sleep = dvb_usb_fe_sleep;
 
+		/* only attach the tuner if the demod is there */
+		if (adap->props.fe[i].tuner_attach != NULL)
+			adap->props.fe[i].tuner_attach(adap);
+
 		if (dvb_register_frontend(&adap->dvb_adap, adap->fe_adap[i].fe)) {
 			err("Frontend %d registration failed.", i);
 			dvb_frontend_detach(adap->fe_adap[i].fe);
@@ -261,10 +265,6 @@
 				return 0;
 		}
 
-		/* only attach the tuner if the demod is there */
-		if (adap->props.fe[i].tuner_attach != NULL)
-			adap->props.fe[i].tuner_attach(adap);
-
 		adap->num_frontends_initialized++;
 	}
 
diff -ruw linux-3.11.10/drivers/media/usb/dvb-usb/dvb-usb.h linux-3.11.10-fbx/drivers/media/usb/dvb-usb/dvb-usb.h
--- linux-3.11.10/drivers/media/usb/dvb-usb/dvb-usb.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/usb/dvb-usb/dvb-usb.h	2015-06-12 14:02:22.866961830 +0200
@@ -294,7 +294,7 @@
 	int generic_bulk_ctrl_endpoint_response;
 
 	int num_device_descs;
-	struct dvb_usb_device_description devices[12];
+	struct dvb_usb_device_description devices[32];
 };
 
 /**
diff -ruw linux-3.11.10/drivers/media/usb/dvb-usb-v2/af9035.c linux-3.11.10-fbx/drivers/media/usb/dvb-usb-v2/af9035.c
--- linux-3.11.10/drivers/media/usb/dvb-usb-v2/af9035.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/usb/dvb-usb-v2/af9035.c	2015-09-29 11:15:06.437736985 +0200
@@ -21,6 +21,9 @@
 
 #include "af9035.h"
 
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE  64
+
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 static u16 af9035_checksum(const u8 *buf, size_t len)
@@ -126,9 +129,15 @@
 /* write multiple registers */
 static int af9035_wr_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len)
 {
-	u8 wbuf[6 + len];
+	u8 wbuf[MAX_XFER_SIZE];
 	u8 mbox = (reg >> 16) & 0xff;
-	struct usb_req req = { CMD_MEM_WR, mbox, sizeof(wbuf), wbuf, 0, NULL };
+	struct usb_req req = { CMD_MEM_WR, mbox, 6 + len, wbuf, 0, NULL };
+
+	if (6 + len > sizeof(wbuf)) {
+		dev_warn(&d->udev->dev, "%s: i2c wr: len=%d is too big!\n",
+			 KBUILD_MODNAME, len);
+		return -EOPNOTSUPP;
+	}
 
 	wbuf[0] = len;
 	wbuf[1] = 2;
@@ -228,9 +237,17 @@
 					msg[1].len);
 		} else {
 			/* I2C */
-			u8 buf[5 + msg[0].len];
-			struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf),
+			u8 buf[MAX_XFER_SIZE];
+			struct usb_req req = { CMD_I2C_RD, 0, 5 + msg[0].len,
 					buf, msg[1].len, msg[1].buf };
+
+			if (5 + msg[0].len > sizeof(buf)) {
+				dev_warn(&d->udev->dev,
+					 "%s: i2c xfer: len=%d is too big!\n",
+					 KBUILD_MODNAME, msg[0].len);
+				ret = -EOPNOTSUPP;
+				goto unlock;
+			}
 			req.mbox |= ((msg[0].addr & 0x80)  >>  3);
 			buf[0] = msg[1].len;
 			buf[1] = msg[0].addr << 1;
@@ -257,9 +274,17 @@
 					msg[0].len - 3);
 		} else {
 			/* I2C */
-			u8 buf[5 + msg[0].len];
-			struct usb_req req = { CMD_I2C_WR, 0, sizeof(buf), buf,
-					0, NULL };
+			u8 buf[MAX_XFER_SIZE];
+			struct usb_req req = { CMD_I2C_WR, 0, 5 + msg[0].len,
+					buf, 0, NULL };
+
+			if (5 + msg[0].len > sizeof(buf)) {
+				dev_warn(&d->udev->dev,
+					 "%s: i2c xfer: len=%d is too big!\n",
+					 KBUILD_MODNAME, msg[0].len);
+				ret = -EOPNOTSUPP;
+				goto unlock;
+			}
 			req.mbox |= ((msg[0].addr & 0x80)  >>  3);
 			buf[0] = msg[0].len;
 			buf[1] = msg[0].addr << 1;
@@ -296,6 +321,7 @@
 		ret = -EOPNOTSUPP;
 	}
 
+unlock:
 	mutex_unlock(&d->i2c_mutex);
 
 	if (ret < 0)
@@ -572,6 +598,8 @@
 	if (ret < 0)
 		goto err;
 
+	msleep(30);
+
 	/* firmware loaded, request boot */
 	req.cmd = CMD_FW_BOOT;
 	ret = af9035_ctrl_msg(d, &req);
@@ -591,6 +619,15 @@
 		goto err;
 	}
 
+	/* tuner RF initial */
+	if (state->chip_type == 0x9135) {
+		ret = af9035_wr_reg(d, 0x80ec4c, 0x68);
+		if (ret < 0)
+			goto err;
+
+		msleep(30);
+	}
+
 	dev_info(&d->udev->dev, "%s: firmware version=%d.%d.%d.%d",
 			KBUILD_MODNAME, rbuf[0], rbuf[1], rbuf[2], rbuf[3]);
 
@@ -1502,15 +1539,37 @@
 	{ DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00aa,
 		&af9035_props, "TerraTec Cinergy T Stick (rev. 2)", NULL) },
 	/* IT9135 devices */
-#if 0
-	{ DVB_USB_DEVICE(0x048d, 0x9135,
-		&af9035_props, "IT9135 reference design", NULL) },
-	{ DVB_USB_DEVICE(0x048d, 0x9006,
-		&af9035_props, "IT9135 reference design", NULL) },
-#endif
+	{ DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135,
+		&af9035_props, "ITE 9135 Generic", RC_MAP_IT913X_V1) },
+	{ DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9005,
+		&af9035_props, "ITE 9135(9005) Generic", RC_MAP_IT913X_V2) },
+	{ DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9006,
+		&af9035_props, "ITE 9135(9006) Generic", RC_MAP_IT913X_V1) },
+	{ DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_1835,
+		&af9035_props, "Avermedia A835B(1835)", RC_MAP_IT913X_V2) },
+	{ DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_2835,
+		&af9035_props, "Avermedia A835B(2835)", RC_MAP_IT913X_V2) },
+	{ DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_3835,
+		&af9035_props, "Avermedia A835B(3835)", RC_MAP_IT913X_V2) },
+	{ DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_4835,
+		&af9035_props, "Avermedia A835B(4835)",	RC_MAP_IT913X_V2) },
+	{ DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_H335,
+		&af9035_props, "Avermedia H335", RC_MAP_IT913X_V2) },
+	{ DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09,
+		&af9035_props, "Kworld UB499-2T T09", RC_MAP_IT913X_V1) },
+	{ DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22_IT9137,
+		&af9035_props, "Sveon STV22 Dual DVB-T HDTV",
+							RC_MAP_IT913X_V1) },
+	{ DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CTVDIGDUAL_V2,
+		&af9035_props, "Digital Dual TV Receiver CTVDIGDUAL_V2",
+							RC_MAP_IT913X_V1) },
 	/* XXX: that same ID [0ccd:0099] is used by af9015 driver too */
 	{ DVB_USB_DEVICE(USB_VID_TERRATEC, 0x0099,
 		&af9035_props, "TerraTec Cinergy T Stick Dual RC (rev. 2)", NULL) },
+	{ DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a05,
+		&af9035_props, "Leadtek WinFast DTV Dongle Dual", NULL) },
+	{ DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xf900,
+		&af9035_props, "Hauppauge WinTV-MiniStick 2", NULL) },
 	{ }
 };
 MODULE_DEVICE_TABLE(usb, af9035_id_table);
diff -ruw linux-3.11.10/drivers/mfd/Kconfig linux-3.11.10-fbx/drivers/mfd/Kconfig
--- linux-3.11.10/drivers/mfd/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/mfd/Kconfig	2015-09-29 11:15:06.505737497 +0200
@@ -1145,8 +1145,6 @@
 	  core support for the WM8994, in order to use the actual
 	  functionaltiy of the device other drivers must be enabled.
 
-endmenu
-endif
 
 menu "Multimedia Capabilities Port drivers"
 	depends on ARCH_SA1100
@@ -1178,3 +1176,6 @@
 	help
 	  Platform configuration infrastructure for the ARM Ltd.
 	  Versatile Express.
+
+endmenu
+endif
diff -ruw linux-3.11.10/drivers/misc/eeprom/at24.c linux-3.11.10-fbx/drivers/misc/eeprom/at24.c
--- linux-3.11.10/drivers/misc/eeprom/at24.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/misc/eeprom/at24.c	2015-09-29 11:15:06.533737716 +0200
@@ -259,8 +259,9 @@
 		if (status == count)
 			return count;
 
-		/* REVISIT: at HZ=100, this is sloooow */
-		msleep(1);
+		if (need_resched())
+			schedule();
+
 	} while (time_before(read_time, timeout));
 
 	return -ETIMEDOUT;
@@ -381,8 +382,9 @@
 		if (status == count)
 			return count;
 
-		/* REVISIT: at HZ=100, this is sloooow */
-		msleep(1);
+		if (need_resched())
+			schedule();
+
 	} while (time_before(write_time, timeout));
 
 	return -ETIMEDOUT;
diff -ruw linux-3.11.10/drivers/misc/Kconfig linux-3.11.10-fbx/drivers/misc/Kconfig
--- linux-3.11.10/drivers/misc/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/misc/Kconfig	2015-09-29 11:15:06.529737679 +0200
@@ -4,6 +4,9 @@
 
 menu "Misc devices"
 
+config WINTEGRA_MMAP
+	bool "wintegra mmap driver"
+
 config SENSORS_LIS3LV02D
 	tristate
 	depends on INPUT
@@ -440,6 +443,18 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called vmw_balloon.
 
+config INTELCE_PIC16PMU
+	tristate "PIC16 PMU, LED, hwmon support"
+	select INPUT_POLLDEV
+	select NEW_LEDS
+	select I2C
+	select HWMON
+	select ARCH_REQUIRE_GPIOLIB
+	---help---
+	  Freebox v6 HD PIC16 PMU interface support, enables
+	  control of the on-board LEDs and reports the power status,
+	  reset status and button status.
+
 config ARM_CHARLCD
 	bool "ARM Ltd. Character LCD Driver"
 	depends on PLAT_VERSATILE
@@ -537,4 +552,7 @@
 source "drivers/misc/altera-stapl/Kconfig"
 source "drivers/misc/mei/Kconfig"
 source "drivers/misc/vmw_vmci/Kconfig"
+source "drivers/misc/remoti/Kconfig"
+source "drivers/misc/hdmi-cec/Kconfig"
+
 endmenu
diff -ruw linux-3.11.10/drivers/misc/Makefile linux-3.11.10-fbx/drivers/misc/Makefile
--- linux-3.11.10/drivers/misc/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/misc/Makefile	2015-09-29 11:15:06.529737679 +0200
@@ -2,6 +2,7 @@
 # Makefile for misc devices that really don't fit anywhere else.
 #
 
+obj-$(CONFIG_WINTEGRA_MMAP)	+= wintegra_mmap.o
 obj-$(CONFIG_IBM_ASM)		+= ibmasm/
 obj-$(CONFIG_AD525X_DPOT)	+= ad525x_dpot.o
 obj-$(CONFIG_AD525X_DPOT_I2C)	+= ad525x_dpot-i2c.o
@@ -26,6 +27,7 @@
 obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
 obj-$(CONFIG_KGDB_TESTS)	+= kgdbts.o
 obj-$(CONFIG_SGI_XP)		+= sgi-xp/
+obj-$(CONFIG_INTELCE_PIC16PMU)	+= pic16-pmu.o
 obj-$(CONFIG_SGI_GRU)		+= sgi-gru/
 obj-$(CONFIG_CS5535_MFGPT)	+= cs5535-mfgpt.o
 obj-$(CONFIG_HP_ILO)		+= hpilo.o
@@ -53,3 +55,5 @@
 obj-$(CONFIG_VMWARE_VMCI)	+= vmw_vmci/
 obj-$(CONFIG_LATTICE_ECP3_CONFIG)	+= lattice-ecp3-config.o
 obj-$(CONFIG_SRAM)		+= sram.o
+obj-y				+= remoti/
+obj-y				+= hdmi-cec/
diff -ruw linux-3.11.10/drivers/mtd/devices/m25p80.c linux-3.11.10-fbx/drivers/mtd/devices/m25p80.c
--- linux-3.11.10/drivers/mtd/devices/m25p80.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/mtd/devices/m25p80.c	2015-09-29 11:15:06.573738016 +0200
@@ -44,8 +44,10 @@
 #define	OPCODE_PP		0x02	/* Page program (up to 256 bytes) */
 #define	OPCODE_BE_4K		0x20	/* Erase 4KiB block */
 #define	OPCODE_BE_32K		0x52	/* Erase 32KiB block */
+#define OPCODE_EWRSR		0x50	/* Enable write to status regs */
 #define	OPCODE_CHIP_ERASE	0xc7	/* Erase whole flash chip */
 #define	OPCODE_SE		0xd8	/* Sector erase (usually 64KiB) */
+#define	OPCODE_RDID_ALT		0x90	/* Read ID (alt) */
 #define	OPCODE_RDID		0x9f	/* Read JEDEC ID */
 
 /* Used for SST flashes only. */
@@ -71,7 +73,7 @@
 
 /* Define max times to check status register before we give up. */
 #define	MAX_READY_WAIT_JIFFIES	(40 * HZ)	/* M25P16 specs 40s max chip erase */
-#define	MAX_CMD_SIZE		5
+#define	MAX_CMD_SIZE		6
 
 #define JEDEC_MFR(_jedec_id)	((_jedec_id) >> 16)
 
@@ -682,6 +684,8 @@
 #define	SECT_4K		0x01		/* OPCODE_BE_4K works uniformly */
 #define	M25P_NO_ERASE	0x02		/* No erase command needed */
 #define	SST_WRITE	0x04		/* use SST byte programming */
+#define ALT_PROBE	0x08		/* use alternate probe method */
+#define ALT_PROBE_ATMEL	0x10		/* use atmel probe method */
 };
 
 #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)	\
@@ -720,6 +724,8 @@
 	{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },
 	{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
 	{ "at26df321",  INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
+	{ "at25f512b",  INFO(0x1f6500, 0x1f65, 32 * 1024, 2,
+			     ALT_PROBE_ATMEL) },
 
 	{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
 
@@ -754,6 +760,8 @@
 	{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
 	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
 	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
+	{ "mx25l512", INFO(0xc20500, 0xc205, 64 * 1024, 1,
+			   ALT_PROBE | SECT_4K) },
 	{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, 0) },
 
 	/* Micron */
@@ -784,6 +792,7 @@
 	{ "s25fl064k",  INFO(0xef4017,      0,  64 * 1024, 128, SECT_4K) },
 
 	/* SST -- large erase sizes are "overlays", "sectors" are 4K */
+	{ "sst25vf512a", INFO(0xbf4800, 0xbf48, 32 * 1024, 2, ALT_PROBE) },
 	{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
 	{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
 	{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) },
@@ -851,6 +860,11 @@
 	{ "cat25c09", CAT25_INFO( 128, 8, 32, 2) },
 	{ "cat25c17", CAT25_INFO( 256, 8, 32, 2) },
 	{ "cat25128", CAT25_INFO(2048, 8, 64, 2) },
+
+	/* EON */
+	{ "en25f05", INFO(0x1c0500, 0x1c05, 64 * 1024, 1,
+			  ALT_PROBE | SECT_4K) },
+
 	{ },
 };
 MODULE_DEVICE_TABLE(spi, m25p_ids);
@@ -894,6 +908,60 @@
 	return ERR_PTR(-ENODEV);
 }
 
+static const struct spi_device_id *alt_probe(struct spi_device *spi)
+{
+	int			tmp;
+	u8			read_id[4] = { OPCODE_RDID_ALT, 0, 0, 0 };
+	u8			data[2];
+	u16			id;
+	struct flash_info	*info;
+
+	read_id[3] = 0x0;
+	tmp = spi_write_then_read(spi, read_id, 4, data, 2);
+	if (tmp < 0) {
+		pr_debug("%s: error %d reading ALT ID\n",
+			 dev_name(&spi->dev), tmp);
+		return ERR_PTR(tmp);
+	}
+
+	id = (data[1] << 8) | data[0];
+
+	for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) {
+		info = (void *)m25p_ids[tmp].driver_data;
+		if ((info->flags & ALT_PROBE) && (info->ext_id == id))
+			return &m25p_ids[tmp];
+	}
+
+	dev_err(&spi->dev, "unrecognized ALT id %04x\n", id);
+	return ERR_PTR(-ENODEV);
+}
+
+static const struct spi_device_id *atmel_probe(struct spi_device *spi)
+{
+	int			tmp;
+	u8			opcode = 0x15;
+	u8			data[2];
+	u16			id;
+	struct flash_info	*info;
+
+	tmp = spi_write_then_read(spi, &opcode, 1, data, 2);
+	if (tmp < 0) {
+		pr_debug("%s: error %d reading ATMEL ID\n",
+			 dev_name(&spi->dev), tmp);
+		return ERR_PTR(tmp);
+	}
+
+	id = (data[1] << 8) | data[0];
+
+	for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) {
+		info = (void *)m25p_ids[tmp].driver_data;
+		if ((info->flags & ALT_PROBE_ATMEL) && (info->ext_id == id))
+			return &m25p_ids[tmp];
+	}
+
+	dev_err(&spi->dev, "unrecognized ATMEL id %04x\n", id);
+	return ERR_PTR(-ENODEV);
+}
 
 /*
  * board specific setup should have ensured the SPI clock used here
@@ -902,7 +970,7 @@
  */
 static int m25p_probe(struct spi_device *spi)
 {
-	const struct spi_device_id	*id = spi_get_device_id(spi);
+	const struct spi_device_id	*id, *jid;
 	struct flash_platform_data	*data;
 	struct m25p			*flash;
 	struct flash_info		*info;
@@ -921,6 +989,7 @@
 	 * newer chips, even if we don't recognize the particular chip.
 	 */
 	data = spi->dev.platform_data;
+	id = NULL;
 	if (data && data->type) {
 		const struct spi_device_id *plat_id;
 
@@ -937,15 +1006,18 @@
 			dev_warn(&spi->dev, "unrecognized id %s\n", data->type);
 	}
 
-	info = (void *)id->driver_data;
-
-	if (info->jedec_id) {
-		const struct spi_device_id *jid;
-
+	jid = alt_probe(spi);
+	if (IS_ERR(jid))
+		jid = atmel_probe(spi);
+	if (IS_ERR(jid))
 		jid = jedec_probe(spi);
+
 		if (IS_ERR(jid)) {
-			return PTR_ERR(jid);
-		} else if (jid != id) {
+		dev_err(&spi->dev, "can't probe flash\n");
+		return -ENODEV;
+	}
+
+	if (id && jid != id) {
 			/*
 			 * JEDEC knows better, so overwrite platform ID. We
 			 * can't trust partitions any longer, but we'll let
@@ -955,20 +1027,17 @@
 			 */
 			dev_warn(&spi->dev, "found %s, expected %s\n",
 				 jid->name, id->name);
-			id = jid;
-			info = (void *)jid->driver_data;
-		}
 	}
 
-	flash = kzalloc(sizeof *flash, GFP_KERNEL);
+	id = jid;
+	info = (void *)jid->driver_data;
+	flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
 	if (!flash)
 		return -ENOMEM;
-	flash->command = kmalloc(MAX_CMD_SIZE + (flash->fast_read ? 1 : 0),
-					GFP_KERNEL);
-	if (!flash->command) {
-		kfree(flash);
+
+	flash->command = devm_kzalloc(&spi->dev, MAX_CMD_SIZE, GFP_KERNEL);
+	if (!flash->command)
 		return -ENOMEM;
-	}
 
 	flash->spi = spi;
 	mutex_init(&flash->lock);
@@ -986,6 +1055,14 @@
 		write_sr(flash, 0);
 	}
 
+	if (info->ext_id == 0xbf48) {
+		u8 ewrsr = OPCODE_EWRSR;
+
+		write_enable(flash);
+		spi_write_then_read(flash->spi, &ewrsr, 1, NULL, 0);
+		write_sr(flash, 0);
+	}
+
 	if (data && data->name)
 		flash->mtd.name = data->name;
 	else
@@ -1081,14 +1158,10 @@
 static int m25p_remove(struct spi_device *spi)
 {
 	struct m25p	*flash = dev_get_drvdata(&spi->dev);
-	int		status;
 
 	/* Clean up MTD stuff. */
-	status = mtd_device_unregister(&flash->mtd);
-	if (status == 0) {
-		kfree(flash->command);
-		kfree(flash);
-	}
+	mtd_device_unregister(&flash->mtd);
+
 	return 0;
 }
 
diff -ruw linux-3.11.10/drivers/mtd/Kconfig linux-3.11.10-fbx/drivers/mtd/Kconfig
--- linux-3.11.10/drivers/mtd/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/mtd/Kconfig	2015-09-29 11:15:06.565737954 +0200
@@ -23,6 +23,9 @@
 	  WARNING: some of the tests will ERASE entire MTD device which they
 	  test. Do not use these tests unless you really know what you do.
 
+config MTD_ERASE_PRINTK
+	bool "write to kernel log when a block is erased"
+
 config MTD_REDBOOT_PARTS
 	tristate "RedBoot partition table parsing"
 	---help---
@@ -155,6 +158,17 @@
 	  This provides partitions parser for devices based on BCM47xx
 	  boards.
 
+config MTD_FBX6HD_PARTS
+	tristate "Freebox V6 HD partitioning support"
+	help
+	  Freebox V6 HD partitioning support
+
+config MTD_FBX6HD_PARTS_WRITE_ALL
+	bool "make all partitions writeable"
+	depends on MTD_FBX6HD_PARTS
+	help
+	  Freebox V6 HD partitions support
+
 comment "User Modules And Translation Layers"
 
 config MTD_BLKDEVS
diff -ruw linux-3.11.10/drivers/mtd/Makefile linux-3.11.10-fbx/drivers/mtd/Makefile
--- linux-3.11.10/drivers/mtd/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/mtd/Makefile	2015-09-29 11:15:06.565737954 +0200
@@ -13,6 +13,7 @@
 obj-$(CONFIG_MTD_AR7_PARTS)	+= ar7part.o
 obj-$(CONFIG_MTD_BCM63XX_PARTS)	+= bcm63xxpart.o
 obj-$(CONFIG_MTD_BCM47XX_PARTS)	+= bcm47xxpart.o
+obj-$(CONFIG_MTD_FBX6HD_PARTS)	+= fbx6hd-mtdparts.o
 
 # 'Users' - code which presents functionality to userspace.
 obj-$(CONFIG_MTD_BLKDEVS)	+= mtd_blkdevs.o
diff -ruw linux-3.11.10/drivers/mtd/mtdchar.c linux-3.11.10-fbx/drivers/mtd/mtdchar.c
--- linux-3.11.10/drivers/mtd/mtdchar.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/mtd/mtdchar.c	2015-09-29 11:15:06.577738045 +0200
@@ -215,6 +215,7 @@
 		{
 			struct mtd_oob_ops ops;
 
+			memset(&ops, 0, sizeof (ops));
 			ops.mode = MTD_OPS_RAW;
 			ops.datbuf = kbuf;
 			ops.oobbuf = NULL;
@@ -309,6 +310,7 @@
 		{
 			struct mtd_oob_ops ops;
 
+			memset(&ops, 0, sizeof (ops));
 			ops.mode = MTD_OPS_RAW;
 			ops.datbuf = kbuf;
 			ops.oobbuf = NULL;
@@ -707,6 +709,10 @@
 			erase->callback = mtdchar_erase_callback;
 			erase->priv = (unsigned long)&waitq;
 
+#ifdef CONFIG_MTD_ERASE_PRINTK
+			printk(KERN_DEBUG "mtd: %s: ERASE offset=@%08llx\n",
+			       mtd->name, erase->addr);
+#endif
 			/*
 			  FIXME: Allow INTERRUPTIBLE. Which means
 			  not having the wait_queue head on the stack.
diff -ruw linux-3.11.10/drivers/mtd/mtdcore.c linux-3.11.10-fbx/drivers/mtd/mtdcore.c
--- linux-3.11.10/drivers/mtd/mtdcore.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/mtd/mtdcore.c	2015-09-29 11:15:06.581738082 +0200
@@ -285,6 +285,33 @@
 		   mtd_bitflip_threshold_show,
 		   mtd_bitflip_threshold_store);
 
+static ssize_t mtd_nand_type_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", mtd->nand_type);
+}
+static DEVICE_ATTR(nand_type, S_IRUGO, mtd_nand_type_show, NULL);
+
+static ssize_t mtd_nand_manufacturer_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", mtd->nand_manufacturer);
+}
+static DEVICE_ATTR(nand_manufacturer, S_IRUGO, mtd_nand_manufacturer_show, NULL);
+
+static ssize_t mtd_nand_onfi_version_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", mtd->onfi_version);
+}
+static DEVICE_ATTR(onfi_version, S_IRUGO, mtd_nand_onfi_version_show, NULL);
+
 static struct attribute *mtd_attrs[] = {
 	&dev_attr_type.attr,
 	&dev_attr_flags.attr,
@@ -297,6 +324,9 @@
 	&dev_attr_name.attr,
 	&dev_attr_ecc_strength.attr,
 	&dev_attr_bitflip_threshold.attr,
+	&dev_attr_nand_type.attr,
+	&dev_attr_nand_manufacturer.attr,
+	&dev_attr_onfi_version.attr,
 	NULL,
 };
 
diff -ruw linux-3.11.10/drivers/mtd/mtdpart.c linux-3.11.10-fbx/drivers/mtd/mtdpart.c
--- linux-3.11.10/drivers/mtd/mtdpart.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/mtd/mtdpart.c	2015-09-29 11:15:06.581738082 +0200
@@ -366,6 +366,9 @@
 	slave->mtd.oobsize = master->oobsize;
 	slave->mtd.oobavail = master->oobavail;
 	slave->mtd.subpage_sft = master->subpage_sft;
+	slave->mtd.nand_type = master->nand_type;
+	slave->mtd.nand_manufacturer = master->nand_manufacturer;
+	slave->mtd.onfi_version = master->onfi_version;
 
 	slave->mtd.name = name;
 	slave->mtd.owner = master->owner;
diff -ruw linux-3.11.10/drivers/mtd/nand/Kconfig linux-3.11.10-fbx/drivers/mtd/nand/Kconfig
--- linux-3.11.10/drivers/mtd/nand/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/mtd/nand/Kconfig	2015-09-29 11:15:06.581738082 +0200
@@ -41,6 +41,15 @@
 	tristate
 	default n
 
+config MTD_FORCE_BAD_BLOCK_ERASE
+	bool "Force erase on bad blocks (useful for bootloader parts)"
+	depends on MTD_NAND
+	default n
+	help
+	  Enable this option only when you need to force an erase on
+	  blocks being marked as "bad" by Linux (i.e: other ECC/bad block
+	  marker layout).
+
 config MTD_NAND_DENALI
         tristate "Support Denali NAND controller"
         help
@@ -544,4 +553,9 @@
 	  Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
 	  to the External Bus Unit (EBU).
 
+config MTD_NAND_DENALI_FBX
+	tristate "NAND Denali controller support"
+	depends on MTD_NAND && PCI
+	default n
+
 endif # MTD_NAND
diff -ruw linux-3.11.10/drivers/mtd/nand/Makefile linux-3.11.10-fbx/drivers/mtd/nand/Makefile
--- linux-3.11.10/drivers/mtd/nand/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/mtd/nand/Makefile	2015-09-29 11:15:06.581738082 +0200
@@ -50,5 +50,6 @@
 obj-$(CONFIG_MTD_NAND_GPMI_NAND)	+= gpmi-nand/
 obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nand.o
 obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
+obj-$(CONFIG_MTD_NAND_DENALI_FBX)	+= denali_nand.o
 
 nand-objs := nand_base.o nand_bbt.o
diff -ruw linux-3.11.10/drivers/mtd/nand/nand_base.c linux-3.11.10-fbx/drivers/mtd/nand/nand_base.c
--- linux-3.11.10/drivers/mtd/nand/nand_base.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/mtd/nand/nand_base.c	2015-09-29 11:15:06.589738136 +0200
@@ -2594,6 +2594,7 @@
 
 	while (len) {
 		/* Check if we have a bad block, we do not erase bad blocks! */
+#ifndef CONFIG_MTD_FORCE_BAD_BLOCK_ERASE
 		if (nand_block_checkbad(mtd, ((loff_t) page) <<
 					chip->page_shift, 0, allowbbt)) {
 			pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
@@ -2601,6 +2602,7 @@
 			instr->state = MTD_ERASE_FAILED;
 			goto erase_exit;
 		}
+#endif
 
 		/*
 		 * Invalidate the page cache, if we erase the block which
@@ -2904,10 +2906,21 @@
 	sanitize_string(p->model, sizeof(p->model));
 	if (!mtd->name)
 		mtd->name = p->model;
+
 	mtd->writesize = le32_to_cpu(p->byte_per_page);
-	mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
+
+	/*
+	 * pages_per_block and blocks_per_lun may not be a power-of-2 size
+	 * (don't ask me who thought of this...). MTD assumes that these
+	 * dimensions will be power-of-2, so just truncate the remaining area.
+	 */
+	mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
+	mtd->erasesize *= mtd->writesize;
+
 	mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
-	chip->chipsize = le32_to_cpu(p->blocks_per_lun);
+
+	/* See erasesize comment */
+	chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
 	chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
 	*busw = 0;
 	if (le16_to_cpu(p->features) & 1)
@@ -3193,6 +3206,8 @@
 {
 	int i, maf_idx;
 	u8 id_data[8];
+	int ret;
+	char onfi_version[5];
 
 	/* Select the device */
 	chip->select_chip(mtd, 0);
@@ -3330,7 +3345,38 @@
 		chip->onfi_version ? chip->onfi_params.model : type->name,
 		(int)(chip->chipsize >> 20), mtd->writesize, mtd->oobsize);
 
+	mtd->nand_type = kstrdup(type->name, GFP_KERNEL);
+	if (!mtd->nand_type)
+		return ERR_PTR(-ENOMEM);
+
+	mtd->nand_manufacturer = kstrdup(nand_manuf_ids[maf_idx].name,
+						GFP_KERNEL);
+	if (!mtd->nand_manufacturer) {
+		ret = -ENOMEM;
+		goto out_nand_type;
+	}
+
+	snprintf(onfi_version, sizeof(onfi_version), "0");
+
+	if (chip->onfi_version)
+		snprintf(onfi_version, sizeof(onfi_version), "%d.%d",
+					chip->onfi_version / 10,
+					chip->onfi_version % 10);
+
+	mtd->onfi_version = kstrdup(onfi_version, GFP_KERNEL);
+	if (!mtd->onfi_version) {
+		ret = -ENOMEM;
+		goto out_nand_manufacturer;
+	}
+
 	return type;
+
+out_nand_manufacturer:
+	kfree(mtd->nand_manufacturer);
+out_nand_type:
+	kfree(mtd->nand_type);
+
+	return ERR_PTR(ret);
 }
 
 /**
@@ -3350,6 +3396,7 @@
 	int i, busw, nand_maf_id, nand_dev_id;
 	struct nand_chip *chip = mtd->priv;
 	struct nand_flash_dev *type;
+	int err;
 
 	/* Get buswidth to select the correct functions */
 	busw = chip->options & NAND_BUSWIDTH_16;
@@ -3364,7 +3411,8 @@
 		if (!(chip->options & NAND_SCAN_SILENT_NODEV))
 			pr_warn("No NAND device found\n");
 		chip->select_chip(mtd, -1);
-		return PTR_ERR(type);
+		err = PTR_ERR(type);
+		goto out_error;
 	}
 
 	chip->select_chip(mtd, -1);
@@ -3392,6 +3440,16 @@
 	mtd->size = i * chip->chipsize;
 
 	return 0;
+
+out_error:
+	if (mtd->nand_type)
+		kfree(mtd->nand_type);
+	if (mtd->nand_manufacturer)
+		kfree(mtd->nand_manufacturer);
+	if (mtd->onfi_version)
+		kfree(mtd->onfi_version);
+
+	return err;
 }
 EXPORT_SYMBOL(nand_scan_ident);
 
@@ -3533,9 +3591,13 @@
 		chip->ecc.read_page = nand_read_page_swecc;
 		chip->ecc.read_subpage = nand_read_subpage;
 		chip->ecc.write_page = nand_write_page_swecc;
+		if (!chip->ecc.read_page_raw)
 		chip->ecc.read_page_raw = nand_read_page_raw;
+		if (!chip->ecc.write_page_raw)
 		chip->ecc.write_page_raw = nand_write_page_raw;
+		if (!chip->ecc.read_oob)
 		chip->ecc.read_oob = nand_read_oob_std;
+		if (!chip->ecc.write_oob)
 		chip->ecc.write_oob = nand_write_oob_std;
 		if (!chip->ecc.size)
 			chip->ecc.size = 256;
@@ -3649,7 +3711,8 @@
 	chip->pagebuf = -1;
 
 	/* Large page NAND with SOFT_ECC should support subpage reads */
-	if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9))
+	if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9) &&
+	    !(chip->options & NAND_NO_RNDOUT))
 		chip->options |= NAND_SUBPAGE_READ;
 
 	/* Fill in remaining MTD driver data */
diff -ruw linux-3.11.10/drivers/mtd/nand/orion_nand.c linux-3.11.10-fbx/drivers/mtd/nand/orion_nand.c
--- linux-3.11.10/drivers/mtd/nand/orion_nand.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/mtd/nand/orion_nand.c	2015-09-29 11:15:06.593738173 +0200
@@ -140,7 +140,11 @@
 	nc->IO_ADDR_R = nc->IO_ADDR_W = io_base;
 	nc->cmd_ctrl = orion_nand_cmd_ctrl;
 	nc->read_buf = orion_nand_read_buf;
-	nc->ecc.mode = NAND_ECC_SOFT;
+	nc->ecc.mode = board->ecc;
+	if (board->ecc == NAND_ECC_SOFT_BCH) {
+		nc->ecc.size = board->bch_ecc_size;
+		nc->ecc.bytes = board->bch_ecc_bytes;
+	}
 
 	if (board->chip_delay)
 		nc->chip_delay = board->chip_delay;
diff -ruw linux-3.11.10/drivers/net/ethernet/Kconfig linux-3.11.10-fbx/drivers/net/ethernet/Kconfig
--- linux-3.11.10/drivers/net/ethernet/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/ethernet/Kconfig	2015-09-29 11:15:06.633738473 +0200
@@ -149,6 +149,7 @@
 	  will be called s6gmac.
 
 source "drivers/net/ethernet/seeq/Kconfig"
+source "drivers/net/ethernet/sigma/Kconfig"
 source "drivers/net/ethernet/silan/Kconfig"
 source "drivers/net/ethernet/sis/Kconfig"
 source "drivers/net/ethernet/sfc/Kconfig"
@@ -163,6 +164,7 @@
 source "drivers/net/ethernet/tundra/Kconfig"
 source "drivers/net/ethernet/via/Kconfig"
 source "drivers/net/ethernet/wiznet/Kconfig"
+source "drivers/net/ethernet/wintegra/Kconfig"
 source "drivers/net/ethernet/xilinx/Kconfig"
 source "drivers/net/ethernet/xircom/Kconfig"
 
diff -ruw linux-3.11.10/drivers/net/ethernet/Makefile linux-3.11.10-fbx/drivers/net/ethernet/Makefile
--- linux-3.11.10/drivers/net/ethernet/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/ethernet/Makefile	2015-09-29 11:15:06.637738502 +0200
@@ -61,6 +61,7 @@
 obj-$(CONFIG_S6GMAC) += s6gmac.o
 obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
 obj-$(CONFIG_NET_VENDOR_SILAN) += silan/
+obj-$(CONFIG_NET_VENDOR_SIGMA) += sigma/
 obj-$(CONFIG_NET_VENDOR_SIS) += sis/
 obj-$(CONFIG_SFC) += sfc/
 obj-$(CONFIG_NET_VENDOR_SGI) += sgi/
@@ -74,5 +75,6 @@
 obj-$(CONFIG_NET_VENDOR_TUNDRA) += tundra/
 obj-$(CONFIG_NET_VENDOR_VIA) += via/
 obj-$(CONFIG_NET_VENDOR_WIZNET) += wiznet/
+obj-$(CONFIG_NET_VENDOR_WINTEGRA) += wintegra/
 obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/
 obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/
diff -ruw linux-3.11.10/drivers/net/ethernet/marvell/Kconfig linux-3.11.10-fbx/drivers/net/ethernet/marvell/Kconfig
--- linux-3.11.10/drivers/net/ethernet/marvell/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/ethernet/marvell/Kconfig	2015-09-29 11:15:06.773739544 +0200
@@ -23,6 +23,7 @@
 	depends on (MV64X60 || PPC32 || PLAT_ORION) && INET
 	select PHYLIB
 	select MVMDIO
+	select MII
 	---help---
 	  This driver supports the gigabit ethernet MACs in the
 	  Marvell Discovery PPC/MIPS chipset family (MV643XX) and
diff -ruw linux-3.11.10/drivers/net/ethernet/marvell/mv643xx_eth.c linux-3.11.10-fbx/drivers/net/ethernet/marvell/mv643xx_eth.c
--- linux-3.11.10/drivers/net/ethernet/marvell/mv643xx_eth.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/ethernet/marvell/mv643xx_eth.c	2015-09-29 11:15:06.773739544 +0200
@@ -64,6 +64,9 @@
 #include <linux/of_irq.h>
 #include <linux/of_net.h>
 #include <linux/of_mdio.h>
+#include <linux/if_vlan.h>
+#include <linux/sort.h>
+#include <linux/fbxbridge.h>
 
 static char mv643xx_eth_driver_name[] = "mv643xx_eth";
 static char mv643xx_eth_driver_version[] = "1.4";
@@ -107,6 +110,7 @@
 #define  DISABLE_AUTO_NEG_FOR_DUPLEX	0x00000004
 #define  FORCE_LINK_PASS		0x00000002
 #define  SERIAL_PORT_ENABLE		0x00000001
+#define PORT_VPT2P			0x0040
 #define PORT_STATUS			0x0044
 #define  TX_FIFO_EMPTY			0x00000400
 #define  TX_IN_PROGRESS			0x00000080
@@ -228,6 +232,11 @@
 
 /* RX & TX descriptor status */
 #define ERROR_SUMMARY			0x00000001
+#define ERROR_CODE_RX_CRC		(0x0 << 1)
+#define ERROR_CODE_RX_OVERRUN		(0x1 << 1)
+#define ERROR_CODE_RX_MAX_LENGTH	(0x2 << 1)
+#define ERROR_CODE_RX_RESOURCE		(0x3 << 1)
+#define ERROR_CODE_MASK			(0x3 << 1)
 
 /* RX descriptor status */
 #define LAYER_4_CHECKSUM_OK		0x40000000
@@ -239,6 +248,7 @@
 #define RX_PKT_IS_ETHERNETV2		0x00800000
 #define RX_PKT_LAYER4_TYPE_MASK		0x00600000
 #define RX_PKT_LAYER4_TYPE_TCP_IPV4	0x00000000
+#define RX_PKT_LAYER4_TYPE_UDP_IPV4	0x00200000
 #define RX_PKT_IS_VLAN_TAGGED		0x00080000
 
 /* TX descriptor command */
@@ -284,6 +294,10 @@
 static int mv643xx_eth_open(struct net_device *dev);
 static int mv643xx_eth_stop(struct net_device *dev);
 
+static int mii_bus_read(struct net_device *dev, int mii_id, int regnum);
+static void mii_bus_write(struct net_device *dev, int mii_id, int regnum,
+			  int value);
+
 
 /* per-port *****************************************************************/
 struct mib_counters {
@@ -320,6 +334,8 @@
 	/* Non MIB hardware counters */
 	u32 rx_discard;
 	u32 rx_overrun;
+	/* Non MIB software counters */
+	u32 rx_packets_q[8];
 };
 
 struct rx_queue {
@@ -330,6 +346,7 @@
 	int rx_desc_count;
 	int rx_curr_desc;
 	int rx_used_desc;
+	unsigned int rx_packets;
 
 	struct rx_desc *rx_desc_area;
 	dma_addr_t rx_desc_dma;
@@ -407,6 +424,11 @@
 	 */
 	struct clk *clk;
 	unsigned int t_clk;
+
+	/*
+	 * mii bus for MII ioctls & low level early switch config.
+	 */
+	struct mii_bus *mii_bus;
 };
 
 
@@ -498,6 +520,28 @@
 	}
 }
 
+static inline bool pkt_is_ipv4(u32 cmd_sts)
+{
+	return (cmd_sts & RX_PKT_IS_IPV4) == RX_PKT_IS_IPV4;
+}
+
+static inline bool pkt_is_vlan(u32 cmd_sts)
+{
+	return (cmd_sts & RX_PKT_IS_VLAN_TAGGED) == RX_PKT_IS_VLAN_TAGGED;
+}
+
+static inline bool pkt_is_tcp4(u32 cmd_sts)
+{
+	return (cmd_sts & RX_PKT_LAYER4_TYPE_MASK) ==
+		RX_PKT_LAYER4_TYPE_TCP_IPV4;
+}
+
+static inline bool pkt_is_udp4(u32 cmd_sts)
+{
+	return (cmd_sts & RX_PKT_LAYER4_TYPE_MASK) ==
+		RX_PKT_LAYER4_TYPE_UDP_IPV4;
+}
+
 static int rxq_process(struct rx_queue *rxq, int budget)
 {
 	struct mv643xx_eth_private *mp = rxq_to_mp(rxq);
@@ -542,6 +586,7 @@
 		 * start of the packet (which we don't count), and a 4
 		 * byte CRC at the end of the packet (which we do count).
 		 */
+		rxq->rx_packets++;
 		stats->rx_packets++;
 		stats->rx_bytes += byte_cnt - 2;
 
@@ -562,10 +607,28 @@
 
 		if (cmd_sts & LAYER_4_CHECKSUM_OK)
 			skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+#ifdef CONFIG_FBXBRIDGE
+		if (mp->dev->fbx_bridge_maybe_port &&
+		    pkt_is_vlan(cmd_sts) &&
+		    pkt_is_ipv4(cmd_sts)) {
+
+			if (pkt_is_tcp4(cmd_sts)) {
+				if (__fbxbridge_fp_in_vlan_tcp4(mp->dev, skb))
+					continue;
+			} else if (pkt_is_udp4(cmd_sts)) {
+				if (__fbxbridge_fp_in_vlan_tcp4(mp->dev, skb))
+					continue;
+			}
+		}
+#endif
+
 		skb->protocol = eth_type_trans(skb, mp->dev);
 
+		if (mp->dev->features & NETIF_F_GRO)
 		napi_gro_receive(&mp->napi, skb);
-
+		else
+			netif_receive_skb(skb);
 		continue;
 
 err:
@@ -578,8 +641,22 @@
 					   "received packet spanning multiple descriptors\n");
 		}
 
-		if (cmd_sts & ERROR_SUMMARY)
+		if (cmd_sts & ERROR_SUMMARY) {
 			stats->rx_errors++;
+			if (cmd_sts & RX_FIRST_DESC) {
+				switch (cmd_sts & ERROR_CODE_MASK) {
+				case ERROR_CODE_RX_MAX_LENGTH:
+					stats->rx_length_errors++;
+					break;
+				case ERROR_CODE_RX_CRC:
+					stats->rx_crc_errors++;
+					break;
+				case ERROR_CODE_RX_OVERRUN:
+					stats->rx_fifo_errors++;
+					break;
+				}
+			}
+		}
 
 		dev_kfree_skb(skb);
 	}
@@ -713,7 +790,7 @@
 	struct tx_desc *desc;
 	u32 cmd_sts;
 	u16 l4i_chk;
-	int length;
+	int maplen, length;
 
 	cmd_sts = TX_FIRST_DESC | GEN_CRC | BUFFER_OWNED_BY_DMA;
 	l4i_chk = 0;
@@ -774,10 +851,16 @@
 		length = skb->len;
 	}
 
+	maplen = length;
+#ifdef CONFIG_FBXBRIDGE
+	if (skb->fbxbridge_state == 2 && maplen > 32)
+		maplen = 32;
+#endif
+
 	desc->l4i_chk = l4i_chk;
 	desc->byte_cnt = length;
 	desc->buf_ptr = dma_map_single(mp->dev->dev.parent, skb->data,
-				       length, DMA_TO_DEVICE);
+				       maplen, DMA_TO_DEVICE);
 
 	__skb_queue_tail(&txq->tx_skb, skb);
 
@@ -1095,6 +1178,7 @@
 static void mib_counters_update(struct mv643xx_eth_private *mp)
 {
 	struct mib_counters *p = &mp->mib_counters;
+	unsigned int i;
 
 	spin_lock_bh(&mp->mib_counters_lock);
 	p->good_octets_received += mib_read(mp, 0x00);
@@ -1130,6 +1214,10 @@
 	/* Non MIB hardware counters */
 	p->rx_discard += rdlp(mp, RX_DISCARD_FRAME_CNT);
 	p->rx_overrun += rdlp(mp, RX_OVERRUN_FRAME_CNT);
+	/* Non MIB software counters */
+	for (i = 0; i < ARRAY_SIZE(mp->rxq); i++)
+		p->rx_packets_q[i] = mp->rxq[i].rx_packets;
+
 	spin_unlock_bh(&mp->mib_counters_lock);
 }
 
@@ -1277,6 +1365,14 @@
 	MIBSTAT(late_collision),
 	MIBSTAT(rx_discard),
 	MIBSTAT(rx_overrun),
+	MIBSTAT(rx_packets_q[0]),
+	MIBSTAT(rx_packets_q[1]),
+	MIBSTAT(rx_packets_q[2]),
+	MIBSTAT(rx_packets_q[3]),
+	MIBSTAT(rx_packets_q[4]),
+	MIBSTAT(rx_packets_q[5]),
+	MIBSTAT(rx_packets_q[6]),
+	MIBSTAT(rx_packets_q[7]),
 };
 
 static int
@@ -1471,6 +1567,243 @@
 	return 0;
 }
 
+static void
+mv643xx_eth_get_channels(struct net_device *dev, struct ethtool_channels *c)
+{
+	struct mv643xx_eth_private *mp = netdev_priv(dev);
+
+	c->max_rx = 8;
+	c->max_tx = 8;
+	c->max_other = 0;
+	c->max_combined = c->max_rx + c->max_tx;
+	c->rx_count = mp->rxq_count;
+	c->tx_count = mp->txq_count;
+}
+
+static int
+mv643xx_eth_set_channels(struct net_device *dev, struct ethtool_channels *c)
+{
+	struct mv643xx_eth_private *mp = netdev_priv(dev);
+	bool was_runnning;
+
+	if (c->rx_count > 8 || c->max_tx > 8 || c->max_other)
+		return -EINVAL;
+
+	was_runnning = netif_running(dev);
+	if (was_runnning)
+		mv643xx_eth_stop(dev);
+
+	mp->rxq_count = c->rx_count;
+	mp->txq_count = c->tx_count;
+
+	netif_set_real_num_rx_queues(dev, mp->rxq_count);
+	netif_set_real_num_tx_queues(dev, mp->txq_count);
+
+	if (was_runnning && mv643xx_eth_open(dev)) {
+		netdev_err(dev,
+			   "fatal error on re-opening device after channels change\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+struct vprio_queue {
+	int	prio;
+	int	queue;
+};
+
+static int cmp_queue_inv(const void *a, const void *b)
+{
+	const struct vprio_queue *pa = a, *pb = b;
+	if (pb->queue != pa->queue)
+		return pb->queue - pa->queue;
+	return pa->prio - pb->prio;
+}
+
+static void dump_vlan_rules(struct mv643xx_eth_private *mp,
+			    struct vprio_queue *vprio_to_queue)
+{
+	unsigned int i;
+	u32 val;
+
+	val = rdlp(mp, PORT_VPT2P);
+	for (i = 0; i < 8; i++) {
+		unsigned int queue;
+
+		queue = (val & (0x7 << i * 3)) >> (i * 3);
+		vprio_to_queue[i].prio = i;
+		vprio_to_queue[i].queue = queue;
+	}
+
+	/* sort with higher tx queue first */
+	sort(vprio_to_queue, 8, sizeof (vprio_to_queue[0]),
+	     cmp_queue_inv, NULL);
+}
+
+static unsigned int find_vlan_rule(struct mv643xx_eth_private *mp,
+				   unsigned int prio)
+{
+	struct vprio_queue vprio_to_queue[8];
+	unsigned int i;
+
+	/* check if we already have a rule for this vlan */
+	dump_vlan_rules(mp, vprio_to_queue);
+	for (i = 0; i < ARRAY_SIZE(vprio_to_queue); i++) {
+		if (vprio_to_queue[i].prio != prio)
+			continue;
+		return i;
+	}
+	/* never reached */
+	return 0;
+}
+
+static int
+mv643xx_eth_get_rxnfc(struct net_device *dev,
+		      struct ethtool_rxnfc *info, u32 *rule_locs)
+{
+	struct mv643xx_eth_private *mp = netdev_priv(dev);
+
+	switch (info->cmd) {
+	case ETHTOOL_GRXFH:
+		return -ENOTSUPP;
+	case ETHTOOL_GRXRINGS:
+		info->data = mp->rxq_count;
+		break;
+
+	case ETHTOOL_GRXCLSRLCNT:
+		info->rule_cnt = 8;
+		info->data = RX_CLS_LOC_SPECIAL;
+		break;
+
+	case ETHTOOL_GRXCLSRLALL:
+	{
+		unsigned int i;
+
+		if (info->rule_cnt < 8)
+			return -EINVAL;
+
+		info->data = 8;
+		info->rule_cnt = 8;
+
+		for (i = 0; i < 8; i++)
+			rule_locs[i] = i;
+
+		break;
+	}
+
+	case ETHTOOL_GRXCLSRULE:
+	{
+		struct vprio_queue vprio_to_queue[8], *r;
+		struct ethtool_flow_ext *h_ext, *m_ext;
+		unsigned int loc;
+
+		loc = info->fs.location;
+		if (loc >= ARRAY_SIZE(vprio_to_queue))
+			return -EINVAL;
+
+		dump_vlan_rules(mp, vprio_to_queue);
+		r = &vprio_to_queue[loc];
+
+		memset(&info->fs, 0, sizeof (info->fs));
+		info->fs.flow_type = ETHER_FLOW | FLOW_EXT;
+		info->fs.ring_cookie = r->queue;
+		info->fs.location = loc;
+
+		m_ext = &info->fs.m_ext;
+		m_ext->vlan_tci |= VLAN_PRIO_MASK;
+
+		h_ext = &info->fs.h_ext;
+		h_ext->vlan_tci |= r->prio << VLAN_PRIO_SHIFT;
+
+		break;
+	}
+	}
+	return 0;
+}
+
+static int
+mv643xx_eth_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info)
+{
+	struct mv643xx_eth_private *mp = netdev_priv(dev);
+
+	switch (info->cmd) {
+	case ETHTOOL_SRXFH:
+		return -ENOTSUPP;
+
+	case ETHTOOL_SRXCLSRLINS:
+	{
+		struct ethhdr *m, z;
+		struct ethtool_flow_ext *h_ext, *m_ext;
+		unsigned int prio;
+		unsigned int rule_nr;
+		u32 val;
+
+		if ((info->fs.flow_type & (FLOW_MAC_EXT | FLOW_EXT)) !=
+		    FLOW_EXT)
+			return -EINVAL;
+
+		info->fs.flow_type &= ~FLOW_EXT;
+		if (info->fs.flow_type != ETHER_FLOW)
+			return -EINVAL;
+
+		if (info->fs.ring_cookie >= mp->rxq_count)
+			return -EINVAL;
+
+		if (info->fs.location != RX_CLS_LOC_ANY)
+			return -EINVAL;
+
+		/* no mask should be set on ethernet */
+		m = &info->fs.m_u.ether_spec;
+		memset(&z, 0, sizeof (z));
+		if (memcmp(m, &z, sizeof (*m)))
+			return -EINVAL;
+
+		/* no mask should be set on ext besides vlan prio */
+		m_ext = &info->fs.m_ext;
+		if (m_ext->vlan_etype ||
+		    m_ext->data[0] ||
+		    m_ext->data[1] ||
+		    ntohs(m_ext->vlan_tci) != VLAN_PRIO_MASK)
+			return -EINVAL;
+
+		/* ok, extract vlan prio */
+		h_ext = &info->fs.h_ext;
+		prio = (ntohs(h_ext->vlan_tci) & VLAN_PRIO_MASK) >>
+			VLAN_PRIO_SHIFT;
+
+		/* update vlan priority table for new rule */
+		rule_nr = find_vlan_rule(mp, prio);
+
+		val = rdlp(mp, PORT_VPT2P);
+		val |= info->fs.ring_cookie << (prio * 3);
+		wrlp(mp, PORT_VPT2P, val);
+
+		info->fs.location = rule_nr;
+		break;
+	}
+
+	case ETHTOOL_SRXCLSRLDEL:
+	{
+		struct vprio_queue vprio_to_queue[8], *r;
+		u32 val;
+
+		if (info->fs.location >= ARRAY_SIZE(vprio_to_queue))
+			return -EINVAL;
+
+		dump_vlan_rules(mp, vprio_to_queue);
+		r = &vprio_to_queue[info->fs.location];
+
+		/* update vlan priority table */
+		val = rdlp(mp, PORT_VPT2P);
+		val &= ~(0x7 << (r->prio * 3));
+		wrlp(mp, PORT_VPT2P, val);
+		break;
+	}
+	}
+
+	return 0;
+}
 
 static int
 mv643xx_eth_set_features(struct net_device *dev, netdev_features_t features)
@@ -1547,6 +1880,10 @@
 	.get_ts_info		= ethtool_op_get_ts_info,
 	.get_wol                = mv643xx_eth_get_wol,
 	.set_wol                = mv643xx_eth_set_wol,
+	.get_channels		= mv643xx_eth_get_channels,
+	.set_channels		= mv643xx_eth_set_channels,
+	.get_rxnfc		= mv643xx_eth_get_rxnfc,
+	.set_rxnfc		= mv643xx_eth_set_rxnfc,
 };
 
 
@@ -2314,13 +2651,22 @@
 	struct mv643xx_eth_private *mp = netdev_priv(dev);
 	int ret;
 
-	if (mp->phy == NULL)
-		return -ENOTSUPP;
-
+	if (mp->phy != NULL) {
 	ret = phy_mii_ioctl(mp->phy, ifr, cmd);
 	if (!ret)
 		mv643xx_adjust_pscr(mp);
-	return ret;
+	} else {
+		struct mii_if_info mii;
+
+		mii.dev = dev;
+		mii.mdio_read = mii_bus_read;
+		mii.mdio_write = mii_bus_write;
+		mii.phy_id = 0;
+		mii.phy_id_mask = 0x3f;
+		mii.reg_num_mask = 0x1f;
+		return generic_mii_ioctl(&mii, if_mii(ifr), cmd, NULL);
+	}
+	return -ENOTSUPP;
 }
 
 static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu)
@@ -2779,6 +3125,43 @@
 	phy_start_aneg(phy);
 }
 
+static int mii_bus_read(struct net_device *dev, int mii_id, int regnum)
+{
+	struct mv643xx_eth_private *mp = netdev_priv(dev);
+	if (!mp->mii_bus)
+		return 0xffff;
+	return mp->mii_bus->read(mp->mii_bus, mii_id, regnum);
+}
+
+static void mii_bus_write(struct net_device *dev, int mii_id, int regnum,
+			 int value)
+{
+	struct mv643xx_eth_private *mp = netdev_priv(dev);
+	if (!mp->mii_bus)
+		return ;
+	mp->mii_bus->write(mp->mii_bus, mii_id, regnum, value);
+}
+
+static int mii_bus_init(struct net_device *dev,
+			struct platform_device *pdev,
+			struct mv643xx_eth_platform_data *pd)
+{
+	struct mv643xx_eth_private *mp = netdev_priv(dev);
+
+	mp->mii_bus = mdio_find_bus("orion-mdio-mii");
+	if (!mp->mii_bus) {
+		dev_err(&pdev->dev, "unable to find mdio bus orion-mdio-mii");
+		return -ENODEV;
+	}
+
+	if (pd->mii_config && pd->mii_config(dev, 1,
+					     mii_bus_read, mii_bus_write)) {
+		dev_err(&pdev->dev, "unable to configure mdio bus\n");
+		return -EIO;
+	}
+	return 0;
+}
+
 static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex)
 {
 	u32 pscr;
@@ -2896,6 +3279,8 @@
 			err = PTR_ERR(mp->phy);
 		else
 			phy_init(mp, pd->speed, pd->duplex);
+	} else {
+		mii_bus_init(dev, pdev, pd);
 	}
 	if (err == -ENODEV) {
 		err = -EPROBE_DEFER;
diff -ruw linux-3.11.10/drivers/net/ethernet/marvell/sky2.c linux-3.11.10-fbx/drivers/net/ethernet/marvell/sky2.c
--- linux-3.11.10/drivers/net/ethernet/marvell/sky2.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/ethernet/marvell/sky2.c	2015-09-29 11:15:06.777739570 +0200
@@ -3049,6 +3049,16 @@
 	int work_done = 0;
 	u16 idx;
 
+	if (status == 0xffffffff) {
+		/*
+		 * this may happen if hardware is removed without
+		 * being properly disabled.
+		 */
+		printk("sky2: serious hardware error.\n");
+		napi_complete(napi);
+		return 0;
+	}
+
 	if (unlikely(status & Y2_IS_ERROR))
 		sky2_err_intr(hw, status);
 
diff -ruw linux-3.11.10/drivers/net/Kconfig linux-3.11.10-fbx/drivers/net/Kconfig
--- linux-3.11.10/drivers/net/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/Kconfig	2015-09-29 11:15:06.605738265 +0200
@@ -362,4 +362,18 @@
 
 source "drivers/net/hyperv/Kconfig"
 
+config TANGO2_ENET
+	tristate "SMP863x Builtin Ethernet support"
+	depends on NET_ETHERNET && TANGO2
+	select MII
+	select CRC32
+	help
+	 This option adds support for the SMP863x integrated Ethernet
+	 controller.  This driver uses NAPI and generic Linux MII
+	 support.
+
+config TANGO2_PCINET_H
+	tristate "SMP863x network over PCI support (smp863x side)"
+	depends on NET_ETHERNET && TANGO2
+
 endif # NETDEVICES
diff -ruw linux-3.11.10/drivers/net/phy/mdio_bus.c linux-3.11.10-fbx/drivers/net/phy/mdio_bus.c
--- linux-3.11.10/drivers/net/phy/mdio_bus.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/phy/mdio_bus.c	2015-09-29 11:15:06.885740392 +0200
@@ -471,3 +471,16 @@
 	class_unregister(&mdio_bus_class);
 	bus_unregister(&mdio_bus_type);
 }
+
+static int match_mdio_bus(struct device *dev, const void *ptr)
+{
+	return !strcmp(dev->kobj.name, ptr);
+}
+
+struct mii_bus *mdio_find_bus(const char *name)
+{
+	struct device *d = class_find_device(&mdio_bus_class, NULL,
+					     name, match_mdio_bus);
+
+	return d ? to_mii_bus(d) : NULL;
+}
diff -ruw linux-3.11.10/drivers/net/ppp/ppp_generic.c linux-3.11.10-fbx/drivers/net/ppp/ppp_generic.c
--- linux-3.11.10/drivers/net/ppp/ppp_generic.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/ppp/ppp_generic.c	2015-09-29 11:15:06.889740421 +0200
@@ -175,6 +175,7 @@
 	struct ppp	*ppp;		/* ppp unit we're connected to */
 	struct net	*chan_net;	/* the net channel belongs to */
 	struct list_head clist;		/* link in list of channels per unit */
+	int		stopped;	/* channel is stopped */
 	rwlock_t	upl;		/* protects `ppp' */
 #ifdef CONFIG_PPP_MULTILINK
 	u8		avail;		/* flag used in multilink stuff */
@@ -1081,7 +1082,7 @@
 	dev->tx_queue_len = 3;
 	dev->type = ARPHRD_PPP;
 	dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
-	dev->features |= NETIF_F_NETNS_LOCAL;
+//	dev->features |= NETIF_F_NETNS_LOCAL;
 	dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
 }
 
@@ -1106,11 +1107,29 @@
 			ppp_send_frame(ppp, skb);
 		/* If there's no work left to do, tell the core net
 		   code that we can accept some more. */
-		if (!ppp->xmit_pending && !skb_peek(&ppp->file.xq))
+		if (!ppp->xmit_pending && !skb_peek(&ppp->file.xq)) {
+			/* only  enable  net  queue  if at  least  one
+			 * channel is not stopped */
+			struct list_head *list;
+			struct channel *pch;
+			bool need_wake;
+
+			list = &ppp->channels;
+			need_wake = false;
+			while ((list = list->next) != &ppp->channels) {
+				pch = list_entry(list, struct channel, clist);
+				if (!pch->stopped) {
+					need_wake = true;
+					break;
+				}
+			}
+
+			if (need_wake)
 			netif_wake_queue(ppp->dev);
 		else
 			netif_stop_queue(ppp->dev);
 	}
+	}
 	ppp_xmit_unlock(ppp);
 }
 
@@ -2337,10 +2356,24 @@
 
 	if (!pch)
 		return;
+	pch->stopped = 0;
 	ppp_channel_push(pch);
 }
 
 /*
+ * Callback from a channel when it want to prevent further transmit on it
+ */
+void
+ppp_output_stop(struct ppp_channel *chan)
+{
+	struct channel *pch = chan->ppp;
+
+	if (pch == 0)
+		return;
+	pch->stopped = 1;
+}
+
+/*
  * Compression control.
  */
 
@@ -2996,6 +3029,7 @@
 EXPORT_SYMBOL(ppp_input);
 EXPORT_SYMBOL(ppp_input_error);
 EXPORT_SYMBOL(ppp_output_wakeup);
+EXPORT_SYMBOL(ppp_output_stop);
 EXPORT_SYMBOL(ppp_register_compressor);
 EXPORT_SYMBOL(ppp_unregister_compressor);
 MODULE_LICENSE("GPL");
diff -ruw linux-3.11.10/drivers/net/ppp/pppoe.c linux-3.11.10-fbx/drivers/net/ppp/pppoe.c
--- linux-3.11.10/drivers/net/ppp/pppoe.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/ppp/pppoe.c	2015-09-29 11:15:06.889740421 +0200
@@ -979,8 +979,6 @@
 	if (error < 0)
 		goto end;
 
-	m->msg_namelen = 0;
-
 	if (skb) {
 		total_len = min_t(size_t, total_len, skb->len);
 		error = skb_copy_datagram_iovec(skb, 0, m->msg_iov, total_len);
diff -ruw linux-3.11.10/drivers/net/tun.c linux-3.11.10-fbx/drivers/net/tun.c
--- linux-3.11.10/drivers/net/tun.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/tun.c	2015-09-29 11:15:06.893740458 +0200
@@ -1067,6 +1067,7 @@
 	struct sk_buff *skb;
 	size_t len = total_len, align = NET_SKB_PAD, linear;
 	struct virtio_net_hdr gso = { 0 };
+	int good_linear;
 	int offset = 0;
 	int copylen;
 	bool zerocopy = false;
@@ -1107,12 +1108,16 @@
 			return -EINVAL;
 	}
 
+	good_linear = SKB_MAX_HEAD(align);
+
 	if (msg_control) {
 		/* There are 256 bytes to be copied in skb, so there is
 		 * enough room for skb expand head in case it is used.
 		 * The rest of the buffer is mapped from userspace.
 		 */
 		copylen = gso.hdr_len ? gso.hdr_len : GOODCOPY_LEN;
+		if (copylen > good_linear)
+			copylen = good_linear;
 		linear = copylen;
 		if (iov_pages(iv, offset + copylen, count) <= MAX_SKB_FRAGS)
 			zerocopy = true;
@@ -1120,6 +1125,9 @@
 
 	if (!zerocopy) {
 		copylen = len;
+		if (gso.hdr_len > good_linear)
+			linear = good_linear;
+		else
 		linear = gso.hdr_len;
 	}
 
@@ -1402,6 +1410,8 @@
 	ret = tun_do_read(tun, tfile, iocb, iv, len,
 			  file->f_flags & O_NONBLOCK);
 	ret = min_t(ssize_t, ret, len);
+	if (ret > 0)
+		iocb->ki_pos = ret;
 out:
 	tun_put(tun);
 	return ret;
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/bmi.c linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/bmi.c
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/bmi.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/bmi.c	2015-09-29 11:15:06.917740641 +0200
@@ -20,14 +20,23 @@
 #include "debug.h"
 #include "htc.h"
 
+void ath10k_bmi_start(struct ath10k *ar)
+{
+	ath10k_dbg(ATH10K_DBG_BMI, "bmi start\n");
+
+	ar->bmi.done_sent = false;
+}
+
 int ath10k_bmi_done(struct ath10k *ar)
 {
 	struct bmi_cmd cmd;
 	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
 	int ret;
 
+	ath10k_dbg(ATH10K_DBG_BMI, "bmi done\n");
+
 	if (ar->bmi.done_sent) {
-		ath10k_dbg(ATH10K_DBG_CORE, "%s skipped\n", __func__);
+		ath10k_dbg(ATH10K_DBG_BMI, "bmi skipped\n");
 		return 0;
 	}
 
@@ -40,7 +49,6 @@
 		return ret;
 	}
 
-	ath10k_dbg(ATH10K_DBG_CORE, "BMI done\n");
 	return 0;
 }
 
@@ -53,6 +61,8 @@
 	u32 resplen = sizeof(resp.get_target_info);
 	int ret;
 
+	ath10k_dbg(ATH10K_DBG_BMI, "bmi get target info\n");
+
 	if (ar->bmi.done_sent) {
 		ath10k_warn("BMI Get Target Info Command disallowed\n");
 		return -EBUSY;
@@ -74,6 +84,7 @@
 
 	target_info->version = __le32_to_cpu(resp.get_target_info.version);
 	target_info->type    = __le32_to_cpu(resp.get_target_info.type);
+
 	return 0;
 }
 
@@ -86,15 +97,14 @@
 	u32 rxlen;
 	int ret;
 
+	ath10k_dbg(ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n",
+		   address, length);
+
 	if (ar->bmi.done_sent) {
 		ath10k_warn("command disallowed\n");
 		return -EBUSY;
 	}
 
-	ath10k_dbg(ATH10K_DBG_CORE,
-		   "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
-		   __func__, ar, address, length);
-
 	while (length) {
 		rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
 
@@ -105,7 +115,8 @@
 		ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
 						  &resp, &rxlen);
 		if (ret) {
-			ath10k_warn("unable to read from the device\n");
+			ath10k_warn("unable to read from the device (%d)\n",
+				    ret);
 			return ret;
 		}
 
@@ -126,15 +137,14 @@
 	u32 txlen;
 	int ret;
 
+	ath10k_dbg(ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n",
+		   address, length);
+
 	if (ar->bmi.done_sent) {
 		ath10k_warn("command disallowed\n");
 		return -EBUSY;
 	}
 
-	ath10k_dbg(ATH10K_DBG_CORE,
-		   "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
-		   __func__, ar, address, length);
-
 	while (length) {
 		txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
 
@@ -149,7 +159,8 @@
 		ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
 						  NULL, NULL);
 		if (ret) {
-			ath10k_warn("unable to write to the device\n");
+			ath10k_warn("unable to write to the device (%d)\n",
+				    ret);
 			return ret;
 		}
 
@@ -172,15 +183,14 @@
 	u32 resplen = sizeof(resp.execute);
 	int ret;
 
+	ath10k_dbg(ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",
+		   address, *param);
+
 	if (ar->bmi.done_sent) {
 		ath10k_warn("command disallowed\n");
 		return -EBUSY;
 	}
 
-	ath10k_dbg(ATH10K_DBG_CORE,
-		   "%s: (device: 0x%p, address: 0x%x, param: %d)\n",
-		   __func__, ar, address, *param);
-
 	cmd.id            = __cpu_to_le32(BMI_EXECUTE);
 	cmd.execute.addr  = __cpu_to_le32(address);
 	cmd.execute.param = __cpu_to_le32(*param);
@@ -208,6 +218,9 @@
 	u32 txlen;
 	int ret;
 
+	ath10k_dbg(ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n",
+		   buffer, length);
+
 	if (ar->bmi.done_sent) {
 		ath10k_warn("command disallowed\n");
 		return -EBUSY;
@@ -242,6 +255,9 @@
 	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
 	int ret;
 
+	ath10k_dbg(ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n",
+		   address);
+
 	if (ar->bmi.done_sent) {
 		ath10k_warn("command disallowed\n");
 		return -EBUSY;
@@ -267,6 +283,10 @@
 	u32 trailer_len = length - head_len;
 	int ret;
 
+	ath10k_dbg(ATH10K_DBG_BMI,
+		   "bmi fast download address 0x%x buffer 0x%p length %d\n",
+		   address, buffer, length);
+
 	ret = ath10k_bmi_lz_stream_start(ar, address);
 	if (ret)
 		return ret;
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/bmi.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/bmi.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/bmi.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/bmi.h	2015-09-29 11:15:06.917740641 +0200
@@ -184,6 +184,7 @@
 #define BMI_CE_NUM_TO_TARG 0
 #define BMI_CE_NUM_TO_HOST 1
 
+void ath10k_bmi_start(struct ath10k *ar);
 int ath10k_bmi_done(struct ath10k *ar);
 int ath10k_bmi_get_target_info(struct ath10k *ar,
 			       struct bmi_target_info *target_info);
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/ce.c linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/ce.c
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/ce.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/ce.c	2015-09-29 11:15:06.917740641 +0200
@@ -76,36 +76,7 @@
 						      u32 ce_ctrl_addr,
 						      unsigned int n)
 {
-	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	void __iomem *indicator_addr;
-
-	if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
-		ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
-		return;
-	}
-
-	/* workaround for QCA988x_1.0 HW CE */
-	indicator_addr = ar_pci->mem + ce_ctrl_addr + DST_WATERMARK_ADDRESS;
-
-	if (ce_ctrl_addr == ath10k_ce_base_address(CDC_WAR_DATA_CE)) {
-		iowrite32((CDC_WAR_MAGIC_STR | n), indicator_addr);
-	} else {
-		unsigned long irq_flags;
-		local_irq_save(irq_flags);
-		iowrite32(1, indicator_addr);
-
-		/*
-		 * PCIE write waits for ACK in IPQ8K, there is no
-		 * need to read back value.
-		 */
-		(void)ioread32(indicator_addr);
-		(void)ioread32(indicator_addr); /* conservative */
-
 		ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
-
-		iowrite32(0, indicator_addr);
-		local_irq_restore(irq_flags);
-	}
 }
 
 static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar,
@@ -272,6 +243,16 @@
 			   misc_ie_addr | CE_ERROR_MASK);
 }
 
+static inline void ath10k_ce_error_intr_disable(struct ath10k *ar,
+						u32 ce_ctrl_addr)
+{
+	u32 misc_ie_addr = ath10k_pci_read32(ar,
+					     ce_ctrl_addr + MISC_IE_ADDRESS);
+
+	ath10k_pci_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS,
+			   misc_ie_addr & ~CE_ERROR_MASK);
+}
+
 static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar,
 						     u32 ce_ctrl_addr,
 						     unsigned int mask)
@@ -285,7 +266,7 @@
  * ath10k_ce_sendlist_send.
  * The caller takes responsibility for any needed locking.
  */
-static int ath10k_ce_send_nolock(struct ce_state *ce_state,
+int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
 				 void *per_transfer_context,
 				 u32 buffer,
 				 unsigned int nbytes,
@@ -293,7 +274,7 @@
 				 unsigned int flags)
 {
 	struct ath10k *ar = ce_state->ar;
-	struct ce_ring_state *src_ring = ce_state->src_ring;
+	struct ath10k_ce_ring *src_ring = ce_state->src_ring;
 	struct ce_desc *desc, *sdesc;
 	unsigned int nentries_mask = src_ring->nentries_mask;
 	unsigned int sw_index = src_ring->sw_index;
@@ -306,11 +287,13 @@
 		ath10k_warn("%s: send more we can (nbytes: %d, max: %d)\n",
 			    __func__, nbytes, ce_state->src_sz_max);
 
-	ath10k_pci_wake(ar);
+	ret = ath10k_pci_wake(ar);
+	if (ret)
+		return ret;
 
 	if (unlikely(CE_RING_DELTA(nentries_mask,
 				   write_index, sw_index - 1) <= 0)) {
-		ret = -EIO;
+		ret = -ENOSR;
 		goto exit;
 	}
 
@@ -346,7 +329,7 @@
 	return ret;
 }
 
-int ath10k_ce_send(struct ce_state *ce_state,
+int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
 		   void *per_transfer_context,
 		   u32 buffer,
 		   unsigned int nbytes,
@@ -365,77 +348,26 @@
 	return ret;
 }
 
-void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, u32 buffer,
-				unsigned int nbytes, u32 flags)
-{
-	unsigned int num_items = sendlist->num_items;
-	struct ce_sendlist_item *item;
-
-	item = &sendlist->item[num_items];
-	item->data = buffer;
-	item->u.nbytes = nbytes;
-	item->flags = flags;
-	sendlist->num_items++;
-}
-
-int ath10k_ce_sendlist_send(struct ce_state *ce_state,
-			    void *per_transfer_context,
-			    struct ce_sendlist *sendlist,
-			    unsigned int transfer_id)
+int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe)
 {
-	struct ce_ring_state *src_ring = ce_state->src_ring;
-	struct ce_sendlist_item *item;
-	struct ath10k *ar = ce_state->ar;
+	struct ath10k *ar = pipe->ar;
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	unsigned int nentries_mask = src_ring->nentries_mask;
-	unsigned int num_items = sendlist->num_items;
-	unsigned int sw_index;
-	unsigned int write_index;
-	int i, delta, ret = -ENOMEM;
+	int delta;
 
 	spin_lock_bh(&ar_pci->ce_lock);
-
-	sw_index = src_ring->sw_index;
-	write_index = src_ring->write_index;
-
-	delta = CE_RING_DELTA(nentries_mask, write_index, sw_index - 1);
-
-	if (delta >= num_items) {
-		/*
-		 * Handle all but the last item uniformly.
-		 */
-		for (i = 0; i < num_items - 1; i++) {
-			item = &sendlist->item[i];
-			ret = ath10k_ce_send_nolock(ce_state,
-						    CE_SENDLIST_ITEM_CTXT,
-						    (u32) item->data,
-						    item->u.nbytes, transfer_id,
-						    item->flags |
-						    CE_SEND_FLAG_GATHER);
-			if (ret)
-				ath10k_warn("CE send failed for item: %d\n", i);
-		}
-		/*
-		 * Provide valid context pointer for final item.
-		 */
-		item = &sendlist->item[i];
-		ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
-					    (u32) item->data, item->u.nbytes,
-					    transfer_id, item->flags);
-		if (ret)
-			ath10k_warn("CE send failed for last item: %d\n", i);
-	}
-
+	delta = CE_RING_DELTA(pipe->src_ring->nentries_mask,
+			      pipe->src_ring->write_index,
+			      pipe->src_ring->sw_index - 1);
 	spin_unlock_bh(&ar_pci->ce_lock);
 
-	return ret;
+	return delta;
 }
 
-int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
+int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state,
 			       void *per_recv_context,
 			       u32 buffer)
 {
-	struct ce_ring_state *dest_ring = ce_state->dest_ring;
+	struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
 	u32 ctrl_addr = ce_state->ctrl_addr;
 	struct ath10k *ar = ce_state->ar;
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -448,7 +380,9 @@
 	write_index = dest_ring->write_index;
 	sw_index = dest_ring->sw_index;
 
-	ath10k_pci_wake(ar);
+	ret = ath10k_pci_wake(ar);
+	if (ret)
+		goto out;
 
 	if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) > 0) {
 		struct ce_desc *base = dest_ring->base_addr_owner_space;
@@ -470,6 +404,8 @@
 		ret = -EIO;
 	}
 	ath10k_pci_sleep(ar);
+
+out:
 	spin_unlock_bh(&ar_pci->ce_lock);
 
 	return ret;
@@ -479,14 +415,14 @@
  * Guts of ath10k_ce_completed_recv_next.
  * The caller takes responsibility for any necessary locking.
  */
-static int ath10k_ce_completed_recv_next_nolock(struct ce_state *ce_state,
+static int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
 						void **per_transfer_contextp,
 						u32 *bufferp,
 						unsigned int *nbytesp,
 						unsigned int *transfer_idp,
 						unsigned int *flagsp)
 {
-	struct ce_ring_state *dest_ring = ce_state->dest_ring;
+	struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
 	unsigned int nentries_mask = dest_ring->nentries_mask;
 	unsigned int sw_index = dest_ring->sw_index;
 
@@ -535,7 +471,7 @@
 	return 0;
 }
 
-int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
+int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state,
 				  void **per_transfer_contextp,
 				  u32 *bufferp,
 				  unsigned int *nbytesp,
@@ -556,11 +492,11 @@
 	return ret;
 }
 
-int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
+int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
 			       void **per_transfer_contextp,
 			       u32 *bufferp)
 {
-	struct ce_ring_state *dest_ring;
+	struct ath10k_ce_ring *dest_ring;
 	unsigned int nentries_mask;
 	unsigned int sw_index;
 	unsigned int write_index;
@@ -612,19 +548,20 @@
  * Guts of ath10k_ce_completed_send_next.
  * The caller takes responsibility for any necessary locking.
  */
-static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state,
+static int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
 						void **per_transfer_contextp,
 						u32 *bufferp,
 						unsigned int *nbytesp,
 						unsigned int *transfer_idp)
 {
-	struct ce_ring_state *src_ring = ce_state->src_ring;
+	struct ath10k_ce_ring *src_ring = ce_state->src_ring;
 	u32 ctrl_addr = ce_state->ctrl_addr;
 	struct ath10k *ar = ce_state->ar;
 	unsigned int nentries_mask = src_ring->nentries_mask;
 	unsigned int sw_index = src_ring->sw_index;
+	struct ce_desc *sdesc, *sbase;
 	unsigned int read_index;
-	int ret = -EIO;
+	int ret;
 
 	if (src_ring->hw_index == sw_index) {
 		/*
@@ -634,16 +571,25 @@
 		 * the SW has really caught up to the HW, or if the cached
 		 * value of the HW index has become stale.
 		 */
-		ath10k_pci_wake(ar);
+
+		ret = ath10k_pci_wake(ar);
+		if (ret)
+			return ret;
+
 		src_ring->hw_index =
 			ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+		src_ring->hw_index &= nentries_mask;
+
 		ath10k_pci_sleep(ar);
 	}
+
 	read_index = src_ring->hw_index;
 
-	if ((read_index != sw_index) && (read_index != 0xffffffff)) {
-		struct ce_desc *sbase = src_ring->shadow_base;
-		struct ce_desc *sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index);
+	if ((read_index == sw_index) || (read_index == 0xffffffff))
+		return -EIO;
+
+	sbase = src_ring->shadow_base;
+	sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index);
 
 		/* Return data from completed source descriptor */
 		*bufferp = __le32_to_cpu(sdesc->addr);
@@ -661,20 +607,18 @@
 		/* Update sw_index */
 		sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
 		src_ring->sw_index = sw_index;
-		ret = 0;
-	}
 
-	return ret;
+	return 0;
 }
 
 /* NB: Modeled after ath10k_ce_completed_send_next */
-int ath10k_ce_cancel_send_next(struct ce_state *ce_state,
+int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
 			       void **per_transfer_contextp,
 			       u32 *bufferp,
 			       unsigned int *nbytesp,
 			       unsigned int *transfer_idp)
 {
-	struct ce_ring_state *src_ring;
+	struct ath10k_ce_ring *src_ring;
 	unsigned int nentries_mask;
 	unsigned int sw_index;
 	unsigned int write_index;
@@ -726,7 +670,7 @@
 	return ret;
 }
 
-int ath10k_ce_completed_send_next(struct ce_state *ce_state,
+int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
 				  void **per_transfer_contextp,
 				  u32 *bufferp,
 				  unsigned int *nbytesp,
@@ -755,53 +699,29 @@
 void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id];
+	struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
 	u32 ctrl_addr = ce_state->ctrl_addr;
-	void *transfer_context;
-	u32 buf;
-	unsigned int nbytes;
-	unsigned int id;
-	unsigned int flags;
+	int ret;
+
+	ret = ath10k_pci_wake(ar);
+	if (ret)
+		return;
 
-	ath10k_pci_wake(ar);
 	spin_lock_bh(&ar_pci->ce_lock);
 
 	/* Clear the copy-complete interrupts that will be handled here. */
 	ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
 					  HOST_IS_COPY_COMPLETE_MASK);
 
-	if (ce_state->recv_cb) {
-		/*
-		 * Pop completed recv buffers and call the registered
-		 * recv callback for each
-		 */
-		while (ath10k_ce_completed_recv_next_nolock(ce_state,
-							    &transfer_context,
-							    &buf, &nbytes,
-							    &id, &flags) == 0) {
 			spin_unlock_bh(&ar_pci->ce_lock);
-			ce_state->recv_cb(ce_state, transfer_context, buf,
-					  nbytes, id, flags);
-			spin_lock_bh(&ar_pci->ce_lock);
-		}
-	}
 
-	if (ce_state->send_cb) {
-		/*
-		 * Pop completed send buffers and call the registered
-		 * send callback for each
-		 */
-		while (ath10k_ce_completed_send_next_nolock(ce_state,
-							    &transfer_context,
-							    &buf,
-							    &nbytes,
-							    &id) == 0) {
-			spin_unlock_bh(&ar_pci->ce_lock);
-			ce_state->send_cb(ce_state, transfer_context,
-					  buf, nbytes, id);
+	if (ce_state->recv_cb)
+		ce_state->recv_cb(ce_state);
+
+	if (ce_state->send_cb)
+		ce_state->send_cb(ce_state);
+
 			spin_lock_bh(&ar_pci->ce_lock);
-		}
-	}
 
 	/*
 	 * Misc CE interrupts are not being handled, but still need
@@ -821,14 +741,16 @@
 
 void ath10k_ce_per_engine_service_any(struct ath10k *ar)
 {
-	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	int ce_id;
+	int ce_id, ret;
 	u32 intr_summary;
 
-	ath10k_pci_wake(ar);
+	ret = ath10k_pci_wake(ar);
+	if (ret)
+		return;
+
 	intr_summary = CE_INTERRUPT_SUMMARY(ar);
 
-	for (ce_id = 0; intr_summary && (ce_id < ar_pci->ce_count); ce_id++) {
+	for (ce_id = 0; intr_summary && (ce_id < CE_COUNT); ce_id++) {
 		if (intr_summary & (1 << ce_id))
 			intr_summary &= ~(1 << ce_id);
 		else
@@ -848,13 +770,16 @@
  *
  * Called with ce_lock held.
  */
-static void ath10k_ce_per_engine_handler_adjust(struct ce_state *ce_state,
+static void ath10k_ce_per_engine_handler_adjust(struct ath10k_ce_pipe *ce_state,
 						int disable_copy_compl_intr)
 {
 	u32 ctrl_addr = ce_state->ctrl_addr;
 	struct ath10k *ar = ce_state->ar;
+	int ret;
 
-	ath10k_pci_wake(ar);
+	ret = ath10k_pci_wake(ar);
+	if (ret)
+		return;
 
 	if ((!disable_copy_compl_intr) &&
 	    (ce_state->send_cb || ce_state->recv_cb))
@@ -867,27 +792,29 @@
 	ath10k_pci_sleep(ar);
 }
 
-void ath10k_ce_disable_interrupts(struct ath10k *ar)
+int ath10k_ce_disable_interrupts(struct ath10k *ar)
 {
-	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	int ce_id;
+	int ce_id, ret;
 
-	ath10k_pci_wake(ar);
-	for (ce_id = 0; ce_id < ar_pci->ce_count; ce_id++) {
-		struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id];
-		u32 ctrl_addr = ce_state->ctrl_addr;
+	ret = ath10k_pci_wake(ar);
+	if (ret)
+		return ret;
+
+	for (ce_id = 0; ce_id < CE_COUNT; ce_id++) {
+		u32 ctrl_addr = ath10k_ce_base_address(ce_id);
 
 		ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
+		ath10k_ce_error_intr_disable(ar, ctrl_addr);
+		ath10k_ce_watermark_intr_disable(ar, ctrl_addr);
 	}
+
 	ath10k_pci_sleep(ar);
+
+	return 0;
 }
 
-void ath10k_ce_send_cb_register(struct ce_state *ce_state,
-				void (*send_cb) (struct ce_state *ce_state,
-						 void *transfer_context,
-						 u32 buffer,
-						 unsigned int nbytes,
-						 unsigned int transfer_id),
+void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
+				void (*send_cb)(struct ath10k_ce_pipe *),
 				int disable_interrupts)
 {
 	struct ath10k *ar = ce_state->ar;
@@ -899,13 +826,8 @@
 	spin_unlock_bh(&ar_pci->ce_lock);
 }
 
-void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
-				void (*recv_cb) (struct ce_state *ce_state,
-						 void *transfer_context,
-						 u32 buffer,
-						 unsigned int nbytes,
-						 unsigned int transfer_id,
-						 unsigned int flags))
+void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state,
+				void (*recv_cb)(struct ath10k_ce_pipe *))
 {
 	struct ath10k *ar = ce_state->ar;
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -918,11 +840,11 @@
 
 static int ath10k_ce_init_src_ring(struct ath10k *ar,
 				   unsigned int ce_id,
-				   struct ce_state *ce_state,
+				   struct ath10k_ce_pipe *ce_state,
 				   const struct ce_attr *attr)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct ce_ring_state *src_ring;
+	struct ath10k_ce_ring *src_ring;
 	unsigned int nentries = attr->src_nentries;
 	unsigned int ce_nbytes;
 	u32 ctrl_addr = ath10k_ce_base_address(ce_id);
@@ -936,25 +858,25 @@
 		return 0;
 	}
 
-	ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *));
+	ce_nbytes = sizeof(struct ath10k_ce_ring) + (nentries * sizeof(void *));
 	ptr = kzalloc(ce_nbytes, GFP_KERNEL);
 	if (ptr == NULL)
 		return -ENOMEM;
 
-	ce_state->src_ring = (struct ce_ring_state *)ptr;
+	ce_state->src_ring = (struct ath10k_ce_ring *)ptr;
 	src_ring = ce_state->src_ring;
 
-	ptr += sizeof(struct ce_ring_state);
+	ptr += sizeof(struct ath10k_ce_ring);
 	src_ring->nentries = nentries;
 	src_ring->nentries_mask = nentries - 1;
 
-	ath10k_pci_wake(ar);
 	src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+	src_ring->sw_index &= src_ring->nentries_mask;
 	src_ring->hw_index = src_ring->sw_index;
 
 	src_ring->write_index =
 		ath10k_ce_src_ring_write_index_get(ar, ctrl_addr);
-	ath10k_pci_sleep(ar);
+	src_ring->write_index &= src_ring->nentries_mask;
 
 	src_ring->per_transfer_context = (void **)ptr;
 
@@ -967,6 +889,12 @@
 				     (nentries * sizeof(struct ce_desc) +
 				      CE_DESC_RING_ALIGN),
 				     &base_addr);
+	if (!src_ring->base_addr_owner_space_unaligned) {
+		kfree(ce_state->src_ring);
+		ce_state->src_ring = NULL;
+		return -ENOMEM;
+	}
+
 	src_ring->base_addr_ce_space_unaligned = base_addr;
 
 	src_ring->base_addr_owner_space = PTR_ALIGN(
@@ -983,12 +911,21 @@
 	src_ring->shadow_base_unaligned =
 		kmalloc((nentries * sizeof(struct ce_desc) +
 			 CE_DESC_RING_ALIGN), GFP_KERNEL);
+	if (!src_ring->shadow_base_unaligned) {
+		pci_free_consistent(ar_pci->pdev,
+				    (nentries * sizeof(struct ce_desc) +
+				     CE_DESC_RING_ALIGN),
+				    src_ring->base_addr_owner_space,
+				    src_ring->base_addr_ce_space);
+		kfree(ce_state->src_ring);
+		ce_state->src_ring = NULL;
+		return -ENOMEM;
+	}
 
 	src_ring->shadow_base = PTR_ALIGN(
 			src_ring->shadow_base_unaligned,
 			CE_DESC_RING_ALIGN);
 
-	ath10k_pci_wake(ar);
 	ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr,
 					 src_ring->base_addr_ce_space);
 	ath10k_ce_src_ring_size_set(ar, ctrl_addr, nentries);
@@ -996,18 +933,21 @@
 	ath10k_ce_src_ring_byte_swap_set(ar, ctrl_addr, 0);
 	ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0);
 	ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries);
-	ath10k_pci_sleep(ar);
+
+	ath10k_dbg(ATH10K_DBG_BOOT,
+		   "boot ce src ring id %d entries %d base_addr %p\n",
+		   ce_id, nentries, src_ring->base_addr_owner_space);
 
 	return 0;
 }
 
 static int ath10k_ce_init_dest_ring(struct ath10k *ar,
 				    unsigned int ce_id,
-				    struct ce_state *ce_state,
+				    struct ath10k_ce_pipe *ce_state,
 				    const struct ce_attr *attr)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct ce_ring_state *dest_ring;
+	struct ath10k_ce_ring *dest_ring;
 	unsigned int nentries = attr->dest_nentries;
 	unsigned int ce_nbytes;
 	u32 ctrl_addr = ath10k_ce_base_address(ce_id);
@@ -1021,23 +961,23 @@
 		return 0;
 	}
 
-	ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *));
+	ce_nbytes = sizeof(struct ath10k_ce_ring) + (nentries * sizeof(void *));
 	ptr = kzalloc(ce_nbytes, GFP_KERNEL);
 	if (ptr == NULL)
 		return -ENOMEM;
 
-	ce_state->dest_ring = (struct ce_ring_state *)ptr;
+	ce_state->dest_ring = (struct ath10k_ce_ring *)ptr;
 	dest_ring = ce_state->dest_ring;
 
-	ptr += sizeof(struct ce_ring_state);
+	ptr += sizeof(struct ath10k_ce_ring);
 	dest_ring->nentries = nentries;
 	dest_ring->nentries_mask = nentries - 1;
 
-	ath10k_pci_wake(ar);
 	dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
+	dest_ring->sw_index &= dest_ring->nentries_mask;
 	dest_ring->write_index =
 		ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr);
-	ath10k_pci_sleep(ar);
+	dest_ring->write_index &= dest_ring->nentries_mask;
 
 	dest_ring->per_transfer_context = (void **)ptr;
 
@@ -1050,6 +990,12 @@
 				     (nentries * sizeof(struct ce_desc) +
 				      CE_DESC_RING_ALIGN),
 				     &base_addr);
+	if (!dest_ring->base_addr_owner_space_unaligned) {
+		kfree(ce_state->dest_ring);
+		ce_state->dest_ring = NULL;
+		return -ENOMEM;
+	}
+
 	dest_ring->base_addr_ce_space_unaligned = base_addr;
 
 	/*
@@ -1066,44 +1012,35 @@
 			dest_ring->base_addr_ce_space_unaligned,
 			CE_DESC_RING_ALIGN);
 
-	ath10k_pci_wake(ar);
 	ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr,
 					  dest_ring->base_addr_ce_space);
 	ath10k_ce_dest_ring_size_set(ar, ctrl_addr, nentries);
 	ath10k_ce_dest_ring_byte_swap_set(ar, ctrl_addr, 0);
 	ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0);
 	ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries);
-	ath10k_pci_sleep(ar);
+
+	ath10k_dbg(ATH10K_DBG_BOOT,
+		   "boot ce dest ring id %d entries %d base_addr %p\n",
+		   ce_id, nentries, dest_ring->base_addr_owner_space);
 
 	return 0;
 }
 
-static struct ce_state *ath10k_ce_init_state(struct ath10k *ar,
+static struct ath10k_ce_pipe *ath10k_ce_init_state(struct ath10k *ar,
 					     unsigned int ce_id,
 					     const struct ce_attr *attr)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct ce_state *ce_state = NULL;
+	struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
 	u32 ctrl_addr = ath10k_ce_base_address(ce_id);
 
 	spin_lock_bh(&ar_pci->ce_lock);
 
-	if (!ar_pci->ce_id_to_state[ce_id]) {
-		ce_state = kzalloc(sizeof(*ce_state), GFP_ATOMIC);
-		if (ce_state == NULL) {
-			spin_unlock_bh(&ar_pci->ce_lock);
-			return NULL;
-		}
-
-		ar_pci->ce_id_to_state[ce_id] = ce_state;
 		ce_state->ar = ar;
 		ce_state->id = ce_id;
 		ce_state->ctrl_addr = ctrl_addr;
-		ce_state->state = CE_RUNNING;
-		/* Save attribute flags */
 		ce_state->attr_flags = attr->flags;
 		ce_state->src_sz_max = attr->src_sz_max;
-	}
 
 	spin_unlock_bh(&ar_pci->ce_lock);
 
@@ -1117,54 +1054,66 @@
  * initialization. It may be that only one side or the other is
  * initialized by software/firmware.
  */
-struct ce_state *ath10k_ce_init(struct ath10k *ar,
+struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar,
 				unsigned int ce_id,
 				const struct ce_attr *attr)
 {
-	struct ce_state *ce_state;
-	u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+	struct ath10k_ce_pipe *ce_state;
+	int ret;
+
+	/*
+	 * Make sure there's enough CE ringbuffer entries for HTT TX to avoid
+	 * additional TX locking checks.
+	 *
+	 * For the lack of a better place do the check here.
+	 */
+	BUILD_BUG_ON(2*TARGET_NUM_MSDU_DESC >
+		     (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
+	BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC >
+		     (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
+
+	ret = ath10k_pci_wake(ar);
+	if (ret)
+		return NULL;
 
 	ce_state = ath10k_ce_init_state(ar, ce_id, attr);
 	if (!ce_state) {
 		ath10k_err("Failed to initialize CE state for ID: %d\n", ce_id);
-		return NULL;
+		goto out;
 	}
 
 	if (attr->src_nentries) {
-		if (ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr)) {
-			ath10k_err("Failed to initialize CE src ring for ID: %d\n",
-				   ce_id);
+		ret = ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr);
+		if (ret) {
+			ath10k_err("Failed to initialize CE src ring for ID: %d (%d)\n",
+				   ce_id, ret);
 			ath10k_ce_deinit(ce_state);
-			return NULL;
+			ce_state = NULL;
+			goto out;
 		}
 	}
 
 	if (attr->dest_nentries) {
-		if (ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr)) {
-			ath10k_err("Failed to initialize CE dest ring for ID: %d\n",
-				   ce_id);
+		ret = ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr);
+		if (ret) {
+			ath10k_err("Failed to initialize CE dest ring for ID: %d (%d)\n",
+				   ce_id, ret);
 			ath10k_ce_deinit(ce_state);
-			return NULL;
+			ce_state = NULL;
+			goto out;
 		}
 	}
 
-	/* Enable CE error interrupts */
-	ath10k_pci_wake(ar);
-	ath10k_ce_error_intr_enable(ar, ctrl_addr);
+out:
 	ath10k_pci_sleep(ar);
-
 	return ce_state;
 }
 
-void ath10k_ce_deinit(struct ce_state *ce_state)
+void ath10k_ce_deinit(struct ath10k_ce_pipe *ce_state)
 {
-	unsigned int ce_id = ce_state->id;
 	struct ath10k *ar = ce_state->ar;
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
-	ce_state->state = CE_UNUSED;
-	ar_pci->ce_id_to_state[ce_id] = NULL;
-
 	if (ce_state->src_ring) {
 		kfree(ce_state->src_ring->shadow_base_unaligned);
 		pci_free_consistent(ar_pci->pdev,
@@ -1185,5 +1134,7 @@
 				    ce_state->dest_ring->base_addr_ce_space);
 		kfree(ce_state->dest_ring);
 	}
-	kfree(ce_state);
+
+	ce_state->src_ring = NULL;
+	ce_state->dest_ring = NULL;
 }
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/ce.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/ce.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/ce.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/ce.h	2015-09-29 11:15:06.917740641 +0200
@@ -23,11 +23,10 @@
 
 /* Maximum number of Copy Engine's supported */
 #define CE_COUNT_MAX 8
-#define CE_HTT_H2T_MSG_SRC_NENTRIES 2048
+#define CE_HTT_H2T_MSG_SRC_NENTRIES 4096
 
 /* Descriptor rings must be aligned to this boundary */
 #define CE_DESC_RING_ALIGN	8
-#define CE_SENDLIST_ITEMS_MAX	12
 #define CE_SEND_FLAG_GATHER	0x00010000
 
 /*
@@ -36,16 +35,9 @@
  * how to use copy engines.
  */
 
-struct ce_state;
+struct ath10k_ce_pipe;
 
 
-/* Copy Engine operational state */
-enum ce_op_state {
-	CE_UNUSED,
-	CE_PAUSED,
-	CE_RUNNING,
-};
-
 #define CE_DESC_FLAGS_GATHER         (1 << 0)
 #define CE_DESC_FLAGS_BYTE_SWAP      (1 << 1)
 #define CE_DESC_FLAGS_META_DATA_MASK 0xFFFC
@@ -57,8 +49,7 @@
 	__le16 flags; /* %CE_DESC_FLAGS_ */
 };
 
-/* Copy Engine Ring internal state */
-struct ce_ring_state {
+struct ath10k_ce_ring {
 	/* Number of entries in this ring; must be power of 2 */
 	unsigned int nentries;
 	unsigned int nentries_mask;
@@ -116,49 +107,20 @@
 	void **per_transfer_context;
 };
 
-/* Copy Engine internal state */
-struct ce_state {
+struct ath10k_ce_pipe {
 	struct ath10k *ar;
 	unsigned int id;
 
 	unsigned int attr_flags;
 
 	u32 ctrl_addr;
-	enum ce_op_state state;
 
-	void (*send_cb) (struct ce_state *ce_state,
-			 void *per_transfer_send_context,
-			 u32 buffer,
-			 unsigned int nbytes,
-			 unsigned int transfer_id);
-	void (*recv_cb) (struct ce_state *ce_state,
-			 void *per_transfer_recv_context,
-			 u32 buffer,
-			 unsigned int nbytes,
-			 unsigned int transfer_id,
-			 unsigned int flags);
+	void (*send_cb)(struct ath10k_ce_pipe *);
+	void (*recv_cb)(struct ath10k_ce_pipe *);
 
 	unsigned int src_sz_max;
-	struct ce_ring_state *src_ring;
-	struct ce_ring_state *dest_ring;
-};
-
-struct ce_sendlist_item {
-	/* e.g. buffer or desc list */
-	dma_addr_t data;
-	union {
-		/* simple buffer */
-		unsigned int nbytes;
-		/* Rx descriptor list */
-		unsigned int ndesc;
-	} u;
-	/* externally-specified flags; OR-ed with internal flags */
-	u32 flags;
-};
-
-struct ce_sendlist {
-	unsigned int num_items;
-	struct ce_sendlist_item item[CE_SENDLIST_ITEMS_MAX];
+	struct ath10k_ce_ring *src_ring;
+	struct ath10k_ce_ring *dest_ring;
 };
 
 /* Copy Engine settable attributes */
@@ -182,7 +144,7 @@
  *
  * Implementation note: pushes 1 buffer to Source ring
  */
-int ath10k_ce_send(struct ce_state *ce_state,
+int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
 		   void *per_transfer_send_context,
 		   u32 buffer,
 		   unsigned int nbytes,
@@ -190,36 +152,18 @@
 		   unsigned int transfer_id,
 		   unsigned int flags);
 
-void ath10k_ce_send_cb_register(struct ce_state *ce_state,
-				void (*send_cb) (struct ce_state *ce_state,
-						 void *transfer_context,
+int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
+			  void *per_transfer_context,
 						 u32 buffer,
 						 unsigned int nbytes,
-						 unsigned int transfer_id),
-				int disable_interrupts);
+			  unsigned int transfer_id,
+			  unsigned int flags);
 
-/* Append a simple buffer (address/length) to a sendlist. */
-void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist,
-				u32 buffer,
-				unsigned int nbytes,
-				/* OR-ed with internal flags */
-				u32 flags);
+void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
+				void (*send_cb)(struct ath10k_ce_pipe *),
+				int disable_interrupts);
 
-/*
- * Queue a "sendlist" of buffers to be sent using gather to a single
- * anonymous destination buffer
- *   ce         - which copy engine to use
- *   sendlist        - list of simple buffers to send using gather
- *   transfer_id     - arbitrary ID; reflected to destination
- * Returns 0 on success; otherwise an error status.
- *
- * Implemenation note: Pushes multiple buffers with Gather to Source ring.
- */
-int ath10k_ce_sendlist_send(struct ce_state *ce_state,
-			    void *per_transfer_send_context,
-			    struct ce_sendlist *sendlist,
-			    /* 14 bits */
-			    unsigned int transfer_id);
+int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe);
 
 /*==================Recv=======================*/
 
@@ -233,17 +177,12 @@
  *
  * Implemenation note: Pushes a buffer to Dest ring.
  */
-int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
+int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state,
 			       void *per_transfer_recv_context,
 			       u32 buffer);
 
-void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
-				void (*recv_cb) (struct ce_state *ce_state,
-						 void *transfer_context,
-						 u32 buffer,
-						 unsigned int nbytes,
-						 unsigned int transfer_id,
-						 unsigned int flags));
+void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state,
+				void (*recv_cb)(struct ath10k_ce_pipe *));
 
 /* recv flags */
 /* Data is byte-swapped */
@@ -253,7 +192,7 @@
  * Supply data for the next completed unprocessed receive descriptor.
  * Pops buffer from Dest ring.
  */
-int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
+int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state,
 				  void **per_transfer_contextp,
 				  u32 *bufferp,
 				  unsigned int *nbytesp,
@@ -263,7 +202,7 @@
  * Supply data for the next completed unprocessed send descriptor.
  * Pops 1 completed send buffer from Source ring.
  */
-int ath10k_ce_completed_send_next(struct ce_state *ce_state,
+int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
 			   void **per_transfer_contextp,
 			   u32 *bufferp,
 			   unsigned int *nbytesp,
@@ -272,7 +211,7 @@
 /*==================CE Engine Initialization=======================*/
 
 /* Initialize an instance of a CE */
-struct ce_state *ath10k_ce_init(struct ath10k *ar,
+struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar,
 				unsigned int ce_id,
 				const struct ce_attr *attr);
 
@@ -282,7 +221,7 @@
  * receive buffers.  Target DMA must be stopped before using
  * this API.
  */
-int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
+int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
 			       void **per_transfer_contextp,
 			       u32 *bufferp);
 
@@ -291,18 +230,18 @@
  * pending sends.  Target DMA must be stopped before using
  * this API.
  */
-int ath10k_ce_cancel_send_next(struct ce_state *ce_state,
+int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
 			       void **per_transfer_contextp,
 			       u32 *bufferp,
 			       unsigned int *nbytesp,
 			       unsigned int *transfer_idp);
 
-void ath10k_ce_deinit(struct ce_state *ce_state);
+void ath10k_ce_deinit(struct ath10k_ce_pipe *ce_state);
 
 /*==================CE Interrupt Handlers====================*/
 void ath10k_ce_per_engine_service_any(struct ath10k *ar);
 void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id);
-void ath10k_ce_disable_interrupts(struct ath10k *ar);
+int ath10k_ce_disable_interrupts(struct ath10k *ar);
 
 /* ce_attr.flags values */
 /* Use NonSnooping PCIe accesses? */
@@ -322,9 +261,6 @@
 	/* CE_ATTR_* values */
 	unsigned int flags;
 
-	/* currently not in use */
-	unsigned int priority;
-
 	/* #entries in source ring - Must be a power of 2 */
 	unsigned int src_nentries;
 
@@ -336,21 +272,8 @@
 
 	/* #entries in destination ring - Must be a power of 2 */
 	unsigned int dest_nentries;
-
-	/* Future use */
-	void *reserved;
 };
 
-/*
- * When using sendlist_send to transfer multiple buffer fragments, the
- * transfer context of each fragment, except last one, will be filled
- * with CE_SENDLIST_ITEM_CTXT. ce_completed_send will return success for
- * each fragment done with send and the transfer context would be
- * CE_SENDLIST_ITEM_CTXT. Upper layer could use this to identify the
- * status of a send completion.
- */
-#define CE_SENDLIST_ITEM_CTXT	((void *)0xcecebeef)
-
 #define SR_BA_ADDRESS		0x0000
 #define SR_SIZE_ADDRESS		0x0004
 #define DR_BA_ADDRESS		0x0008
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/core.c linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/core.c
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/core.c	2015-09-29 11:15:06.917740641 +0200
@@ -39,17 +39,6 @@
 
 static const struct ath10k_hw_params ath10k_hw_params_list[] = {
 	{
-		.id = QCA988X_HW_1_0_VERSION,
-		.name = "qca988x hw1.0",
-		.patch_load_addr = QCA988X_HW_1_0_PATCH_LOAD_ADDR,
-		.fw = {
-			.dir = QCA988X_HW_1_0_FW_DIR,
-			.fw = QCA988X_HW_1_0_FW_FILE,
-			.otp = QCA988X_HW_1_0_OTP_FILE,
-			.board = QCA988X_HW_1_0_BOARD_DATA_FILE,
-		},
-	},
-	{
 		.id = QCA988X_HW_2_0_VERSION,
 		.name = "qca988x hw2.0",
 		.patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
@@ -64,31 +53,9 @@
 
 static void ath10k_send_suspend_complete(struct ath10k *ar)
 {
-	ath10k_dbg(ATH10K_DBG_CORE, "%s\n", __func__);
+	ath10k_dbg(ATH10K_DBG_BOOT, "boot suspend complete\n");
 
-	ar->is_target_paused = true;
-	wake_up(&ar->event_queue);
-}
-
-static int ath10k_check_fw_version(struct ath10k *ar)
-{
-	char version[32];
-
-	if (ar->fw_version_major >= SUPPORTED_FW_MAJOR &&
-	    ar->fw_version_minor >= SUPPORTED_FW_MINOR &&
-	    ar->fw_version_release >= SUPPORTED_FW_RELEASE &&
-	    ar->fw_version_build >= SUPPORTED_FW_BUILD)
-		return 0;
-
-	snprintf(version, sizeof(version), "%u.%u.%u.%u",
-		 SUPPORTED_FW_MAJOR, SUPPORTED_FW_MINOR,
-		 SUPPORTED_FW_RELEASE, SUPPORTED_FW_BUILD);
-
-	ath10k_warn("WARNING: Firmware version %s is not officially supported.\n",
-		    ar->hw->wiphy->fw_version);
-	ath10k_warn("Please upgrade to version %s (or newer)\n", version);
-
-	return 0;
+	complete(&ar->target_suspend);
 }
 
 static int ath10k_init_connect_htc(struct ath10k *ar)
@@ -100,7 +67,7 @@
 		goto conn_fail;
 
 	/* Start HTC */
-	status = ath10k_htc_start(ar->htc);
+	status = ath10k_htc_start(&ar->htc);
 	if (status)
 		goto conn_fail;
 
@@ -112,11 +79,11 @@
 		goto timeout;
 	}
 
-	ath10k_dbg(ATH10K_DBG_CORE, "core wmi ready\n");
+	ath10k_dbg(ATH10K_DBG_BOOT, "boot wmi ready\n");
 	return 0;
 
 timeout:
-	ath10k_htc_stop(ar->htc);
+	ath10k_htc_stop(&ar->htc);
 conn_fail:
 	return status;
 }
@@ -200,8 +167,7 @@
 	return fw;
 }
 
-static int ath10k_push_board_ext_data(struct ath10k *ar,
-				      const struct firmware *fw)
+static int ath10k_push_board_ext_data(struct ath10k *ar)
 {
 	u32 board_data_size = QCA988X_BOARD_DATA_SZ;
 	u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ;
@@ -214,21 +180,21 @@
 		return ret;
 	}
 
-	ath10k_dbg(ATH10K_DBG_CORE,
-		   "ath10k: Board extended Data download addr: 0x%x\n",
+	ath10k_dbg(ATH10K_DBG_BOOT,
+		   "boot push board extended data addr 0x%x\n",
 		   board_ext_data_addr);
 
 	if (board_ext_data_addr == 0)
 		return 0;
 
-	if (fw->size != (board_data_size + board_ext_data_size)) {
+	if (ar->board_len != (board_data_size + board_ext_data_size)) {
 		ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n",
-			   fw->size, board_data_size, board_ext_data_size);
+			   ar->board_len, board_data_size, board_ext_data_size);
 		return -EINVAL;
 	}
 
 	ret = ath10k_bmi_write_memory(ar, board_ext_data_addr,
-				      fw->data + board_data_size,
+				      ar->board_data + board_data_size,
 				      board_ext_data_size);
 	if (ret) {
 		ath10k_err("could not write board ext data (%d)\n", ret);
@@ -249,18 +215,9 @@
 {
 	u32 board_data_size = QCA988X_BOARD_DATA_SZ;
 	u32 address;
-	const struct firmware *fw;
 	int ret;
 
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.board);
-	if (IS_ERR(fw)) {
-		ath10k_err("could not fetch board data fw file (%ld)\n",
-			   PTR_ERR(fw));
-		return PTR_ERR(fw);
-	}
-
-	ret = ath10k_push_board_ext_data(ar, fw);
+	ret = ath10k_push_board_ext_data(ar);
 	if (ret) {
 		ath10k_err("could not push board ext data (%d)\n", ret);
 		goto exit;
@@ -272,8 +229,9 @@
 		goto exit;
 	}
 
-	ret = ath10k_bmi_write_memory(ar, address, fw->data,
-				      min_t(u32, board_data_size, fw->size));
+	ret = ath10k_bmi_write_memory(ar, address, ar->board_data,
+				      min_t(u32, board_data_size,
+					    ar->board_len));
 	if (ret) {
 		ath10k_err("could not write board data (%d)\n", ret);
 		goto exit;
@@ -286,34 +244,21 @@
 	}
 
 exit:
-	release_firmware(fw);
 	return ret;
 }
 
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
-	const struct firmware *fw;
-	u32 address;
+	u32 address = ar->hw_params.patch_load_addr;
 	u32 exec_param;
 	int ret;
 
 	/* OTP is optional */
 
-	if (ar->hw_params.fw.otp == NULL) {
-		ath10k_info("otp file not defined\n");
-		return 0;
-	}
-
-	address = ar->hw_params.patch_load_addr;
-
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.otp);
-	if (IS_ERR(fw)) {
-		ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw));
+	if (!ar->otp_data || !ar->otp_len)
 		return 0;
-	}
 
-	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
+	ret = ath10k_bmi_fast_download(ar, address, ar->otp_data, ar->otp_len);
 	if (ret) {
 		ath10k_err("could not write otp (%d)\n", ret);
 		goto exit;
@@ -327,39 +272,301 @@
 	}
 
 exit:
-	release_firmware(fw);
 	return ret;
 }
 
 static int ath10k_download_fw(struct ath10k *ar)
 {
-	const struct firmware *fw;
 	u32 address;
 	int ret;
 
-	if (ar->hw_params.fw.fw == NULL)
-		return -EINVAL;
-
 	address = ar->hw_params.patch_load_addr;
 
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.fw);
-	if (IS_ERR(fw)) {
-		ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw));
-		return PTR_ERR(fw);
-	}
-
-	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
+	ret = ath10k_bmi_fast_download(ar, address, ar->firmware_data,
+				       ar->firmware_len);
 	if (ret) {
 		ath10k_err("could not write fw (%d)\n", ret);
 		goto exit;
 	}
 
 exit:
-	release_firmware(fw);
 	return ret;
 }
 
+static void ath10k_core_free_firmware_files(struct ath10k *ar)
+{
+	if (ar->board && !IS_ERR(ar->board))
+		release_firmware(ar->board);
+
+	if (ar->otp && !IS_ERR(ar->otp))
+		release_firmware(ar->otp);
+
+	if (ar->firmware && !IS_ERR(ar->firmware))
+		release_firmware(ar->firmware);
+
+	ar->board = NULL;
+	ar->board_data = NULL;
+	ar->board_len = 0;
+
+	ar->otp = NULL;
+	ar->otp_data = NULL;
+	ar->otp_len = 0;
+
+	ar->firmware = NULL;
+	ar->firmware_data = NULL;
+	ar->firmware_len = 0;
+}
+
+static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar)
+{
+	int ret = 0;
+
+	if (ar->hw_params.fw.fw == NULL) {
+		ath10k_err("firmware file not defined\n");
+		return -EINVAL;
+	}
+
+	if (ar->hw_params.fw.board == NULL) {
+		ath10k_err("board data file not defined");
+		return -EINVAL;
+	}
+
+	ar->board = ath10k_fetch_fw_file(ar,
+					 ar->hw_params.fw.dir,
+					 ar->hw_params.fw.board);
+	if (IS_ERR(ar->board)) {
+		ret = PTR_ERR(ar->board);
+		ath10k_err("could not fetch board data (%d)\n", ret);
+		goto err;
+	}
+
+	ar->board_data = ar->board->data;
+	ar->board_len = ar->board->size;
+
+	ar->firmware = ath10k_fetch_fw_file(ar,
+					    ar->hw_params.fw.dir,
+					    ar->hw_params.fw.fw);
+	if (IS_ERR(ar->firmware)) {
+		ret = PTR_ERR(ar->firmware);
+		ath10k_err("could not fetch firmware (%d)\n", ret);
+		goto err;
+	}
+
+	ar->firmware_data = ar->firmware->data;
+	ar->firmware_len = ar->firmware->size;
+
+	/* OTP may be undefined. If so, don't fetch it at all */
+	if (ar->hw_params.fw.otp == NULL)
+		return 0;
+
+	ar->otp = ath10k_fetch_fw_file(ar,
+				       ar->hw_params.fw.dir,
+				       ar->hw_params.fw.otp);
+	if (IS_ERR(ar->otp)) {
+		ret = PTR_ERR(ar->otp);
+		ath10k_err("could not fetch otp (%d)\n", ret);
+		goto err;
+	}
+
+	ar->otp_data = ar->otp->data;
+	ar->otp_len = ar->otp->size;
+
+	return 0;
+
+err:
+	ath10k_core_free_firmware_files(ar);
+	return ret;
+}
+
+static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
+{
+	size_t magic_len, len, ie_len;
+	int ie_id, i, index, bit, ret;
+	struct ath10k_fw_ie *hdr;
+	const u8 *data;
+	__le32 *timestamp;
+
+	/* first fetch the firmware file (firmware-*.bin) */
+	ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name);
+	if (IS_ERR(ar->firmware)) {
+		ath10k_err("Could not fetch firmware file '%s': %ld\n",
+			   name, PTR_ERR(ar->firmware));
+		return PTR_ERR(ar->firmware);
+	}
+
+	data = ar->firmware->data;
+	len = ar->firmware->size;
+
+	/* magic also includes the null byte, check that as well */
+	magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1;
+
+	if (len < magic_len) {
+		ath10k_err("firmware image too small to contain magic: %zu\n",
+			   len);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) {
+		ath10k_err("Invalid firmware magic\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/* jump over the padding */
+	magic_len = ALIGN(magic_len, 4);
+
+	len -= magic_len;
+	data += magic_len;
+
+	/* loop elements */
+	while (len > sizeof(struct ath10k_fw_ie)) {
+		hdr = (struct ath10k_fw_ie *)data;
+
+		ie_id = le32_to_cpu(hdr->id);
+		ie_len = le32_to_cpu(hdr->len);
+
+		len -= sizeof(*hdr);
+		data += sizeof(*hdr);
+
+		if (len < ie_len) {
+			ath10k_err("Invalid length for FW IE %d (%zu < %zu)\n",
+				   ie_id, len, ie_len);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		switch (ie_id) {
+		case ATH10K_FW_IE_FW_VERSION:
+			if (ie_len > sizeof(ar->hw->wiphy->fw_version) - 1)
+				break;
+
+			memcpy(ar->hw->wiphy->fw_version, data, ie_len);
+			ar->hw->wiphy->fw_version[ie_len] = '\0';
+
+			ath10k_dbg(ATH10K_DBG_BOOT,
+				   "found fw version %s\n",
+				    ar->hw->wiphy->fw_version);
+			break;
+		case ATH10K_FW_IE_TIMESTAMP:
+			if (ie_len != sizeof(u32))
+				break;
+
+			timestamp = (__le32 *)data;
+
+			ath10k_dbg(ATH10K_DBG_BOOT, "found fw timestamp %d\n",
+				   le32_to_cpup(timestamp));
+			break;
+		case ATH10K_FW_IE_FEATURES:
+			ath10k_dbg(ATH10K_DBG_BOOT,
+				   "found firmware features ie (%zd B)\n",
+				   ie_len);
+
+			for (i = 0; i < ATH10K_FW_FEATURE_COUNT; i++) {
+				index = i / 8;
+				bit = i % 8;
+
+				if (index == ie_len)
+					break;
+
+				if (data[index] & (1 << bit)) {
+					ath10k_dbg(ATH10K_DBG_BOOT,
+						   "Enabling feature bit: %i\n",
+						   i);
+					__set_bit(i, ar->fw_features);
+				}
+			}
+
+			ath10k_dbg_dump(ATH10K_DBG_BOOT, "features", "",
+					ar->fw_features,
+					sizeof(ar->fw_features));
+			break;
+		case ATH10K_FW_IE_FW_IMAGE:
+			ath10k_dbg(ATH10K_DBG_BOOT,
+				   "found fw image ie (%zd B)\n",
+				   ie_len);
+
+			ar->firmware_data = data;
+			ar->firmware_len = ie_len;
+
+			break;
+		case ATH10K_FW_IE_OTP_IMAGE:
+			ath10k_dbg(ATH10K_DBG_BOOT,
+				   "found otp image ie (%zd B)\n",
+				   ie_len);
+
+			ar->otp_data = data;
+			ar->otp_len = ie_len;
+
+			break;
+		default:
+			ath10k_warn("Unknown FW IE: %u\n",
+				    le32_to_cpu(hdr->id));
+			break;
+		}
+
+		/* jump over the padding */
+		ie_len = ALIGN(ie_len, 4);
+
+		len -= ie_len;
+		data += ie_len;
+	}
+
+	if (!ar->firmware_data || !ar->firmware_len) {
+		ath10k_warn("No ATH10K_FW_IE_FW_IMAGE found from %s, skipping\n",
+			    name);
+		ret = -ENOMEDIUM;
+		goto err;
+	}
+
+	/* now fetch the board file */
+	if (ar->hw_params.fw.board == NULL) {
+		ath10k_err("board data file not defined");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ar->board = ath10k_fetch_fw_file(ar,
+					 ar->hw_params.fw.dir,
+					 ar->hw_params.fw.board);
+	if (IS_ERR(ar->board)) {
+		ret = PTR_ERR(ar->board);
+		ath10k_err("could not fetch board data (%d)\n", ret);
+		goto err;
+	}
+
+	ar->board_data = ar->board->data;
+	ar->board_len = ar->board->size;
+
+	return 0;
+
+err:
+	ath10k_core_free_firmware_files(ar);
+	return ret;
+}
+
+static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
+{
+	int ret;
+
+	ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API2_FILE);
+	if (ret == 0) {
+		ar->fw_api = 2;
+		goto out;
+	}
+
+	ret = ath10k_core_fetch_firmware_api_1(ar);
+	if (ret)
+		return ret;
+
+	ar->fw_api = 1;
+
+out:
+	ath10k_dbg(ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api);
+
+	return 0;
+}
+
 static int ath10k_init_download_firmware(struct ath10k *ar)
 {
 	int ret;
@@ -393,10 +600,8 @@
 		return ret;
 	}
 
-	if (!uart_print) {
-		ath10k_info("UART prints disabled\n");
+	if (!uart_print)
 		return 0;
-	}
 
 	ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, 7);
 	if (ret) {
@@ -410,6 +615,13 @@
 		return ret;
 	}
 
+	/* Set the UART baud rate to 19200. */
+	ret = ath10k_bmi_write32(ar, hi_desired_baud_rate, 19200);
+	if (ret) {
+		ath10k_warn("could not set the baud rate (%d)\n", ret);
+		return ret;
+	}
+
 	ath10k_info("UART prints enabled\n");
 	return 0;
 }
@@ -434,14 +646,42 @@
 
 	ar->hw_params = *hw_params;
 
-	ath10k_info("Hardware name %s version 0x%x\n",
+	ath10k_dbg(ATH10K_DBG_BOOT, "Hardware name %s version 0x%x\n",
 		    ar->hw_params.name, ar->target_version);
 
 	return 0;
 }
 
+static void ath10k_core_restart(struct work_struct *work)
+{
+	struct ath10k *ar = container_of(work, struct ath10k, restart_work);
+
+	mutex_lock(&ar->conf_mutex);
+
+	switch (ar->state) {
+	case ATH10K_STATE_ON:
+		ath10k_halt(ar);
+		ar->state = ATH10K_STATE_RESTARTING;
+		ieee80211_restart_hw(ar->hw);
+		break;
+	case ATH10K_STATE_OFF:
+		/* this can happen if driver is being unloaded
+		 * or if the crash happens during FW probing */
+		ath10k_warn("cannot restart a device that hasn't been started\n");
+		break;
+	case ATH10K_STATE_RESTARTING:
+	case ATH10K_STATE_RESTARTED:
+		ar->state = ATH10K_STATE_WEDGED;
+		/* fall through */
+	case ATH10K_STATE_WEDGED:
+		ath10k_warn("device is wedged, will not restart\n");
+		break;
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
 struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
-				  enum ath10k_bus bus,
 				  const struct ath10k_hif_ops *hif_ops)
 {
 	struct ath10k *ar;
@@ -458,13 +698,11 @@
 
 	ar->hif.priv = hif_priv;
 	ar->hif.ops = hif_ops;
-	ar->hif.bus = bus;
-
-	ar->free_vdev_map = 0xFF; /* 8 vdevs */
 
 	init_completion(&ar->scan.started);
 	init_completion(&ar->scan.completed);
 	init_completion(&ar->scan.on_channel);
+	init_completion(&ar->target_suspend);
 
 	init_completion(&ar->install_key_done);
 	init_completion(&ar->vdev_setup_done);
@@ -485,7 +723,10 @@
 	INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work);
 	skb_queue_head_init(&ar->offchan_tx_queue);
 
-	init_waitqueue_head(&ar->event_queue);
+	INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work);
+	skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
+
+	INIT_WORK(&ar->restart_work, ath10k_core_restart);
 
 	return ar;
 
@@ -504,24 +745,13 @@
 }
 EXPORT_SYMBOL(ath10k_core_destroy);
 
-
-int ath10k_core_register(struct ath10k *ar)
+int ath10k_core_start(struct ath10k *ar)
 {
-	struct ath10k_htc_ops htc_ops;
-	struct bmi_target_info target_info;
 	int status;
 
-	memset(&target_info, 0, sizeof(target_info));
-	status = ath10k_bmi_get_target_info(ar, &target_info);
-	if (status)
-		goto err;
-
-	ar->target_version = target_info.version;
-	ar->hw->wiphy->hw_version = target_info.version;
+	lockdep_assert_held(&ar->conf_mutex);
 
-	status = ath10k_init_hw_params(ar);
-	if (status)
-		goto err;
+	ath10k_bmi_start(ar);
 
 	if (ath10k_init_configure_target(ar)) {
 		status = -EINVAL;
@@ -536,44 +766,49 @@
 	if (status)
 		goto err;
 
-	htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete;
+	ar->htc.htc_ops.target_send_suspend_complete =
+		ath10k_send_suspend_complete;
 
-	ar->htc = ath10k_htc_create(ar, &htc_ops);
-	if (IS_ERR(ar->htc)) {
-		status = PTR_ERR(ar->htc);
-		ath10k_err("could not create HTC (%d)\n", status);
+	status = ath10k_htc_init(ar);
+	if (status) {
+		ath10k_err("could not init HTC (%d)\n", status);
 		goto err;
 	}
 
 	status = ath10k_bmi_done(ar);
 	if (status)
-		goto err_htc_destroy;
+		goto err;
 
 	status = ath10k_wmi_attach(ar);
 	if (status) {
 		ath10k_err("WMI attach failed: %d\n", status);
-		goto err_htc_destroy;
+		goto err;
 	}
 
-	status = ath10k_htc_wait_target(ar->htc);
-	if (status)
+	status = ath10k_hif_start(ar);
+	if (status) {
+		ath10k_err("could not start HIF: %d\n", status);
 		goto err_wmi_detach;
+	}
 
-	ar->htt = ath10k_htt_attach(ar);
-	if (!ar->htt) {
-		status = -ENOMEM;
-		goto err_wmi_detach;
+	status = ath10k_htc_wait_target(&ar->htc);
+	if (status) {
+		ath10k_err("failed to connect to HTC: %d\n", status);
+		goto err_hif_stop;
+	}
+
+	status = ath10k_htt_attach(ar);
+	if (status) {
+		ath10k_err("could not attach htt (%d)\n", status);
+		goto err_hif_stop;
 	}
 
 	status = ath10k_init_connect_htc(ar);
 	if (status)
 		goto err_htt_detach;
 
-	ath10k_info("firmware %s booted\n", ar->hw->wiphy->fw_version);
-
-	status = ath10k_check_fw_version(ar);
-	if (status)
-		goto err_disconnect_htc;
+	ath10k_dbg(ATH10K_DBG_BOOT, "firmware %s booted\n",
+		   ar->hw->wiphy->fw_version);
 
 	status = ath10k_wmi_cmd_init(ar);
 	if (status) {
@@ -588,77 +823,215 @@
 		goto err_disconnect_htc;
 	}
 
-	status = ath10k_htt_attach_target(ar->htt);
+	status = ath10k_htt_attach_target(&ar->htt);
 	if (status)
 		goto err_disconnect_htc;
 
-	status = ath10k_mac_register(ar);
+	status = ath10k_debug_start(ar);
 	if (status)
 		goto err_disconnect_htc;
 
-	status = ath10k_debug_create(ar);
-	if (status) {
-		ath10k_err("unable to initialize debugfs\n");
-		goto err_unregister_mac;
-	}
+	ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
+	INIT_LIST_HEAD(&ar->arvifs);
+
+	if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags))
+		ath10k_info("%s (0x%x) fw %s api %d htt %d.%d\n",
+			    ar->hw_params.name, ar->target_version,
+			    ar->hw->wiphy->fw_version, ar->fw_api,
+			    ar->htt.target_version_major,
+			    ar->htt.target_version_minor);
+
+	__set_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags);
 
 	return 0;
 
-err_unregister_mac:
-	ath10k_mac_unregister(ar);
 err_disconnect_htc:
-	ath10k_htc_stop(ar->htc);
+	ath10k_htc_stop(&ar->htc);
 err_htt_detach:
-	ath10k_htt_detach(ar->htt);
+	ath10k_htt_detach(&ar->htt);
+err_hif_stop:
+	ath10k_hif_stop(ar);
 err_wmi_detach:
 	ath10k_wmi_detach(ar);
-err_htc_destroy:
-	ath10k_htc_destroy(ar->htc);
 err:
 	return status;
 }
-EXPORT_SYMBOL(ath10k_core_register);
+EXPORT_SYMBOL(ath10k_core_start);
 
-void ath10k_core_unregister(struct ath10k *ar)
+int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt)
 {
-	/* We must unregister from mac80211 before we stop HTC and HIF.
-	 * Otherwise we will fail to submit commands to FW and mac80211 will be
-	 * unhappy about callback failures. */
-	ath10k_mac_unregister(ar);
-	ath10k_htc_stop(ar->htc);
-	ath10k_htt_detach(ar->htt);
+	int ret;
+
+	INIT_COMPLETION(ar->target_suspend);
+
+	ret = ath10k_wmi_pdev_suspend_target(ar, suspend_opt);
+	if (ret) {
+		ath10k_warn("could not suspend target (%d)\n", ret);
+		return ret;
+	}
+
+	ret = wait_for_completion_timeout(&ar->target_suspend, 1 * HZ);
+
+	if (ret == 0) {
+		ath10k_warn("suspend timed out - target pause event never came\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+void ath10k_core_stop(struct ath10k *ar)
+{
+	lockdep_assert_held(&ar->conf_mutex);
+
+	/* try to suspend target */
+	ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR);
+	ath10k_debug_stop(ar);
+	ath10k_htc_stop(&ar->htc);
+	ath10k_htt_detach(&ar->htt);
 	ath10k_wmi_detach(ar);
-	ath10k_htc_destroy(ar->htc);
 }
-EXPORT_SYMBOL(ath10k_core_unregister);
+EXPORT_SYMBOL(ath10k_core_stop);
 
-int ath10k_core_target_suspend(struct ath10k *ar)
+/* mac80211 manages fw/hw initialization through start/stop hooks. However in
+ * order to know what hw capabilities should be advertised to mac80211 it is
+ * necessary to load the firmware (and tear it down immediately since start
+ * hook will try to init it again) before registering */
+static int ath10k_core_probe_fw(struct ath10k *ar)
 {
-	int ret;
+	struct bmi_target_info target_info;
+	int ret = 0;
+
+	ret = ath10k_hif_power_up(ar);
+	if (ret) {
+		ath10k_err("could not start pci hif (%d)\n", ret);
+		return ret;
+	}
 
-	ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+	memset(&target_info, 0, sizeof(target_info));
+	ret = ath10k_bmi_get_target_info(ar, &target_info);
+	if (ret) {
+		ath10k_err("could not get target info (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
 
-	ret = ath10k_wmi_pdev_suspend_target(ar);
-	if (ret)
-		ath10k_warn("could not suspend target (%d)\n", ret);
+	ar->target_version = target_info.version;
+	ar->hw->wiphy->hw_version = target_info.version;
+
+	ret = ath10k_init_hw_params(ar);
+	if (ret) {
+		ath10k_err("could not get hw params (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
+	ret = ath10k_core_fetch_firmware_files(ar);
+	if (ret) {
+		ath10k_err("could not fetch firmware files (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
+	mutex_lock(&ar->conf_mutex);
 
+	ret = ath10k_core_start(ar);
+	if (ret) {
+		ath10k_err("could not init core (%d)\n", ret);
+		ath10k_core_free_firmware_files(ar);
+		ath10k_hif_power_down(ar);
+		mutex_unlock(&ar->conf_mutex);
 	return ret;
 }
-EXPORT_SYMBOL(ath10k_core_target_suspend);
 
-int ath10k_core_target_resume(struct ath10k *ar)
+	ath10k_core_stop(ar);
+
+	mutex_unlock(&ar->conf_mutex);
+
+	ath10k_hif_power_down(ar);
+	return 0;
+}
+
+static int ath10k_core_check_chip_id(struct ath10k *ar)
 {
-	int ret;
+	u32 hw_revision = MS(ar->chip_id, SOC_CHIP_ID_REV);
 
-	ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+	ath10k_dbg(ATH10K_DBG_BOOT, "boot chip_id 0x%08x hw_revision 0x%x\n",
+		   ar->chip_id, hw_revision);
 
-	ret = ath10k_wmi_pdev_resume_target(ar);
-	if (ret)
-		ath10k_warn("could not resume target (%d)\n", ret);
+	/* Check that we are not using hw1.0 (some of them have same pci id
+	 * as hw2.0) before doing anything else as ath10k crashes horribly
+	 * due to missing hw1.0 workarounds. */
+	switch (hw_revision) {
+	case QCA988X_HW_1_0_CHIP_ID_REV:
+		ath10k_err("ERROR: qca988x hw1.0 is not supported\n");
+		return -EOPNOTSUPP;
 
-	return ret;
+	case QCA988X_HW_2_0_CHIP_ID_REV:
+		/* known hardware revision, continue normally */
+		return 0;
+
+	default:
+		ath10k_warn("Warning: hardware revision unknown (0x%x), expect problems\n",
+			    ar->chip_id);
+		return 0;
+	}
+
+	return 0;
 }
-EXPORT_SYMBOL(ath10k_core_target_resume);
+
+int ath10k_core_register(struct ath10k *ar, u32 chip_id)
+{
+	int status;
+
+	ar->chip_id = chip_id;
+
+	status = ath10k_core_check_chip_id(ar);
+	if (status) {
+		ath10k_err("Unsupported chip id 0x%08x\n", ar->chip_id);
+		return status;
+	}
+
+	status = ath10k_core_probe_fw(ar);
+	if (status) {
+		ath10k_err("could not probe fw (%d)\n", status);
+		return status;
+	}
+
+	status = ath10k_mac_register(ar);
+	if (status) {
+		ath10k_err("could not register to mac80211 (%d)\n", status);
+		goto err_release_fw;
+	}
+
+	status = ath10k_debug_create(ar);
+	if (status) {
+		ath10k_err("unable to initialize debugfs\n");
+		goto err_unregister_mac;
+	}
+
+	return 0;
+
+err_unregister_mac:
+	ath10k_mac_unregister(ar);
+err_release_fw:
+	ath10k_core_free_firmware_files(ar);
+	return status;
+}
+EXPORT_SYMBOL(ath10k_core_register);
+
+void ath10k_core_unregister(struct ath10k *ar)
+{
+	/* We must unregister from mac80211 before we stop HTC and HIF.
+	 * Otherwise we will fail to submit commands to FW and mac80211 will be
+	 * unhappy about callback failures. */
+	ath10k_mac_unregister(ar);
+
+	ath10k_core_free_firmware_files(ar);
+
+	ath10k_debug_destroy(ar);
+}
+EXPORT_SYMBOL(ath10k_core_unregister);
 
 MODULE_AUTHOR("Qualcomm Atheros");
 MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/core.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/core.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/core.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/core.h	2015-09-29 11:15:06.917740641 +0200
@@ -23,12 +23,14 @@
 #include <linux/types.h>
 #include <linux/pci.h>
 
+#include "htt.h"
 #include "htc.h"
 #include "hw.h"
 #include "targaddrs.h"
 #include "wmi.h"
 #include "../ath.h"
 #include "../regd.h"
+#include "../dfs_pattern_detector.h"
 
 #define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB)
 #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
@@ -37,35 +39,42 @@
 #define ATH10K_SCAN_ID 0
 #define WMI_READY_TIMEOUT (5 * HZ)
 #define ATH10K_FLUSH_TIMEOUT_HZ (5*HZ)
+#define ATH10K_NUM_CHANS 38
 
 /* Antenna noise floor */
 #define ATH10K_DEFAULT_NOISE_FLOOR -95
 
-struct ath10k;
+#define ATH10K_MAX_NUM_MGMT_PENDING 128
 
-enum ath10k_bus {
-	ATH10K_BUS_PCI,
-};
+/* number of failed packets */
+#define ATH10K_KICKOUT_THRESHOLD 50
+
+/*
+ * Use insanely high numbers to make sure that the firmware implementation
+ * won't start, we have the same functionality already in hostapd. Unit
+ * is seconds.
+ */
+#define ATH10K_KEEPALIVE_MIN_IDLE 3747
+#define ATH10K_KEEPALIVE_MAX_IDLE 3895
+#define ATH10K_KEEPALIVE_MAX_UNRESPONSIVE 3900
+
+struct ath10k;
 
 struct ath10k_skb_cb {
 	dma_addr_t paddr;
-	bool is_mapped;
-	bool is_aborted;
+	u8 vdev_id;
 
 	struct {
-		u8 vdev_id;
-		u16 msdu_id;
 		u8 tid;
 		bool is_offchan;
-		bool is_conf;
-		bool discard;
-		bool no_ack;
-		u8 refcount;
-		struct sk_buff *txfrag;
-		struct sk_buff *msdu;
+		struct ath10k_htt_txbuf *txbuf;
+		u32 txbuf_paddr;
 	} __packed htt;
 
-	/* 4 bytes left on 64bit arch */
+	struct {
+		bool dtim_zero;
+		bool deliver_cab;
+	} bcn;
 } __packed;
 
 static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
@@ -75,32 +84,6 @@
 	return (struct ath10k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data;
 }
 
-static inline int ath10k_skb_map(struct device *dev, struct sk_buff *skb)
-{
-	if (ATH10K_SKB_CB(skb)->is_mapped)
-		return -EINVAL;
-
-	ATH10K_SKB_CB(skb)->paddr = dma_map_single(dev, skb->data, skb->len,
-						   DMA_TO_DEVICE);
-
-	if (unlikely(dma_mapping_error(dev, ATH10K_SKB_CB(skb)->paddr)))
-		return -EIO;
-
-	ATH10K_SKB_CB(skb)->is_mapped = true;
-	return 0;
-}
-
-static inline int ath10k_skb_unmap(struct device *dev, struct sk_buff *skb)
-{
-	if (!ATH10K_SKB_CB(skb)->is_mapped)
-		return -EINVAL;
-
-	dma_unmap_single(dev, ATH10K_SKB_CB(skb)->paddr, skb->len,
-			 DMA_TO_DEVICE);
-	ATH10K_SKB_CB(skb)->is_mapped = false;
-	return 0;
-}
-
 static inline u32 host_interest_item_address(u32 item_offset)
 {
 	return QCA988X_HOST_INTEREST_ADDRESS + item_offset;
@@ -110,15 +93,26 @@
 	bool done_sent;
 };
 
+#define ATH10K_MAX_MEM_REQS 16
+
+struct ath10k_mem_chunk {
+	void *vaddr;
+	dma_addr_t paddr;
+	u32 len;
+	u32 req_id;
+};
+
 struct ath10k_wmi {
 	enum ath10k_htc_ep_id eid;
 	struct completion service_ready;
 	struct completion unified_ready;
-	atomic_t pending_tx_count;
-	wait_queue_head_t wq;
+	wait_queue_head_t tx_credits_wq;
+	struct wmi_cmd_map *cmd;
+	struct wmi_vdev_param_map *vdev_param;
+	struct wmi_pdev_param_map *pdev_param;
 
-	struct sk_buff_head wmi_event_list;
-	struct work_struct wmi_event_work;
+	u32 num_mem_chunks;
+	struct ath10k_mem_chunk mem_chunks[ATH10K_MAX_MEM_REQS];
 };
 
 struct ath10k_peer_stat {
@@ -187,6 +181,14 @@
 
 };
 
+struct ath10k_dfs_stats {
+	u32 phy_errors;
+	u32 pulses_total;
+	u32 pulses_detected;
+	u32 pulses_discarded;
+	u32 radar_detected;
+};
+
 #define ATH10K_MAX_NUM_PEER_IDS (1 << 11) /* htt rx_desc limit */
 
 struct ath10k_peer {
@@ -197,26 +199,49 @@
 	struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
 };
 
+struct ath10k_sta {
+	struct ath10k_vif *arvif;
+
+	/* the following are protected by ar->data_lock */
+	u32 changed; /* IEEE80211_RC_* */
+	u32 bw;
+	u32 nss;
+	u32 smps;
+
+	struct work_struct update_wk;
+};
+
 #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
 
 struct ath10k_vif {
+	struct list_head list;
+
 	u32 vdev_id;
 	enum wmi_vdev_type vdev_type;
 	enum wmi_vdev_subtype vdev_subtype;
 	u32 beacon_interval;
 	u32 dtim_period;
+	struct sk_buff *beacon;
+	/* protected by data_lock */
+	bool beacon_sent;
 
 	struct ath10k *ar;
 	struct ieee80211_vif *vif;
 
+	bool is_started;
+	bool is_up;
+	u32 aid;
+	u8 bssid[ETH_ALEN];
+
+	struct work_struct wep_key_work;
 	struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
-	u8 def_wep_key_index;
+	u8 def_wep_key_idx;
+	u8 def_wep_key_newidx;
 
 	u16 tx_seq_no;
 
 	union {
 		struct {
-			u8 bssid[ETH_ALEN];
 			u32 uapsd;
 		} sta;
 		struct {
@@ -230,10 +255,11 @@
 			u32 noa_len;
 			u8 *noa_data;
 		} ap;
-		struct {
-			u8 bssid[ETH_ALEN];
-		} ibss;
 	} u;
+
+	u8 fixed_rate;
+	u8 fixed_nss;
+	u8 force_sgi;
 };
 
 struct ath10k_vif_iter {
@@ -248,6 +274,58 @@
 	u32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
 
 	struct completion event_stats_compl;
+
+	unsigned long htt_stats_mask;
+	struct delayed_work htt_stats_dwork;
+	struct ath10k_dfs_stats dfs_stats;
+	struct ath_dfs_pool_stats dfs_pool_stats;
+
+	u32 fw_dbglog_mask;
+};
+
+enum ath10k_state {
+	ATH10K_STATE_OFF = 0,
+	ATH10K_STATE_ON,
+
+	/* When doing firmware recovery the device is first powered down.
+	 * mac80211 is supposed to call in to start() hook later on. It is
+	 * however possible that driver unloading and firmware crash overlap.
+	 * mac80211 can wait on conf_mutex in stop() while the device is
+	 * stopped in ath10k_core_restart() work holding conf_mutex. The state
+	 * RESTARTED means that the device is up and mac80211 has started hw
+	 * reconfiguration. Once mac80211 is done with the reconfiguration we
+	 * set the state to STATE_ON in restart_complete(). */
+	ATH10K_STATE_RESTARTING,
+	ATH10K_STATE_RESTARTED,
+
+	/* The device has crashed while restarting hw. This state is like ON
+	 * but commands are blocked in HTC and -ECOMM response is given. This
+	 * prevents completion timeouts and makes the driver more responsive to
+	 * userspace commands. This is also prevents recursive recovery. */
+	ATH10K_STATE_WEDGED,
+};
+
+enum ath10k_fw_features {
+	/* wmi_mgmt_rx_hdr contains extra RSSI information */
+	ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX = 0,
+
+	/* firmware from 10X branch */
+	ATH10K_FW_FEATURE_WMI_10X = 1,
+
+	/* firmware support tx frame management over WMI, otherwise it's HTT */
+	ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX = 2,
+
+	/* Firmware does not support P2P */
+	ATH10K_FW_FEATURE_NO_P2P = 3,
+
+	/* keep last */
+	ATH10K_FW_FEATURE_COUNT,
+};
+
+enum ath10k_dev_flags {
+	/* Indicates that ath10k device is during CAC phase of DFS */
+	ATH10K_CAC_RUNNING,
+	ATH10K_FLAG_FIRST_BOOT_DONE,
 };
 
 struct ath10k {
@@ -256,6 +334,7 @@
 	struct device *dev;
 	u8 mac_addr[ETH_ALEN];
 
+	u32 chip_id;
 	u32 target_version;
 	u8 fw_version_major;
 	u32 fw_version_minor;
@@ -266,6 +345,9 @@
 	u32 hw_max_tx_power;
 	u32 ht_cap_info;
 	u32 vht_cap_info;
+	u32 num_rf_chains;
+
+	DECLARE_BITMAP(fw_features, ATH10K_FW_FEATURE_COUNT);
 
 	struct targetdef *targetdef;
 	struct hostdef *hostdef;
@@ -274,19 +356,15 @@
 
 	struct {
 		void *priv;
-		enum ath10k_bus bus;
 		const struct ath10k_hif_ops *ops;
 	} hif;
 
-	struct ath10k_wmi wmi;
-
-	wait_queue_head_t event_queue;
-	bool is_target_paused;
+	struct completion target_suspend;
 
 	struct ath10k_bmi bmi;
-
-	struct ath10k_htc *htc;
-	struct ath10k_htt *htt;
+	struct ath10k_wmi wmi;
+	struct ath10k_htc htc;
+	struct ath10k_htt htt;
 
 	struct ath10k_hw_params {
 		u32 id;
@@ -301,6 +379,20 @@
 		} fw;
 	} hw_params;
 
+	const struct firmware *board;
+	const void *board_data;
+	size_t board_len;
+
+	const struct firmware *otp;
+	const void *otp_data;
+	size_t otp_len;
+
+	const struct firmware *firmware;
+	const void *firmware_data;
+	size_t firmware_len;
+
+	int fw_api;
+
 	struct {
 		struct completion started;
 		struct completion completed;
@@ -323,11 +415,16 @@
 	/* valid during scan; needed for mgmt rx during scan */
 	struct ieee80211_channel *scan_channel;
 
+	/* current operating channel definition */
+	struct cfg80211_chan_def chandef;
+
 	int free_vdev_map;
 	int monitor_vdev_id;
 	bool monitor_enabled;
 	bool monitor_present;
 	unsigned int filter_flags;
+	unsigned long dev_flags;
+	u32 dfs_block_radar_events;
 
 	struct wmi_pdev_set_wmm_params_arg wmm_params;
 	struct completion install_key_done;
@@ -342,28 +439,46 @@
 	/* protects shared structure data */
 	spinlock_t data_lock;
 
+	struct list_head arvifs;
 	struct list_head peers;
 	wait_queue_head_t peer_mapping_wq;
 
+	/* number of created peers; protected by data_lock */
+	int num_peers;
+
 	struct work_struct offchan_tx_work;
 	struct sk_buff_head offchan_tx_queue;
 	struct completion offchan_tx_completed;
 	struct sk_buff *offchan_tx_skb;
 
+	struct work_struct wmi_mgmt_tx_work;
+	struct sk_buff_head wmi_mgmt_tx_queue;
+
+	enum ath10k_state state;
+
+	struct work_struct restart_work;
+
+	/* cycle count is reported twice for each visited channel during scan.
+	 * access protected by data_lock */
+	u32 survey_last_rx_clear_count;
+	u32 survey_last_cycle_count;
+	struct survey_info survey[ATH10K_NUM_CHANS];
+
+	struct dfs_pattern_detector *dfs_detector;
+
 #ifdef CONFIG_ATH10K_DEBUGFS
 	struct ath10k_debug debug;
 #endif
 };
 
 struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
-				  enum ath10k_bus bus,
 				  const struct ath10k_hif_ops *hif_ops);
 void ath10k_core_destroy(struct ath10k *ar);
 
-int ath10k_core_register(struct ath10k *ar);
+int ath10k_core_start(struct ath10k *ar);
+int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt);
+void ath10k_core_stop(struct ath10k *ar);
+int ath10k_core_register(struct ath10k *ar, u32 chip_id);
 void ath10k_core_unregister(struct ath10k *ar);
 
-int ath10k_core_target_suspend(struct ath10k *ar);
-int ath10k_core_target_resume(struct ath10k *ar);
-
 #endif /* _CORE_H_ */
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/debug.c linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/debug.c
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/debug.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/debug.c	2015-09-29 11:15:06.917740641 +0200
@@ -21,6 +21,9 @@
 #include "core.h"
 #include "debug.h"
 
+/* ms */
+#define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000
+
 static int ath10k_printk(const char *level, const char *fmt, ...)
 {
 	struct va_format vaf;
@@ -161,7 +164,7 @@
 	struct wmi_pdev_stats *ps;
 	int i;
 
-	mutex_lock(&ar->conf_mutex);
+	spin_lock_bh(&ar->data_lock);
 
 	stats = &ar->debug.target_stats;
 
@@ -259,7 +262,7 @@
 		}
 	}
 
-	mutex_unlock(&ar->conf_mutex);
+	spin_unlock_bh(&ar->data_lock);
 	complete(&ar->debug.event_stats_compl);
 }
 
@@ -268,35 +271,35 @@
 {
 	struct ath10k *ar = file->private_data;
 	struct ath10k_target_stats *fw_stats;
-	char *buf;
+	char *buf = NULL;
 	unsigned int len = 0, buf_len = 2500;
-	ssize_t ret_cnt;
+	ssize_t ret_cnt = 0;
 	long left;
 	int i;
 	int ret;
 
 	fw_stats = &ar->debug.target_stats;
 
+	mutex_lock(&ar->conf_mutex);
+
+	if (ar->state != ATH10K_STATE_ON)
+		goto exit;
+
 	buf = kzalloc(buf_len, GFP_KERNEL);
 	if (!buf)
-		return -ENOMEM;
+		goto exit;
 
 	ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
 	if (ret) {
 		ath10k_warn("could not request stats (%d)\n", ret);
-		kfree(buf);
-		return -EIO;
+		goto exit;
 	}
 
 	left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+	if (left <= 0)
+		goto exit;
 
-	if (left <= 0) {
-		kfree(buf);
-		return -ETIMEDOUT;
-	}
-
-	mutex_lock(&ar->conf_mutex);
-
+	spin_lock_bh(&ar->data_lock);
 	len += scnprintf(buf + len, buf_len - len, "\n");
 	len += scnprintf(buf + len, buf_len - len, "%30s\n",
 			 "ath10k PDEV stats");
@@ -424,14 +427,15 @@
 				 fw_stats->peer_stat[i].peer_tx_rate);
 		len += scnprintf(buf + len, buf_len - len, "\n");
 	}
+	spin_unlock_bh(&ar->data_lock);
 
 	if (len > buf_len)
 		len = buf_len;
 
 	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 
+exit:
 	mutex_unlock(&ar->conf_mutex);
-
 	kfree(buf);
 	return ret_cnt;
 }
@@ -443,6 +447,341 @@
 	.llseek = default_llseek,
 };
 
+static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
+					     char __user *user_buf,
+					     size_t count, loff_t *ppos)
+{
+	const char buf[] = "To simulate firmware crash write the keyword"
+			   " `crash` to this file.\nThis will force firmware"
+			   " to report a crash to the host system.\n";
+	return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+}
+
+static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
+					      const char __user *user_buf,
+					      size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	char buf[32] = {};
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+	if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	if (ar->state != ATH10K_STATE_ON &&
+	    ar->state != ATH10K_STATE_RESTARTED) {
+		ret = -ENETDOWN;
+		goto exit;
+	}
+
+	ath10k_info("simulating firmware crash\n");
+
+	ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
+	if (ret)
+		ath10k_warn("failed to force fw hang (%d)\n", ret);
+
+	if (ret == 0)
+		ret = count;
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static const struct file_operations fops_simulate_fw_crash = {
+	.read = ath10k_read_simulate_fw_crash,
+	.write = ath10k_write_simulate_fw_crash,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+static ssize_t ath10k_read_chip_id(struct file *file, char __user *user_buf,
+				   size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	unsigned int len;
+	char buf[50];
+
+	len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->chip_id);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_chip_id = {
+	.read = ath10k_read_chip_id,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+static int ath10k_debug_htt_stats_req(struct ath10k *ar)
+{
+	u64 cookie;
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	if (ar->debug.htt_stats_mask == 0)
+		/* htt stats are disabled */
+		return 0;
+
+	if (ar->state != ATH10K_STATE_ON)
+		return 0;
+
+	cookie = get_jiffies_64();
+
+	ret = ath10k_htt_h2t_stats_req(&ar->htt, ar->debug.htt_stats_mask,
+				       cookie);
+	if (ret) {
+		ath10k_warn("failed to send htt stats request: %d\n", ret);
+		return ret;
+	}
+
+	queue_delayed_work(ar->workqueue, &ar->debug.htt_stats_dwork,
+			   msecs_to_jiffies(ATH10K_DEBUG_HTT_STATS_INTERVAL));
+
+	return 0;
+}
+
+static void ath10k_debug_htt_stats_dwork(struct work_struct *work)
+{
+	struct ath10k *ar = container_of(work, struct ath10k,
+					 debug.htt_stats_dwork.work);
+
+	mutex_lock(&ar->conf_mutex);
+
+	ath10k_debug_htt_stats_req(ar);
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
+static ssize_t ath10k_read_htt_stats_mask(struct file *file,
+					    char __user *user_buf,
+					    size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	char buf[32];
+	unsigned int len;
+
+	len = scnprintf(buf, sizeof(buf), "%lu\n", ar->debug.htt_stats_mask);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath10k_write_htt_stats_mask(struct file *file,
+					     const char __user *user_buf,
+					     size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	unsigned long mask;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 0, &mask);
+	if (ret)
+		return ret;
+
+	/* max 8 bit masks (for now) */
+	if (mask > 0xff)
+		return -E2BIG;
+
+	mutex_lock(&ar->conf_mutex);
+
+	ar->debug.htt_stats_mask = mask;
+
+	ret = ath10k_debug_htt_stats_req(ar);
+	if (ret)
+		goto out;
+
+	ret = count;
+
+out:
+	mutex_unlock(&ar->conf_mutex);
+
+	return ret;
+}
+
+static const struct file_operations fops_htt_stats_mask = {
+	.read = ath10k_read_htt_stats_mask,
+	.write = ath10k_write_htt_stats_mask,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+static ssize_t ath10k_read_fw_dbglog(struct file *file,
+					    char __user *user_buf,
+					    size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	unsigned int len;
+	char buf[32];
+
+	len = scnprintf(buf, sizeof(buf), "0x%08x\n",
+			ar->debug.fw_dbglog_mask);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath10k_write_fw_dbglog(struct file *file,
+				      const char __user *user_buf,
+				      size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	unsigned long mask;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 0, &mask);
+	if (ret)
+		return ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	ar->debug.fw_dbglog_mask = mask;
+
+	if (ar->state == ATH10K_STATE_ON) {
+		ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask);
+		if (ret) {
+			ath10k_warn("dbglog cfg failed from debugfs: %d\n",
+				    ret);
+			goto exit;
+		}
+	}
+
+	ret = count;
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+
+	return ret;
+}
+
+static const struct file_operations fops_fw_dbglog = {
+	.read = ath10k_read_fw_dbglog,
+	.write = ath10k_write_fw_dbglog,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+int ath10k_debug_start(struct ath10k *ar)
+{
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	ret = ath10k_debug_htt_stats_req(ar);
+	if (ret)
+		/* continue normally anyway, this isn't serious */
+		ath10k_warn("failed to start htt stats workqueue: %d\n", ret);
+
+	if (ar->debug.fw_dbglog_mask) {
+		ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask);
+		if (ret)
+			/* not serious */
+			ath10k_warn("failed to enable dbglog during start: %d",
+				    ret);
+	}
+
+	return 0;
+}
+
+void ath10k_debug_stop(struct ath10k *ar)
+{
+	lockdep_assert_held(&ar->conf_mutex);
+
+	/* Must not use _sync to avoid deadlock, we do that in
+	 * ath10k_debug_destroy(). The check for htt_stats_mask is to avoid
+	 * warning from del_timer(). */
+	if (ar->debug.htt_stats_mask != 0)
+		cancel_delayed_work(&ar->debug.htt_stats_dwork);
+}
+
+static ssize_t ath10k_write_simulate_radar(struct file *file,
+					   const char __user *user_buf,
+					   size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+
+	ieee80211_radar_detected(ar->hw);
+
+	return count;
+}
+
+static const struct file_operations fops_simulate_radar = {
+	.write = ath10k_write_simulate_radar,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+#define ATH10K_DFS_STAT(s, p) (\
+	len += scnprintf(buf + len, size - len, "%-28s : %10u\n", s, \
+			 ar->debug.dfs_stats.p))
+
+#define ATH10K_DFS_POOL_STAT(s, p) (\
+	len += scnprintf(buf + len, size - len, "%-28s : %10u\n", s, \
+			 ar->debug.dfs_pool_stats.p))
+
+static ssize_t ath10k_read_dfs_stats(struct file *file, char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	int retval = 0, len = 0;
+	const int size = 8000;
+	struct ath10k *ar = file->private_data;
+	char *buf;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	if (!ar->dfs_detector) {
+		len += scnprintf(buf + len, size - len, "DFS not enabled\n");
+		goto exit;
+	}
+
+	ar->debug.dfs_pool_stats =
+			ar->dfs_detector->get_stats(ar->dfs_detector);
+
+	len += scnprintf(buf + len, size - len, "Pulse detector statistics:\n");
+
+	ATH10K_DFS_STAT("reported phy errors", phy_errors);
+	ATH10K_DFS_STAT("pulse events reported", pulses_total);
+	ATH10K_DFS_STAT("DFS pulses detected", pulses_detected);
+	ATH10K_DFS_STAT("DFS pulses discarded", pulses_discarded);
+	ATH10K_DFS_STAT("Radars detected", radar_detected);
+
+	len += scnprintf(buf + len, size - len, "Global Pool statistics:\n");
+	ATH10K_DFS_POOL_STAT("Pool references", pool_reference);
+	ATH10K_DFS_POOL_STAT("Pulses allocated", pulse_allocated);
+	ATH10K_DFS_POOL_STAT("Pulses alloc error", pulse_alloc_error);
+	ATH10K_DFS_POOL_STAT("Pulses in use", pulse_used);
+	ATH10K_DFS_POOL_STAT("Seqs. allocated", pseq_allocated);
+	ATH10K_DFS_POOL_STAT("Seqs. alloc error", pseq_alloc_error);
+	ATH10K_DFS_POOL_STAT("Seqs. in use", pseq_used);
+
+exit:
+	if (len > size)
+		len = size;
+
+	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+
+	return retval;
+}
+
+static const struct file_operations fops_dfs_stats = {
+	.read = ath10k_read_dfs_stats,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 int ath10k_debug_create(struct ath10k *ar)
 {
 	ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
@@ -451,6 +790,9 @@
 	if (!ar->debug.debugfs_phy)
 		return -ENOMEM;
 
+	INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork,
+			  ath10k_debug_htt_stats_dwork);
+
 	init_completion(&ar->debug.event_stats_compl);
 
 	debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
@@ -459,8 +801,40 @@
 	debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
 			    &fops_wmi_services);
 
+	debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
+			    ar, &fops_simulate_fw_crash);
+
+	debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy,
+			    ar, &fops_chip_id);
+
+	debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy,
+			    ar, &fops_htt_stats_mask);
+
+	debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy,
+			    ar, &fops_fw_dbglog);
+
+	if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)) {
+		debugfs_create_file("dfs_simulate_radar", S_IWUSR,
+				    ar->debug.debugfs_phy, ar,
+				    &fops_simulate_radar);
+
+		debugfs_create_bool("dfs_block_radar_events", S_IWUSR,
+				    ar->debug.debugfs_phy,
+				    &ar->dfs_block_radar_events);
+
+		debugfs_create_file("dfs_stats", S_IRUSR,
+				    ar->debug.debugfs_phy, ar,
+				    &fops_dfs_stats);
+	}
+
 	return 0;
 }
+
+void ath10k_debug_destroy(struct ath10k *ar)
+{
+	cancel_delayed_work_sync(&ar->debug.htt_stats_dwork);
+}
+
 #endif /* CONFIG_ATH10K_DEBUGFS */
 
 #ifdef CONFIG_ATH10K_DEBUG
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/debug.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/debug.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/debug.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/debug.h	2015-09-29 11:15:06.917740641 +0200
@@ -27,11 +27,13 @@
 	ATH10K_DBG_HTC		= 0x00000004,
 	ATH10K_DBG_HTT		= 0x00000008,
 	ATH10K_DBG_MAC		= 0x00000010,
-	ATH10K_DBG_CORE		= 0x00000020,
+	ATH10K_DBG_BOOT		= 0x00000020,
 	ATH10K_DBG_PCI_DUMP	= 0x00000040,
 	ATH10K_DBG_HTT_DUMP	= 0x00000080,
 	ATH10K_DBG_MGMT		= 0x00000100,
 	ATH10K_DBG_DATA		= 0x00000200,
+	ATH10K_DBG_BMI		= 0x00000400,
+	ATH10K_DBG_REGULATORY	= 0x00000800,
 	ATH10K_DBG_ANY		= 0xffffffff,
 };
 
@@ -42,19 +44,37 @@
 extern __printf(1, 2) int ath10k_warn(const char *fmt, ...);
 
 #ifdef CONFIG_ATH10K_DEBUGFS
+int ath10k_debug_start(struct ath10k *ar);
+void ath10k_debug_stop(struct ath10k *ar);
 int ath10k_debug_create(struct ath10k *ar);
+void ath10k_debug_destroy(struct ath10k *ar);
 void ath10k_debug_read_service_map(struct ath10k *ar,
 				   void *service_map,
 				   size_t map_size);
 void ath10k_debug_read_target_stats(struct ath10k *ar,
 				    struct wmi_stats_event *ev);
 
+#define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++)
+
 #else
+static inline int ath10k_debug_start(struct ath10k *ar)
+{
+	return 0;
+}
+
+static inline void ath10k_debug_stop(struct ath10k *ar)
+{
+}
+
 static inline int ath10k_debug_create(struct ath10k *ar)
 {
 	return 0;
 }
 
+static inline void ath10k_debug_destroy(struct ath10k *ar)
+{
+}
+
 static inline void ath10k_debug_read_service_map(struct ath10k *ar,
 						 void *service_map,
 						 size_t map_size)
@@ -65,6 +85,9 @@
 						  struct wmi_stats_event *ev)
 {
 }
+
+#define ATH10K_DFS_STAT_INC(ar, c) do { } while (0)
+
 #endif /* CONFIG_ATH10K_DEBUGFS */
 
 #ifdef CONFIG_ATH10K_DEBUG
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/hif.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/hif.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/hif.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/hif.h	2015-09-29 11:15:06.921740666 +0200
@@ -21,6 +21,14 @@
 #include <linux/kernel.h>
 #include "core.h"
 
+struct ath10k_hif_sg_item {
+	u16 transfer_id;
+	void *transfer_context; /* NULL = tx completion callback not called */
+	void *vaddr; /* for debugging mostly */
+	u32 paddr;
+	u16 len;
+};
+
 struct ath10k_hif_cb {
 	int (*tx_completion)(struct ath10k *ar,
 			     struct sk_buff *wbuf,
@@ -31,11 +39,9 @@
 };
 
 struct ath10k_hif_ops {
-	/* Send the head of a buffer to HIF for transmission to the target. */
-	int (*send_head)(struct ath10k *ar, u8 pipe_id,
-			 unsigned int transfer_id,
-			 unsigned int nbytes,
-			 struct sk_buff *buf);
+	/* send a scatter-gather list to the target */
+	int (*tx_sg)(struct ath10k *ar, u8 pipe_id,
+		     struct ath10k_hif_sg_item *items, int n_items);
 
 	/*
 	 * API to handle HIF-specific BMI message exchanges, this API is
@@ -46,8 +52,11 @@
 				void *request, u32 request_len,
 				void *response, u32 *response_len);
 
+	/* Post BMI phase, after FW is loaded. Starts regular operation */
 	int (*start)(struct ath10k *ar);
 
+	/* Clean up what start() did. This does not revert to BMI phase. If
+	 * desired so, call power_down() and power_up() */
 	void (*stop)(struct ath10k *ar);
 
 	int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
@@ -66,19 +75,28 @@
 	 */
 	void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force);
 
-	void (*init)(struct ath10k *ar,
+	void (*set_callbacks)(struct ath10k *ar,
 		     struct ath10k_hif_cb *callbacks);
 
 	u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+
+	/* Power up the device and enter BMI transfer mode for FW download */
+	int (*power_up)(struct ath10k *ar);
+
+	/* Power down the device and free up resources. stop() must be called
+	 * before this if start() was called earlier */
+	void (*power_down)(struct ath10k *ar);
+
+	int (*suspend)(struct ath10k *ar);
+	int (*resume)(struct ath10k *ar);
 };
 
 
-static inline int ath10k_hif_send_head(struct ath10k *ar, u8 pipe_id,
-				       unsigned int transfer_id,
-				       unsigned int nbytes,
-				       struct sk_buff *buf)
+static inline int ath10k_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
+				   struct ath10k_hif_sg_item *items,
+				   int n_items)
 {
-	return ar->hif.ops->send_head(ar, pipe_id, transfer_id, nbytes, buf);
+	return ar->hif.ops->tx_sg(ar, pipe_id, items, n_items);
 }
 
 static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar,
@@ -122,10 +140,10 @@
 	ar->hif.ops->send_complete_check(ar, pipe_id, force);
 }
 
-static inline void ath10k_hif_init(struct ath10k *ar,
+static inline void ath10k_hif_set_callbacks(struct ath10k *ar,
 				   struct ath10k_hif_cb *callbacks)
 {
-	ar->hif.ops->init(ar, callbacks);
+	ar->hif.ops->set_callbacks(ar, callbacks);
 }
 
 static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
@@ -134,4 +152,30 @@
 	return ar->hif.ops->get_free_queue_number(ar, pipe_id);
 }
 
+static inline int ath10k_hif_power_up(struct ath10k *ar)
+{
+	return ar->hif.ops->power_up(ar);
+}
+
+static inline void ath10k_hif_power_down(struct ath10k *ar)
+{
+	ar->hif.ops->power_down(ar);
+}
+
+static inline int ath10k_hif_suspend(struct ath10k *ar)
+{
+	if (!ar->hif.ops->suspend)
+		return -EOPNOTSUPP;
+
+	return ar->hif.ops->suspend(ar);
+}
+
+static inline int ath10k_hif_resume(struct ath10k *ar)
+{
+	if (!ar->hif.ops->resume)
+		return -EOPNOTSUPP;
+
+	return ar->hif.ops->resume(ar);
+}
+
 #endif /* _HIF_H_ */
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/htc.c linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/htc.c
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/htc.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/htc.c	2015-09-29 11:15:06.921740666 +0200
@@ -63,7 +63,9 @@
 static inline void ath10k_htc_restore_tx_skb(struct ath10k_htc *htc,
 					     struct sk_buff *skb)
 {
-	ath10k_skb_unmap(htc->ar->dev, skb);
+	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+
+	dma_unmap_single(htc->ar->dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE);
 	skb_pull(skb, sizeof(struct ath10k_htc_hdr));
 }
 
@@ -103,10 +105,10 @@
 	struct ath10k_htc_hdr *hdr;
 
 	hdr = (struct ath10k_htc_hdr *)skb->data;
-	memset(hdr, 0, sizeof(*hdr));
 
 	hdr->eid = ep->eid;
 	hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr));
+	hdr->flags = 0;
 
 	spin_lock_bh(&ep->htc->tx_lock);
 	hdr->seq_no = ep->seq_no++;
@@ -117,197 +119,96 @@
 	spin_unlock_bh(&ep->htc->tx_lock);
 }
 
-static int ath10k_htc_issue_skb(struct ath10k_htc *htc,
-				struct ath10k_htc_ep *ep,
-				struct sk_buff *skb,
-				u8 credits)
+int ath10k_htc_send(struct ath10k_htc *htc,
+		    enum ath10k_htc_ep_id eid,
+		    struct sk_buff *skb)
 {
+	struct ath10k_htc_ep *ep = &htc->endpoint[eid];
 	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+	struct ath10k_hif_sg_item sg_item;
+	struct device *dev = htc->ar->dev;
+	int credits = 0;
 	int ret;
 
-	ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
-		   ep->eid, skb);
-
-	ath10k_htc_prepare_tx_skb(ep, skb);
-
-	ret = ath10k_skb_map(htc->ar->dev, skb);
-	if (ret)
-		goto err;
-
-	ret = ath10k_hif_send_head(htc->ar,
-				   ep->ul_pipe_id,
-				   ep->eid,
-				   skb->len,
-				   skb);
-	if (unlikely(ret))
-		goto err;
+	if (htc->ar->state == ATH10K_STATE_WEDGED)
+		return -ECOMM;
 
-	return 0;
-err:
-	ath10k_warn("HTC issue failed: %d\n", ret);
-
-	spin_lock_bh(&htc->tx_lock);
-	ep->tx_credits += credits;
-	spin_unlock_bh(&htc->tx_lock);
+	if (eid >= ATH10K_HTC_EP_COUNT) {
+		ath10k_warn("Invalid endpoint id: %d\n", eid);
+		return -ENOENT;
+	}
 
-	/* this is the simplest way to handle out-of-resources for non-credit
-	 * based endpoints. credit based endpoints can still get -ENOSR, but
-	 * this is highly unlikely as credit reservation should prevent that */
-	if (ret == -ENOSR) {
+	/* FIXME: This looks ugly, can we fix it? */
 		spin_lock_bh(&htc->tx_lock);
-		__skb_queue_head(&ep->tx_queue, skb);
+	if (htc->stopped) {
 		spin_unlock_bh(&htc->tx_lock);
-
-		return ret;
+		return -ESHUTDOWN;
 	}
+	spin_unlock_bh(&htc->tx_lock);
 
-	skb_cb->is_aborted = true;
-	ath10k_htc_notify_tx_completion(ep, skb);
+	skb_push(skb, sizeof(struct ath10k_htc_hdr));
 
-	return ret;
+	if (ep->tx_credit_flow_enabled) {
+		credits = DIV_ROUND_UP(skb->len, htc->target_credit_size);
+		spin_lock_bh(&htc->tx_lock);
+		if (ep->tx_credits < credits) {
+			spin_unlock_bh(&htc->tx_lock);
+			ret = -EAGAIN;
+			goto err_pull;
 }
-
-static struct sk_buff *ath10k_htc_get_skb_credit_based(struct ath10k_htc *htc,
-						       struct ath10k_htc_ep *ep,
-						       u8 *credits)
-{
-	struct sk_buff *skb;
-	struct ath10k_skb_cb *skb_cb;
-	int credits_required;
-	int remainder;
-	unsigned int transfer_len;
-
-	lockdep_assert_held(&htc->tx_lock);
-
-	skb = __skb_dequeue(&ep->tx_queue);
-	if (!skb)
-		return NULL;
-
-	skb_cb = ATH10K_SKB_CB(skb);
-	transfer_len = skb->len;
-
-	if (likely(transfer_len <= htc->target_credit_size)) {
-		credits_required = 1;
-	} else {
-		/* figure out how many credits this message requires */
-		credits_required = transfer_len / htc->target_credit_size;
-		remainder = transfer_len % htc->target_credit_size;
-
-		if (remainder)
-			credits_required++;
+		ep->tx_credits -= credits;
+		spin_unlock_bh(&htc->tx_lock);
 	}
 
-	ath10k_dbg(ATH10K_DBG_HTC, "Credits required %d got %d\n",
-		   credits_required, ep->tx_credits);
+	ath10k_htc_prepare_tx_skb(ep, skb);
 
-	if (ep->tx_credits < credits_required) {
-		__skb_queue_head(&ep->tx_queue, skb);
-		return NULL;
-	}
+	skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
+	ret = dma_mapping_error(dev, skb_cb->paddr);
+	if (ret)
+		goto err_credits;
 
-	ep->tx_credits -= credits_required;
-	*credits = credits_required;
-	return skb;
-}
+	sg_item.transfer_id = ep->eid;
+	sg_item.transfer_context = skb;
+	sg_item.vaddr = skb->data;
+	sg_item.paddr = skb_cb->paddr;
+	sg_item.len = skb->len;
 
-static void ath10k_htc_send_work(struct work_struct *work)
-{
-	struct ath10k_htc_ep *ep = container_of(work,
-					struct ath10k_htc_ep, send_work);
-	struct ath10k_htc *htc = ep->htc;
-	struct sk_buff *skb;
-	u8 credits = 0;
-	int ret;
+	ret = ath10k_hif_tx_sg(htc->ar, ep->ul_pipe_id, &sg_item, 1);
+	if (ret)
+		goto err_unmap;
 
-	while (true) {
-		if (ep->ul_is_polled)
-			ath10k_htc_send_complete_check(ep, 0);
+	return 0;
 
+err_unmap:
+	dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE);
+err_credits:
+	if (ep->tx_credit_flow_enabled) {
 		spin_lock_bh(&htc->tx_lock);
-		if (ep->tx_credit_flow_enabled)
-			skb = ath10k_htc_get_skb_credit_based(htc, ep,
-							      &credits);
-		else
-			skb = __skb_dequeue(&ep->tx_queue);
+		ep->tx_credits += credits;
 		spin_unlock_bh(&htc->tx_lock);
 
-		if (!skb)
-			break;
-
-		ret = ath10k_htc_issue_skb(htc, ep, skb, credits);
-		if (ret == -ENOSR)
-			break;
-	}
+		if (ep->ep_ops.ep_tx_credits)
+			ep->ep_ops.ep_tx_credits(htc->ar);
 }
-
-int ath10k_htc_send(struct ath10k_htc *htc,
-		    enum ath10k_htc_ep_id eid,
-		    struct sk_buff *skb)
-{
-	struct ath10k_htc_ep *ep = &htc->endpoint[eid];
-
-	if (eid >= ATH10K_HTC_EP_COUNT) {
-		ath10k_warn("Invalid endpoint id: %d\n", eid);
-		return -ENOENT;
-	}
-
-	skb_push(skb, sizeof(struct ath10k_htc_hdr));
-
-	spin_lock_bh(&htc->tx_lock);
-	__skb_queue_tail(&ep->tx_queue, skb);
-	spin_unlock_bh(&htc->tx_lock);
-
-	queue_work(htc->ar->workqueue, &ep->send_work);
-	return 0;
+err_pull:
+	skb_pull(skb, sizeof(struct ath10k_htc_hdr));
+	return ret;
 }
 
 static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
 					    struct sk_buff *skb,
 					    unsigned int eid)
 {
-	struct ath10k_htc *htc = ar->htc;
+	struct ath10k_htc *htc = &ar->htc;
 	struct ath10k_htc_ep *ep = &htc->endpoint[eid];
-	bool stopping;
-
-	ath10k_htc_notify_tx_completion(ep, skb);
-	/* the skb now belongs to the completion handler */
-
-	spin_lock_bh(&htc->tx_lock);
-	stopping = htc->stopping;
-	spin_unlock_bh(&htc->tx_lock);
-
-	if (!ep->tx_credit_flow_enabled && !stopping)
-		/*
-		 * note: when using TX credit flow, the re-checking of
-		 * queues happens when credits flow back from the target.
-		 * in the non-TX credit case, we recheck after the packet
-		 * completes
-		 */
-		queue_work(ar->workqueue, &ep->send_work);
 
+	if (WARN_ON_ONCE(!skb))
 	return 0;
-}
-
-/* flush endpoint TX queue */
-static void ath10k_htc_flush_endpoint_tx(struct ath10k_htc *htc,
-					 struct ath10k_htc_ep *ep)
-{
-	struct sk_buff *skb;
-	struct ath10k_skb_cb *skb_cb;
-
-	spin_lock_bh(&htc->tx_lock);
-	for (;;) {
-		skb = __skb_dequeue(&ep->tx_queue);
-		if (!skb)
-			break;
 
-		skb_cb = ATH10K_SKB_CB(skb);
-		skb_cb->is_aborted = true;
 		ath10k_htc_notify_tx_completion(ep, skb);
-	}
-	spin_unlock_bh(&htc->tx_lock);
+	/* the skb now belongs to the completion handler */
 
-	cancel_work_sync(&ep->send_work);
+	return 0;
 }
 
 /***********/
@@ -339,8 +240,11 @@
 		ep = &htc->endpoint[report->eid];
 		ep->tx_credits += report->credits;
 
-		if (ep->tx_credits && !skb_queue_empty(&ep->tx_queue))
-			queue_work(htc->ar->workqueue, &ep->send_work);
+		if (ep->ep_ops.ep_tx_credits) {
+			spin_unlock_bh(&htc->tx_lock);
+			ep->ep_ops.ep_tx_credits(htc->ar);
+			spin_lock_bh(&htc->tx_lock);
+		}
 	}
 	spin_unlock_bh(&htc->tx_lock);
 }
@@ -414,7 +318,7 @@
 					    u8 pipe_id)
 {
 	int status = 0;
-	struct ath10k_htc *htc = ar->htc;
+	struct ath10k_htc *htc = &ar->htc;
 	struct ath10k_htc_hdr *hdr;
 	struct ath10k_htc_ep *ep;
 	u16 payload_len;
@@ -598,10 +502,8 @@
 		ep->max_ep_message_len = 0;
 		ep->max_tx_queue_depth = 0;
 		ep->eid = i;
-		skb_queue_head_init(&ep->tx_queue);
 		ep->htc = htc;
 		ep->tx_credit_flow_enabled = true;
-		INIT_WORK(&ep->send_work, ath10k_htc_send_work);
 	}
 }
 
@@ -646,14 +548,6 @@
 	u16 credit_count;
 	u16 credit_size;
 
-	INIT_COMPLETION(htc->ctl_resp);
-
-	status = ath10k_hif_start(htc->ar);
-	if (status) {
-		ath10k_err("could not start HIF (%d)\n", status);
-		goto err_start;
-	}
-
 	status = wait_for_completion_timeout(&htc->ctl_resp,
 					     ATH10K_HTC_WAIT_TIMEOUT_HZ);
 	if (status <= 0) {
@@ -661,15 +555,13 @@
 			status = -ETIMEDOUT;
 
 		ath10k_err("ctl_resp never came in (%d)\n", status);
-		goto err_target;
+		return status;
 	}
 
 	if (htc->control_resp_len < sizeof(msg->hdr) + sizeof(msg->ready)) {
 		ath10k_err("Invalid HTC ready msg len:%d\n",
 			   htc->control_resp_len);
-
-		status = -ECOMM;
-		goto err_target;
+		return -ECOMM;
 	}
 
 	msg = (struct ath10k_htc_msg *)htc->control_resp_buffer;
@@ -679,8 +571,7 @@
 
 	if (message_id != ATH10K_HTC_MSG_READY_ID) {
 		ath10k_err("Invalid HTC ready msg: 0x%x\n", message_id);
-		status = -ECOMM;
-		goto err_target;
+		return -ECOMM;
 	}
 
 	htc->total_transmit_credits = credit_count;
@@ -693,9 +584,8 @@
 
 	if ((htc->total_transmit_credits == 0) ||
 	    (htc->target_credit_size == 0)) {
-		status = -ECOMM;
 		ath10k_err("Invalid credit size received\n");
-		goto err_target;
+		return -ECOMM;
 	}
 
 	ath10k_htc_setup_target_buffer_assignments(htc);
@@ -712,14 +602,10 @@
 	status = ath10k_htc_connect_service(htc, &conn_req, &conn_resp);
 	if (status) {
 		ath10k_err("could not connect to htc service (%d)\n", status);
-		goto err_target;
+		return status;
 	}
 
 	return 0;
-err_target:
-	ath10k_hif_stop(htc->ar);
-err_start:
-	return status;
 }
 
 int ath10k_htc_connect_service(struct ath10k_htc *htc,
@@ -751,7 +637,8 @@
 	tx_alloc = ath10k_htc_get_credit_allocation(htc,
 						    conn_req->service_id);
 	if (!tx_alloc)
-		ath10k_warn("HTC Service %s does not allocate target credits\n",
+		ath10k_dbg(ATH10K_DBG_BOOT,
+			   "boot htc service %s does not allocate target credits\n",
 			    htc_service_name(conn_req->service_id));
 
 	skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
@@ -770,16 +657,16 @@
 
 	flags |= SM(tx_alloc, ATH10K_HTC_CONN_FLAGS_RECV_ALLOC);
 
-	req_msg = &msg->connect_service;
-	req_msg->flags = __cpu_to_le16(flags);
-	req_msg->service_id = __cpu_to_le16(conn_req->service_id);
-
 	/* Only enable credit flow control for WMI ctrl service */
 	if (conn_req->service_id != ATH10K_HTC_SVC_ID_WMI_CONTROL) {
 		flags |= ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
 		disable_credit_flow_ctrl = true;
 	}
 
+	req_msg = &msg->connect_service;
+	req_msg->flags = __cpu_to_le16(flags);
+	req_msg->service_id = __cpu_to_le16(conn_req->service_id);
+
 	INIT_COMPLETION(htc->ctl_resp);
 
 	status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
@@ -871,19 +758,19 @@
 	if (status)
 		return status;
 
-	ath10k_dbg(ATH10K_DBG_HTC,
-		   "HTC service: %s UL pipe: %d DL pipe: %d eid: %d ready\n",
+	ath10k_dbg(ATH10K_DBG_BOOT,
+		   "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n",
 		   htc_service_name(ep->service_id), ep->ul_pipe_id,
 		   ep->dl_pipe_id, ep->eid);
 
-	ath10k_dbg(ATH10K_DBG_HTC,
-		   "EP %d UL polled: %d, DL polled: %d\n",
+	ath10k_dbg(ATH10K_DBG_BOOT,
+		   "boot htc ep %d ul polled %d dl polled %d\n",
 		   ep->eid, ep->ul_is_polled, ep->dl_is_polled);
 
 	if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
 		ep->tx_credit_flow_enabled = false;
-		ath10k_dbg(ATH10K_DBG_HTC,
-			   "HTC service: %s eid: %d TX flow control disabled\n",
+		ath10k_dbg(ATH10K_DBG_BOOT,
+			   "boot htc service '%s' eid %d TX flow control disabled\n",
 			   htc_service_name(ep->service_id), assigned_eid);
 	}
 
@@ -943,39 +830,23 @@
  */
 void ath10k_htc_stop(struct ath10k_htc *htc)
 {
-	int i;
-	struct ath10k_htc_ep *ep;
-
 	spin_lock_bh(&htc->tx_lock);
-	htc->stopping = true;
+	htc->stopped = true;
 	spin_unlock_bh(&htc->tx_lock);
 
-	for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
-		ep = &htc->endpoint[i];
-		ath10k_htc_flush_endpoint_tx(htc, ep);
-	}
-
 	ath10k_hif_stop(htc->ar);
-	ath10k_htc_reset_endpoint_states(htc);
 }
 
 /* registered target arrival callback from the HIF layer */
-struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
-				     struct ath10k_htc_ops *htc_ops)
+int ath10k_htc_init(struct ath10k *ar)
 {
 	struct ath10k_hif_cb htc_callbacks;
 	struct ath10k_htc_ep *ep = NULL;
-	struct ath10k_htc *htc = NULL;
-
-	/* FIXME: use struct ath10k instead */
-	htc = kzalloc(sizeof(struct ath10k_htc), GFP_KERNEL);
-	if (!htc)
-		return ERR_PTR(-ENOMEM);
+	struct ath10k_htc *htc = &ar->htc;
 
 	spin_lock_init(&htc->tx_lock);
 
-	memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops));
-
+	htc->stopped = false;
 	ath10k_htc_reset_endpoint_states(htc);
 
 	/* setup HIF layer callbacks */
@@ -986,15 +857,10 @@
 	/* Get HIF default pipe for HTC message exchange */
 	ep = &htc->endpoint[ATH10K_HTC_EP_0];
 
-	ath10k_hif_init(ar, &htc_callbacks);
+	ath10k_hif_set_callbacks(ar, &htc_callbacks);
 	ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id);
 
 	init_completion(&htc->ctl_resp);
 
-	return htc;
-}
-
-void ath10k_htc_destroy(struct ath10k_htc *htc)
-{
-	kfree(htc);
+	return 0;
 }
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/htc.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/htc.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/htc.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/htc.h	2015-09-29 11:15:06.921740666 +0200
@@ -276,6 +276,7 @@
 struct ath10k_htc_ep_ops {
 	void (*ep_tx_complete)(struct ath10k *, struct sk_buff *);
 	void (*ep_rx_complete)(struct ath10k *, struct sk_buff *);
+	void (*ep_tx_credits)(struct ath10k *);
 };
 
 /* service connection information */
@@ -315,15 +316,11 @@
 	int ul_is_polled; /* call HIF to get tx completions */
 	int dl_is_polled; /* call HIF to fetch rx (not implemented) */
 
-	struct sk_buff_head tx_queue;
-
 	u8 seq_no; /* for debugging */
 	int tx_credits;
 	int tx_credit_size;
 	int tx_credits_per_max_message;
 	bool tx_credit_flow_enabled;
-
-	struct work_struct send_work;
 };
 
 struct ath10k_htc_svc_tx_credits {
@@ -335,7 +332,7 @@
 	struct ath10k *ar;
 	struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT];
 
-	/* protects endpoint and stopping fields */
+	/* protects endpoint and stopped fields */
 	spinlock_t tx_lock;
 
 	struct ath10k_htc_ops htc_ops;
@@ -349,11 +346,10 @@
 	struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT];
 	int target_credit_size;
 
-	bool stopping;
+	bool stopped;
 };
 
-struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
-				     struct ath10k_htc_ops *htc_ops);
+int ath10k_htc_init(struct ath10k *ar);
 int ath10k_htc_wait_target(struct ath10k_htc *htc);
 int ath10k_htc_start(struct ath10k_htc *htc);
 int ath10k_htc_connect_service(struct ath10k_htc *htc,
@@ -362,7 +358,6 @@
 int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
 		    struct sk_buff *packet);
 void ath10k_htc_stop(struct ath10k_htc *htc);
-void ath10k_htc_destroy(struct ath10k_htc *htc);
 struct sk_buff *ath10k_htc_alloc_skb(int size);
 
 #endif
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/htt.c linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/htt.c
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/htt.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/htt.c	2015-09-29 11:15:06.921740666 +0200
@@ -16,6 +16,7 @@
  */
 
 #include <linux/slab.h>
+#include <linux/if_ether.h>
 
 #include "htt.h"
 #include "core.h"
@@ -36,7 +37,7 @@
 	/* connect to control service */
 	conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG;
 
-	status = ath10k_htc_connect_service(htt->ar->htc, &conn_req,
+	status = ath10k_htc_connect_service(&htt->ar->htc, &conn_req,
 					    &conn_resp);
 
 	if (status)
@@ -47,15 +48,11 @@
 	return 0;
 }
 
-struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
+int ath10k_htt_attach(struct ath10k *ar)
 {
-	struct ath10k_htt *htt;
+	struct ath10k_htt *htt = &ar->htt;
 	int ret;
 
-	htt = kzalloc(sizeof(*htt), GFP_KERNEL);
-	if (!htt)
-		return NULL;
-
 	htt->ar = ar;
 	htt->max_throughput_mbps = 800;
 
@@ -65,8 +62,11 @@
 	 * since ath10k_htt_rx_attach involves sending a rx ring configure
 	 * message to the target.
 	 */
-	if (ath10k_htt_htc_attach(htt))
+	ret = ath10k_htt_htc_attach(htt);
+	if (ret) {
+		ath10k_err("could not attach htt htc (%d)\n", ret);
 		goto err_htc_attach;
+	}
 
 	ret = ath10k_htt_tx_attach(htt);
 	if (ret) {
@@ -74,8 +74,11 @@
 		goto err_htc_attach;
 	}
 
-	if (ath10k_htt_rx_attach(htt))
+	ret = ath10k_htt_rx_attach(htt);
+	if (ret) {
+		ath10k_err("could not attach htt rx (%d)\n", ret);
 		goto err_rx_attach;
+	}
 
 	/*
 	 * Prefetch enough data to satisfy target
@@ -89,34 +92,28 @@
 		8 + /* llc snap */
 		2; /* ip4 dscp or ip6 priority */
 
-	return htt;
+	return 0;
 
 err_rx_attach:
 	ath10k_htt_tx_detach(htt);
 err_htc_attach:
-	kfree(htt);
-	return NULL;
+	return ret;
 }
 
 #define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ)
 
 static int ath10k_htt_verify_version(struct ath10k_htt *htt)
 {
-	ath10k_dbg(ATH10K_DBG_HTT,
-		   "htt target version %d.%d; host version %d.%d\n",
-		    htt->target_version_major,
-		    htt->target_version_minor,
-		    HTT_CURRENT_VERSION_MAJOR,
-		    HTT_CURRENT_VERSION_MINOR);
+	ath10k_dbg(ATH10K_DBG_BOOT, "htt target version %d.%d\n",
+		   htt->target_version_major, htt->target_version_minor);
 
-	if (htt->target_version_major != HTT_CURRENT_VERSION_MAJOR) {
-		ath10k_err("htt major versions are incompatible!\n");
+	if (htt->target_version_major != 2 &&
+	    htt->target_version_major != 3) {
+		ath10k_err("unsupported htt major version %d. supported versions are 2 and 3\n",
+			   htt->target_version_major);
 		return -ENOTSUPP;
 	}
 
-	if (htt->target_version_minor != HTT_CURRENT_VERSION_MINOR)
-		ath10k_warn("htt minor version differ but still compatible\n");
-
 	return 0;
 }
 
@@ -148,5 +145,4 @@
 {
 	ath10k_htt_rx_detach(htt);
 	ath10k_htt_tx_detach(htt);
-	kfree(htt);
 }
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/htt.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/htt.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/htt.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/htt.h	2015-09-29 11:15:06.921740666 +0200
@@ -19,14 +19,12 @@
 #define _HTT_H_
 
 #include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/dmapool.h>
 
-#include "core.h"
 #include "htc.h"
 #include "rx_desc.h"
 
-#define HTT_CURRENT_VERSION_MAJOR	2
-#define HTT_CURRENT_VERSION_MINOR	1
-
 enum htt_dbg_stats_type {
 	HTT_DBG_STATS_WAL_PDEV_TXRX = 1 << 0,
 	HTT_DBG_STATS_RX_REORDER    = 1 << 1,
@@ -46,6 +44,9 @@
 	HTT_H2T_MSG_TYPE_SYNC               = 4,
 	HTT_H2T_MSG_TYPE_AGGR_CFG           = 5,
 	HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 6,
+
+	/* This command is used for sending management frames in HTT < 3.0.
+	 * HTT >= 3.0 uses TX_FRM for everything. */
 	HTT_H2T_MSG_TYPE_MGMT_TX            = 7,
 
 	HTT_H2T_NUM_MSGS /* keep this last */
@@ -1181,9 +1182,20 @@
 		u32 info1;
 		u32 info2;
 	} rate;
+
+	u32 tsf;
 	bool fcs_err;
+	bool amsdu_more;
+	bool mic_err;
 };
 
+struct ath10k_htt_txbuf {
+	struct htt_data_tx_desc_frag frags[2];
+	struct ath10k_htc_hdr htc_hdr;
+	struct htt_cmd_hdr cmd_hdr;
+	struct htt_data_tx_desc cmd_tx;
+} __packed;
+
 struct ath10k_htt {
 	struct ath10k *ar;
 	enum ath10k_htc_ep_id eid;
@@ -1265,10 +1277,18 @@
 	struct sk_buff **pending_tx;
 	unsigned long *used_msdu_ids; /* bitmap */
 	wait_queue_head_t empty_tx_wq;
+	struct dma_pool *tx_pool;
 
 	/* set if host-fw communication goes haywire
 	 * used to avoid further failures */
 	bool rx_confused;
+	struct tasklet_struct rx_replenish_task;
+
+	/* This is used to group tx/rx completions separately and process them
+	 * in batches to reduce cache stalls */
+	struct tasklet_struct txrx_compl_task;
+	struct sk_buff_head tx_compl_q;
+	struct sk_buff_head rx_compl_q;
 };
 
 #define RX_HTT_HDR_STATUS_LEN 64
@@ -1309,6 +1329,10 @@
 #define HTT_RX_BUF_SIZE 1920
 #define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc))
 
+/* Refill a bunch of RX buffers for each refill round so that FW/HW can handle
+ * aggregated traffic more nicely. */
+#define ATH10K_HTT_MAX_NUM_REFILL 16
+
 /*
  * DMA_MAP expects the buffer to be an integral number of cache lines.
  * Rather than checking the actual cache line size, this code makes a
@@ -1317,7 +1341,7 @@
 #define HTT_LOG2_MAX_CACHE_LINE_SIZE 7	/* 2^7 = 128 */
 #define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1)
 
-struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar);
+int ath10k_htt_attach(struct ath10k *ar);
 int ath10k_htt_attach_target(struct ath10k_htt *htt);
 void ath10k_htt_detach(struct ath10k_htt *htt);
 
@@ -1328,6 +1352,7 @@
 void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb);
 void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb);
 int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt);
+int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie);
 int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt);
 
 void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
@@ -1335,4 +1360,5 @@
 void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
 int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);
 int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *);
+
 #endif
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/htt_rx.c linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/htt_rx.c
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/htt_rx.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/htt_rx.c	2015-09-29 11:15:06.921740666 +0200
@@ -15,10 +15,12 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "core.h"
 #include "htc.h"
 #include "htt.h"
 #include "txrx.h"
 #include "debug.h"
+#include "trace.h"
 
 #include <linux/log2.h>
 
@@ -39,6 +41,10 @@
 /* when under memory pressure rx ring refill may fail and needs a retry */
 #define HTT_RX_RING_REFILL_RETRY_MS 50
 
+
+static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
+static void ath10k_htt_txrx_compl_task(unsigned long ptr);
+
 static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
 {
 	int size;
@@ -176,10 +182,27 @@
 
 static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
 {
-	int ret, num_to_fill;
+	int ret, num_deficit, num_to_fill;
 
+	/* Refilling the whole RX ring buffer proves to be a bad idea. The
+	 * reason is RX may take up significant amount of CPU cycles and starve
+	 * other tasks, e.g. TX on an ethernet device while acting as a bridge
+	 * with ath10k wlan interface. This ended up with very poor performance
+	 * once CPU the host system was overwhelmed with RX on ath10k.
+	 *
+	 * By limiting the number of refills the replenishing occurs
+	 * progressively. This in turns makes use of the fact tasklets are
+	 * processed in FIFO order. This means actual RX processing can starve
+	 * out refilling. If there's not enough buffers on RX ring FW will not
+	 * report RX until it is refilled with enough buffers. This
+	 * automatically balances load wrt to CPU power.
+	 *
+	 * This probably comes at a cost of lower maximum throughput but
+	 * improves the avarage and stability. */
 	spin_lock_bh(&htt->rx_ring.lock);
-	num_to_fill = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
+	num_deficit = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
+	num_to_fill = min(ATH10K_HTT_MAX_NUM_REFILL, num_deficit);
+	num_deficit -= num_to_fill;
 	ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill);
 	if (ret == -ENOMEM) {
 		/*
@@ -190,6 +213,8 @@
 		 */
 		mod_timer(&htt->rx_ring.refill_retry_timer, jiffies +
 			  msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS));
+	} else if (num_deficit > 0) {
+		tasklet_schedule(&htt->rx_replenish_task);
 	}
 	spin_unlock_bh(&htt->rx_ring.lock);
 }
@@ -200,17 +225,16 @@
 	ath10k_htt_rx_msdu_buff_replenish(htt);
 }
 
-static unsigned ath10k_htt_rx_ring_elems(struct ath10k_htt *htt)
-{
-	return (__le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr) -
-		htt->rx_ring.sw_rd_idx.msdu_payld) & htt->rx_ring.size_mask;
-}
-
 void ath10k_htt_rx_detach(struct ath10k_htt *htt)
 {
 	int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld;
 
 	del_timer_sync(&htt->rx_ring.refill_retry_timer);
+	tasklet_kill(&htt->rx_replenish_task);
+	tasklet_kill(&htt->txrx_compl_task);
+
+	skb_queue_purge(&htt->tx_compl_q);
+	skb_queue_purge(&htt->rx_compl_q);
 
 	while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) {
 		struct sk_buff *skb =
@@ -244,10 +268,12 @@
 	int idx;
 	struct sk_buff *msdu;
 
-	spin_lock_bh(&htt->rx_ring.lock);
+	lockdep_assert_held(&htt->rx_ring.lock);
 
-	if (ath10k_htt_rx_ring_elems(htt) == 0)
-		ath10k_warn("htt rx ring is empty!\n");
+	if (htt->rx_ring.fill_cnt == 0) {
+		ath10k_warn("tried to pop sk_buff from an empty rx ring\n");
+		return NULL;
+	}
 
 	idx = htt->rx_ring.sw_rd_idx.msdu_payld;
 	msdu = htt->rx_ring.netbufs_ring[idx];
@@ -257,7 +283,6 @@
 	htt->rx_ring.sw_rd_idx.msdu_payld = idx;
 	htt->rx_ring.fill_cnt--;
 
-	spin_unlock_bh(&htt->rx_ring.lock);
 	return msdu;
 }
 
@@ -281,8 +306,7 @@
 	struct sk_buff *msdu;
 	struct htt_rx_desc *rx_desc;
 
-	if (ath10k_htt_rx_ring_elems(htt) == 0)
-		ath10k_warn("htt rx ring is empty!\n");
+	lockdep_assert_held(&htt->rx_ring.lock);
 
 	if (htt->rx_confused) {
 		ath10k_warn("htt is confused. refusing rx\n");
@@ -298,7 +322,7 @@
 				 msdu->len + skb_tailroom(msdu),
 				 DMA_FROM_DEVICE);
 
-		ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
+		ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx pop: ",
 				msdu->data, msdu->len + skb_tailroom(msdu));
 
 		rx_desc = (struct htt_rx_desc *)msdu->data;
@@ -391,8 +415,8 @@
 					 next->len + skb_tailroom(next),
 					 DMA_FROM_DEVICE);
 
-			ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
-					next->data,
+			ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL,
+					"htt rx chained: ", next->data,
 					next->len + skb_tailroom(next));
 
 			skb_trim(next, 0);
@@ -404,12 +428,6 @@
 			msdu_chaining = 1;
 		}
 
-		if (msdu_len > 0) {
-			/* This may suggest FW bug? */
-			ath10k_warn("htt rx msdu len not consumed (%d)\n",
-				    msdu_len);
-		}
-
 		last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
 				RX_MSDU_END_INFO0_LAST_MSDU;
 
@@ -440,6 +458,12 @@
 	return msdu_chaining;
 }
 
+static void ath10k_htt_rx_replenish_task(unsigned long ptr)
+{
+	struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
+	ath10k_htt_rx_msdu_buff_replenish(htt);
+}
+
 int ath10k_htt_rx_attach(struct ath10k_htt *htt)
 {
 	dma_addr_t paddr;
@@ -500,7 +524,16 @@
 	if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level))
 		goto err_fill_ring;
 
-	ath10k_dbg(ATH10K_DBG_HTT, "HTT RX ring size: %d, fill_level: %d\n",
+	tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task,
+		     (unsigned long)htt);
+
+	skb_queue_head_init(&htt->tx_compl_q);
+	skb_queue_head_init(&htt->rx_compl_q);
+
+	tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task,
+		     (unsigned long)htt);
+
+	ath10k_dbg(ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n",
 		   htt->rx_ring.size, htt->rx_ring.fill_level);
 	return 0;
 
@@ -589,138 +622,140 @@
 	return false;
 }
 
-static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
+struct rfc1042_hdr {
+	u8 llc_dsap;
+	u8 llc_ssap;
+	u8 llc_ctrl;
+	u8 snap_oui[3];
+	__be16 snap_type;
+} __packed;
+
+struct amsdu_subframe_hdr {
+	u8 dst[ETH_ALEN];
+	u8 src[ETH_ALEN];
+	__be16 len;
+} __packed;
+
+static int ath10k_htt_rx_nwifi_hdrlen(struct ieee80211_hdr *hdr)
+{
+	/* nwifi header is padded to 4 bytes. this fixes 4addr rx */
+	return round_up(ieee80211_hdrlen(hdr->frame_control), 4);
+}
+
+static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
 			struct htt_rx_info *info)
 {
 	struct htt_rx_desc *rxd;
-	struct sk_buff *amsdu;
 	struct sk_buff *first;
-	struct ieee80211_hdr *hdr;
 	struct sk_buff *skb = info->skb;
 	enum rx_msdu_decap_format fmt;
 	enum htt_rx_mpdu_encrypt_type enctype;
+	struct ieee80211_hdr *hdr;
+	u8 hdr_buf[64], addr[ETH_ALEN], *qos;
 	unsigned int hdr_len;
-	int crypto_len;
 
 	rxd = (void *)skb->data - sizeof(*rxd);
-	fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
-			RX_MSDU_START_INFO1_DECAP_FORMAT);
 	enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
 			RX_MPDU_START_INFO0_ENCRYPT_TYPE);
 
-	/* FIXME: No idea what assumptions are safe here. Need logs */
-	if ((fmt == RX_MSDU_DECAP_RAW && skb->next) ||
-	    (fmt == RX_MSDU_DECAP_8023_SNAP_LLC)) {
-		ath10k_htt_rx_free_msdu_chain(skb->next);
-		skb->next = NULL;
-		return -ENOTSUPP;
-	}
-
-	/* A-MSDU max is a little less than 8K */
-	amsdu = dev_alloc_skb(8*1024);
-	if (!amsdu) {
-		ath10k_warn("A-MSDU allocation failed\n");
-		ath10k_htt_rx_free_msdu_chain(skb->next);
-		skb->next = NULL;
-		return -ENOMEM;
-	}
-
-	if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) {
-		int hdrlen;
-
-		hdr = (void *)rxd->rx_hdr_status;
-		hdrlen = ieee80211_hdrlen(hdr->frame_control);
-		memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen);
-	}
+	hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+	hdr_len = ieee80211_hdrlen(hdr->frame_control);
+	memcpy(hdr_buf, hdr, hdr_len);
+	hdr = (struct ieee80211_hdr *)hdr_buf;
 
 	first = skb;
 	while (skb) {
 		void *decap_hdr;
-		int decap_len = 0;
+		int len;
 
 		rxd = (void *)skb->data - sizeof(*rxd);
 		fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
 				RX_MSDU_START_INFO1_DECAP_FORMAT);
 		decap_hdr = (void *)rxd->rx_hdr_status;
 
-		if (skb == first) {
-			/* We receive linked A-MSDU subframe skbuffs. The
-			 * first one contains the original 802.11 header (and
-			 * possible crypto param) in the RX descriptor. The
-			 * A-MSDU subframe header follows that. Each part is
-			 * aligned to 4 byte boundary. */
+		skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
 
-			hdr = (void *)amsdu->data;
-			hdr_len = ieee80211_hdrlen(hdr->frame_control);
-			crypto_len = ath10k_htt_rx_crypto_param_len(enctype);
-
-			decap_hdr += roundup(hdr_len, 4);
-			decap_hdr += roundup(crypto_len, 4);
-		}
-
-		if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
-			/* Ethernet2 decap inserts ethernet header in place of
-			 * A-MSDU subframe header. */
-			skb_pull(skb, 6 + 6 + 2);
-
-			/* A-MSDU subframe header length */
-			decap_len += 6 + 6 + 2;
-
-			/* Ethernet2 decap also strips the LLC/SNAP so we need
-			 * to re-insert it. The LLC/SNAP follows A-MSDU
-			 * subframe header. */
-			/* FIXME: Not all LLCs are 8 bytes long */
-			decap_len += 8;
-
-			memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
+		/* First frame in an A-MSDU chain has more decapped data. */
+		if (skb == first) {
+			len = round_up(ieee80211_hdrlen(hdr->frame_control), 4);
+			len += round_up(ath10k_htt_rx_crypto_param_len(enctype),
+					4);
+			decap_hdr += len;
 		}
 
-		if (fmt == RX_MSDU_DECAP_NATIVE_WIFI) {
-			/* Native Wifi decap inserts regular 802.11 header
-			 * in place of A-MSDU subframe header. */
+		switch (fmt) {
+		case RX_MSDU_DECAP_RAW:
+			/* remove trailing FCS */
+			skb_trim(skb, skb->len - FCS_LEN);
+			break;
+		case RX_MSDU_DECAP_NATIVE_WIFI:
+			/* pull decapped header and copy DA */
 			hdr = (struct ieee80211_hdr *)skb->data;
-			skb_pull(skb, ieee80211_hdrlen(hdr->frame_control));
-
-			/* A-MSDU subframe header length */
-			decap_len += 6 + 6 + 2;
+			hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
+			memcpy(addr, ieee80211_get_DA(hdr), ETH_ALEN);
+			skb_pull(skb, hdr_len);
 
-			memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
-		}
+			/* push original 802.11 header */
+			hdr = (struct ieee80211_hdr *)hdr_buf;
+			hdr_len = ieee80211_hdrlen(hdr->frame_control);
+			memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
 
-		if (fmt == RX_MSDU_DECAP_RAW)
-			skb_trim(skb, skb->len - 4); /* remove FCS */
+			/* original A-MSDU header has the bit set but we're
+			 * not including A-MSDU subframe header */
+			hdr = (struct ieee80211_hdr *)skb->data;
+			qos = ieee80211_get_qos_ctl(hdr);
+			qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
 
-		memcpy(skb_put(amsdu, skb->len), skb->data, skb->len);
+			/* original 802.11 header has a different DA */
+			memcpy(ieee80211_get_DA(hdr), addr, ETH_ALEN);
+			break;
+		case RX_MSDU_DECAP_ETHERNET2_DIX:
+			/* strip ethernet header and insert decapped 802.11
+			 * header, amsdu subframe header and rfc1042 header */
 
-		/* A-MSDU subframes are padded to 4bytes
-		 * but relative to first subframe, not the whole MPDU */
-		if (skb->next && ((decap_len + skb->len) & 3)) {
-			int padlen = 4 - ((decap_len + skb->len) & 3);
-			memset(skb_put(amsdu, padlen), 0, padlen);
+			len = 0;
+			len += sizeof(struct rfc1042_hdr);
+			len += sizeof(struct amsdu_subframe_hdr);
+
+			skb_pull(skb, sizeof(struct ethhdr));
+			memcpy(skb_push(skb, len), decap_hdr, len);
+			memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+			break;
+		case RX_MSDU_DECAP_8023_SNAP_LLC:
+			/* insert decapped 802.11 header making a singly
+			 * A-MSDU */
+			memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+			break;
 		}
 
+		info->skb = skb;
+		info->encrypt_type = enctype;
 		skb = skb->next;
-	}
+		info->skb->next = NULL;
 
-	info->skb = amsdu;
-	info->encrypt_type = enctype;
+		if (skb)
+			info->amsdu_more = true;
 
-	ath10k_htt_rx_free_msdu_chain(first);
+		ath10k_process_rx(htt->ar, info);
+	}
 
-	return 0;
+	/* FIXME: It might be nice to re-assemble the A-MSDU when there's a
+	 * monitor interface active for sniffing purposes. */
 }
 
-static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
+static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
 {
 	struct sk_buff *skb = info->skb;
 	struct htt_rx_desc *rxd;
 	struct ieee80211_hdr *hdr;
 	enum rx_msdu_decap_format fmt;
 	enum htt_rx_mpdu_encrypt_type enctype;
+	int hdr_len;
+	void *rfc1042;
 
 	/* This shouldn't happen. If it does than it may be a FW bug. */
 	if (skb->next) {
-		ath10k_warn("received chained non A-MSDU frame\n");
+		ath10k_warn("htt rx received chained non A-MSDU frame\n");
 		ath10k_htt_rx_free_msdu_chain(skb->next);
 		skb->next = NULL;
 	}
@@ -730,49 +765,53 @@
 			RX_MSDU_START_INFO1_DECAP_FORMAT);
 	enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
 			RX_MPDU_START_INFO0_ENCRYPT_TYPE);
-	hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
+	hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+	hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+	skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
 
 	switch (fmt) {
 	case RX_MSDU_DECAP_RAW:
 		/* remove trailing FCS */
-		skb_trim(skb, skb->len - 4);
+		skb_trim(skb, skb->len - FCS_LEN);
 		break;
 	case RX_MSDU_DECAP_NATIVE_WIFI:
-		/* nothing to do here */
+		/* Pull decapped header */
+		hdr = (struct ieee80211_hdr *)skb->data;
+		hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
+		skb_pull(skb, hdr_len);
+
+		/* Push original header */
+		hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+		hdr_len = ieee80211_hdrlen(hdr->frame_control);
+		memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
 		break;
 	case RX_MSDU_DECAP_ETHERNET2_DIX:
-		/* macaddr[6] + macaddr[6] + ethertype[2] */
-		skb_pull(skb, 6 + 6 + 2);
+		/* strip ethernet header and insert decapped 802.11 header and
+		 * rfc1042 header */
+
+		rfc1042 = hdr;
+		rfc1042 += roundup(hdr_len, 4);
+		rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4);
+
+		skb_pull(skb, sizeof(struct ethhdr));
+		memcpy(skb_push(skb, sizeof(struct rfc1042_hdr)),
+		       rfc1042, sizeof(struct rfc1042_hdr));
+		memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
 		break;
 	case RX_MSDU_DECAP_8023_SNAP_LLC:
-		/* macaddr[6] + macaddr[6] + len[2] */
-		/* we don't need this for non-A-MSDU */
-		skb_pull(skb, 6 + 6 + 2);
-		break;
-	}
-
-	if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
-		void *llc;
-		int llclen;
+		/* remove A-MSDU subframe header and insert
+		 * decapped 802.11 header. rfc1042 header is already there */
 
-		llclen = 8;
-		llc  = hdr;
-		llc += roundup(ieee80211_hdrlen(hdr->frame_control), 4);
-		llc += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4);
-
-		skb_push(skb, llclen);
-		memcpy(skb->data, llc, llclen);
-	}
-
-	if (fmt >= RX_MSDU_DECAP_ETHERNET2_DIX) {
-		int len = ieee80211_hdrlen(hdr->frame_control);
-		skb_push(skb, len);
-		memcpy(skb->data, hdr, len);
+		skb_pull(skb, sizeof(struct amsdu_subframe_hdr));
+		memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+		break;
 	}
 
 	info->skb = skb;
 	info->encrypt_type = enctype;
-	return 0;
+
+	ath10k_process_rx(htt->ar, info);
 }
 
 static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb)
@@ -803,6 +842,65 @@
 	return false;
 }
 
+static bool ath10k_htt_rx_has_mic_err(struct sk_buff *skb)
+{
+	struct htt_rx_desc *rxd;
+	u32 flags;
+
+	rxd = (void *)skb->data - sizeof(*rxd);
+	flags = __le32_to_cpu(rxd->attention.flags);
+
+	if (flags & RX_ATTENTION_FLAGS_TKIP_MIC_ERR)
+		return true;
+
+	return false;
+}
+
+static bool ath10k_htt_rx_is_mgmt(struct sk_buff *skb)
+{
+	struct htt_rx_desc *rxd;
+	u32 flags;
+
+	rxd = (void *)skb->data - sizeof(*rxd);
+	flags = __le32_to_cpu(rxd->attention.flags);
+
+	if (flags & RX_ATTENTION_FLAGS_MGMT_TYPE)
+		return true;
+
+	return false;
+}
+
+static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
+{
+	struct htt_rx_desc *rxd;
+	u32 flags, info;
+	bool is_ip4, is_ip6;
+	bool is_tcp, is_udp;
+	bool ip_csum_ok, tcpudp_csum_ok;
+
+	rxd = (void *)skb->data - sizeof(*rxd);
+	flags = __le32_to_cpu(rxd->attention.flags);
+	info = __le32_to_cpu(rxd->msdu_start.info1);
+
+	is_ip4 = !!(info & RX_MSDU_START_INFO1_IPV4_PROTO);
+	is_ip6 = !!(info & RX_MSDU_START_INFO1_IPV6_PROTO);
+	is_tcp = !!(info & RX_MSDU_START_INFO1_TCP_PROTO);
+	is_udp = !!(info & RX_MSDU_START_INFO1_UDP_PROTO);
+	ip_csum_ok = !(flags & RX_ATTENTION_FLAGS_IP_CHKSUM_FAIL);
+	tcpudp_csum_ok = !(flags & RX_ATTENTION_FLAGS_TCP_UDP_CHKSUM_FAIL);
+
+	if (!is_ip4 && !is_ip6)
+		return CHECKSUM_NONE;
+	if (!is_tcp && !is_udp)
+		return CHECKSUM_NONE;
+	if (!ip_csum_ok)
+		return CHECKSUM_NONE;
+	if (!tcpudp_csum_ok)
+		return CHECKSUM_NONE;
+
+	return CHECKSUM_UNNECESSARY;
+}
+
 static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
 				  struct htt_rx_indication *rx)
 {
@@ -813,7 +911,8 @@
 	int fw_desc_len;
 	u8 *fw_desc;
 	int i, j;
-	int ret;
+
+	lockdep_assert_held(&htt->rx_ring.lock);
 
 	memset(&info, 0, sizeof(info));
 
@@ -858,6 +957,8 @@
 			}
 
 			if (ath10k_htt_rx_has_decrypt_err(msdu_head)) {
+				ath10k_dbg(ATH10K_DBG_HTT,
+					   "htt rx dropping due to decrypt-err\n");
 				ath10k_htt_rx_free_msdu_chain(msdu_head);
 				continue;
 			}
@@ -865,13 +966,16 @@
 			status = info.status;
 
 			/* Skip mgmt frames while we handle this in WMI */
-			if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL) {
+			if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL ||
+			    ath10k_htt_rx_is_mgmt(msdu_head)) {
+				ath10k_dbg(ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
 				ath10k_htt_rx_free_msdu_chain(msdu_head);
 				continue;
 			}
 
 			if (status != HTT_RX_IND_MPDU_STATUS_OK &&
 			    status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR &&
+			    status != HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER &&
 			    !htt->ar->monitor_enabled) {
 				ath10k_dbg(ATH10K_DBG_HTT,
 					   "htt rx ignoring frame w/ status %d\n",
@@ -880,46 +984,51 @@
 				continue;
 			}
 
+			if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
+				ath10k_dbg(ATH10K_DBG_HTT,
+					   "htt rx CAC running\n");
+				ath10k_htt_rx_free_msdu_chain(msdu_head);
+				continue;
+			}
+
 			/* FIXME: we do not support chaining yet.
 			 * this needs investigation */
 			if (msdu_chaining) {
-				ath10k_warn("msdu_chaining is true\n");
+				ath10k_warn("htt rx msdu_chaining is true\n");
 				ath10k_htt_rx_free_msdu_chain(msdu_head);
 				continue;
 			}
 
 			info.skb     = msdu_head;
 			info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
+			info.mic_err = ath10k_htt_rx_has_mic_err(msdu_head);
+
+			if (info.fcs_err)
+				ath10k_dbg(ATH10K_DBG_HTT,
+					   "htt rx has FCS err\n");
+
+			if (info.mic_err)
+				ath10k_dbg(ATH10K_DBG_HTT,
+					   "htt rx has MIC err\n");
+
 			info.signal  = ATH10K_DEFAULT_NOISE_FLOOR;
 			info.signal += rx->ppdu.combined_rssi;
 
 			info.rate.info0 = rx->ppdu.info0;
 			info.rate.info1 = __le32_to_cpu(rx->ppdu.info1);
 			info.rate.info2 = __le32_to_cpu(rx->ppdu.info2);
+			info.tsf = __le32_to_cpu(rx->ppdu.tsf);
 
 			hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
 
 			if (ath10k_htt_rx_hdr_is_amsdu(hdr))
-				ret = ath10k_htt_rx_amsdu(htt, &info);
+				ath10k_htt_rx_amsdu(htt, &info);
 			else
-				ret = ath10k_htt_rx_msdu(htt, &info);
-
-			if (ret && !info.fcs_err) {
-				ath10k_warn("error processing msdus %d\n", ret);
-				dev_kfree_skb_any(info.skb);
-				continue;
-			}
-
-			if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data))
-				ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n");
-
-			ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ",
-					info.skb->data, info.skb->len);
-			ath10k_process_rx(htt->ar, &info);
+				ath10k_htt_rx_msdu(htt, &info);
 		}
 	}
 
-	ath10k_htt_rx_msdu_buff_replenish(htt);
+	tasklet_schedule(&htt->rx_replenish_task);
 }
 
 static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
@@ -942,8 +1051,11 @@
 
 	msdu_head = NULL;
 	msdu_tail = NULL;
+
+	spin_lock_bh(&htt->rx_ring.lock);
 	msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
 						&msdu_head, &msdu_tail);
+	spin_unlock_bh(&htt->rx_ring.lock);
 
 	ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n");
 
@@ -979,6 +1091,7 @@
 	info.status = HTT_RX_IND_MPDU_STATUS_OK;
 	info.encrypt_type = MS(__le32_to_cpu(rxd->mpdu_start.info0),
 				RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+	info.skb->ip_summed = ath10k_htt_rx_get_csum_state(info.skb);
 
 	if (tkip_mic_err) {
 		ath10k_warn("tkip mic error\n");
@@ -1022,7 +1135,7 @@
 
 	skb_trim(info.skb, info.skb->len - trim);
 
-	ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt frag mpdu: ",
+	ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ",
 			info.skb->data, info.skb->len);
 	ath10k_process_rx(htt->ar, &info);
 
@@ -1034,16 +1147,55 @@
 	}
 }
 
+static void ath10k_htt_rx_frm_tx_compl(struct ath10k *ar,
+				       struct sk_buff *skb)
+{
+	struct ath10k_htt *htt = &ar->htt;
+	struct htt_resp *resp = (struct htt_resp *)skb->data;
+	struct htt_tx_done tx_done = {};
+	int status = MS(resp->data_tx_completion.flags, HTT_DATA_TX_STATUS);
+	__le16 msdu_id;
+	int i;
+
+	lockdep_assert_held(&htt->tx_lock);
+
+	switch (status) {
+	case HTT_DATA_TX_STATUS_NO_ACK:
+		tx_done.no_ack = true;
+		break;
+	case HTT_DATA_TX_STATUS_OK:
+		break;
+	case HTT_DATA_TX_STATUS_DISCARD:
+	case HTT_DATA_TX_STATUS_POSTPONE:
+	case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL:
+		tx_done.discard = true;
+		break;
+	default:
+		ath10k_warn("unhandled tx completion status %d\n", status);
+		tx_done.discard = true;
+		break;
+	}
+
+	ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n",
+		   resp->data_tx_completion.num_msdus);
+
+	for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
+		msdu_id = resp->data_tx_completion.msdus[i];
+		tx_done.msdu_id = __le16_to_cpu(msdu_id);
+		ath10k_txrx_tx_unref(htt, &tx_done);
+	}
+}
+
 void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
 {
-	struct ath10k_htt *htt = ar->htt;
+	struct ath10k_htt *htt = &ar->htt;
 	struct htt_resp *resp = (struct htt_resp *)skb->data;
 
 	/* confirm alignment */
 	if (!IS_ALIGNED((unsigned long)skb->data, 4))
 		ath10k_warn("unaligned htt message, expect trouble\n");
 
-	ath10k_dbg(ATH10K_DBG_HTT, "HTT RX, msg_type: 0x%0X\n",
+	ath10k_dbg(ATH10K_DBG_HTT, "htt rx, msg_type: 0x%0X\n",
 		   resp->hdr.msg_type);
 	switch (resp->hdr.msg_type) {
 	case HTT_T2H_MSG_TYPE_VERSION_CONF: {
@@ -1052,10 +1204,12 @@
 		complete(&htt->target_version_received);
 		break;
 	}
-	case HTT_T2H_MSG_TYPE_RX_IND: {
-		ath10k_htt_rx_handler(htt, &resp->rx_ind);
-		break;
-	}
+	case HTT_T2H_MSG_TYPE_RX_IND:
+		spin_lock_bh(&htt->rx_ring.lock);
+		__skb_queue_tail(&htt->rx_compl_q, skb);
+		spin_unlock_bh(&htt->rx_ring.lock);
+		tasklet_schedule(&htt->txrx_compl_task);
+		return;
 	case HTT_T2H_MSG_TYPE_PEER_MAP: {
 		struct htt_peer_map_event ev = {
 			.vdev_id = resp->peer_map.vdev_id,
@@ -1090,44 +1244,17 @@
 			break;
 		}
 
-		ath10k_txrx_tx_completed(htt, &tx_done);
-		break;
-	}
-	case HTT_T2H_MSG_TYPE_TX_COMPL_IND: {
-		struct htt_tx_done tx_done = {};
-		int status = MS(resp->data_tx_completion.flags,
-				HTT_DATA_TX_STATUS);
-		__le16 msdu_id;
-		int i;
-
-		switch (status) {
-		case HTT_DATA_TX_STATUS_NO_ACK:
-			tx_done.no_ack = true;
-			break;
-		case HTT_DATA_TX_STATUS_OK:
-			break;
-		case HTT_DATA_TX_STATUS_DISCARD:
-		case HTT_DATA_TX_STATUS_POSTPONE:
-		case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL:
-			tx_done.discard = true;
-			break;
-		default:
-			ath10k_warn("unhandled tx completion status %d\n",
-				    status);
-			tx_done.discard = true;
-			break;
-		}
-
-		ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n",
-			   resp->data_tx_completion.num_msdus);
-
-		for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
-			msdu_id = resp->data_tx_completion.msdus[i];
-			tx_done.msdu_id = __le16_to_cpu(msdu_id);
-			ath10k_txrx_tx_completed(htt, &tx_done);
-		}
+		spin_lock_bh(&htt->tx_lock);
+		ath10k_txrx_tx_unref(htt, &tx_done);
+		spin_unlock_bh(&htt->tx_lock);
 		break;
 	}
+	case HTT_T2H_MSG_TYPE_TX_COMPL_IND:
+		spin_lock_bh(&htt->tx_lock);
+		__skb_queue_tail(&htt->tx_compl_q, skb);
+		spin_unlock_bh(&htt->tx_lock);
+		tasklet_schedule(&htt->txrx_compl_task);
+		return;
 	case HTT_T2H_MSG_TYPE_SEC_IND: {
 		struct ath10k *ar = htt->ar;
 		struct htt_security_indication *ev = &resp->security_indication;
@@ -1149,8 +1276,10 @@
 	case HTT_T2H_MSG_TYPE_TEST:
 		/* FIX THIS */
 		break;
-	case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
 	case HTT_T2H_MSG_TYPE_STATS_CONF:
+		trace_ath10k_htt_stats(skb->data, skb->len);
+		break;
+	case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
 	case HTT_T2H_MSG_TYPE_RX_ADDBA:
 	case HTT_T2H_MSG_TYPE_RX_DELBA:
 	case HTT_T2H_MSG_TYPE_RX_FLUSH:
@@ -1165,3 +1294,25 @@
 	/* Free the indication buffer */
 	dev_kfree_skb_any(skb);
 }
+
+static void ath10k_htt_txrx_compl_task(unsigned long ptr)
+{
+	struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
+	struct htt_resp *resp;
+	struct sk_buff *skb;
+
+	spin_lock_bh(&htt->tx_lock);
+	while ((skb = __skb_dequeue(&htt->tx_compl_q))) {
+		ath10k_htt_rx_frm_tx_compl(htt->ar, skb);
+		dev_kfree_skb_any(skb);
+	}
+	spin_unlock_bh(&htt->tx_lock);
+
+	spin_lock_bh(&htt->rx_ring.lock);
+	while ((skb = __skb_dequeue(&htt->rx_compl_q))) {
+		resp = (struct htt_resp *)skb->data;
+		ath10k_htt_rx_handler(htt, &resp->rx_ind);
+		dev_kfree_skb_any(skb);
+	}
+	spin_unlock_bh(&htt->rx_ring.lock);
+}
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/htt_tx.c linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/htt_tx.c
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/htt_tx.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/htt_tx.c	2015-09-29 11:15:06.921740666 +0200
@@ -85,18 +85,15 @@
 
 int ath10k_htt_tx_attach(struct ath10k_htt *htt)
 {
-	u8 pipe;
-
 	spin_lock_init(&htt->tx_lock);
 	init_waitqueue_head(&htt->empty_tx_wq);
 
-	/* At the beginning free queue number should hint us the maximum
-	 * queue length */
-	pipe = htt->ar->htc->endpoint[htt->eid].ul_pipe_id;
-	htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar,
-								   pipe);
+	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, htt->ar->fw_features))
+		htt->max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC;
+	else
+		htt->max_num_pending_tx = TARGET_NUM_MSDU_DESC;
 
-	ath10k_dbg(ATH10K_DBG_HTT, "htt tx max num pending tx %d\n",
+	ath10k_dbg(ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n",
 		   htt->max_num_pending_tx);
 
 	htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) *
@@ -112,34 +109,36 @@
 		return -ENOMEM;
 	}
 
+	htt->tx_pool = dma_pool_create("ath10k htt tx pool", htt->ar->dev,
+				       sizeof(struct ath10k_htt_txbuf), 4, 0);
+	if (!htt->tx_pool) {
+		kfree(htt->used_msdu_ids);
+		kfree(htt->pending_tx);
+		return -ENOMEM;
+	}
+
 	return 0;
 }
 
 static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
 {
-	struct sk_buff *txdesc;
+	struct htt_tx_done tx_done = {0};
 	int msdu_id;
 
-	/* No locks needed. Called after communication with the device has
-	 * been stopped. */
-
+	spin_lock_bh(&htt->tx_lock);
 	for (msdu_id = 0; msdu_id < htt->max_num_pending_tx; msdu_id++) {
 		if (!test_bit(msdu_id, htt->used_msdu_ids))
 			continue;
 
-		txdesc = htt->pending_tx[msdu_id];
-		if (!txdesc)
-			continue;
-
 		ath10k_dbg(ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n",
 			   msdu_id);
 
-		if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0)
-			ATH10K_SKB_CB(txdesc)->htt.refcount = 1;
+		tx_done.discard = 1;
+		tx_done.msdu_id = msdu_id;
 
-		ATH10K_SKB_CB(txdesc)->htt.discard = true;
-		ath10k_txrx_tx_unref(htt, txdesc);
+		ath10k_txrx_tx_unref(htt, &tx_done);
 	}
+	spin_unlock_bh(&htt->tx_lock);
 }
 
 void ath10k_htt_tx_detach(struct ath10k_htt *htt)
@@ -147,31 +146,13 @@
 	ath10k_htt_tx_cleanup_pending(htt);
 	kfree(htt->pending_tx);
 	kfree(htt->used_msdu_ids);
+	dma_pool_destroy(htt->tx_pool);
 	return;
 }
 
 void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
 {
-	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
-	struct ath10k_htt *htt = ar->htt;
-
-	if (skb_cb->htt.is_conf) {
 		dev_kfree_skb_any(skb);
-		return;
-	}
-
-	if (skb_cb->is_aborted) {
-		skb_cb->htt.discard = true;
-
-		/* if the skbuff is aborted we need to make sure we'll free up
-		 * the tx resources, we can't simply run tx_unref() 2 times
-		 * because if htt tx completion came in earlier we'd access
-		 * unallocated memory */
-		if (skb_cb->htt.refcount > 1)
-			skb_cb->htt.refcount = 1;
-	}
-
-	ath10k_txrx_tx_unref(htt, skb);
 }
 
 int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
@@ -192,10 +173,48 @@
 	cmd = (struct htt_cmd *)skb->data;
 	cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_VERSION_REQ;
 
-	ATH10K_SKB_CB(skb)->htt.is_conf = true;
+	ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
+	if (ret) {
+		dev_kfree_skb_any(skb);
+		return ret;
+	}
+
+	return 0;
+}
 
-	ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie)
+{
+	struct htt_stats_req *req;
+	struct sk_buff *skb;
+	struct htt_cmd *cmd;
+	int len = 0, ret;
+
+	len += sizeof(cmd->hdr);
+	len += sizeof(cmd->stats_req);
+
+	skb = ath10k_htc_alloc_skb(len);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_put(skb, len);
+	cmd = (struct htt_cmd *)skb->data;
+	cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_STATS_REQ;
+
+	req = &cmd->stats_req;
+
+	memset(req, 0, sizeof(*req));
+
+	/* currently we support only max 8 bit masks so no need to worry
+	 * about endian support */
+	req->upload_types[0] = mask;
+	req->reset_types[0] = mask;
+	req->stat_type = HTT_STATS_REQ_CFG_STAT_TYPE_INVALID;
+	req->cookie_lsb = cpu_to_le32(cookie & 0xffffffff);
+	req->cookie_msb = cpu_to_le32((cookie & 0xffffffff00000000ULL) >> 32);
+
+	ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
 	if (ret) {
+		ath10k_warn("failed to send htt type stats request: %d", ret);
 		dev_kfree_skb_any(skb);
 		return ret;
 	}
@@ -279,9 +298,7 @@
 
 #undef desc_offset
 
-	ATH10K_SKB_CB(skb)->htt.is_conf = true;
-
-	ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+	ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
 	if (ret) {
 		dev_kfree_skb_any(skb);
 		return ret;
@@ -293,10 +310,10 @@
 int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
 {
 	struct device *dev = htt->ar->dev;
-	struct ath10k_skb_cb *skb_cb;
 	struct sk_buff *txdesc = NULL;
 	struct htt_cmd *cmd;
-	u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
+	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
+	u8 vdev_id = skb_cb->vdev_id;
 	int len = 0;
 	int msdu_id = -1;
 	int res;
@@ -304,30 +321,32 @@
 
 	res = ath10k_htt_tx_inc_pending(htt);
 	if (res)
-		return res;
+		goto err;
 
 	len += sizeof(cmd->hdr);
 	len += sizeof(cmd->mgmt_tx);
 
-	txdesc = ath10k_htc_alloc_skb(len);
-	if (!txdesc) {
-		res = -ENOMEM;
-		goto err;
-	}
-
 	spin_lock_bh(&htt->tx_lock);
-	msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
-	if (msdu_id < 0) {
+	res = ath10k_htt_tx_alloc_msdu_id(htt);
+	if (res < 0) {
 		spin_unlock_bh(&htt->tx_lock);
-		res = msdu_id;
-		goto err;
+		goto err_tx_dec;
 	}
-	htt->pending_tx[msdu_id] = txdesc;
+	msdu_id = res;
+	htt->pending_tx[msdu_id] = msdu;
 	spin_unlock_bh(&htt->tx_lock);
 
-	res = ath10k_skb_map(dev, msdu);
+	txdesc = ath10k_htc_alloc_skb(len);
+	if (!txdesc) {
+		res = -ENOMEM;
+		goto err_free_msdu_id;
+	}
+
+	skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len,
+				       DMA_TO_DEVICE);
+	res = dma_mapping_error(dev, skb_cb->paddr);
 	if (res)
-		goto err;
+		goto err_free_txdesc;
 
 	skb_put(txdesc, len);
 	cmd = (struct htt_cmd *)txdesc->data;
@@ -339,172 +358,184 @@
 	memcpy(cmd->mgmt_tx.hdr, msdu->data,
 	       min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN));
 
-	/* refcount is decremented by HTC and HTT completions until it reaches
-	 * zero and is freed */
-	skb_cb = ATH10K_SKB_CB(txdesc);
-	skb_cb->htt.msdu_id = msdu_id;
-	skb_cb->htt.refcount = 2;
-	skb_cb->htt.msdu = msdu;
+	skb_cb->htt.txbuf = NULL;
 
-	res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+	res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
 	if (res)
-		goto err;
+		goto err_unmap_msdu;
 
 	return 0;
 
-err:
-	ath10k_skb_unmap(dev, msdu);
-
-	if (txdesc)
+err_unmap_msdu:
+	dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
+err_free_txdesc:
 		dev_kfree_skb_any(txdesc);
-	if (msdu_id >= 0) {
+err_free_msdu_id:
 		spin_lock_bh(&htt->tx_lock);
 		htt->pending_tx[msdu_id] = NULL;
 		ath10k_htt_tx_free_msdu_id(htt, msdu_id);
 		spin_unlock_bh(&htt->tx_lock);
-	}
+err_tx_dec:
 	ath10k_htt_tx_dec_pending(htt);
+err:
 	return res;
 }
 
 int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
 {
 	struct device *dev = htt->ar->dev;
-	struct htt_cmd *cmd;
-	struct htt_data_tx_desc_frag *tx_frags;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
-	struct ath10k_skb_cb *skb_cb;
-	struct sk_buff *txdesc = NULL;
-	struct sk_buff *txfrag = NULL;
-	u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
-	u8 tid;
-	int prefetch_len, desc_len, frag_len;
-	dma_addr_t frags_paddr;
-	int msdu_id = -1;
+	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
+	struct ath10k_hif_sg_item sg_items[2];
+	struct htt_data_tx_desc_frag *frags;
+	u8 vdev_id = skb_cb->vdev_id;
+	u8 tid = skb_cb->htt.tid;
+	int prefetch_len;
 	int res;
-	u8 flags0;
-	u16 flags1;
+	u8 flags0 = 0;
+	u16 msdu_id, flags1 = 0;
+	dma_addr_t paddr;
+	u32 frags_paddr;
+	bool use_frags;
 
 	res = ath10k_htt_tx_inc_pending(htt);
 	if (res)
-		return res;
-
-	prefetch_len = min(htt->prefetch_len, msdu->len);
-	prefetch_len = roundup(prefetch_len, 4);
-
-	desc_len = sizeof(cmd->hdr) + sizeof(cmd->data_tx) + prefetch_len;
-	frag_len = sizeof(*tx_frags) * 2;
-
-	txdesc = ath10k_htc_alloc_skb(desc_len);
-	if (!txdesc) {
-		res = -ENOMEM;
-		goto err;
-	}
-
-	txfrag = dev_alloc_skb(frag_len);
-	if (!txfrag) {
-		res = -ENOMEM;
-		goto err;
-	}
-
-	if (!IS_ALIGNED((unsigned long)txdesc->data, 4)) {
-		ath10k_warn("htt alignment check failed. dropping packet.\n");
-		res = -EIO;
 		goto err;
-	}
 
 	spin_lock_bh(&htt->tx_lock);
-	msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
-	if (msdu_id < 0) {
+	res = ath10k_htt_tx_alloc_msdu_id(htt);
+	if (res < 0) {
 		spin_unlock_bh(&htt->tx_lock);
-		res = msdu_id;
-		goto err;
+		goto err_tx_dec;
 	}
-	htt->pending_tx[msdu_id] = txdesc;
+	msdu_id = res;
+	htt->pending_tx[msdu_id] = msdu;
 	spin_unlock_bh(&htt->tx_lock);
 
-	res = ath10k_skb_map(dev, msdu);
+	prefetch_len = min(htt->prefetch_len, msdu->len);
+	prefetch_len = roundup(prefetch_len, 4);
+
+	/* Since HTT 3.0 there is no separate mgmt tx command. However in case
+	 * of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx
+	 * fragment list host driver specifies directly frame pointer. */
+	use_frags = htt->target_version_major < 3 ||
+		    !ieee80211_is_mgmt(hdr->frame_control);
+
+	skb_cb->htt.txbuf = dma_pool_alloc(htt->tx_pool, GFP_ATOMIC,
+					   &paddr);
+	if (!skb_cb->htt.txbuf)
+		goto err_free_msdu_id;
+	skb_cb->htt.txbuf_paddr = paddr;
+
+	skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len,
+				       DMA_TO_DEVICE);
+	res = dma_mapping_error(dev, skb_cb->paddr);
 	if (res)
-		goto err;
+		goto err_free_txbuf;
 
-	/* tx fragment list must be terminated with zero-entry */
-	skb_put(txfrag, frag_len);
-	tx_frags = (struct htt_data_tx_desc_frag *)txfrag->data;
-	tx_frags[0].paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr);
-	tx_frags[0].len   = __cpu_to_le32(msdu->len);
-	tx_frags[1].paddr = __cpu_to_le32(0);
-	tx_frags[1].len   = __cpu_to_le32(0);
+	if (likely(use_frags)) {
+		frags = skb_cb->htt.txbuf->frags;
 
-	res = ath10k_skb_map(dev, txfrag);
-	if (res)
-		goto err;
+		frags[0].paddr = __cpu_to_le32(skb_cb->paddr);
+		frags[0].len = __cpu_to_le32(msdu->len);
+		frags[1].paddr = 0;
+		frags[1].len = 0;
 
-	ath10k_dbg(ATH10K_DBG_HTT, "txfrag 0x%llx msdu 0x%llx\n",
-		   (unsigned long long) ATH10K_SKB_CB(txfrag)->paddr,
-		   (unsigned long long) ATH10K_SKB_CB(msdu)->paddr);
-	ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "txfrag: ",
-			txfrag->data, frag_len);
-	ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "msdu: ",
-			msdu->data, msdu->len);
+		flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
+			     HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
 
-	skb_put(txdesc, desc_len);
-	cmd = (struct htt_cmd *)txdesc->data;
-	memset(cmd, 0, desc_len);
+		frags_paddr = skb_cb->htt.txbuf_paddr;
+	} else {
+		flags0 |= SM(ATH10K_HW_TXRX_MGMT,
+			     HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
 
-	tid = ATH10K_SKB_CB(msdu)->htt.tid;
+		frags_paddr = skb_cb->paddr;
+	}
 
-	ath10k_dbg(ATH10K_DBG_HTT, "htt data tx using tid %hhu\n", tid);
+	/* Normally all commands go through HTC which manages tx credits for
+	 * each endpoint and notifies when tx is completed.
+	 *
+	 * HTT endpoint is creditless so there's no need to care about HTC
+	 * flags. In that case it is trivial to fill the HTC header here.
+	 *
+	 * MSDU transmission is considered completed upon HTT event. This
+	 * implies no relevant resources can be freed until after the event is
+	 * received. That's why HTC tx completion handler itself is ignored by
+	 * setting NULL to transfer_context for all sg items.
+	 *
+	 * There is simply no point in pushing HTT TX_FRM through HTC tx path
+	 * as it's a waste of resources. By bypassing HTC it is possible to
+	 * avoid extra memory allocations, compress data structures and thus
+	 * improve performance. */
+
+	skb_cb->htt.txbuf->htc_hdr.eid = htt->eid;
+	skb_cb->htt.txbuf->htc_hdr.len = __cpu_to_le16(
+			sizeof(skb_cb->htt.txbuf->cmd_hdr) +
+			sizeof(skb_cb->htt.txbuf->cmd_tx) +
+			prefetch_len);
+	skb_cb->htt.txbuf->htc_hdr.flags = 0;
 
-	flags0  = 0;
 	if (!ieee80211_has_protected(hdr->frame_control))
 		flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;
+
 	flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
-	flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
-		     HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
 
-	flags1  = 0;
 	flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
 	flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID);
+	flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD;
+	flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD;
 
-	frags_paddr = ATH10K_SKB_CB(txfrag)->paddr;
-
-	cmd->hdr.msg_type        = HTT_H2T_MSG_TYPE_TX_FRM;
-	cmd->data_tx.flags0      = flags0;
-	cmd->data_tx.flags1      = __cpu_to_le16(flags1);
-	cmd->data_tx.len         = __cpu_to_le16(msdu->len);
-	cmd->data_tx.id          = __cpu_to_le16(msdu_id);
-	cmd->data_tx.frags_paddr = __cpu_to_le32(frags_paddr);
-	cmd->data_tx.peerid      = __cpu_to_le32(HTT_INVALID_PEERID);
-
-	memcpy(cmd->data_tx.prefetch, msdu->data, prefetch_len);
-
-	/* refcount is decremented by HTC and HTT completions until it reaches
-	 * zero and is freed */
-	skb_cb = ATH10K_SKB_CB(txdesc);
-	skb_cb->htt.msdu_id = msdu_id;
-	skb_cb->htt.refcount = 2;
-	skb_cb->htt.txfrag = txfrag;
-	skb_cb->htt.msdu = msdu;
+	skb_cb->htt.txbuf->cmd_hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM;
+	skb_cb->htt.txbuf->cmd_tx.flags0 = flags0;
+	skb_cb->htt.txbuf->cmd_tx.flags1 = __cpu_to_le16(flags1);
+	skb_cb->htt.txbuf->cmd_tx.len = __cpu_to_le16(msdu->len);
+	skb_cb->htt.txbuf->cmd_tx.id = __cpu_to_le16(msdu_id);
+	skb_cb->htt.txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr);
+	skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID);
+
+	ath10k_dbg(ATH10K_DBG_HTT,
+		   "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu\n",
+		   flags0, flags1, msdu->len, msdu_id, frags_paddr,
+		   (u32)skb_cb->paddr, vdev_id, tid);
+	ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt tx msdu: ",
+			msdu->data, msdu->len);
 
-	res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+	sg_items[0].transfer_id = 0;
+	sg_items[0].transfer_context = NULL;
+	sg_items[0].vaddr = &skb_cb->htt.txbuf->htc_hdr;
+	sg_items[0].paddr = skb_cb->htt.txbuf_paddr +
+			    sizeof(skb_cb->htt.txbuf->frags);
+	sg_items[0].len = sizeof(skb_cb->htt.txbuf->htc_hdr) +
+			  sizeof(skb_cb->htt.txbuf->cmd_hdr) +
+			  sizeof(skb_cb->htt.txbuf->cmd_tx);
+
+	sg_items[1].transfer_id = 0;
+	sg_items[1].transfer_context = NULL;
+	sg_items[1].vaddr = msdu->data;
+	sg_items[1].paddr = skb_cb->paddr;
+	sg_items[1].len = prefetch_len;
+
+	res = ath10k_hif_tx_sg(htt->ar,
+			       htt->ar->htc.endpoint[htt->eid].ul_pipe_id,
+			       sg_items, ARRAY_SIZE(sg_items));
 	if (res)
-		goto err;
+		goto err_unmap_msdu;
 
 	return 0;
-err:
-	if (txfrag)
-		ath10k_skb_unmap(dev, txfrag);
-	if (txdesc)
-		dev_kfree_skb_any(txdesc);
-	if (txfrag)
-		dev_kfree_skb_any(txfrag);
-	if (msdu_id >= 0) {
+
+err_unmap_msdu:
+	dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
+err_free_txbuf:
+	dma_pool_free(htt->tx_pool,
+		      skb_cb->htt.txbuf,
+		      skb_cb->htt.txbuf_paddr);
+err_free_msdu_id:
 		spin_lock_bh(&htt->tx_lock);
 		htt->pending_tx[msdu_id] = NULL;
 		ath10k_htt_tx_free_msdu_id(htt, msdu_id);
 		spin_unlock_bh(&htt->tx_lock);
-	}
+err_tx_dec:
 	ath10k_htt_tx_dec_pending(htt);
-	ath10k_skb_unmap(dev, msdu);
+err:
 	return res;
 }
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/hw.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/hw.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/hw.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/hw.h	2015-09-29 11:15:06.921740666 +0200
@@ -20,28 +20,37 @@
 
 #include "targaddrs.h"
 
-/* Supported FW version */
-#define SUPPORTED_FW_MAJOR	1
-#define SUPPORTED_FW_MINOR	0
-#define SUPPORTED_FW_RELEASE	0
-#define SUPPORTED_FW_BUILD	629
-
-/* QCA988X 1.0 definitions */
-#define QCA988X_HW_1_0_VERSION		0x4000002c
-#define QCA988X_HW_1_0_FW_DIR		"ath10k/QCA988X/hw1.0"
-#define QCA988X_HW_1_0_FW_FILE		"firmware.bin"
-#define QCA988X_HW_1_0_OTP_FILE		"otp.bin"
-#define QCA988X_HW_1_0_BOARD_DATA_FILE	"board.bin"
-#define QCA988X_HW_1_0_PATCH_LOAD_ADDR	0x1234
+/* QCA988X 1.0 definitions (unsupported) */
+#define QCA988X_HW_1_0_CHIP_ID_REV	0x0
 
 /* QCA988X 2.0 definitions */
 #define QCA988X_HW_2_0_VERSION		0x4100016c
+#define QCA988X_HW_2_0_CHIP_ID_REV	0x2
 #define QCA988X_HW_2_0_FW_DIR		"ath10k/QCA988X/hw2.0"
 #define QCA988X_HW_2_0_FW_FILE		"firmware.bin"
 #define QCA988X_HW_2_0_OTP_FILE		"otp.bin"
 #define QCA988X_HW_2_0_BOARD_DATA_FILE	"board.bin"
 #define QCA988X_HW_2_0_PATCH_LOAD_ADDR	0x1234
 
+#define ATH10K_FW_API2_FILE		"firmware-2.bin"
+
+/* includes also the null byte */
+#define ATH10K_FIRMWARE_MAGIC               "QCA-ATH10K"
+
+struct ath10k_fw_ie {
+	__le32 id;
+	__le32 len;
+	u8 data[0];
+};
+
+enum ath10k_fw_ie_type {
+	ATH10K_FW_IE_FW_VERSION = 0,
+	ATH10K_FW_IE_TIMESTAMP = 1,
+	ATH10K_FW_IE_FEATURES = 2,
+	ATH10K_FW_IE_FW_IMAGE = 3,
+	ATH10K_FW_IE_OTP_IMAGE = 4,
+};
+
 /* Known pecularities:
  *  - current FW doesn't support raw rx mode (last tested v599)
  *  - current FW dumps upon raw tx mode (last tested v599)
@@ -53,6 +62,9 @@
 	ATH10K_HW_TXRX_RAW = 0,
 	ATH10K_HW_TXRX_NATIVE_WIFI = 1,
 	ATH10K_HW_TXRX_ETHERNET = 2,
+
+	/* Valid for HTT >= 3.0. Used for management frames in TX_FRM. */
+	ATH10K_HW_TXRX_MGMT = 3,
 };
 
 enum ath10k_mcast2ucast_mode {
@@ -60,6 +72,7 @@
 	ATH10K_MCAST2UCAST_ENABLED = 1,
 };
 
+/* Target specific defines for MAIN firmware */
 #define TARGET_NUM_VDEVS			8
 #define TARGET_NUM_PEER_AST			2
 #define TARGET_NUM_WDS_ENTRIES			32
@@ -75,7 +88,11 @@
 #define TARGET_RX_CHAIN_MASK			(BIT(0) | BIT(1) | BIT(2))
 #define TARGET_RX_TIMEOUT_LO_PRI		100
 #define TARGET_RX_TIMEOUT_HI_PRI		40
-#define TARGET_RX_DECAP_MODE			ATH10K_HW_TXRX_ETHERNET
+
+/* Native Wifi decap mode is used to align IP frames to 4-byte boundaries and
+ * avoid a very expensive re-alignment in mac80211. */
+#define TARGET_RX_DECAP_MODE			ATH10K_HW_TXRX_NATIVE_WIFI
+
 #define TARGET_SCAN_MAX_PENDING_REQS		4
 #define TARGET_BMISS_OFFLOAD_MAX_VDEV		3
 #define TARGET_ROAM_OFFLOAD_MAX_VDEV		3
@@ -90,6 +107,37 @@
 #define TARGET_NUM_MSDU_DESC			(1024 + 400)
 #define TARGET_MAX_FRAG_ENTRIES			0
 
+/* Target specific defines for 10.X firmware */
+#define TARGET_10X_NUM_VDEVS			16
+#define TARGET_10X_NUM_PEER_AST			2
+#define TARGET_10X_NUM_WDS_ENTRIES		32
+#define TARGET_10X_DMA_BURST_SIZE		0
+#define TARGET_10X_MAC_AGGR_DELIM		0
+#define TARGET_10X_AST_SKID_LIMIT		16
+#define TARGET_10X_NUM_PEERS			(128 + (TARGET_10X_NUM_VDEVS))
+#define TARGET_10X_NUM_PEERS_MAX		128
+#define TARGET_10X_NUM_OFFLOAD_PEERS		0
+#define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS	0
+#define TARGET_10X_NUM_PEER_KEYS		2
+#define TARGET_10X_NUM_TIDS			256
+#define TARGET_10X_TX_CHAIN_MASK		(BIT(0) | BIT(1) | BIT(2))
+#define TARGET_10X_RX_CHAIN_MASK		(BIT(0) | BIT(1) | BIT(2))
+#define TARGET_10X_RX_TIMEOUT_LO_PRI		100
+#define TARGET_10X_RX_TIMEOUT_HI_PRI		40
+#define TARGET_10X_RX_DECAP_MODE		ATH10K_HW_TXRX_NATIVE_WIFI
+#define TARGET_10X_SCAN_MAX_PENDING_REQS	4
+#define TARGET_10X_BMISS_OFFLOAD_MAX_VDEV	2
+#define TARGET_10X_ROAM_OFFLOAD_MAX_VDEV	2
+#define TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES	8
+#define TARGET_10X_GTK_OFFLOAD_MAX_VDEV		3
+#define TARGET_10X_NUM_MCAST_GROUPS		0
+#define TARGET_10X_NUM_MCAST_TABLE_ELEMS	0
+#define TARGET_10X_MCAST2UCAST_MODE		ATH10K_MCAST2UCAST_DISABLED
+#define TARGET_10X_TX_DBG_LOG_SIZE		1024
+#define TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1
+#define TARGET_10X_VOW_CONFIG			0
+#define TARGET_10X_NUM_MSDU_DESC		(1024 + 400)
+#define TARGET_10X_MAX_FRAG_ENTRIES		0
 
 /* Number of Copy Engines supported */
 #define CE_COUNT 8
@@ -157,8 +205,11 @@
 #define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS	0x0006c000
 #define PCIE_LOCAL_BASE_ADDRESS			0x00080000
 
+#define SOC_RESET_CONTROL_ADDRESS		0x00000000
 #define SOC_RESET_CONTROL_OFFSET		0x00000000
 #define SOC_RESET_CONTROL_SI0_RST_MASK		0x00000001
+#define SOC_RESET_CONTROL_CE_RST_MASK		0x00040000
+#define SOC_RESET_CONTROL_CPU_WARM_RST_MASK	0x00000040
 #define SOC_CPU_CLOCK_OFFSET			0x00000020
 #define SOC_CPU_CLOCK_STANDARD_LSB		0
 #define SOC_CPU_CLOCK_STANDARD_MASK		0x00000003
@@ -168,6 +219,12 @@
 #define SOC_LPO_CAL_OFFSET			0x000000e0
 #define SOC_LPO_CAL_ENABLE_LSB			20
 #define SOC_LPO_CAL_ENABLE_MASK			0x00100000
+#define SOC_LF_TIMER_CONTROL0_ADDRESS		0x00000050
+#define SOC_LF_TIMER_CONTROL0_ENABLE_MASK	0x00000004
+
+#define SOC_CHIP_ID_ADDRESS			0x000000ec
+#define SOC_CHIP_ID_REV_LSB			8
+#define SOC_CHIP_ID_REV_MASK			0x00000f00
 
 #define WLAN_RESET_CONTROL_COLD_RST_MASK	0x00000008
 #define WLAN_RESET_CONTROL_WARM_RST_MASK	0x00000004
@@ -218,8 +275,10 @@
 #define CORE_CTRL_CPU_INTR_MASK			0x00002000
 #define CORE_CTRL_ADDRESS			0x0000
 #define PCIE_INTR_ENABLE_ADDRESS		0x0008
+#define PCIE_INTR_CAUSE_ADDRESS			0x000c
 #define PCIE_INTR_CLR_ADDRESS			0x0014
 #define SCRATCH_3_ADDRESS			0x0030
+#define CPU_INTR_ADDRESS			0x0010
 
 /* Firmware indications to the Host via SCRATCH_3 register. */
 #define FW_INDICATOR_ADDRESS	(SOC_CORE_BASE_ADDRESS + SCRATCH_3_ADDRESS)
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/Kconfig linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/Kconfig
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/Kconfig	2015-09-29 11:15:06.917740641 +0200
@@ -37,3 +37,10 @@
 	---help---
 	  Select this to ath10k use tracing infrastructure.
 
+config ATH10K_DFS_CERTIFIED
+	bool "Atheros DFS support for certified platforms"
+	depends on ATH10K && CFG80211_CERTIFICATION_ONUS
+	default n
+	---help---
+	  This option enables DFS support for initiating radiation on
+	  ath10k.
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/mac.c linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/mac.c
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/mac.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/mac.c	2015-09-29 11:15:06.921740666 +0200
@@ -20,6 +20,7 @@
 #include <net/mac80211.h>
 #include <linux/etherdevice.h>
 
+#include "hif.h"
 #include "core.h"
 #include "debug.h"
 #include "wmi.h"
@@ -43,6 +44,8 @@
 		.macaddr = macaddr,
 	};
 
+	lockdep_assert_held(&arvif->ar->conf_mutex);
+
 	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
 		arg.key_flags = WMI_KEY_PAIRWISE;
 	else
@@ -87,6 +90,8 @@
 	struct ath10k *ar = arvif->ar;
 	int ret;
 
+	lockdep_assert_held(&ar->conf_mutex);
+
 	INIT_COMPLETION(ar->install_key_done);
 
 	ret = ath10k_send_key(arvif, key, cmd, macaddr);
@@ -317,16 +322,94 @@
 	lockdep_assert_held(&ar->conf_mutex);
 
 	ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
-	if (ret)
+	if (ret) {
+		ath10k_warn("Failed to create wmi peer: %i\n", ret);
 		return ret;
+	}
 
 	ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
-	if (ret)
+	if (ret) {
+		ath10k_warn("Failed to wait for created wmi peer: %i\n", ret);
+		return ret;
+	}
+	spin_lock_bh(&ar->data_lock);
+	ar->num_peers++;
+	spin_unlock_bh(&ar->data_lock);
+
+	return 0;
+}
+
+static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
+{
+	struct ath10k *ar = arvif->ar;
+	u32 param;
+	int ret;
+
+	param = ar->wmi.pdev_param->sta_kickout_th;
+	ret = ath10k_wmi_pdev_set_param(ar, param,
+					ATH10K_KICKOUT_THRESHOLD);
+	if (ret) {
+		ath10k_warn("Failed to set kickout threshold: %d\n", ret);
+		return ret;
+	}
+
+	param = ar->wmi.vdev_param->ap_keepalive_min_idle_inactive_time_secs;
+	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
+					ATH10K_KEEPALIVE_MIN_IDLE);
+	if (ret) {
+		ath10k_warn("Failed to set keepalive minimum idle time : %d\n",
+			    ret);
+		return ret;
+	}
+
+	param = ar->wmi.vdev_param->ap_keepalive_max_idle_inactive_time_secs;
+	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
+					ATH10K_KEEPALIVE_MAX_IDLE);
+	if (ret) {
+		ath10k_warn("Failed to set keepalive maximum idle time: %d\n",
+			    ret);
+		return ret;
+	}
+
+	param = ar->wmi.vdev_param->ap_keepalive_max_unresponsive_time_secs;
+	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
+					ATH10K_KEEPALIVE_MAX_UNRESPONSIVE);
+	if (ret) {
+		ath10k_warn("Failed to set keepalive maximum unresponsive time: %d\n",
+			    ret);
 		return ret;
+	}
 
 	return 0;
 }
 
+static int  ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
+{
+	struct ath10k *ar = arvif->ar;
+	u32 vdev_param;
+
+	if (value != 0xFFFFFFFF)
+		value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold,
+			      ATH10K_RTS_MAX);
+
+	vdev_param = ar->wmi.vdev_param->rts_threshold;
+	return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value);
+}
+
+static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value)
+{
+	struct ath10k *ar = arvif->ar;
+	u32 vdev_param;
+
+	if (value != 0xFFFFFFFF)
+		value = clamp_t(u32, arvif->ar->hw->wiphy->frag_threshold,
+				ATH10K_FRAGMT_THRESHOLD_MIN,
+				ATH10K_FRAGMT_THRESHOLD_MAX);
+
+	vdev_param = ar->wmi.vdev_param->fragmentation_threshold;
+	return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value);
+}
+
 static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
 {
 	int ret;
@@ -341,6 +424,10 @@
 	if (ret)
 		return ret;
 
+	spin_lock_bh(&ar->data_lock);
+	ar->num_peers--;
+	spin_unlock_bh(&ar->data_lock);
+
 	return 0;
 }
 
@@ -360,7 +447,23 @@
 
 		list_del(&peer->list);
 		kfree(peer);
+		ar->num_peers--;
+	}
+	spin_unlock_bh(&ar->data_lock);
+}
+
+static void ath10k_peer_cleanup_all(struct ath10k *ar)
+{
+	struct ath10k_peer *peer, *tmp;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	spin_lock_bh(&ar->data_lock);
+	list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
+		list_del(&peer->list);
+		kfree(peer);
 	}
+	ar->num_peers = 0;
 	spin_unlock_bh(&ar->data_lock);
 }
 
@@ -372,6 +475,8 @@
 {
 	int ret;
 
+	lockdep_assert_held(&ar->conf_mutex);
+
 	ret = wait_for_completion_timeout(&ar->vdev_setup_done,
 					  ATH10K_VDEV_SETUP_TIMEOUT_HZ);
 	if (ret == 0)
@@ -383,8 +488,7 @@
 static int ath10k_vdev_start(struct ath10k_vif *arvif)
 {
 	struct ath10k *ar = arvif->ar;
-	struct ieee80211_conf *conf = &ar->hw->conf;
-	struct ieee80211_channel *channel = conf->chandef.chan;
+	struct cfg80211_chan_def *chandef = &ar->chandef;
 	struct wmi_vdev_start_request_arg arg = {};
 	int ret = 0;
 
@@ -396,26 +500,33 @@
 	arg.dtim_period = arvif->dtim_period;
 	arg.bcn_intval = arvif->beacon_interval;
 
-	arg.channel.freq = channel->center_freq;
-
-	arg.channel.band_center_freq1 = conf->chandef.center_freq1;
-
-	arg.channel.mode = chan_to_phymode(&conf->chandef);
-
-	arg.channel.min_power = channel->max_power * 3;
-	arg.channel.max_power = channel->max_power * 4;
-	arg.channel.max_reg_power = channel->max_reg_power * 4;
-	arg.channel.max_antenna_gain = channel->max_antenna_gain;
+	arg.channel.freq = chandef->chan->center_freq;
+	arg.channel.band_center_freq1 = chandef->center_freq1;
+	arg.channel.mode = chan_to_phymode(chandef);
+
+	arg.channel.min_power = 0;
+	arg.channel.max_power = chandef->chan->max_power * 2;
+	arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
+	arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2;
 
 	if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
 		arg.ssid = arvif->u.ap.ssid;
 		arg.ssid_len = arvif->u.ap.ssid_len;
 		arg.hidden_ssid = arvif->u.ap.hidden_ssid;
+
+		/* For now allow DFS for AP mode */
+		arg.channel.chan_radar =
+			!!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
 	} else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
 		arg.ssid = arvif->vif->bss_conf.ssid;
 		arg.ssid_len = arvif->vif->bss_conf.ssid_len;
 	}
 
+	ath10k_dbg(ATH10K_DBG_MAC,
+		   "mac vdev %d start center_freq %d phymode %s\n",
+		   arg.vdev_id, arg.channel.freq,
+		   ath10k_wmi_phymode_str(arg.channel.mode));
+
 	ret = ath10k_wmi_vdev_start(ar, &arg);
 	if (ret) {
 		ath10k_warn("WMI vdev start failed: ret %d\n", ret);
@@ -457,27 +568,32 @@
 
 static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
 {
-	struct ieee80211_channel *channel = ar->hw->conf.chandef.chan;
+	struct cfg80211_chan_def *chandef = &ar->chandef;
+	struct ieee80211_channel *channel = chandef->chan;
 	struct wmi_vdev_start_request_arg arg = {};
-	enum nl80211_channel_type type;
 	int ret = 0;
 
 	lockdep_assert_held(&ar->conf_mutex);
 
-	type = cfg80211_get_chandef_type(&ar->hw->conf.chandef);
+	if (!ar->monitor_present) {
+		ath10k_warn("mac montor stop -- monitor is not present\n");
+		return -EINVAL;
+	}
 
 	arg.vdev_id = vdev_id;
 	arg.channel.freq = channel->center_freq;
-	arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1;
+	arg.channel.band_center_freq1 = chandef->center_freq1;
 
 	/* TODO setup this dynamically, what in case we
 	   don't have any vifs? */
-	arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef);
-
-	arg.channel.min_power = channel->max_power * 3;
-	arg.channel.max_power = channel->max_power * 4;
-	arg.channel.max_reg_power = channel->max_reg_power * 4;
-	arg.channel.max_antenna_gain = channel->max_antenna_gain;
+	arg.channel.mode = chan_to_phymode(chandef);
+	arg.channel.chan_radar =
+			!!(channel->flags & IEEE80211_CHAN_RADAR);
+
+	arg.channel.min_power = 0;
+	arg.channel.max_power = channel->max_power * 2;
+	arg.channel.max_reg_power = channel->max_reg_power * 2;
+	arg.channel.max_antenna_gain = channel->max_antenna_gain * 2;
 
 	ret = ath10k_wmi_vdev_start(ar, &arg);
 	if (ret) {
@@ -516,12 +632,19 @@
 
 	lockdep_assert_held(&ar->conf_mutex);
 
-	/* For some reasons, ath10k_wmi_vdev_down() here couse
-	 * often ath10k_wmi_vdev_stop() to fail. Next we could
-	 * not run monitor vdev and driver reload
-	 * required. Don't see such problems we skip
-	 * ath10k_wmi_vdev_down() here.
-	 */
+	if (!ar->monitor_present) {
+		ath10k_warn("mac montor stop -- monitor is not present\n");
+		return -EINVAL;
+	}
+
+	if (!ar->monitor_enabled) {
+		ath10k_warn("mac montor stop -- monitor is not enabled\n");
+		return -EINVAL;
+	}
+
+	ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id);
+	if (ret)
+		ath10k_warn("Monitor vdev down failed: %d\n", ret);
 
 	ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
 	if (ret)
@@ -563,7 +686,7 @@
 		goto vdev_fail;
 	}
 
-	ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface created, vdev id: %d\n",
+	ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d created\n",
 		   ar->monitor_vdev_id);
 
 	ar->monitor_present = true;
@@ -595,18 +718,137 @@
 	ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
 	ar->monitor_present = false;
 
-	ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface destroyed, vdev id: %d\n",
+	ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n",
 		   ar->monitor_vdev_id);
 	return ret;
 }
 
+static int ath10k_start_cac(struct ath10k *ar)
+{
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	set_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+
+	ret = ath10k_monitor_create(ar);
+	if (ret) {
+		clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+		return ret;
+	}
+
+	ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
+	if (ret) {
+		clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+		ath10k_monitor_destroy(ar);
+		return ret;
+	}
+
+	ath10k_dbg(ATH10K_DBG_MAC, "mac cac start monitor vdev %d\n",
+		   ar->monitor_vdev_id);
+
+	return 0;
+}
+
+static int ath10k_stop_cac(struct ath10k *ar)
+{
+	lockdep_assert_held(&ar->conf_mutex);
+
+	/* CAC is not running - do nothing */
+	if (!test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags))
+		return 0;
+
+	ath10k_monitor_stop(ar);
+	ath10k_monitor_destroy(ar);
+	clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+
+	ath10k_dbg(ATH10K_DBG_MAC, "mac cac finished\n");
+
+	return 0;
+}
+
+static const char *ath10k_dfs_state(enum nl80211_dfs_state dfs_state)
+{
+	switch (dfs_state) {
+	case NL80211_DFS_USABLE:
+		return "USABLE";
+	case NL80211_DFS_UNAVAILABLE:
+		return "UNAVAILABLE";
+	case NL80211_DFS_AVAILABLE:
+		return "AVAILABLE";
+	default:
+		WARN_ON(1);
+		return "bug";
+	}
+}
+
+static void ath10k_config_radar_detection(struct ath10k *ar)
+{
+	struct ieee80211_channel *chan = ar->hw->conf.chandef.chan;
+	bool radar = ar->hw->conf.radar_enabled;
+	bool chan_radar = !!(chan->flags & IEEE80211_CHAN_RADAR);
+	enum nl80211_dfs_state dfs_state = chan->dfs_state;
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	ath10k_dbg(ATH10K_DBG_MAC,
+		   "mac radar config update: chan %dMHz radar %d chan radar %d chan state %s\n",
+		   chan->center_freq, radar, chan_radar,
+		   ath10k_dfs_state(dfs_state));
+
+	/*
+	 * It's safe to call it even if CAC is not started.
+	 * This call here guarantees changing channel, etc. will stop CAC.
+	 */
+	ath10k_stop_cac(ar);
+
+	if (!radar)
+		return;
+
+	if (!chan_radar)
+		return;
+
+	if (dfs_state != NL80211_DFS_USABLE)
+		return;
+
+	ret = ath10k_start_cac(ar);
+	if (ret) {
+		/*
+		 * Not possible to start CAC on current channel so starting
+		 * radiation is not allowed, make this channel DFS_UNAVAILABLE
+		 * by indicating that radar was detected.
+		 */
+		ath10k_warn("failed to start CAC (%d)\n", ret);
+		ieee80211_radar_detected(ar->hw);
+	}
+}
+
 static void ath10k_control_beaconing(struct ath10k_vif *arvif,
 				struct ieee80211_bss_conf *info)
 {
 	int ret = 0;
 
+	lockdep_assert_held(&arvif->ar->conf_mutex);
+
 	if (!info->enable_beacon) {
 		ath10k_vdev_stop(arvif);
+
+		arvif->is_started = false;
+		arvif->is_up = false;
+
+		spin_lock_bh(&arvif->ar->data_lock);
+		if (arvif->beacon) {
+			dma_unmap_single(arvif->ar->dev,
+					 ATH10K_SKB_CB(arvif->beacon)->paddr,
+					 arvif->beacon->len, DMA_TO_DEVICE);
+			dev_kfree_skb_any(arvif->beacon);
+
+			arvif->beacon = NULL;
+			arvif->beacon_sent = false;
+		}
+		spin_unlock_bh(&arvif->ar->data_lock);
+
 		return;
 	}
 
@@ -616,39 +858,51 @@
 	if (ret)
 		return;
 
-	ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, 0, info->bssid);
+	arvif->aid = 0;
+	memcpy(arvif->bssid, info->bssid, ETH_ALEN);
+
+	ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
+				 arvif->bssid);
 	if (ret) {
 		ath10k_warn("Failed to bring up VDEV: %d\n",
 			    arvif->vdev_id);
+		ath10k_vdev_stop(arvif);
 		return;
 	}
-	ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d up\n", arvif->vdev_id);
+
+	arvif->is_started = true;
+	arvif->is_up = true;
+
+	ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id);
 }
 
 static void ath10k_control_ibss(struct ath10k_vif *arvif,
 				struct ieee80211_bss_conf *info,
 				const u8 self_peer[ETH_ALEN])
 {
+	u32 vdev_param;
 	int ret = 0;
 
+	lockdep_assert_held(&arvif->ar->conf_mutex);
+
 	if (!info->ibss_joined) {
 		ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
 		if (ret)
 			ath10k_warn("Failed to delete IBSS self peer:%pM for VDEV:%d ret:%d\n",
 				    self_peer, arvif->vdev_id, ret);
 
-		if (is_zero_ether_addr(arvif->u.ibss.bssid))
+		if (is_zero_ether_addr(arvif->bssid))
 			return;
 
 		ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id,
-					 arvif->u.ibss.bssid);
+					 arvif->bssid);
 		if (ret) {
 			ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n",
-				    arvif->u.ibss.bssid, arvif->vdev_id, ret);
+				    arvif->bssid, arvif->vdev_id, ret);
 			return;
 		}
 
-		memset(arvif->u.ibss.bssid, 0, ETH_ALEN);
+		memset(arvif->bssid, 0, ETH_ALEN);
 
 		return;
 	}
@@ -660,8 +914,8 @@
 		return;
 	}
 
-	ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
-					WMI_VDEV_PARAM_ATIM_WINDOW,
+	vdev_param = arvif->ar->wmi.vdev_param->atim_window;
+	ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, vdev_param,
 					ATH10K_DEFAULT_ATIM);
 	if (ret)
 		ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n",
@@ -671,45 +925,45 @@
 /*
  * Review this when mac80211 gains per-interface powersave support.
  */
-static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
 {
-	struct ath10k_generic_iter *ar_iter = data;
-	struct ieee80211_conf *conf = &ar_iter->ar->hw->conf;
-	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	struct ath10k *ar = arvif->ar;
+	struct ieee80211_conf *conf = &ar->hw->conf;
 	enum wmi_sta_powersave_param param;
 	enum wmi_sta_ps_mode psmode;
 	int ret;
 
-	if (vif->type != NL80211_IFTYPE_STATION)
-		return;
+	lockdep_assert_held(&arvif->ar->conf_mutex);
+
+	if (arvif->vif->type != NL80211_IFTYPE_STATION)
+		return 0;
 
 	if (conf->flags & IEEE80211_CONF_PS) {
 		psmode = WMI_STA_PS_MODE_ENABLED;
 		param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
 
-		ret = ath10k_wmi_set_sta_ps_param(ar_iter->ar,
-						  arvif->vdev_id,
-						  param,
+		ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
 						  conf->dynamic_ps_timeout);
 		if (ret) {
 			ath10k_warn("Failed to set inactivity time for VDEV: %d\n",
 				    arvif->vdev_id);
-			return;
+			return ret;
 		}
-
-		ar_iter->ret = ret;
 	} else {
 		psmode = WMI_STA_PS_MODE_DISABLED;
 	}
 
-	ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id,
-					     psmode);
-	if (ar_iter->ret)
+	ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d psmode %s\n",
+		   arvif->vdev_id, psmode ? "enable" : "disable");
+
+	ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode);
+	if (ret) {
 		ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n",
 			    psmode, arvif->vdev_id);
-	else
-		ath10k_dbg(ATH10K_DBG_MAC, "Set PS Mode: %d for VDEV: %d\n",
-			   psmode, arvif->vdev_id);
+		return ret;
+	}
+
+	return 0;
 }
 
 /**********************/
@@ -722,6 +976,8 @@
 				      struct ieee80211_bss_conf *bss_conf,
 				      struct wmi_peer_assoc_complete_arg *arg)
 {
+	lockdep_assert_held(&ar->conf_mutex);
+
 	memcpy(arg->addr, sta->addr, ETH_ALEN);
 	arg->vdev_id = arvif->vdev_id;
 	arg->peer_aid = sta->aid;
@@ -764,6 +1020,8 @@
 	const u8 *rsnie = NULL;
 	const u8 *wpaie = NULL;
 
+	lockdep_assert_held(&ar->conf_mutex);
+
 	bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
 			       info->bssid, NULL, 0, 0, 0);
 	if (bss) {
@@ -804,6 +1062,8 @@
 	u32 ratemask;
 	int i;
 
+	lockdep_assert_held(&ar->conf_mutex);
+
 	sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
 	ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band];
 	rates = sband->bitrates;
@@ -824,9 +1084,10 @@
 				   struct wmi_peer_assoc_complete_arg *arg)
 {
 	const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
-	int smps;
 	int i, n;
 
+	lockdep_assert_held(&ar->conf_mutex);
+
 	if (!ht_cap->ht_supported)
 		return;
 
@@ -868,17 +1129,6 @@
 		arg->peer_flags |= WMI_PEER_STBC;
 	}
 
-	smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS;
-	smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT;
-
-	if (smps == WLAN_HT_CAP_SM_PS_STATIC) {
-		arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
-		arg->peer_flags |= WMI_PEER_STATIC_MIMOPS;
-	} else if (smps == WLAN_HT_CAP_SM_PS_DYNAMIC) {
-		arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
-		arg->peer_flags |= WMI_PEER_DYN_MIMOPS;
-	}
-
 	if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2])
 		arg->peer_rate_caps |= WMI_RC_TS_FLAG;
 	else if (ht_cap->mcs.rx_mask[1])
@@ -888,33 +1138,44 @@
 		if (ht_cap->mcs.rx_mask[i/8] & (1 << i%8))
 			arg->peer_ht_rates.rates[n++] = i;
 
+	/*
+	 * This is a workaround for HT-enabled STAs which break the spec
+	 * and have no HT capabilities RX mask (no HT RX MCS map).
+	 *
+	 * As per spec, in section 20.3.5 Modulation and coding scheme (MCS),
+	 * MCS 0 through 7 are mandatory in 20MHz with 800 ns GI at all STAs.
+	 *
+	 * Firmware asserts if such situation occurs.
+	 */
+	if (n == 0) {
+		arg->peer_ht_rates.num_rates = 8;
+		for (i = 0; i < arg->peer_ht_rates.num_rates; i++)
+			arg->peer_ht_rates.rates[i] = i;
+	} else {
 	arg->peer_ht_rates.num_rates = n;
-	arg->peer_num_spatial_streams = max((n+7) / 8, 1);
+		arg->peer_num_spatial_streams = sta->rx_nss;
+	}
 
-	ath10k_dbg(ATH10K_DBG_MAC, "mcs cnt %d nss %d\n",
+	ath10k_dbg(ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n",
+		   arg->addr,
 		   arg->peer_ht_rates.num_rates,
 		   arg->peer_num_spatial_streams);
 }
 
-static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
+static int ath10k_peer_assoc_qos_ap(struct ath10k *ar,
 				       struct ath10k_vif *arvif,
-				       struct ieee80211_sta *sta,
-				       struct ieee80211_bss_conf *bss_conf,
-				       struct wmi_peer_assoc_complete_arg *arg)
+				    struct ieee80211_sta *sta)
 {
 	u32 uapsd = 0;
 	u32 max_sp = 0;
+	int ret = 0;
 
-	if (sta->wme)
-		arg->peer_flags |= WMI_PEER_QOS;
+	lockdep_assert_held(&ar->conf_mutex);
 
 	if (sta->wme && sta->uapsd_queues) {
-		ath10k_dbg(ATH10K_DBG_MAC, "uapsd_queues: 0x%X, max_sp: %d\n",
+		ath10k_dbg(ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n",
 			   sta->uapsd_queues, sta->max_sp);
 
-		arg->peer_flags |= WMI_PEER_APSD;
-		arg->peer_flags |= WMI_RC_UAPSD_FLAG;
-
 		if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
 			uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN |
 				 WMI_AP_PS_UAPSD_AC3_TRIGGER_EN;
@@ -932,35 +1193,40 @@
 		if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP)
 			max_sp = sta->max_sp;
 
-		ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+		ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
 					   sta->addr,
 					   WMI_AP_PS_PEER_PARAM_UAPSD,
 					   uapsd);
+		if (ret) {
+			ath10k_warn("failed to set ap ps peer param uapsd: %d\n",
+				    ret);
+			return ret;
+		}
 
-		ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+		ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
 					   sta->addr,
 					   WMI_AP_PS_PEER_PARAM_MAX_SP,
 					   max_sp);
+		if (ret) {
+			ath10k_warn("failed to set ap ps peer param max sp: %d\n",
+				    ret);
+			return ret;
+		}
 
 		/* TODO setup this based on STA listen interval and
 		   beacon interval. Currently we don't know
 		   sta->listen_interval - mac80211 patch required.
 		   Currently use 10 seconds */
-		ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
-					   sta->addr,
-					   WMI_AP_PS_PEER_PARAM_AGEOUT_TIME,
-					   10);
+		ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, sta->addr,
+					WMI_AP_PS_PEER_PARAM_AGEOUT_TIME, 10);
+		if (ret) {
+			ath10k_warn("failed to set ap ps peer param ageout time: %d\n",
+				    ret);
+			return ret;
 	}
 }
 
-static void ath10k_peer_assoc_h_qos_sta(struct ath10k *ar,
-					struct ath10k_vif *arvif,
-					struct ieee80211_sta *sta,
-					struct ieee80211_bss_conf *bss_conf,
-					struct wmi_peer_assoc_complete_arg *arg)
-{
-	if (bss_conf->qos)
-		arg->peer_flags |= WMI_PEER_QOS;
+	return 0;
 }
 
 static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
@@ -968,14 +1234,27 @@
 				    struct wmi_peer_assoc_complete_arg *arg)
 {
 	const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+	u8 ampdu_factor;
 
 	if (!vht_cap->vht_supported)
 		return;
 
 	arg->peer_flags |= WMI_PEER_VHT;
-
 	arg->peer_vht_caps = vht_cap->cap;
 
+
+	ampdu_factor = (vht_cap->cap &
+			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >>
+		       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+
+	/* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to
+	 * zero in VHT IE. Using it would result in degraded throughput.
+	 * arg->peer_max_mpdu at this point contains HT max_mpdu so keep
+	 * it if VHT max_mpdu is smaller. */
+	arg->peer_max_mpdu = max(arg->peer_max_mpdu,
+				 (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+					ampdu_factor)) - 1);
+
 	if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
 		arg->peer_flags |= WMI_PEER_80MHZ;
 
@@ -988,7 +1267,8 @@
 	arg->peer_vht_rates.tx_mcs_set =
 		__le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
 
-	ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer\n");
+	ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n",
+		   sta->addr, arg->peer_max_mpdu, arg->peer_flags);
 }
 
 static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
@@ -999,10 +1279,17 @@
 {
 	switch (arvif->vdev_type) {
 	case WMI_VDEV_TYPE_AP:
-		ath10k_peer_assoc_h_qos_ap(ar, arvif, sta, bss_conf, arg);
+		if (sta->wme)
+			arg->peer_flags |= WMI_PEER_QOS;
+
+		if (sta->wme && sta->uapsd_queues) {
+			arg->peer_flags |= WMI_PEER_APSD;
+			arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG;
+		}
 		break;
 	case WMI_VDEV_TYPE_STA:
-		ath10k_peer_assoc_h_qos_sta(ar, arvif, sta, bss_conf, arg);
+		if (bss_conf->qos)
+			arg->peer_flags |= WMI_PEER_QOS;
 		break;
 	default:
 		break;
@@ -1016,8 +1303,6 @@
 {
 	enum wmi_phy_mode phymode = MODE_UNKNOWN;
 
-	/* FIXME: add VHT */
-
 	switch (ar->hw->conf.chandef.chan->band) {
 	case IEEE80211_BAND_2GHZ:
 		if (sta->ht_cap.ht_supported) {
@@ -1031,7 +1316,17 @@
 
 		break;
 	case IEEE80211_BAND_5GHZ:
-		if (sta->ht_cap.ht_supported) {
+		/*
+		 * Check VHT first.
+		 */
+		if (sta->vht_cap.vht_supported) {
+			if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
+				phymode = MODE_11AC_VHT80;
+			else if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+				phymode = MODE_11AC_VHT40;
+			else if (sta->bandwidth == IEEE80211_STA_RX_BW_20)
+				phymode = MODE_11AC_VHT20;
+		} else if (sta->ht_cap.ht_supported) {
 			if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
 				phymode = MODE_11NA_HT40;
 			else
@@ -1045,39 +1340,74 @@
 		break;
 	}
 
+	ath10k_dbg(ATH10K_DBG_MAC, "mac peer %pM phymode %s\n",
+		   sta->addr, ath10k_wmi_phymode_str(phymode));
+
 	arg->peer_phymode = phymode;
 	WARN_ON(phymode == MODE_UNKNOWN);
 }
 
-static int ath10k_peer_assoc(struct ath10k *ar,
+static int ath10k_peer_assoc_prepare(struct ath10k *ar,
 			     struct ath10k_vif *arvif,
 			     struct ieee80211_sta *sta,
-			     struct ieee80211_bss_conf *bss_conf)
+				     struct ieee80211_bss_conf *bss_conf,
+				     struct wmi_peer_assoc_complete_arg *arg)
 {
-	struct wmi_peer_assoc_complete_arg arg;
+	lockdep_assert_held(&ar->conf_mutex);
 
-	memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
+	memset(arg, 0, sizeof(*arg));
 
-	ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
-	ath10k_peer_assoc_h_crypto(ar, arvif, &arg);
-	ath10k_peer_assoc_h_rates(ar, sta, &arg);
-	ath10k_peer_assoc_h_ht(ar, sta, &arg);
-	ath10k_peer_assoc_h_vht(ar, sta, &arg);
-	ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, &arg);
-	ath10k_peer_assoc_h_phymode(ar, arvif, sta, &arg);
+	ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, arg);
+	ath10k_peer_assoc_h_crypto(ar, arvif, arg);
+	ath10k_peer_assoc_h_rates(ar, sta, arg);
+	ath10k_peer_assoc_h_ht(ar, sta, arg);
+	ath10k_peer_assoc_h_vht(ar, sta, arg);
+	ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, arg);
+	ath10k_peer_assoc_h_phymode(ar, arvif, sta, arg);
 
-	return ath10k_wmi_peer_assoc(ar, &arg);
+	return 0;
 }
 
-/* can be called only in mac80211 callbacks due to `key_count` usage */
-static void ath10k_bss_assoc(struct ieee80211_hw *hw,
-			     struct ieee80211_vif *vif,
-			     struct ieee80211_bss_conf *bss_conf)
-{
-	struct ath10k *ar = hw->priv;
-	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
-	struct ieee80211_sta *ap_sta;
-	int ret;
+static const u32 ath10k_smps_map[] = {
+	[WLAN_HT_CAP_SM_PS_STATIC] = WMI_PEER_SMPS_STATIC,
+	[WLAN_HT_CAP_SM_PS_DYNAMIC] = WMI_PEER_SMPS_DYNAMIC,
+	[WLAN_HT_CAP_SM_PS_INVALID] = WMI_PEER_SMPS_PS_NONE,
+	[WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE,
+};
+
+static int ath10k_setup_peer_smps(struct ath10k *ar, struct ath10k_vif *arvif,
+				  const u8 *addr,
+				  const struct ieee80211_sta_ht_cap *ht_cap)
+{
+	int smps;
+
+	if (!ht_cap->ht_supported)
+		return 0;
+
+	smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS;
+	smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+	if (smps >= ARRAY_SIZE(ath10k_smps_map))
+		return -EINVAL;
+
+	return ath10k_wmi_peer_set_param(ar, arvif->vdev_id, addr,
+					 WMI_PEER_SMPS_STATE,
+					 ath10k_smps_map[smps]);
+}
+
+/* can be called only in mac80211 callbacks due to `key_count` usage */
+static void ath10k_bss_assoc(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif,
+			     struct ieee80211_bss_conf *bss_conf)
+{
+	struct ath10k *ar = hw->priv;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	struct ieee80211_sta_ht_cap ht_cap;
+	struct wmi_peer_assoc_complete_arg peer_arg;
+	struct ieee80211_sta *ap_sta;
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
 
 	rcu_read_lock();
 
@@ -1089,24 +1419,49 @@
 		return;
 	}
 
-	ret = ath10k_peer_assoc(ar, arvif, ap_sta, bss_conf);
+	/* ap_sta must be accessed only within rcu section which must be left
+	 * before calling ath10k_setup_peer_smps() which might sleep. */
+	ht_cap = ap_sta->ht_cap;
+
+	ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta,
+					bss_conf, &peer_arg);
 	if (ret) {
-		ath10k_warn("Peer assoc failed for %pM\n", bss_conf->bssid);
+		ath10k_warn("Peer assoc prepare failed for %pM\n: %d",
+			    bss_conf->bssid, ret);
 		rcu_read_unlock();
 		return;
 	}
 
 	rcu_read_unlock();
 
-	ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid,
-				 bss_conf->bssid);
-	if (ret)
-		ath10k_warn("VDEV: %d up failed: ret %d\n",
-			    arvif->vdev_id, ret);
-	else
+	ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
+	if (ret) {
+		ath10k_warn("Peer assoc failed for %pM\n: %d",
+			    bss_conf->bssid, ret);
+		return;
+	}
+
+	ret = ath10k_setup_peer_smps(ar, arvif, bss_conf->bssid, &ht_cap);
+	if (ret) {
+		ath10k_warn("failed to setup peer SMPS: %d\n", ret);
+		return;
+	}
+
 		ath10k_dbg(ATH10K_DBG_MAC,
-			   "VDEV: %d associated, BSSID: %pM, AID: %d\n",
+		   "mac vdev %d up (associated) bssid %pM aid %d\n",
 			   arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
+
+	arvif->aid = bss_conf->aid;
+	memcpy(arvif->bssid, bss_conf->bssid, ETH_ALEN);
+
+	ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid);
+	if (ret) {
+		ath10k_warn("VDEV: %d up failed: ret %d\n",
+			    arvif->vdev_id, ret);
+		return;
+	}
+
+	arvif->is_up = true;
 }
 
 /*
@@ -1119,17 +1474,20 @@
 	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
 	int ret;
 
+	lockdep_assert_held(&ar->conf_mutex);
+
 	/*
 	 * For some reason, calling VDEV-DOWN before VDEV-STOP
 	 * makes the FW to send frames via HTT after disassociation.
 	 * No idea why this happens, even though VDEV-DOWN is supposed
 	 * to be analogous to link down, so just stop the VDEV.
 	 */
-	ret = ath10k_vdev_stop(arvif);
-	if (!ret)
-		ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d stopped\n",
+	ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d stop (disassociated\n",
 			   arvif->vdev_id);
 
+	/* FIXME: check return value */
+	ret = ath10k_vdev_stop(arvif);
+
 	/*
 	 * If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and
 	 * report beacons from previously associated network through HTT.
@@ -1137,24 +1495,42 @@
 	 * interfaces as it expects there is no rx when no interface is
 	 * running.
 	 */
+	ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d down\n", arvif->vdev_id);
+
+	/* FIXME: why don't we print error if wmi call fails? */
 	ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
-	if (ret)
-		ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d ath10k_wmi_vdev_down failed (%d)\n",
-			   arvif->vdev_id, ret);
 
-	ath10k_wmi_flush_tx(ar);
+	arvif->def_wep_key_idx = 0;
 
-	arvif->def_wep_key_index = 0;
+	arvif->is_started = false;
+	arvif->is_up = false;
 }
 
 static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
 				struct ieee80211_sta *sta)
 {
+	struct wmi_peer_assoc_complete_arg peer_arg;
 	int ret = 0;
 
-	ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
+	lockdep_assert_held(&ar->conf_mutex);
+
+	ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg);
+	if (ret) {
+		ath10k_warn("WMI peer assoc prepare failed for %pM\n",
+			    sta->addr);
+		return ret;
+	}
+
+	ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
+	if (ret) {
+		ath10k_warn("Peer assoc failed for STA %pM\n: %d",
+			    sta->addr, ret);
+		return ret;
+	}
+
+	ret = ath10k_setup_peer_smps(ar, arvif, sta->addr, &sta->ht_cap);
 	if (ret) {
-		ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
+		ath10k_warn("failed to setup peer SMPS: %d\n", ret);
 		return ret;
 	}
 
@@ -1164,6 +1540,13 @@
 		return ret;
 	}
 
+	ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta);
+	if (ret) {
+		ath10k_warn("could not set qos params for STA %pM, %d\n",
+			    sta->addr, ret);
+		return ret;
+	}
+
 	return ret;
 }
 
@@ -1172,6 +1555,8 @@
 {
 	int ret = 0;
 
+	lockdep_assert_held(&ar->conf_mutex);
+
 	ret = ath10k_clear_peer_keys(arvif, sta->addr);
 	if (ret) {
 		ath10k_warn("could not clear all peer wep keys (%d)\n", ret);
@@ -1198,6 +1583,8 @@
 	int ret;
 	int i;
 
+	lockdep_assert_held(&ar->conf_mutex);
+
 	bands = hw->wiphy->bands;
 	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
 		if (!bands[band])
@@ -1234,19 +1621,22 @@
 			ch->allow_vht = true;
 
 			ch->allow_ibss =
-				!(channel->flags & IEEE80211_CHAN_NO_IBSS);
+				!(channel->flags & IEEE80211_CHAN_NO_IR);
 
 			ch->ht40plus =
 				!(channel->flags & IEEE80211_CHAN_NO_HT40PLUS);
 
-			passive = channel->flags & IEEE80211_CHAN_PASSIVE_SCAN;
+			ch->chan_radar =
+				!!(channel->flags & IEEE80211_CHAN_RADAR);
+
+			passive = channel->flags & IEEE80211_CHAN_NO_IR;
 			ch->passive = passive;
 
 			ch->freq = channel->center_freq;
-			ch->min_power = channel->max_power * 3;
-			ch->max_power = channel->max_power * 4;
-			ch->max_reg_power = channel->max_reg_power * 4;
-			ch->max_antenna_gain = channel->max_antenna_gain;
+			ch->min_power = 0;
+			ch->max_power = channel->max_power * 2;
+			ch->max_reg_power = channel->max_reg_power * 2;
+			ch->max_antenna_gain = channel->max_antenna_gain * 2;
 			ch->reg_class_id = 0; /* FIXME */
 
 			/* FIXME: why use only legacy modes, why not any
@@ -1261,8 +1651,8 @@
 				continue;
 
 			ath10k_dbg(ATH10K_DBG_WMI,
-				   "%s: [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
-				   __func__, ch - arg.channels, arg.n_channels,
+				   "mac channel [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
+				    ch - arg.channels, arg.n_channels,
 				   ch->freq, ch->max_power, ch->max_reg_power,
 				   ch->max_antenna_gain, ch->mode);
 
@@ -1276,37 +1666,87 @@
 	return ret;
 }
 
-static void ath10k_reg_notifier(struct wiphy *wiphy,
-				struct regulatory_request *request)
+static void ath10k_regd_update(struct ath10k *ar)
 {
-	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
 	struct reg_dmn_pair_mapping *regpair;
-	struct ath10k *ar = hw->priv;
 	int ret;
 
-	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+	lockdep_assert_held(&ar->conf_mutex);
 
 	ret = ath10k_update_channel_list(ar);
 	if (ret)
 		ath10k_warn("could not update channel list (%d)\n", ret);
 
 	regpair = ar->ath_common.regulatory.regpair;
+
 	/* Target allows setting up per-band regdomain but ath_common provides
 	 * a combined one only */
 	ret = ath10k_wmi_pdev_set_regdomain(ar,
-					    regpair->regDmnEnum,
-					    regpair->regDmnEnum, /* 2ghz */
-					    regpair->regDmnEnum, /* 5ghz */
+					    regpair->reg_domain,
+					    regpair->reg_domain, /* 2ghz */
+					    regpair->reg_domain, /* 5ghz */
 					    regpair->reg_2ghz_ctl,
 					    regpair->reg_5ghz_ctl);
 	if (ret)
 		ath10k_warn("could not set pdev regdomain (%d)\n", ret);
 }
 
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+				struct regulatory_request *request)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct ath10k *ar = hw->priv;
+	bool result;
+
+	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+	if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) {
+		ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs region 0x%x\n",
+			   request->dfs_region);
+		result = ar->dfs_detector->set_dfs_domain(ar->dfs_detector,
+							  request->dfs_region);
+		if (!result)
+			ath10k_warn("dfs region 0x%X not supported, will trigger radar for every pulse\n",
+				    request->dfs_region);
+	}
+
+	mutex_lock(&ar->conf_mutex);
+	if (ar->state == ATH10K_STATE_ON)
+		ath10k_regd_update(ar);
+	mutex_unlock(&ar->conf_mutex);
+}
+
 /***************/
 /* TX handlers */
 /***************/
 
+static u8 ath10k_tx_h_get_tid(struct ieee80211_hdr *hdr)
+{
+	if (ieee80211_is_mgmt(hdr->frame_control))
+		return HTT_DATA_TX_EXT_TID_MGMT;
+
+	if (!ieee80211_is_data_qos(hdr->frame_control))
+		return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+
+	if (!is_unicast_ether_addr(ieee80211_get_DA(hdr)))
+		return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+
+	return ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK;
+}
+
+static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar,
+				  struct ieee80211_tx_info *info)
+{
+	if (info->control.vif)
+		return ath10k_vif_to_arvif(info->control.vif)->vdev_id;
+
+	if (ar->monitor_enabled)
+		return ar->monitor_vdev_id;
+
+	ath10k_warn("could not resolve vdev id\n");
+	return 0;
+}
+
 /*
  * Frames sent to the FW have to be in "Native Wifi" format.
  * Strip the QoS field from the 802.11 header.
@@ -1322,9 +1762,33 @@
 		return;
 
 	qos_ctl = ieee80211_get_qos_ctl(hdr);
-	memmove(qos_ctl, qos_ctl + IEEE80211_QOS_CTL_LEN,
-		skb->len - ieee80211_hdrlen(hdr->frame_control));
-	skb_trim(skb, skb->len - IEEE80211_QOS_CTL_LEN);
+	memmove(skb->data + IEEE80211_QOS_CTL_LEN,
+		skb->data, (void *)qos_ctl - (void *)skb->data);
+	skb_pull(skb, IEEE80211_QOS_CTL_LEN);
+}
+
+static void ath10k_tx_wep_key_work(struct work_struct *work)
+{
+	struct ath10k_vif *arvif = container_of(work, struct ath10k_vif,
+						wep_key_work);
+	int ret, keyidx = arvif->def_wep_key_newidx;
+
+	if (arvif->def_wep_key_idx == keyidx)
+		return;
+
+	ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
+		   arvif->vdev_id, keyidx);
+
+	ret = ath10k_wmi_vdev_set_param(arvif->ar,
+					arvif->vdev_id,
+					arvif->ar->wmi.vdev_param->def_keyid,
+					keyidx);
+	if (ret) {
+		ath10k_warn("could not update wep keyidx (%d)\n", ret);
+		return;
+	}
+
+	arvif->def_wep_key_idx = keyidx;
 }
 
 static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
@@ -1335,11 +1799,6 @@
 	struct ath10k *ar = arvif->ar;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct ieee80211_key_conf *key = info->control.hw_key;
-	int ret;
-
-	/* TODO AP mode should be implemented */
-	if (vif->type != NL80211_IFTYPE_STATION)
-		return;
 
 	if (!ieee80211_has_protected(hdr->frame_control))
 		return;
@@ -1351,20 +1810,14 @@
 	    key->cipher != WLAN_CIPHER_SUITE_WEP104)
 		return;
 
-	if (key->keyidx == arvif->def_wep_key_index)
-		return;
-
-	ath10k_dbg(ATH10K_DBG_MAC, "new wep keyidx will be %d\n", key->keyidx);
-
-	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-					WMI_VDEV_PARAM_DEF_KEYID,
-					key->keyidx);
-	if (ret) {
-		ath10k_warn("could not update wep keyidx (%d)\n", ret);
+	if (key->keyidx == arvif->def_wep_key_idx)
 		return;
-	}
 
-	arvif->def_wep_key_index = key->keyidx;
+	/* FIXME: Most likely a few frames will be TXed with an old key. Simply
+	 * queueing frames until key index is updated is not an option because
+	 * sk_buff may need more processing to be done, e.g. offchannel */
+	arvif->def_wep_key_newidx = key->keyidx;
+	ieee80211_queue_work(ar->hw, &arvif->wep_key_work);
 }
 
 static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
@@ -1394,19 +1847,42 @@
 static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-	int ret;
+	int ret = 0;
 
-	if (ieee80211_is_mgmt(hdr->frame_control))
-		ret = ath10k_htt_mgmt_tx(ar->htt, skb);
-	else if (ieee80211_is_nullfunc(hdr->frame_control))
+	if (ar->htt.target_version_major >= 3) {
+		/* Since HTT 3.0 there is no separate mgmt tx command */
+		ret = ath10k_htt_tx(&ar->htt, skb);
+		goto exit;
+	}
+
+	if (ieee80211_is_mgmt(hdr->frame_control)) {
+		if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
+			     ar->fw_features)) {
+			if (skb_queue_len(&ar->wmi_mgmt_tx_queue) >=
+			    ATH10K_MAX_NUM_MGMT_PENDING) {
+				ath10k_warn("wmi mgmt_tx queue limit reached\n");
+				ret = -EBUSY;
+				goto exit;
+			}
+
+			skb_queue_tail(&ar->wmi_mgmt_tx_queue, skb);
+			ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work);
+		} else {
+			ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
+		}
+	} else if (!test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
+			     ar->fw_features) &&
+		   ieee80211_is_nullfunc(hdr->frame_control)) {
 		/* FW does not report tx status properly for NullFunc frames
 		 * unless they are sent through mgmt tx path. mac80211 sends
-		 * those frames when it detects link/beacon loss and depends on
-		 * the tx status to be correct. */
-		ret = ath10k_htt_mgmt_tx(ar->htt, skb);
-	else
-		ret = ath10k_htt_tx(ar->htt, skb);
+		 * those frames when it detects link/beacon loss and depends
+		 * on the tx status to be correct. */
+		ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
+	} else {
+		ret = ath10k_htt_tx(&ar->htt, skb);
+	}
 
+exit:
 	if (ret) {
 		ath10k_warn("tx failed (%d). dropping packet.\n", ret);
 		ieee80211_free_txskb(ar->hw, skb);
@@ -1450,18 +1926,19 @@
 
 		mutex_lock(&ar->conf_mutex);
 
-		ath10k_dbg(ATH10K_DBG_MAC, "processing offchannel skb %p\n",
+		ath10k_dbg(ATH10K_DBG_MAC, "mac offchannel skb %p\n",
 			   skb);
 
 		hdr = (struct ieee80211_hdr *)skb->data;
 		peer_addr = ieee80211_get_DA(hdr);
-		vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id;
+		vdev_id = ATH10K_SKB_CB(skb)->vdev_id;
 
 		spin_lock_bh(&ar->data_lock);
 		peer = ath10k_peer_find(ar, vdev_id, peer_addr);
 		spin_unlock_bh(&ar->data_lock);
 
 		if (peer)
+			/* FIXME: should this use ath10k_warn()? */
 			ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n",
 				   peer_addr, vdev_id);
 
@@ -1496,6 +1973,38 @@
 	}
 }
 
+void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar)
+{
+	struct sk_buff *skb;
+
+	for (;;) {
+		skb = skb_dequeue(&ar->wmi_mgmt_tx_queue);
+		if (!skb)
+			break;
+
+		ieee80211_free_txskb(ar->hw, skb);
+	}
+}
+
+void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
+{
+	struct ath10k *ar = container_of(work, struct ath10k, wmi_mgmt_tx_work);
+	struct sk_buff *skb;
+	int ret;
+
+	for (;;) {
+		skb = skb_dequeue(&ar->wmi_mgmt_tx_queue);
+		if (!skb)
+			break;
+
+		ret = ath10k_wmi_mgmt_tx(ar, skb);
+		if (ret) {
+			ath10k_warn("wmi mgmt_tx failed (%d)\n", ret);
+			ieee80211_free_txskb(ar->hw, skb);
+		}
+	}
+}
+
 /************/
 /* Scanning */
 /************/
@@ -1552,11 +2061,13 @@
 	ret = ath10k_wmi_stop_scan(ar, &arg);
 	if (ret) {
 		ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
+		spin_lock_bh(&ar->data_lock);
+		ar->scan.in_progress = false;
+		ath10k_offchan_tx_purge(ar);
+		spin_unlock_bh(&ar->data_lock);
 		return -EIO;
 	}
 
-	ath10k_wmi_flush_tx(ar);
-
 	ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ);
 	if (ret == 0)
 		ath10k_warn("timed out while waiting for scan to stop\n");
@@ -1590,10 +2101,6 @@
 	if (ret)
 		return ret;
 
-	/* make sure we submit the command so the completion
-	* timeout makes sense */
-	ath10k_wmi_flush_tx(ar);
-
 	ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ);
 	if (ret == 0) {
 		ath10k_abort_scan(ar);
@@ -1621,16 +2128,7 @@
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct ath10k *ar = hw->priv;
-	struct ath10k_vif *arvif = NULL;
-	u32 vdev_id = 0;
-	u8 tid;
-
-	if (info->control.vif) {
-		arvif = ath10k_vif_to_arvif(info->control.vif);
-		vdev_id = arvif->vdev_id;
-	} else if (ar->monitor_enabled) {
-		vdev_id = ar->monitor_vdev_id;
-	}
+	u8 tid, vdev_id;
 
 	/* We should disable CCK RATE due to P2P */
 	if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
@@ -1638,26 +2136,26 @@
 
 	/* we must calculate tid before we apply qos workaround
 	 * as we'd lose the qos control field */
-	tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
-	if (ieee80211_is_data_qos(hdr->frame_control) &&
-	    is_unicast_ether_addr(ieee80211_get_DA(hdr))) {
-		u8 *qc = ieee80211_get_qos_ctl(hdr);
-		tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
-	}
+	tid = ath10k_tx_h_get_tid(hdr);
+	vdev_id = ath10k_tx_h_get_vdev_id(ar, info);
 
+	/* it makes no sense to process injected frames like that */
+	if (info->control.vif &&
+	    info->control.vif->type != NL80211_IFTYPE_MONITOR) {
 	ath10k_tx_h_qos_workaround(hw, control, skb);
 	ath10k_tx_h_update_wep_key(skb);
 	ath10k_tx_h_add_p2p_noa_ie(ar, skb);
 	ath10k_tx_h_seq_no(skb);
+	}
 
-	memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb)));
-	ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
+	ATH10K_SKB_CB(skb)->vdev_id = vdev_id;
+	ATH10K_SKB_CB(skb)->htt.is_offchan = false;
 	ATH10K_SKB_CB(skb)->htt.tid = tid;
 
 	if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
 		spin_lock_bh(&ar->data_lock);
 		ATH10K_SKB_CB(skb)->htt.is_offchan = true;
-		ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id;
+		ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id;
 		spin_unlock_bh(&ar->data_lock);
 
 		ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb);
@@ -1673,67 +2171,277 @@
 /*
  * Initialize various parameters with default vaules.
  */
+void ath10k_halt(struct ath10k *ar)
+{
+	lockdep_assert_held(&ar->conf_mutex);
+
+	ath10k_stop_cac(ar);
+	del_timer_sync(&ar->scan.timeout);
+	ath10k_offchan_tx_purge(ar);
+	ath10k_mgmt_over_wmi_tx_purge(ar);
+	ath10k_peer_cleanup_all(ar);
+	ath10k_core_stop(ar);
+	ath10k_hif_power_down(ar);
+
+	spin_lock_bh(&ar->data_lock);
+	if (ar->scan.in_progress) {
+		del_timer(&ar->scan.timeout);
+		ar->scan.in_progress = false;
+		ieee80211_scan_completed(ar->hw, true);
+	}
+	spin_unlock_bh(&ar->data_lock);
+}
+
 static int ath10k_start(struct ieee80211_hw *hw)
 {
 	struct ath10k *ar = hw->priv;
-	int ret;
+	int ret = 0;
+
+	mutex_lock(&ar->conf_mutex);
+
+	if (ar->state != ATH10K_STATE_OFF &&
+	    ar->state != ATH10K_STATE_RESTARTING) {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	ret = ath10k_hif_power_up(ar);
+	if (ret) {
+		ath10k_err("could not init hif (%d)\n", ret);
+		ar->state = ATH10K_STATE_OFF;
+		goto exit;
+	}
+
+	ret = ath10k_core_start(ar);
+	if (ret) {
+		ath10k_err("could not init core (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		ar->state = ATH10K_STATE_OFF;
+		goto exit;
+	}
+
+	if (ar->state == ATH10K_STATE_OFF)
+		ar->state = ATH10K_STATE_ON;
+	else if (ar->state == ATH10K_STATE_RESTARTING)
+		ar->state = ATH10K_STATE_RESTARTED;
 
-	ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
+	ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1);
 	if (ret)
 		ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
 			    ret);
 
-	ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 0);
+	ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 1);
 	if (ret)
 		ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
 			    ret);
 
-	return 0;
+	/*
+	 * By default FW set ARP frames ac to voice (6). In that case ARP
+	 * exchange is not working properly for UAPSD enabled AP. ARP requests
+	 * which arrives with access category 0 are processed by network stack
+	 * and send back with access category 0, but FW changes access category
+	 * to 6. Set ARP frames access category to best effort (0) solves
+	 * this problem.
+	 */
+
+	ret = ath10k_wmi_pdev_set_param(ar,
+					ar->wmi.pdev_param->arp_ac_override, 0);
+	if (ret) {
+		ath10k_warn("could not set arp ac override parameter: %d\n",
+			    ret);
+		goto exit;
+	}
+
+	ath10k_regd_update(ar);
+	ret = 0;
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
 }
 
 static void ath10k_stop(struct ieee80211_hw *hw)
 {
 	struct ath10k *ar = hw->priv;
 
-	/* avoid leaks in case FW never confirms scan for offchannel */
+	mutex_lock(&ar->conf_mutex);
+	if (ar->state == ATH10K_STATE_ON ||
+	    ar->state == ATH10K_STATE_RESTARTED ||
+	    ar->state == ATH10K_STATE_WEDGED)
+		ath10k_halt(ar);
+
+	ar->state = ATH10K_STATE_OFF;
+	mutex_unlock(&ar->conf_mutex);
+
+	ath10k_mgmt_over_wmi_tx_purge(ar);
+
 	cancel_work_sync(&ar->offchan_tx_work);
-	ath10k_offchan_tx_purge(ar);
+	cancel_work_sync(&ar->wmi_mgmt_tx_work);
+	cancel_work_sync(&ar->restart_work);
 }
 
-static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+static int ath10k_config_ps(struct ath10k *ar)
 {
-	struct ath10k_generic_iter ar_iter;
-	struct ath10k *ar = hw->priv;
-	struct ieee80211_conf *conf = &hw->conf;
+	struct ath10k_vif *arvif;
 	int ret = 0;
-	u32 flags;
 
-	mutex_lock(&ar->conf_mutex);
+	lockdep_assert_held(&ar->conf_mutex);
 
-	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-		ath10k_dbg(ATH10K_DBG_MAC, "Config channel %d mhz\n",
-			   conf->chandef.chan->center_freq);
-		spin_lock_bh(&ar->data_lock);
-		ar->rx_channel = conf->chandef.chan;
-		spin_unlock_bh(&ar->data_lock);
+	list_for_each_entry(arvif, &ar->arvifs, list) {
+		ret = ath10k_mac_vif_setup_ps(arvif);
+		if (ret) {
+			ath10k_warn("could not setup powersave (%d)\n", ret);
+			break;
+		}
 	}
 
-	if (changed & IEEE80211_CONF_CHANGE_PS) {
-		memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-		ar_iter.ar = ar;
-		flags = IEEE80211_IFACE_ITER_RESUME_ALL;
-
-		ieee80211_iterate_active_interfaces_atomic(hw,
-							   flags,
-							   ath10k_ps_iter,
-							   &ar_iter);
-
-		ret = ar_iter.ret;
+	return ret;
 	}
 
-	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
-		if (conf->flags & IEEE80211_CONF_MONITOR)
-			ret = ath10k_monitor_create(ar);
+static const char *chandef_get_width(enum nl80211_chan_width width)
+{
+	switch (width) {
+	case NL80211_CHAN_WIDTH_20_NOHT:
+		return "20 (noht)";
+	case NL80211_CHAN_WIDTH_20:
+		return "20";
+	case NL80211_CHAN_WIDTH_40:
+		return "40";
+	case NL80211_CHAN_WIDTH_80:
+		return "80";
+	case NL80211_CHAN_WIDTH_80P80:
+		return "80+80";
+	case NL80211_CHAN_WIDTH_160:
+		return "160";
+	case NL80211_CHAN_WIDTH_5:
+		return "5";
+	case NL80211_CHAN_WIDTH_10:
+		return "10";
+	}
+	return "?";
+}
+
+static void ath10k_config_chan(struct ath10k *ar)
+{
+	struct ath10k_vif *arvif;
+	bool monitor_was_enabled;
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	ath10k_dbg(ATH10K_DBG_MAC,
+		   "mac config channel to %dMHz (cf1 %dMHz cf2 %dMHz width %s)\n",
+		   ar->chandef.chan->center_freq,
+		   ar->chandef.center_freq1,
+		   ar->chandef.center_freq2,
+		   chandef_get_width(ar->chandef.width));
+
+	/* First stop monitor interface. Some FW versions crash if there's a
+	 * lone monitor interface. */
+	monitor_was_enabled = ar->monitor_enabled;
+
+	if (ar->monitor_enabled)
+		ath10k_monitor_stop(ar);
+
+	list_for_each_entry(arvif, &ar->arvifs, list) {
+		if (!arvif->is_started)
+			continue;
+
+		if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+			continue;
+
+		ret = ath10k_vdev_stop(arvif);
+		if (ret) {
+			ath10k_warn("could not stop vdev %d (%d)\n",
+				    arvif->vdev_id, ret);
+			continue;
+		}
+	}
+
+	/* all vdevs are now stopped - now attempt to restart them */
+
+	list_for_each_entry(arvif, &ar->arvifs, list) {
+		if (!arvif->is_started)
+			continue;
+
+		if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+			continue;
+
+		ret = ath10k_vdev_start(arvif);
+		if (ret) {
+			ath10k_warn("could not start vdev %d (%d)\n",
+				    arvif->vdev_id, ret);
+			continue;
+		}
+
+		if (!arvif->is_up)
+			continue;
+
+		ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
+					 arvif->bssid);
+		if (ret) {
+			ath10k_warn("could not bring vdev up %d (%d)\n",
+				    arvif->vdev_id, ret);
+			continue;
+		}
+	}
+
+	if (monitor_was_enabled)
+		ath10k_monitor_start(ar, ar->monitor_vdev_id);
+}
+
+static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+{
+	struct ath10k *ar = hw->priv;
+	struct ieee80211_conf *conf = &hw->conf;
+	int ret = 0;
+	u32 param;
+
+	mutex_lock(&ar->conf_mutex);
+
+	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+		ath10k_dbg(ATH10K_DBG_MAC,
+			   "mac config channel %d mhz flags 0x%x\n",
+			   conf->chandef.chan->center_freq,
+			   conf->chandef.chan->flags);
+
+		spin_lock_bh(&ar->data_lock);
+		ar->rx_channel = conf->chandef.chan;
+		spin_unlock_bh(&ar->data_lock);
+
+		ath10k_config_radar_detection(ar);
+
+		if (!cfg80211_chandef_identical(&ar->chandef, &conf->chandef)) {
+			ar->chandef = conf->chandef;
+			ath10k_config_chan(ar);
+		}
+	}
+
+	if (changed & IEEE80211_CONF_CHANGE_POWER) {
+		ath10k_dbg(ATH10K_DBG_MAC, "mac config power %d\n",
+			   hw->conf.power_level);
+
+		param = ar->wmi.pdev_param->txpower_limit2g;
+		ret = ath10k_wmi_pdev_set_param(ar, param,
+						hw->conf.power_level * 2);
+		if (ret)
+			ath10k_warn("mac failed to set 2g txpower %d (%d)\n",
+				    hw->conf.power_level, ret);
+
+		param = ar->wmi.pdev_param->txpower_limit5g;
+		ret = ath10k_wmi_pdev_set_param(ar, param,
+						hw->conf.power_level * 2);
+		if (ret)
+			ath10k_warn("mac failed to set 5g txpower %d (%d)\n",
+				    hw->conf.power_level, ret);
+	}
+
+	if (changed & IEEE80211_CONF_CHANGE_PS)
+		ath10k_config_ps(ar);
+
+	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+		if (conf->flags & IEEE80211_CONF_MONITOR)
+			ret = ath10k_monitor_create(ar);
 		else
 			ret = ath10k_monitor_destroy(ar);
 	}
@@ -1758,27 +2466,32 @@
 	int ret = 0;
 	u32 value;
 	int bit;
+	u32 vdev_param;
 
 	mutex_lock(&ar->conf_mutex);
 
+	memset(arvif, 0, sizeof(*arvif));
+
 	arvif->ar = ar;
 	arvif->vif = vif;
 
+	INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work);
+	INIT_LIST_HEAD(&arvif->list);
+
 	if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) {
 		ath10k_warn("Only one monitor interface allowed\n");
 		ret = -EBUSY;
-		goto exit;
+		goto err;
 	}
 
 	bit = ffs(ar->free_vdev_map);
 	if (bit == 0) {
 		ret = -EBUSY;
-		goto exit;
+		goto err;
 	}
 
 	arvif->vdev_id = bit - 1;
 	arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
-	ar->free_vdev_map &= ~(1 << arvif->vdev_id);
 
 	if (ar->p2p)
 		arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
@@ -1807,32 +2520,48 @@
 		break;
 	}
 
-	ath10k_dbg(ATH10K_DBG_MAC, "Add interface: id %d type %d subtype %d\n",
+	ath10k_dbg(ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d\n",
 		   arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype);
 
 	ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
 				     arvif->vdev_subtype, vif->addr);
 	if (ret) {
 		ath10k_warn("WMI vdev create failed: ret %d\n", ret);
-		goto exit;
+		goto err;
 	}
 
-	ret = ath10k_wmi_vdev_set_param(ar, 0, WMI_VDEV_PARAM_DEF_KEYID,
-					arvif->def_wep_key_index);
-	if (ret)
+	ar->free_vdev_map &= ~BIT(arvif->vdev_id);
+	list_add(&arvif->list, &ar->arvifs);
+
+	vdev_param = ar->wmi.vdev_param->def_keyid;
+	ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param,
+					arvif->def_wep_key_idx);
+	if (ret) {
 		ath10k_warn("Failed to set default keyid: %d\n", ret);
+		goto err_vdev_delete;
+	}
 
-	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-					WMI_VDEV_PARAM_TX_ENCAP_TYPE,
+	vdev_param = ar->wmi.vdev_param->tx_encap_type;
+	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
 					ATH10K_HW_TXRX_NATIVE_WIFI);
-	if (ret)
+	/* 10.X firmware does not support this VDEV parameter. Do not warn */
+	if (ret && ret != -EOPNOTSUPP) {
 		ath10k_warn("Failed to set TX encap: %d\n", ret);
+		goto err_vdev_delete;
+	}
 
 	if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
 		ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
 		if (ret) {
 			ath10k_warn("Failed to create peer for AP: %d\n", ret);
-			goto exit;
+			goto err_vdev_delete;
+		}
+
+		ret = ath10k_mac_set_kickout(arvif);
+		if (ret) {
+			ath10k_warn("Failed to set kickout parameters: %d\n",
+				    ret);
+			goto err_peer_delete;
 		}
 	}
 
@@ -1841,29 +2570,62 @@
 		value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
 		ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
 						  param, value);
-		if (ret)
+		if (ret) {
 			ath10k_warn("Failed to set RX wake policy: %d\n", ret);
+			goto err_peer_delete;
+		}
 
 		param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
 		value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
 		ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
 						  param, value);
-		if (ret)
+		if (ret) {
 			ath10k_warn("Failed to set TX wake thresh: %d\n", ret);
+			goto err_peer_delete;
+		}
 
 		param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
 		value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
 		ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
 						  param, value);
-		if (ret)
+		if (ret) {
 			ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
+			goto err_peer_delete;
+		}
+	}
+
+	ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold);
+	if (ret) {
+		ath10k_warn("failed to set rts threshold for vdev %d (%d)\n",
+			    arvif->vdev_id, ret);
+		goto err_peer_delete;
+	}
+
+	ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold);
+	if (ret) {
+		ath10k_warn("failed to set frag threshold for vdev %d (%d)\n",
+			    arvif->vdev_id, ret);
+		goto err_peer_delete;
 	}
 
 	if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
 		ar->monitor_present = true;
 
-exit:
 	mutex_unlock(&ar->conf_mutex);
+	return 0;
+
+err_peer_delete:
+	if (arvif->vdev_type == WMI_VDEV_TYPE_AP)
+		ath10k_wmi_peer_delete(ar, arvif->vdev_id, vif->addr);
+
+err_vdev_delete:
+	ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
+	ar->free_vdev_map &= ~BIT(arvif->vdev_id);
+	list_del(&arvif->list);
+
+err:
+	mutex_unlock(&ar->conf_mutex);
+
 	return ret;
 }
 
@@ -1876,9 +2638,17 @@
 
 	mutex_lock(&ar->conf_mutex);
 
-	ath10k_dbg(ATH10K_DBG_MAC, "Remove interface: id %d\n", arvif->vdev_id);
+	cancel_work_sync(&arvif->wep_key_work);
+
+	spin_lock_bh(&ar->data_lock);
+	if (arvif->beacon) {
+		dev_kfree_skb_any(arvif->beacon);
+		arvif->beacon = NULL;
+	}
+	spin_unlock_bh(&ar->data_lock);
 
 	ar->free_vdev_map |= 1 << (arvif->vdev_id);
+	list_del(&arvif->list);
 
 	if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
 		ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr);
@@ -1888,6 +2658,9 @@
 		kfree(arvif->u.ap.noa_data);
 	}
 
+	ath10k_dbg(ATH10K_DBG_MAC, "mac vdev delete %d (remove interface)\n",
+		   arvif->vdev_id);
+
 	ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
 	if (ret)
 		ath10k_warn("WMI vdev delete failed: %d\n", ret);
@@ -1927,20 +2700,28 @@
 	*total_flags &= SUPPORTED_FILTERS;
 	ar->filter_flags = *total_flags;
 
+	/* Monitor must not be started if it wasn't created first.
+	 * Promiscuous mode may be started on a non-monitor interface - in
+	 * such case the monitor vdev is not created so starting the
+	 * monitor makes no sense. Since ath10k uses no special RX filters
+	 * (only BSS filter in STA mode) there's no need for any special
+	 * action here. */
 	if ((ar->filter_flags & FIF_PROMISC_IN_BSS) &&
-	    !ar->monitor_enabled) {
+	    !ar->monitor_enabled && ar->monitor_present) {
+		ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d start\n",
+			   ar->monitor_vdev_id);
+
 		ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
 		if (ret)
 			ath10k_warn("Unable to start monitor mode\n");
-		else
-			ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode started\n");
 	} else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) &&
-		   ar->monitor_enabled) {
+		   ar->monitor_enabled && ar->monitor_present) {
+		ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d stop\n",
+			   ar->monitor_vdev_id);
+
 		ret = ath10k_monitor_stop(ar);
 		if (ret)
 			ath10k_warn("Unable to stop monitor mode\n");
-		else
-			ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode stopped\n");
 	}
 
 	mutex_unlock(&ar->conf_mutex);
@@ -1954,6 +2735,7 @@
 	struct ath10k *ar = hw->priv;
 	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
 	int ret = 0;
+	u32 vdev_param, pdev_param;
 
 	mutex_lock(&ar->conf_mutex);
 
@@ -1962,44 +2744,44 @@
 
 	if (changed & BSS_CHANGED_BEACON_INT) {
 		arvif->beacon_interval = info->beacon_int;
-		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-						WMI_VDEV_PARAM_BEACON_INTERVAL,
+		vdev_param = ar->wmi.vdev_param->beacon_interval;
+		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
 						arvif->beacon_interval);
+		ath10k_dbg(ATH10K_DBG_MAC,
+			   "mac vdev %d beacon_interval %d\n",
+			   arvif->vdev_id, arvif->beacon_interval);
+
 		if (ret)
 			ath10k_warn("Failed to set beacon interval for VDEV: %d\n",
 				    arvif->vdev_id);
-		else
-			ath10k_dbg(ATH10K_DBG_MAC,
-				   "Beacon interval: %d set for VDEV: %d\n",
-				   arvif->beacon_interval, arvif->vdev_id);
 	}
 
 	if (changed & BSS_CHANGED_BEACON) {
-		ret = ath10k_wmi_pdev_set_param(ar,
-						WMI_PDEV_PARAM_BEACON_TX_MODE,
+		ath10k_dbg(ATH10K_DBG_MAC,
+			   "vdev %d set beacon tx mode to staggered\n",
+			   arvif->vdev_id);
+
+		pdev_param = ar->wmi.pdev_param->beacon_tx_mode;
+		ret = ath10k_wmi_pdev_set_param(ar, pdev_param,
 						WMI_BEACON_STAGGERED_MODE);
 		if (ret)
 			ath10k_warn("Failed to set beacon mode for VDEV: %d\n",
 				    arvif->vdev_id);
-		else
-			ath10k_dbg(ATH10K_DBG_MAC,
-				   "Set staggered beacon mode for VDEV: %d\n",
-				   arvif->vdev_id);
 	}
 
 	if (changed & BSS_CHANGED_BEACON_INFO) {
 		arvif->dtim_period = info->dtim_period;
 
-		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-						WMI_VDEV_PARAM_DTIM_PERIOD,
+		ath10k_dbg(ATH10K_DBG_MAC,
+			   "mac vdev %d dtim_period %d\n",
+			   arvif->vdev_id, arvif->dtim_period);
+
+		vdev_param = ar->wmi.vdev_param->dtim_period;
+		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
 						arvif->dtim_period);
 		if (ret)
 			ath10k_warn("Failed to set dtim period for VDEV: %d\n",
 				    arvif->vdev_id);
-		else
-			ath10k_dbg(ATH10K_DBG_MAC,
-				   "Set dtim period: %d for VDEV: %d\n",
-				   arvif->dtim_period, arvif->vdev_id);
 	}
 
 	if (changed & BSS_CHANGED_SSID &&
@@ -2012,30 +2794,35 @@
 
 	if (changed & BSS_CHANGED_BSSID) {
 		if (!is_zero_ether_addr(info->bssid)) {
+			ath10k_dbg(ATH10K_DBG_MAC,
+				   "mac vdev %d create peer %pM\n",
+				   arvif->vdev_id, info->bssid);
+
 			ret = ath10k_peer_create(ar, arvif->vdev_id,
 						 info->bssid);
 			if (ret)
-				ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
-					    info->bssid, arvif->vdev_id);
-			else
-				ath10k_dbg(ATH10K_DBG_MAC,
-					   "Added peer: %pM for VDEV: %d\n",
-					   info->bssid, arvif->vdev_id);
-
+				ath10k_warn("Failed to add peer %pM for vdev %d when changin bssid: %i\n",
+					    info->bssid, arvif->vdev_id, ret);
 
 			if (vif->type == NL80211_IFTYPE_STATION) {
 				/*
 				 * this is never erased as we it for crypto key
 				 * clearing; this is FW requirement
 				 */
-				memcpy(arvif->u.sta.bssid, info->bssid,
-				       ETH_ALEN);
+				memcpy(arvif->bssid, info->bssid, ETH_ALEN);
 
-				ret = ath10k_vdev_start(arvif);
-				if (!ret)
 					ath10k_dbg(ATH10K_DBG_MAC,
-						   "VDEV: %d started with BSSID: %pM\n",
+					   "mac vdev %d start %pM\n",
 						   arvif->vdev_id, info->bssid);
+
+				ret = ath10k_vdev_start(arvif);
+				if (ret) {
+					ath10k_warn("failed to start vdev: %d\n",
+						    ret);
+					goto exit;
+				}
+
+				arvif->is_started = true;
 			}
 
 			/*
@@ -2044,7 +2831,7 @@
 			 * IBSS in order to remove BSSID peer.
 			 */
 			if (vif->type == NL80211_IFTYPE_ADHOC)
-				memcpy(arvif->u.ibss.bssid, info->bssid,
+				memcpy(arvif->bssid, info->bssid,
 				       ETH_ALEN);
 		}
 	}
@@ -2059,16 +2846,15 @@
 		else
 			cts_prot = 0;
 
-		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-						WMI_VDEV_PARAM_ENABLE_RTSCTS,
+		ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n",
+			   arvif->vdev_id, cts_prot);
+
+		vdev_param = ar->wmi.vdev_param->enable_rtscts;
+		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
 						cts_prot);
 		if (ret)
 			ath10k_warn("Failed to set CTS prot for VDEV: %d\n",
 				    arvif->vdev_id);
-		else
-			ath10k_dbg(ATH10K_DBG_MAC,
-				   "Set CTS prot: %d for VDEV: %d\n",
-				   cts_prot, arvif->vdev_id);
 	}
 
 	if (changed & BSS_CHANGED_ERP_SLOT) {
@@ -2079,16 +2865,15 @@
 		else
 			slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */
 
-		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-						WMI_VDEV_PARAM_SLOT_TIME,
+		ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d slot_time %d\n",
+			   arvif->vdev_id, slottime);
+
+		vdev_param = ar->wmi.vdev_param->slot_time;
+		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
 						slottime);
 		if (ret)
 			ath10k_warn("Failed to set erp slot for VDEV: %d\n",
 				    arvif->vdev_id);
-		else
-			ath10k_dbg(ATH10K_DBG_MAC,
-				   "Set slottime: %d for VDEV: %d\n",
-				   slottime, arvif->vdev_id);
 	}
 
 	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
@@ -2098,16 +2883,16 @@
 		else
 			preamble = WMI_VDEV_PREAMBLE_LONG;
 
-		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-						WMI_VDEV_PARAM_PREAMBLE,
+		ath10k_dbg(ATH10K_DBG_MAC,
+			   "mac vdev %d preamble %dn",
+			   arvif->vdev_id, preamble);
+
+		vdev_param = ar->wmi.vdev_param->preamble;
+		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
 						preamble);
 		if (ret)
 			ath10k_warn("Failed to set preamble for VDEV: %d\n",
 				    arvif->vdev_id);
-		else
-			ath10k_dbg(ATH10K_DBG_MAC,
-				   "Set preamble: %d for VDEV: %d\n",
-				   preamble, arvif->vdev_id);
 	}
 
 	if (changed & BSS_CHANGED_ASSOC) {
@@ -2115,6 +2900,7 @@
 			ath10k_bss_assoc(hw, vif, info);
 	}
 
+exit:
 	mutex_unlock(&ar->conf_mutex);
 }
 
@@ -2164,6 +2950,8 @@
 			arg.ssids[i].len  = req->ssids[i].ssid_len;
 			arg.ssids[i].ssid = req->ssids[i].ssid;
 		}
+	} else {
+		arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE;
 	}
 
 	if (req->n_channels) {
@@ -2201,6 +2989,44 @@
 	mutex_unlock(&ar->conf_mutex);
 }
 
+static void ath10k_set_key_h_def_keyidx(struct ath10k *ar,
+					struct ath10k_vif *arvif,
+					enum set_key_cmd cmd,
+					struct ieee80211_key_conf *key)
+{
+	u32 vdev_param = arvif->ar->wmi.vdev_param->def_keyid;
+	int ret;
+
+	/* 10.1 firmware branch requires default key index to be set to group
+	 * key index after installing it. Otherwise FW/HW Txes corrupted
+	 * frames with multi-vif APs. This is not required for main firmware
+	 * branch (e.g. 636).
+	 *
+	 * FIXME: This has been tested only in AP. It remains unknown if this
+	 * is required for multi-vif STA interfaces on 10.1 */
+
+	if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
+		return;
+
+	if (key->cipher == WLAN_CIPHER_SUITE_WEP40)
+		return;
+
+	if (key->cipher == WLAN_CIPHER_SUITE_WEP104)
+		return;
+
+	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+		return;
+
+	if (cmd != SET_KEY)
+		return;
+
+	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+					key->keyidx);
+	if (ret)
+		ath10k_warn("failed to set group key as default key: %d\n",
+			    ret);
+}
+
 static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 			  struct ieee80211_vif *vif, struct ieee80211_sta *sta,
 			  struct ieee80211_key_conf *key)
@@ -2262,6 +3088,8 @@
 		goto exit;
 	}
 
+	ath10k_set_key_h_def_keyidx(ar, arvif, cmd, key);
+
 	spin_lock_bh(&ar->data_lock);
 	peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr);
 	if (peer && cmd == SET_KEY)
@@ -2278,6 +3106,69 @@
 	return ret;
 }
 
+static void ath10k_sta_rc_update_wk(struct work_struct *wk)
+{
+	struct ath10k *ar;
+	struct ath10k_vif *arvif;
+	struct ath10k_sta *arsta;
+	struct ieee80211_sta *sta;
+	u32 changed, bw, nss, smps;
+	int err;
+
+	arsta = container_of(wk, struct ath10k_sta, update_wk);
+	sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv);
+	arvif = arsta->arvif;
+	ar = arvif->ar;
+
+	spin_lock_bh(&ar->data_lock);
+
+	changed = arsta->changed;
+	arsta->changed = 0;
+
+	bw = arsta->bw;
+	nss = arsta->nss;
+	smps = arsta->smps;
+
+	spin_unlock_bh(&ar->data_lock);
+
+	mutex_lock(&ar->conf_mutex);
+
+	if (changed & IEEE80211_RC_BW_CHANGED) {
+		ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM peer bw %d\n",
+			   sta->addr, bw);
+
+		err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+						WMI_PEER_CHAN_WIDTH, bw);
+		if (err)
+			ath10k_warn("failed to update STA %pM peer bw %d: %d\n",
+				    sta->addr, bw, err);
+	}
+
+	if (changed & IEEE80211_RC_NSS_CHANGED) {
+		ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM nss %d\n",
+			   sta->addr, nss);
+
+		err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+						WMI_PEER_NSS, nss);
+		if (err)
+			ath10k_warn("failed to update STA %pM nss %d: %d\n",
+				    sta->addr, nss, err);
+	}
+
+	if (changed & IEEE80211_RC_SMPS_CHANGED) {
+		ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM smps %d\n",
+			   sta->addr, smps);
+
+		err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+						WMI_PEER_SMPS_STATE, smps);
+		if (err)
+			ath10k_warn("failed to update STA %pM smps %d: %d\n",
+				    sta->addr, smps, err);
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
 static int ath10k_sta_state(struct ieee80211_hw *hw,
 			    struct ieee80211_vif *vif,
 			    struct ieee80211_sta *sta,
@@ -2286,8 +3177,22 @@
 {
 	struct ath10k *ar = hw->priv;
 	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+	int max_num_peers;
 	int ret = 0;
 
+	if (old_state == IEEE80211_STA_NOTEXIST &&
+	    new_state == IEEE80211_STA_NONE) {
+		memset(arsta, 0, sizeof(*arsta));
+		arsta->arvif = arvif;
+		INIT_WORK(&arsta->update_wk, ath10k_sta_rc_update_wk);
+	}
+
+	/* cancel must be done outside the mutex to avoid deadlock */
+	if ((old_state == IEEE80211_STA_NONE &&
+	     new_state == IEEE80211_STA_NOTEXIST))
+		cancel_work_sync(&arsta->update_wk);
+
 	mutex_lock(&ar->conf_mutex);
 
 	if (old_state == IEEE80211_STA_NOTEXIST &&
@@ -2296,27 +3201,38 @@
 		/*
 		 * New station addition.
 		 */
-		ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
-		if (ret)
-			ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
-				    sta->addr, arvif->vdev_id);
+		if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+			max_num_peers = TARGET_10X_NUM_PEERS_MAX - 1;
 		else
+			max_num_peers = TARGET_NUM_PEERS;
+
+		if (ar->num_peers >= max_num_peers) {
+			ath10k_warn("Number of peers exceeded: peers number %d (max peers %d)\n",
+				    ar->num_peers, max_num_peers);
+			ret = -ENOBUFS;
+			goto exit;
+		}
+
 			ath10k_dbg(ATH10K_DBG_MAC,
-				   "Added peer: %pM for VDEV: %d\n",
-				   sta->addr, arvif->vdev_id);
+			   "mac vdev %d peer create %pM (new sta) num_peers %d\n",
+			   arvif->vdev_id, sta->addr, ar->num_peers);
+
+		ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
+		if (ret)
+			ath10k_warn("Failed to add peer %pM for vdev %d when adding a new sta: %i\n",
+				    sta->addr, arvif->vdev_id, ret);
 	} else if ((old_state == IEEE80211_STA_NONE &&
 		    new_state == IEEE80211_STA_NOTEXIST)) {
 		/*
 		 * Existing station deletion.
 		 */
+		ath10k_dbg(ATH10K_DBG_MAC,
+			   "mac vdev %d peer delete %pM (sta gone)\n",
+			   arvif->vdev_id, sta->addr);
 		ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
 		if (ret)
 			ath10k_warn("Failed to delete peer: %pM for VDEV: %d\n",
 				    sta->addr, arvif->vdev_id);
-		else
-			ath10k_dbg(ATH10K_DBG_MAC,
-				   "Removed peer: %pM for VDEV: %d\n",
-				   sta->addr, arvif->vdev_id);
 
 		if (vif->type == NL80211_IFTYPE_STATION)
 			ath10k_bss_disassoc(hw, vif);
@@ -2327,14 +3243,13 @@
 		/*
 		 * New association.
 		 */
+		ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM associated\n",
+			   sta->addr);
+
 		ret = ath10k_station_assoc(ar, arvif, sta);
 		if (ret)
 			ath10k_warn("Failed to associate station: %pM\n",
 				    sta->addr);
-		else
-			ath10k_dbg(ATH10K_DBG_MAC,
-				   "Station %pM moved to assoc state\n",
-				   sta->addr);
 	} else if (old_state == IEEE80211_STA_ASSOC &&
 		   new_state == IEEE80211_STA_AUTH &&
 		   (vif->type == NL80211_IFTYPE_AP ||
@@ -2342,16 +3257,15 @@
 		/*
 		 * Disassociation.
 		 */
+		ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM disassociated\n",
+			   sta->addr);
+
 		ret = ath10k_station_disassoc(ar, arvif, sta);
 		if (ret)
 			ath10k_warn("Failed to disassociate station: %pM\n",
 				    sta->addr);
-		else
-			ath10k_dbg(ATH10K_DBG_MAC,
-				   "Station %pM moved to disassociated state\n",
-				   sta->addr);
 	}
-
+exit:
 	mutex_unlock(&ar->conf_mutex);
 	return ret;
 }
@@ -2363,7 +3277,9 @@
 	u32 value = 0;
 	int ret = 0;
 
-	if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
+	lockdep_assert_held(&ar->conf_mutex);
+
+	if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
 		return 0;
 
 	switch (ac) {
@@ -2552,86 +3468,57 @@
  * Both RTS and Fragmentation threshold are interface-specific
  * in ath10k, but device-specific in mac80211.
  */
-static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
-{
-	struct ath10k_generic_iter *ar_iter = data;
-	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
-	u32 rts = ar_iter->ar->hw->wiphy->rts_threshold;
-
-	rts = min_t(u32, rts, ATH10K_RTS_MAX);
-
-	ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
-						 WMI_VDEV_PARAM_RTS_THRESHOLD,
-						 rts);
-	if (ar_iter->ret)
-		ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
-			    arvif->vdev_id);
-	else
-		ath10k_dbg(ATH10K_DBG_MAC,
-			   "Set RTS threshold: %d for VDEV: %d\n",
-			   rts, arvif->vdev_id);
-}
 
 static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 {
-	struct ath10k_generic_iter ar_iter;
 	struct ath10k *ar = hw->priv;
-
-	memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-	ar_iter.ar = ar;
+	struct ath10k_vif *arvif;
+	int ret = 0;
 
 	mutex_lock(&ar->conf_mutex);
-	ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-					    ath10k_set_rts_iter, &ar_iter);
-	mutex_unlock(&ar->conf_mutex);
+	list_for_each_entry(arvif, &ar->arvifs, list) {
+		ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d rts threshold %d\n",
+			   arvif->vdev_id, value);
 
-	return ar_iter.ret;
+		ret = ath10k_mac_set_rts(arvif, value);
+		if (ret) {
+			ath10k_warn("could not set rts threshold for vdev %d (%d)\n",
+				    arvif->vdev_id, ret);
+			break;
 }
+	}
+	mutex_unlock(&ar->conf_mutex);
 
-static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
-{
-	struct ath10k_generic_iter *ar_iter = data;
-	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
-	u32 frag = ar_iter->ar->hw->wiphy->frag_threshold;
-	int ret;
-
-	frag = clamp_t(u32, frag,
-		       ATH10K_FRAGMT_THRESHOLD_MIN,
-		       ATH10K_FRAGMT_THRESHOLD_MAX);
-
-	ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
-					WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
-					frag);
-
-	ar_iter->ret = ret;
-	if (ar_iter->ret)
-		ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
-			    arvif->vdev_id);
-	else
-		ath10k_dbg(ATH10K_DBG_MAC,
-			   "Set frag threshold: %d for VDEV: %d\n",
-			   frag, arvif->vdev_id);
+	return ret;
 }
 
 static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 {
-	struct ath10k_generic_iter ar_iter;
 	struct ath10k *ar = hw->priv;
-
-	memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-	ar_iter.ar = ar;
+	struct ath10k_vif *arvif;
+	int ret = 0;
 
 	mutex_lock(&ar->conf_mutex);
-	ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-					    ath10k_set_frag_iter, &ar_iter);
+	list_for_each_entry(arvif, &ar->arvifs, list) {
+		ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n",
+			   arvif->vdev_id, value);
+
+		ret = ath10k_mac_set_rts(arvif, value);
+		if (ret) {
+			ath10k_warn("could not set fragmentation threshold for vdev %d (%d)\n",
+				    arvif->vdev_id, ret);
+			break;
+		}
+	}
 	mutex_unlock(&ar->conf_mutex);
 
-	return ar_iter.ret;
+	return ret;
 }
 
 static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
 	struct ath10k *ar = hw->priv;
+	bool skip;
 	int ret;
 
 	/* mac80211 doesn't care if we really xmit queued frames or not
@@ -2639,16 +3526,29 @@
 	if (drop)
 		return;
 
-	ret = wait_event_timeout(ar->htt->empty_tx_wq, ({
+	mutex_lock(&ar->conf_mutex);
+
+	if (ar->state == ATH10K_STATE_WEDGED)
+		goto skip;
+
+	ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
 			bool empty;
-			spin_lock_bh(&ar->htt->tx_lock);
-			empty = bitmap_empty(ar->htt->used_msdu_ids,
-					     ar->htt->max_num_pending_tx);
-			spin_unlock_bh(&ar->htt->tx_lock);
-			(empty);
+
+			spin_lock_bh(&ar->htt.tx_lock);
+			empty = (ar->htt.num_pending_tx == 0);
+			spin_unlock_bh(&ar->htt.tx_lock);
+
+			skip = (ar->state == ATH10K_STATE_WEDGED);
+
+			(empty || skip);
 		}), ATH10K_FLUSH_TIMEOUT_HZ);
-	if (ret <= 0)
-		ath10k_warn("tx not flushed\n");
+
+	if (ret <= 0 || skip)
+		ath10k_warn("tx not flushed (skip %i ar-state %i): %i\n",
+			    skip, ar->state, ret);
+
+skip:
+	mutex_unlock(&ar->conf_mutex);
 }
 
 /* TODO: Implement this function properly
@@ -2660,6 +3560,551 @@
 	return 1;
 }
 
+#ifdef CONFIG_PM
+static int ath10k_suspend(struct ieee80211_hw *hw,
+			  struct cfg80211_wowlan *wowlan)
+{
+	struct ath10k *ar = hw->priv;
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	ret = ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND);
+	if (ret) {
+		if (ret == -ETIMEDOUT)
+			goto resume;
+		ret = 1;
+		goto exit;
+	}
+
+	ret = ath10k_hif_suspend(ar);
+	if (ret) {
+		ath10k_warn("could not suspend hif (%d)\n", ret);
+		goto resume;
+	}
+
+	ret = 0;
+	goto exit;
+resume:
+	ret = ath10k_wmi_pdev_resume_target(ar);
+	if (ret)
+		ath10k_warn("could not resume target (%d)\n", ret);
+
+	ret = 1;
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static int ath10k_resume(struct ieee80211_hw *hw)
+{
+	struct ath10k *ar = hw->priv;
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	ret = ath10k_hif_resume(ar);
+	if (ret) {
+		ath10k_warn("could not resume hif (%d)\n", ret);
+		ret = 1;
+		goto exit;
+	}
+
+	ret = ath10k_wmi_pdev_resume_target(ar);
+	if (ret) {
+		ath10k_warn("could not resume target (%d)\n", ret);
+		ret = 1;
+		goto exit;
+	}
+
+	ret = 0;
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+#endif
+
+static void ath10k_restart_complete(struct ieee80211_hw *hw)
+{
+	struct ath10k *ar = hw->priv;
+
+	mutex_lock(&ar->conf_mutex);
+
+	/* If device failed to restart it will be in a different state, e.g.
+	 * ATH10K_STATE_WEDGED */
+	if (ar->state == ATH10K_STATE_RESTARTED) {
+		ath10k_info("device successfully recovered\n");
+		ar->state = ATH10K_STATE_ON;
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
+static int ath10k_get_survey(struct ieee80211_hw *hw, int idx,
+			     struct survey_info *survey)
+{
+	struct ath10k *ar = hw->priv;
+	struct ieee80211_supported_band *sband;
+	struct survey_info *ar_survey = &ar->survey[idx];
+	int ret = 0;
+
+	mutex_lock(&ar->conf_mutex);
+
+	sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ];
+	if (sband && idx >= sband->n_channels) {
+		idx -= sband->n_channels;
+		sband = NULL;
+	}
+
+	if (!sband)
+		sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ];
+
+	if (!sband || idx >= sband->n_channels) {
+		ret = -ENOENT;
+		goto exit;
+	}
+
+	spin_lock_bh(&ar->data_lock);
+	memcpy(survey, ar_survey, sizeof(*survey));
+	spin_unlock_bh(&ar->data_lock);
+
+	survey->channel = &sband->channels[idx];
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+/* Helper table for legacy fixed_rate/bitrate_mask */
+static const u8 cck_ofdm_rate[] = {
+	/* CCK */
+	3, /* 1Mbps */
+	2, /* 2Mbps */
+	1, /* 5.5Mbps */
+	0, /* 11Mbps */
+	/* OFDM */
+	3, /* 6Mbps */
+	7, /* 9Mbps */
+	2, /* 12Mbps */
+	6, /* 18Mbps */
+	1, /* 24Mbps */
+	5, /* 36Mbps */
+	0, /* 48Mbps */
+	4, /* 54Mbps */
+};
+
+/* Check if only one bit set */
+static int ath10k_check_single_mask(u32 mask)
+{
+	int bit;
+
+	bit = ffs(mask);
+	if (!bit)
+		return 0;
+
+	mask &= ~BIT(bit - 1);
+	if (mask)
+		return 2;
+
+	return 1;
+}
+
+static bool
+ath10k_default_bitrate_mask(struct ath10k *ar,
+			    enum ieee80211_band band,
+			    const struct cfg80211_bitrate_mask *mask)
+{
+	u32 legacy = 0x00ff;
+	u8 ht = 0xff, i;
+	u16 vht = 0x3ff;
+
+	switch (band) {
+	case IEEE80211_BAND_2GHZ:
+		legacy = 0x00fff;
+		vht = 0;
+		break;
+	case IEEE80211_BAND_5GHZ:
+		break;
+	default:
+		return false;
+	}
+
+	if (mask->control[band].legacy != legacy)
+		return false;
+
+	for (i = 0; i < ar->num_rf_chains; i++)
+		if (mask->control[band].ht_mcs[i] != ht)
+			return false;
+
+	for (i = 0; i < ar->num_rf_chains; i++)
+		if (mask->control[band].vht_mcs[i] != vht)
+			return false;
+
+	return true;
+}
+
+static bool
+ath10k_bitrate_mask_nss(const struct cfg80211_bitrate_mask *mask,
+			enum ieee80211_band band,
+			u8 *fixed_nss)
+{
+	int ht_nss = 0, vht_nss = 0, i;
+
+	/* check legacy */
+	if (ath10k_check_single_mask(mask->control[band].legacy))
+		return false;
+
+	/* check HT */
+	for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+		if (mask->control[band].ht_mcs[i] == 0xff)
+			continue;
+		else if (mask->control[band].ht_mcs[i] == 0x00)
+			break;
+		else
+			return false;
+	}
+
+	ht_nss = i;
+
+	/* check VHT */
+	for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+		if (mask->control[band].vht_mcs[i] == 0x03ff)
+			continue;
+		else if (mask->control[band].vht_mcs[i] == 0x0000)
+			break;
+		else
+			return false;
+	}
+
+	vht_nss = i;
+
+	if (ht_nss > 0 && vht_nss > 0)
+		return false;
+
+	if (ht_nss)
+		*fixed_nss = ht_nss;
+	else if (vht_nss)
+		*fixed_nss = vht_nss;
+	else
+		return false;
+
+	return true;
+}
+
+static bool
+ath10k_bitrate_mask_correct(const struct cfg80211_bitrate_mask *mask,
+			    enum ieee80211_band band,
+			    enum wmi_rate_preamble *preamble)
+{
+	int legacy = 0, ht = 0, vht = 0, i;
+
+	*preamble = WMI_RATE_PREAMBLE_OFDM;
+
+	/* check legacy */
+	legacy = ath10k_check_single_mask(mask->control[band].legacy);
+	if (legacy > 1)
+		return false;
+
+	/* check HT */
+	for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+		ht += ath10k_check_single_mask(mask->control[band].ht_mcs[i]);
+	if (ht > 1)
+		return false;
+
+	/* check VHT */
+	for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+		vht += ath10k_check_single_mask(mask->control[band].vht_mcs[i]);
+	if (vht > 1)
+		return false;
+
+	/* Currently we support only one fixed_rate */
+	if ((legacy + ht + vht) != 1)
+		return false;
+
+	if (ht)
+		*preamble = WMI_RATE_PREAMBLE_HT;
+	else if (vht)
+		*preamble = WMI_RATE_PREAMBLE_VHT;
+
+	return true;
+}
+
+static bool
+ath10k_bitrate_mask_rate(const struct cfg80211_bitrate_mask *mask,
+			 enum ieee80211_band band,
+			 u8 *fixed_rate,
+			 u8 *fixed_nss)
+{
+	u8 rate = 0, pream = 0, nss = 0, i;
+	enum wmi_rate_preamble preamble;
+
+	/* Check if single rate correct */
+	if (!ath10k_bitrate_mask_correct(mask, band, &preamble))
+		return false;
+
+	pream = preamble;
+
+	switch (preamble) {
+	case WMI_RATE_PREAMBLE_CCK:
+	case WMI_RATE_PREAMBLE_OFDM:
+		i = ffs(mask->control[band].legacy) - 1;
+
+		if (band == IEEE80211_BAND_2GHZ && i < 4)
+			pream = WMI_RATE_PREAMBLE_CCK;
+
+		if (band == IEEE80211_BAND_5GHZ)
+			i += 4;
+
+		if (i >= ARRAY_SIZE(cck_ofdm_rate))
+			return false;
+
+		rate = cck_ofdm_rate[i];
+		break;
+	case WMI_RATE_PREAMBLE_HT:
+		for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+			if (mask->control[band].ht_mcs[i])
+				break;
+
+		if (i == IEEE80211_HT_MCS_MASK_LEN)
+			return false;
+
+		rate = ffs(mask->control[band].ht_mcs[i]) - 1;
+		nss = i;
+		break;
+	case WMI_RATE_PREAMBLE_VHT:
+		for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+			if (mask->control[band].vht_mcs[i])
+				break;
+
+		if (i == NL80211_VHT_NSS_MAX)
+			return false;
+
+		rate = ffs(mask->control[band].vht_mcs[i]) - 1;
+		nss = i;
+		break;
+	}
+
+	*fixed_nss = nss + 1;
+	nss <<= 4;
+	pream <<= 6;
+
+	ath10k_dbg(ATH10K_DBG_MAC, "mac fixed rate pream 0x%02x nss 0x%02x rate 0x%02x\n",
+		   pream, nss, rate);
+
+	*fixed_rate = pream | nss | rate;
+
+	return true;
+}
+
+static bool ath10k_get_fixed_rate_nss(const struct cfg80211_bitrate_mask *mask,
+				      enum ieee80211_band band,
+				      u8 *fixed_rate,
+				      u8 *fixed_nss)
+{
+	/* First check full NSS mask, if we can simply limit NSS */
+	if (ath10k_bitrate_mask_nss(mask, band, fixed_nss))
+		return true;
+
+	/* Next Check single rate is set */
+	return ath10k_bitrate_mask_rate(mask, band, fixed_rate, fixed_nss);
+}
+
+static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif,
+				       u8 fixed_rate,
+				       u8 fixed_nss,
+				       u8 force_sgi)
+{
+	struct ath10k *ar = arvif->ar;
+	u32 vdev_param;
+	int ret = 0;
+
+	mutex_lock(&ar->conf_mutex);
+
+	if (arvif->fixed_rate == fixed_rate &&
+	    arvif->fixed_nss == fixed_nss &&
+	    arvif->force_sgi == force_sgi)
+		goto exit;
+
+	if (fixed_rate == WMI_FIXED_RATE_NONE)
+		ath10k_dbg(ATH10K_DBG_MAC, "mac disable fixed bitrate mask\n");
+
+	if (force_sgi)
+		ath10k_dbg(ATH10K_DBG_MAC, "mac force sgi\n");
+
+	vdev_param = ar->wmi.vdev_param->fixed_rate;
+	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+					vdev_param, fixed_rate);
+	if (ret) {
+		ath10k_warn("Could not set fixed_rate param 0x%02x: %d\n",
+			    fixed_rate, ret);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	arvif->fixed_rate = fixed_rate;
+
+	vdev_param = ar->wmi.vdev_param->nss;
+	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+					vdev_param, fixed_nss);
+
+	if (ret) {
+		ath10k_warn("Could not set fixed_nss param %d: %d\n",
+			    fixed_nss, ret);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	arvif->fixed_nss = fixed_nss;
+
+	vdev_param = ar->wmi.vdev_param->sgi;
+	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+					force_sgi);
+
+	if (ret) {
+		ath10k_warn("Could not set sgi param %d: %d\n",
+			    force_sgi, ret);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	arvif->force_sgi = force_sgi;
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif,
+				   const struct cfg80211_bitrate_mask *mask)
+{
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	struct ath10k *ar = arvif->ar;
+	enum ieee80211_band band = ar->hw->conf.chandef.chan->band;
+	u8 fixed_rate = WMI_FIXED_RATE_NONE;
+	u8 fixed_nss = ar->num_rf_chains;
+	u8 force_sgi;
+
+	force_sgi = mask->control[band].gi;
+	if (force_sgi == NL80211_TXRATE_FORCE_LGI)
+		return -EINVAL;
+
+	if (!ath10k_default_bitrate_mask(ar, band, mask)) {
+		if (!ath10k_get_fixed_rate_nss(mask, band,
+					       &fixed_rate,
+					       &fixed_nss))
+			return -EINVAL;
+	}
+
+	if (fixed_rate == WMI_FIXED_RATE_NONE && force_sgi) {
+		ath10k_warn("Could not force SGI usage for default rate settings\n");
+		return -EINVAL;
+	}
+
+	return ath10k_set_fixed_rate_param(arvif, fixed_rate,
+					   fixed_nss, force_sgi);
+}
+
+static void ath10k_channel_switch_beacon(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct cfg80211_chan_def *chandef)
+{
+	/* there's no need to do anything here. vif->csa_active is enough */
+	return;
+}
+
+static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
+				 struct ieee80211_vif *vif,
+				 struct ieee80211_sta *sta,
+				 u32 changed)
+{
+	struct ath10k *ar = hw->priv;
+	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+	u32 bw, smps;
+
+	spin_lock_bh(&ar->data_lock);
+
+	ath10k_dbg(ATH10K_DBG_MAC,
+		   "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n",
+		   sta->addr, changed, sta->bandwidth, sta->rx_nss,
+		   sta->smps_mode);
+
+	if (changed & IEEE80211_RC_BW_CHANGED) {
+		bw = WMI_PEER_CHWIDTH_20MHZ;
+
+		switch (sta->bandwidth) {
+		case IEEE80211_STA_RX_BW_20:
+			bw = WMI_PEER_CHWIDTH_20MHZ;
+			break;
+		case IEEE80211_STA_RX_BW_40:
+			bw = WMI_PEER_CHWIDTH_40MHZ;
+			break;
+		case IEEE80211_STA_RX_BW_80:
+			bw = WMI_PEER_CHWIDTH_80MHZ;
+			break;
+		case IEEE80211_STA_RX_BW_160:
+			ath10k_warn("mac sta rc update for %pM: invalid bw %d\n",
+				    sta->addr, sta->bandwidth);
+			bw = WMI_PEER_CHWIDTH_20MHZ;
+			break;
+		}
+
+		arsta->bw = bw;
+	}
+
+	if (changed & IEEE80211_RC_NSS_CHANGED)
+		arsta->nss = sta->rx_nss;
+
+	if (changed & IEEE80211_RC_SMPS_CHANGED) {
+		smps = WMI_PEER_SMPS_PS_NONE;
+
+		switch (sta->smps_mode) {
+		case IEEE80211_SMPS_AUTOMATIC:
+		case IEEE80211_SMPS_OFF:
+			smps = WMI_PEER_SMPS_PS_NONE;
+			break;
+		case IEEE80211_SMPS_STATIC:
+			smps = WMI_PEER_SMPS_STATIC;
+			break;
+		case IEEE80211_SMPS_DYNAMIC:
+			smps = WMI_PEER_SMPS_DYNAMIC;
+			break;
+		case IEEE80211_SMPS_NUM_MODES:
+			ath10k_warn("mac sta rc update for %pM: invalid smps: %d\n",
+				    sta->addr, sta->smps_mode);
+			smps = WMI_PEER_SMPS_PS_NONE;
+			break;
+		}
+
+		arsta->smps = smps;
+	}
+
+	if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
+		/* FIXME: Not implemented. Probably the only way to do it would
+		 * be to re-assoc the peer. */
+		changed &= ~IEEE80211_RC_SUPP_RATES_CHANGED;
+		ath10k_dbg(ATH10K_DBG_MAC,
+			   "mac sta rc update for %pM: changing supported rates not implemented\n",
+			   sta->addr);
+	}
+
+	arsta->changed |= changed;
+
+	spin_unlock_bh(&ar->data_lock);
+
+	ieee80211_queue_work(hw, &arsta->update_wk);
+}
+
+static u64 ath10k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	/*
+	 * FIXME: Return 0 for time being. Need to figure out whether FW
+	 * has the API to fetch 64-bit local TSF
+	 */
+
+	return 0;
+}
+
 static const struct ieee80211_ops ath10k_ops = {
 	.tx				= ath10k_tx,
 	.start				= ath10k_start,
@@ -2680,6 +4125,16 @@
 	.set_frag_threshold		= ath10k_set_frag_threshold,
 	.flush				= ath10k_flush,
 	.tx_last_beacon			= ath10k_tx_last_beacon,
+	.restart_complete		= ath10k_restart_complete,
+	.get_survey			= ath10k_get_survey,
+	.set_bitrate_mask		= ath10k_set_bitrate_mask,
+	.channel_switch_beacon		= ath10k_channel_switch_beacon,
+	.sta_rc_update			= ath10k_sta_rc_update,
+	.get_tsf			= ath10k_get_tsf,
+#ifdef CONFIG_PM
+	.suspend			= ath10k_suspend,
+	.resume				= ath10k_resume,
+#endif
 };
 
 #define RATETAB_ENT(_rate, _rateid, _flags) { \
@@ -2797,36 +4252,66 @@
 	.max	= 8,
 	.types	= BIT(NL80211_IFTYPE_STATION)
 		| BIT(NL80211_IFTYPE_P2P_CLIENT)
-		| BIT(NL80211_IFTYPE_P2P_GO)
-		| BIT(NL80211_IFTYPE_AP)
-	}
+	},
+	{
+	.max	= 3,
+	.types	= BIT(NL80211_IFTYPE_P2P_GO)
+	},
+	{
+	.max	= 7,
+	.types	= BIT(NL80211_IFTYPE_AP)
+	},
 };
 
-static const struct ieee80211_iface_combination ath10k_if_comb = {
+static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = {
+	{
+	.max	= 8,
+	.types	= BIT(NL80211_IFTYPE_AP)
+	},
+};
+
+static const struct ieee80211_iface_combination ath10k_if_comb[] = {
+	{
 	.limits = ath10k_if_limits,
 	.n_limits = ARRAY_SIZE(ath10k_if_limits),
 	.max_interfaces = 8,
 	.num_different_channels = 1,
 	.beacon_int_infra_match = true,
+	},
+};
+
+static const struct ieee80211_iface_combination ath10k_10x_if_comb[] = {
+	{
+		.limits = ath10k_10x_if_limits,
+		.n_limits = ARRAY_SIZE(ath10k_10x_if_limits),
+		.max_interfaces = 8,
+		.num_different_channels = 1,
+		.beacon_int_infra_match = true,
+#ifdef CONFIG_ATH10K_DFS_CERTIFIED
+		.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
+	},
 };
 
 static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
 {
 	struct ieee80211_sta_vht_cap vht_cap = {0};
 	u16 mcs_map;
+	int i;
 
 	vht_cap.vht_supported = 1;
 	vht_cap.cap = ar->vht_cap_info;
 
-	/* FIXME: check dynamically how many streams board supports */
-	mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
-		IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
-		IEEE80211_VHT_MCS_SUPPORT_0_9 << 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;
+	mcs_map = 0;
+	for (i = 0; i < 8; i++) {
+		if (i < ar->num_rf_chains)
+			mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i*2);
+		else
+			mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2);
+	}
 
 	vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
 	vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
@@ -2889,7 +4374,7 @@
 	if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK)
 		ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
 
-	for (i = 0; i < WMI_MAX_SPATIAL_STREAM; i++)
+	for (i = 0; i < ar->num_rf_chains; i++)
 		ht_cap.mcs.rx_mask[i] = 0xFF;
 
 	ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
@@ -2948,8 +4433,10 @@
 		channels = kmemdup(ath10k_2ghz_channels,
 				   sizeof(ath10k_2ghz_channels),
 				   GFP_KERNEL);
-		if (!channels)
-			return -ENOMEM;
+		if (!channels) {
+			ret = -ENOMEM;
+			goto err_free;
+		}
 
 		band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
 		band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels);
@@ -2968,11 +4455,8 @@
 				   sizeof(ath10k_5ghz_channels),
 				   GFP_KERNEL);
 		if (!channels) {
-			if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
-				band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
-				kfree(band->channels);
-			}
-			return -ENOMEM;
+			ret = -ENOMEM;
+			goto err_free;
 		}
 
 		band = &ar->mac.sbands[IEEE80211_BAND_5GHZ];
@@ -2988,7 +4472,10 @@
 	ar->hw->wiphy->interface_modes =
 		BIT(NL80211_IFTYPE_STATION) |
 		BIT(NL80211_IFTYPE_ADHOC) |
-		BIT(NL80211_IFTYPE_AP) |
+		BIT(NL80211_IFTYPE_AP);
+
+	if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features))
+		ar->hw->wiphy->interface_modes |=
 		BIT(NL80211_IFTYPE_P2P_CLIENT) |
 		BIT(NL80211_IFTYPE_P2P_GO);
 
@@ -3001,7 +4488,12 @@
 			IEEE80211_HW_HAS_RATE_CONTROL |
 			IEEE80211_HW_SUPPORTS_STATIC_SMPS |
 			IEEE80211_HW_WANT_MONITOR_VIF |
-			IEEE80211_HW_AP_LINK_PS;
+			IEEE80211_HW_AP_LINK_PS |
+			IEEE80211_HW_SPECTRUM_MGMT;
+
+	/* MSDU can have HTT TX fragment pushed in front. The additional 4
+	 * bytes is used for padding/alignment if necessary. */
+	ar->hw->extra_tx_headroom += sizeof(struct htt_data_tx_desc_frag)*2 + 4;
 
 	if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
 		ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
@@ -3015,11 +4507,13 @@
 	ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN;
 
 	ar->hw->vif_data_size = sizeof(struct ath10k_vif);
+	ar->hw->sta_data_size = sizeof(struct ath10k_sta);
 
 	ar->hw->channel_change_time = 5000;
 	ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
 
 	ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+	ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
 	ar->hw->wiphy->max_remain_on_channel_duration = 5000;
 
 	ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
@@ -3029,32 +4523,56 @@
 	 */
 	ar->hw->queues = 4;
 
-	ar->hw->wiphy->iface_combinations = &ath10k_if_comb;
-	ar->hw->wiphy->n_iface_combinations = 1;
+	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+		ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb;
+		ar->hw->wiphy->n_iface_combinations =
+			ARRAY_SIZE(ath10k_10x_if_comb);
+	} else {
+		ar->hw->wiphy->iface_combinations = ath10k_if_comb;
+		ar->hw->wiphy->n_iface_combinations =
+			ARRAY_SIZE(ath10k_if_comb);
+	}
+
+	ar->hw->netdev_features = NETIF_F_HW_CSUM;
+
+	if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)) {
+		/* Init ath dfs pattern detector */
+		ar->ath_common.debug_mask = ATH_DBG_DFS;
+		ar->dfs_detector = dfs_pattern_detector_init(&ar->ath_common,
+							     NL80211_DFS_UNSET);
+
+		if (!ar->dfs_detector)
+			ath10k_warn("dfs pattern detector init failed\n");
+	}
 
 	ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
 			    ath10k_reg_notifier);
 	if (ret) {
 		ath10k_err("Regulatory initialization failed\n");
-		return ret;
+		goto err_free;
 	}
 
 	ret = ieee80211_register_hw(ar->hw);
 	if (ret) {
 		ath10k_err("ieee80211 registration failed: %d\n", ret);
-		return ret;
+		goto err_free;
 	}
 
 	if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
 		ret = regulatory_hint(ar->hw->wiphy,
 				      ar->ath_common.regulatory.alpha2);
 		if (ret)
-			goto exit;
+			goto err_unregister;
 	}
 
 	return 0;
-exit:
+
+err_unregister:
 	ieee80211_unregister_hw(ar->hw);
+err_free:
+	kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
+	kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
+
 	return ret;
 }
 
@@ -3062,6 +4580,9 @@
 {
 	ieee80211_unregister_hw(ar->hw);
 
+	if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector)
+		ar->dfs_detector->exit(ar->dfs_detector);
+
 	kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
 	kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
 
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/mac.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/mac.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/mac.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/mac.h	2015-09-29 11:15:06.921740666 +0200
@@ -34,6 +34,9 @@
 void ath10k_reset_scan(unsigned long ptr);
 void ath10k_offchan_tx_purge(struct ath10k *ar);
 void ath10k_offchan_tx_work(struct work_struct *work);
+void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar);
+void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work);
+void ath10k_halt(struct ath10k *ar);
 
 static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
 {
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/pci.c linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/pci.c
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/pci.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/pci.c	2015-09-29 11:15:06.921740666 +0200
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
+#include <linux/bitops.h>
 
 #include "core.h"
 #include "debug.h"
@@ -32,15 +33,24 @@
 #include "ce.h"
 #include "pci.h"
 
-unsigned int ath10k_target_ps;
+enum ath10k_pci_irq_mode {
+	ATH10K_PCI_IRQ_AUTO = 0,
+	ATH10K_PCI_IRQ_LEGACY = 1,
+	ATH10K_PCI_IRQ_MSI = 2,
+};
+
+static unsigned int ath10k_target_ps;
+static unsigned int ath10k_pci_irq_mode = ATH10K_PCI_IRQ_AUTO;
+
 module_param(ath10k_target_ps, uint, 0644);
 MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option");
 
-#define QCA988X_1_0_DEVICE_ID	(0xabcd)
+module_param_named(irq_mode, ath10k_pci_irq_mode, uint, 0644);
+MODULE_PARM_DESC(irq_mode, "0: auto, 1: legacy, 2: msi (default: 0)");
+
 #define QCA988X_2_0_DEVICE_ID	(0x003c)
 
 static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = {
-	{ PCI_VDEVICE(ATHEROS, QCA988X_1_0_DEVICE_ID) }, /* PCI-E QCA988X V1 */
 	{ PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */
 	{0}
 };
@@ -48,54 +58,245 @@
 static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address,
 				       u32 *data);
 
-static void ath10k_pci_process_ce(struct ath10k *ar);
 static int ath10k_pci_post_rx(struct ath10k *ar);
-static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
+static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info,
 					     int num);
-static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info);
-static void ath10k_pci_stop_ce(struct ath10k *ar);
+static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info);
+static int ath10k_pci_cold_reset(struct ath10k *ar);
+static int ath10k_pci_warm_reset(struct ath10k *ar);
+static int ath10k_pci_wait_for_target_init(struct ath10k *ar);
+static int ath10k_pci_init_irq(struct ath10k *ar);
+static int ath10k_pci_deinit_irq(struct ath10k *ar);
+static int ath10k_pci_request_irq(struct ath10k *ar);
+static void ath10k_pci_free_irq(struct ath10k *ar);
+static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe,
+			       struct ath10k_ce_pipe *rx_pipe,
+			       struct bmi_xfer *xfer);
 
 static const struct ce_attr host_ce_config_wlan[] = {
-	/* host->target HTC control and raw streams */
-	{ /* CE0 */ CE_ATTR_FLAGS, 0, 16, 256, 0, NULL,},
-	/* could be moved to share CE3 */
-	/* target->host HTT + HTC control */
-	{ /* CE1 */ CE_ATTR_FLAGS, 0, 0, 512, 512, NULL,},
-	/* target->host WMI */
-	{ /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 32, NULL,},
-	/* host->target WMI */
-	{ /* CE3 */ CE_ATTR_FLAGS, 0, 32, 2048, 0, NULL,},
-	/* host->target HTT */
-	{ /* CE4 */ CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, 0,
-		    CE_HTT_H2T_MSG_SRC_NENTRIES, 256, 0, NULL,},
-	/* unused */
-	{ /* CE5 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
-	/* Target autonomous hif_memcpy */
-	{ /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
-	/* ce_diag, the Diagnostic Window */
-	{ /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,},
+	/* CE0: host->target HTC control and raw streams */
+	{
+		.flags = CE_ATTR_FLAGS,
+		.src_nentries = 16,
+		.src_sz_max = 256,
+		.dest_nentries = 0,
+	},
+
+	/* CE1: target->host HTT + HTC control */
+	{
+		.flags = CE_ATTR_FLAGS,
+		.src_nentries = 0,
+		.src_sz_max = 512,
+		.dest_nentries = 512,
+	},
+
+	/* CE2: target->host WMI */
+	{
+		.flags = CE_ATTR_FLAGS,
+		.src_nentries = 0,
+		.src_sz_max = 2048,
+		.dest_nentries = 32,
+	},
+
+	/* CE3: host->target WMI */
+	{
+		.flags = CE_ATTR_FLAGS,
+		.src_nentries = 32,
+		.src_sz_max = 2048,
+		.dest_nentries = 0,
+	},
+
+	/* CE4: host->target HTT */
+	{
+		.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
+		.src_nentries = CE_HTT_H2T_MSG_SRC_NENTRIES,
+		.src_sz_max = 256,
+		.dest_nentries = 0,
+	},
+
+	/* CE5: unused */
+	{
+		.flags = CE_ATTR_FLAGS,
+		.src_nentries = 0,
+		.src_sz_max = 0,
+		.dest_nentries = 0,
+	},
+
+	/* CE6: target autonomous hif_memcpy */
+	{
+		.flags = CE_ATTR_FLAGS,
+		.src_nentries = 0,
+		.src_sz_max = 0,
+		.dest_nentries = 0,
+	},
+
+	/* CE7: ce_diag, the Diagnostic Window */
+	{
+		.flags = CE_ATTR_FLAGS,
+		.src_nentries = 2,
+		.src_sz_max = DIAG_TRANSFER_LIMIT,
+		.dest_nentries = 2,
+	},
 };
 
 /* Target firmware's Copy Engine configuration. */
 static const struct ce_pipe_config target_ce_config_wlan[] = {
-	/* host->target HTC control and raw streams */
-	{ /* CE0 */ 0, PIPEDIR_OUT, 32, 256, CE_ATTR_FLAGS, 0,},
-	/* target->host HTT + HTC control */
-	{ /* CE1 */ 1, PIPEDIR_IN, 32, 512, CE_ATTR_FLAGS, 0,},
-	/* target->host WMI */
-	{ /* CE2 */ 2, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,},
-	/* host->target WMI */
-	{ /* CE3 */ 3, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
-	/* host->target HTT */
-	{ /* CE4 */ 4, PIPEDIR_OUT, 256, 256, CE_ATTR_FLAGS, 0,},
+	/* CE0: host->target HTC control and raw streams */
+	{
+		.pipenum = 0,
+		.pipedir = PIPEDIR_OUT,
+		.nentries = 32,
+		.nbytes_max = 256,
+		.flags = CE_ATTR_FLAGS,
+		.reserved = 0,
+	},
+
+	/* CE1: target->host HTT + HTC control */
+	{
+		.pipenum = 1,
+		.pipedir = PIPEDIR_IN,
+		.nentries = 32,
+		.nbytes_max = 512,
+		.flags = CE_ATTR_FLAGS,
+		.reserved = 0,
+	},
+
+	/* CE2: target->host WMI */
+	{
+		.pipenum = 2,
+		.pipedir = PIPEDIR_IN,
+		.nentries = 32,
+		.nbytes_max = 2048,
+		.flags = CE_ATTR_FLAGS,
+		.reserved = 0,
+	},
+
+	/* CE3: host->target WMI */
+	{
+		.pipenum = 3,
+		.pipedir = PIPEDIR_OUT,
+		.nentries = 32,
+		.nbytes_max = 2048,
+		.flags = CE_ATTR_FLAGS,
+		.reserved = 0,
+	},
+
+	/* CE4: host->target HTT */
+	{
+		.pipenum = 4,
+		.pipedir = PIPEDIR_OUT,
+		.nentries = 256,
+		.nbytes_max = 256,
+		.flags = CE_ATTR_FLAGS,
+		.reserved = 0,
+	},
+
 	/* NB: 50% of src nentries, since tx has 2 frags */
-	/* unused */
-	{ /* CE5 */ 5, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
-	/* Reserved for target autonomous hif_memcpy */
-	{ /* CE6 */ 6, PIPEDIR_INOUT, 32, 4096, CE_ATTR_FLAGS, 0,},
+
+	/* CE5: unused */
+	{
+		.pipenum = 5,
+		.pipedir = PIPEDIR_OUT,
+		.nentries = 32,
+		.nbytes_max = 2048,
+		.flags = CE_ATTR_FLAGS,
+		.reserved = 0,
+	},
+
+	/* CE6: Reserved for target autonomous hif_memcpy */
+	{
+		.pipenum = 6,
+		.pipedir = PIPEDIR_INOUT,
+		.nentries = 32,
+		.nbytes_max = 4096,
+		.flags = CE_ATTR_FLAGS,
+		.reserved = 0,
+	},
+
 	/* CE7 used only by Host */
 };
 
+static bool ath10k_pci_irq_pending(struct ath10k *ar)
+{
+	u32 cause;
+
+	/* Check if the shared legacy irq is for us */
+	cause = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+				  PCIE_INTR_CAUSE_ADDRESS);
+	if (cause & (PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL))
+		return true;
+
+	return false;
+}
+
+static void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar)
+{
+	/* IMPORTANT: INTR_CLR register has to be set after
+	 * INTR_ENABLE is set to 0, otherwise interrupt can not be
+	 * really cleared. */
+	ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS,
+			   0);
+	ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_CLR_ADDRESS,
+			   PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL);
+
+	/* IMPORTANT: this extra read transaction is required to
+	 * flush the posted write buffer. */
+	(void) ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+				 PCIE_INTR_ENABLE_ADDRESS);
+}
+
+static void ath10k_pci_enable_legacy_irq(struct ath10k *ar)
+{
+	ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
+			   PCIE_INTR_ENABLE_ADDRESS,
+			   PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL);
+
+	/* IMPORTANT: this extra read transaction is required to
+	 * flush the posted write buffer. */
+	(void) ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+				 PCIE_INTR_ENABLE_ADDRESS);
+}
+
+static irqreturn_t ath10k_pci_early_irq_handler(int irq, void *arg)
+{
+	struct ath10k *ar = arg;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	if (ar_pci->num_msi_intrs == 0) {
+		if (!ath10k_pci_irq_pending(ar))
+			return IRQ_NONE;
+
+		ath10k_pci_disable_and_clear_legacy_irq(ar);
+	}
+
+	tasklet_schedule(&ar_pci->early_irq_tasklet);
+
+	return IRQ_HANDLED;
+}
+
+static int ath10k_pci_request_early_irq(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ret;
+
+	/* Regardless whether MSI-X/MSI/legacy irqs have been set up the first
+	 * interrupt from irq vector is triggered in all cases for FW
+	 * indication/errors */
+	ret = request_irq(ar_pci->pdev->irq, ath10k_pci_early_irq_handler,
+			  IRQF_SHARED, "ath10k_pci (early)", ar);
+	if (ret) {
+		ath10k_warn("failed to request early irq: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void ath10k_pci_free_early_irq(struct ath10k *ar)
+{
+	free_irq(ath10k_pci_priv(ar)->pdev->irq, ar);
+}
+
 /*
  * Diagnostic read/write access is provided for startup/config/debug usage.
  * Caller must guarantee proper alignment, when applicable, and single user
@@ -110,7 +311,7 @@
 	unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
 	unsigned int id;
 	unsigned int flags;
-	struct ce_state *ce_diag;
+	struct ath10k_ce_pipe *ce_diag;
 	/* Host buffer address in CE space */
 	u32 ce_data;
 	dma_addr_t ce_data_base = 0;
@@ -274,7 +475,7 @@
 	unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
 	unsigned int id;
 	unsigned int flags;
-	struct ce_state *ce_diag;
+	struct ath10k_ce_pipe *ce_diag;
 	void *data_buf = NULL;
 	u32 ce_data;	/* Host buffer address in CE space */
 	dma_addr_t ce_data_base = 0;
@@ -422,18 +623,7 @@
 	return (RTC_STATE_V_GET(val) == RTC_STATE_V_ON);
 }
 
-static void ath10k_pci_wait(struct ath10k *ar)
-{
-	int n = 100;
-
-	while (n-- && !ath10k_pci_target_is_awake(ar))
-		msleep(10);
-
-	if (n < 0)
-		ath10k_warn("Unable to wakeup target\n");
-}
-
-void ath10k_do_pci_wake(struct ath10k *ar)
+int ath10k_do_pci_wake(struct ath10k *ar)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 	void __iomem *pci_addr = ar_pci->mem;
@@ -449,18 +639,19 @@
 	atomic_inc(&ar_pci->keep_awake_count);
 
 	if (ar_pci->verified_awake)
-		return;
+		return 0;
 
 	for (;;) {
 		if (ath10k_pci_target_is_awake(ar)) {
 			ar_pci->verified_awake = true;
-			break;
+			return 0;
 		}
 
 		if (tot_delay > PCIE_WAKE_TIMEOUT) {
-			ath10k_warn("target takes too long to wake up (awake count %d)\n",
+			ath10k_warn("target took longer %d us to wake up (awake count %d)\n",
+				    PCIE_WAKE_TIMEOUT,
 				    atomic_read(&ar_pci->keep_awake_count));
-			break;
+			return -ETIMEDOUT;
 		}
 
 		udelay(curr_delay);
@@ -485,195 +676,134 @@
 	}
 }
 
-/*
- * FIXME: Handle OOM properly.
- */
-static inline
-struct ath10k_pci_compl *get_free_compl(struct hif_ce_pipe_info *pipe_info)
-{
-	struct ath10k_pci_compl *compl = NULL;
-
-	spin_lock_bh(&pipe_info->pipe_lock);
-	if (list_empty(&pipe_info->compl_free)) {
-		ath10k_warn("Completion buffers are full\n");
-		goto exit;
-	}
-	compl = list_first_entry(&pipe_info->compl_free,
-				 struct ath10k_pci_compl, list);
-	list_del(&compl->list);
-exit:
-	spin_unlock_bh(&pipe_info->pipe_lock);
-	return compl;
-}
-
 /* Called by lower (CE) layer when a send to Target completes. */
-static void ath10k_pci_ce_send_done(struct ce_state *ce_state,
-				    void *transfer_context,
-				    u32 ce_data,
-				    unsigned int nbytes,
-				    unsigned int transfer_id)
+static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state)
 {
 	struct ath10k *ar = ce_state->ar;
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct hif_ce_pipe_info *pipe_info =  &ar_pci->pipe_info[ce_state->id];
-	struct ath10k_pci_compl *compl;
-	bool process = false;
-
-	do {
-		/*
-		 * For the send completion of an item in sendlist, just
-		 * increment num_sends_allowed. The upper layer callback will
-		 * be triggered when last fragment is done with send.
-		 */
-		if (transfer_context == CE_SENDLIST_ITEM_CTXT) {
-			spin_lock_bh(&pipe_info->pipe_lock);
-			pipe_info->num_sends_allowed++;
-			spin_unlock_bh(&pipe_info->pipe_lock);
-			continue;
-		}
-
-		compl = get_free_compl(pipe_info);
-		if (!compl)
-			break;
+	struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
+	void *transfer_context;
+	u32 ce_data;
+	unsigned int nbytes;
+	unsigned int transfer_id;
 
-		compl->send_or_recv = HIF_CE_COMPLETE_SEND;
-		compl->ce_state = ce_state;
-		compl->pipe_info = pipe_info;
-		compl->transfer_context = transfer_context;
-		compl->nbytes = nbytes;
-		compl->transfer_id = transfer_id;
-		compl->flags = 0;
-
-		/*
-		 * Add the completion to the processing queue.
-		 */
-		spin_lock_bh(&ar_pci->compl_lock);
-		list_add_tail(&compl->list, &ar_pci->compl_process);
-		spin_unlock_bh(&ar_pci->compl_lock);
-
-		process = true;
-	} while (ath10k_ce_completed_send_next(ce_state,
-							   &transfer_context,
+	while (ath10k_ce_completed_send_next(ce_state, &transfer_context,
 							   &ce_data, &nbytes,
-							   &transfer_id) == 0);
-
-	/*
-	 * If only some of the items within a sendlist have completed,
-	 * don't invoke completion processing until the entire sendlist
-	 * has been sent.
-	 */
-	if (!process)
-		return;
+					     &transfer_id) == 0) {
+		/* no need to call tx completion for NULL pointers */
+		if (transfer_context == NULL)
+			continue;
 
-	ath10k_pci_process_ce(ar);
+		cb->tx_completion(ar, transfer_context, transfer_id);
+	}
 }
 
 /* Called by lower (CE) layer when data is received from the Target. */
-static void ath10k_pci_ce_recv_data(struct ce_state *ce_state,
-				    void *transfer_context, u32 ce_data,
-				    unsigned int nbytes,
-				    unsigned int transfer_id,
-				    unsigned int flags)
+static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state)
 {
 	struct ath10k *ar = ce_state->ar;
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct hif_ce_pipe_info *pipe_info =  &ar_pci->pipe_info[ce_state->id];
-	struct ath10k_pci_compl *compl;
+	struct ath10k_pci_pipe *pipe_info =  &ar_pci->pipe_info[ce_state->id];
+	struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
 	struct sk_buff *skb;
+	void *transfer_context;
+	u32 ce_data;
+	unsigned int nbytes, max_nbytes;
+	unsigned int transfer_id;
+	unsigned int flags;
+	int err;
 
-	do {
-		compl = get_free_compl(pipe_info);
-		if (!compl)
-			break;
-
-		compl->send_or_recv = HIF_CE_COMPLETE_RECV;
-		compl->ce_state = ce_state;
-		compl->pipe_info = pipe_info;
-		compl->transfer_context = transfer_context;
-		compl->nbytes = nbytes;
-		compl->transfer_id = transfer_id;
-		compl->flags = flags;
+	while (ath10k_ce_completed_recv_next(ce_state, &transfer_context,
+					     &ce_data, &nbytes, &transfer_id,
+					     &flags) == 0) {
+		err = ath10k_pci_post_rx_pipe(pipe_info, 1);
+		if (unlikely(err)) {
+			/* FIXME: retry */
+			ath10k_warn("failed to replenish CE rx ring %d: %d\n",
+				    pipe_info->pipe_num, err);
+		}
 
 		skb = transfer_context;
+		max_nbytes = skb->len + skb_tailroom(skb);
 		dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
-				 skb->len + skb_tailroom(skb),
-				 DMA_FROM_DEVICE);
-		/*
-		 * Add the completion to the processing queue.
-		 */
-		spin_lock_bh(&ar_pci->compl_lock);
-		list_add_tail(&compl->list, &ar_pci->compl_process);
-		spin_unlock_bh(&ar_pci->compl_lock);
+				 max_nbytes, DMA_FROM_DEVICE);
 
-	} while (ath10k_ce_completed_recv_next(ce_state,
-							   &transfer_context,
-							   &ce_data, &nbytes,
-							   &transfer_id,
-							   &flags) == 0);
+		if (unlikely(max_nbytes < nbytes)) {
+			ath10k_warn("rxed more than expected (nbytes %d, max %d)",
+				    nbytes, max_nbytes);
+			dev_kfree_skb_any(skb);
+			continue;
+		}
 
-	ath10k_pci_process_ce(ar);
+		skb_put(skb, nbytes);
+		cb->rx_completion(ar, skb, pipe_info->pipe_num);
+	}
 }
 
-/* Send the first nbytes bytes of the buffer */
-static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
-				    unsigned int transfer_id,
-				    unsigned int bytes, struct sk_buff *nbuf)
+static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
+				struct ath10k_hif_sg_item *items, int n_items)
 {
-	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(nbuf);
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe_id]);
-	struct ce_state *ce_hdl = pipe_info->ce_hdl;
-	struct ce_sendlist sendlist;
-	unsigned int len;
-	u32 flags = 0;
-	int ret;
+	struct ath10k_pci_pipe *pci_pipe = &ar_pci->pipe_info[pipe_id];
+	struct ath10k_ce_pipe *ce_pipe = pci_pipe->ce_hdl;
+	struct ath10k_ce_ring *src_ring = ce_pipe->src_ring;
+	unsigned int nentries_mask = src_ring->nentries_mask;
+	unsigned int sw_index = src_ring->sw_index;
+	unsigned int write_index = src_ring->write_index;
+	int err, i;
 
-	memset(&sendlist, 0, sizeof(struct ce_sendlist));
+	spin_lock_bh(&ar_pci->ce_lock);
 
-	len = min(bytes, nbuf->len);
-	bytes -= len;
-
-	if (len & 3)
-		ath10k_warn("skb not aligned to 4-byte boundary (%d)\n", len);
+	if (unlikely(CE_RING_DELTA(nentries_mask,
+				   write_index, sw_index - 1) < n_items)) {
+		err = -ENOBUFS;
+		goto unlock;
+	}
 
+	for (i = 0; i < n_items - 1; i++) {
 	ath10k_dbg(ATH10K_DBG_PCI,
-		   "pci send data vaddr %p paddr 0x%llx len %d as %d bytes\n",
-		   nbuf->data, (unsigned long long) skb_cb->paddr,
-		   nbuf->len, len);
-	ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL,
-			"ath10k tx: data: ",
-			nbuf->data, nbuf->len);
-
-	ath10k_ce_sendlist_buf_add(&sendlist, skb_cb->paddr, len, flags);
-
-	/* Make sure we have resources to handle this request */
-	spin_lock_bh(&pipe_info->pipe_lock);
-	if (!pipe_info->num_sends_allowed) {
-		ath10k_warn("Pipe: %d is full\n", pipe_id);
-		spin_unlock_bh(&pipe_info->pipe_lock);
-		return -ENOSR;
+			   "pci tx item %d paddr 0x%08x len %d n_items %d\n",
+			   i, items[i].paddr, items[i].len, n_items);
+		ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, "item data: ",
+				items[i].vaddr, items[i].len);
+
+		err = ath10k_ce_send_nolock(ce_pipe,
+					    items[i].transfer_context,
+					    items[i].paddr,
+					    items[i].len,
+					    items[i].transfer_id,
+					    CE_SEND_FLAG_GATHER);
+		if (err)
+			goto unlock;
 	}
-	pipe_info->num_sends_allowed--;
-	spin_unlock_bh(&pipe_info->pipe_lock);
 
-	ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, &sendlist, transfer_id);
-	if (ret)
-		ath10k_warn("CE send failed: %p\n", nbuf);
+	/* `i` is equal to `n_items -1` after for() */
 
-	return ret;
+	ath10k_dbg(ATH10K_DBG_PCI,
+		   "pci tx item %d paddr 0x%08x len %d n_items %d\n",
+		   i, items[i].paddr, items[i].len, n_items);
+	ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, "item data: ",
+			items[i].vaddr, items[i].len);
+
+	err = ath10k_ce_send_nolock(ce_pipe,
+				    items[i].transfer_context,
+				    items[i].paddr,
+				    items[i].len,
+				    items[i].transfer_id,
+				    0);
+	if (err)
+		goto unlock;
+
+	err = 0;
+unlock:
+	spin_unlock_bh(&ar_pci->ce_lock);
+	return err;
 }
 
 static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe]);
-	int ret;
-
-	spin_lock_bh(&pipe_info->pipe_lock);
-	ret = pipe_info->num_sends_allowed;
-	spin_unlock_bh(&pipe_info->pipe_lock);
-
-	return ret;
+	return ath10k_ce_num_free_src_entries(ar_pci->pipe_info[pipe].ce_hdl);
 }
 
 static void ath10k_pci_hif_dump_area(struct ath10k *ar)
@@ -687,14 +817,13 @@
 	ath10k_err("firmware crashed!\n");
 	ath10k_err("hardware name %s version 0x%x\n",
 		   ar->hw_params.name, ar->target_version);
-	ath10k_err("firmware version: %u.%u.%u.%u\n", ar->fw_version_major,
-		   ar->fw_version_minor, ar->fw_version_release,
-		   ar->fw_version_build);
+	ath10k_err("firmware version: %s\n", ar->hw->wiphy->fw_version);
 
 	host_addr = host_interest_item_address(HI_ITEM(hi_failure_state));
-	if (ath10k_pci_diag_read_mem(ar, host_addr,
-				     &reg_dump_area, sizeof(u32)) != 0) {
-		ath10k_warn("could not read hi_failure_state\n");
+	ret = ath10k_pci_diag_read_mem(ar, host_addr,
+				       &reg_dump_area, sizeof(u32));
+	if (ret) {
+		ath10k_err("failed to read FW dump area address: %d\n", ret);
 		return;
 	}
 
@@ -704,7 +833,7 @@
 				       &reg_dump_values[0],
 				       REG_DUMP_COUNT_QCA988X * sizeof(u32));
 	if (ret != 0) {
-		ath10k_err("could not dump FW Dump Area\n");
+		ath10k_err("failed to read FW dump area: %d\n", ret);
 		return;
 	}
 
@@ -718,6 +847,8 @@
 			   reg_dump_values[i + 1],
 			   reg_dump_values[i + 2],
 			   reg_dump_values[i + 3]);
+
+	queue_work(ar->workqueue, &ar->restart_work);
 }
 
 static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
@@ -744,7 +875,7 @@
 	ath10k_ce_per_engine_service(ar, pipe);
 }
 
-static void ath10k_pci_hif_post_init(struct ath10k *ar,
+static void ath10k_pci_hif_set_callbacks(struct ath10k *ar,
 				     struct ath10k_hif_cb *callbacks)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -755,205 +886,48 @@
 	       sizeof(ar_pci->msg_callbacks_current));
 }
 
-static int ath10k_pci_start_ce(struct ath10k *ar)
+static int ath10k_pci_setup_ce_irq(struct ath10k *ar)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct ce_state *ce_diag = ar_pci->ce_diag;
 	const struct ce_attr *attr;
-	struct hif_ce_pipe_info *pipe_info;
-	struct ath10k_pci_compl *compl;
-	int i, pipe_num, completions, disable_interrupts;
-
-	spin_lock_init(&ar_pci->compl_lock);
-	INIT_LIST_HEAD(&ar_pci->compl_process);
+	struct ath10k_pci_pipe *pipe_info;
+	int pipe_num, disable_interrupts;
 
-	for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+	for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
 		pipe_info = &ar_pci->pipe_info[pipe_num];
 
-		spin_lock_init(&pipe_info->pipe_lock);
-		INIT_LIST_HEAD(&pipe_info->compl_free);
-
 		/* Handle Diagnostic CE specially */
-		if (pipe_info->ce_hdl == ce_diag)
+		if (pipe_info->ce_hdl == ar_pci->ce_diag)
 			continue;
 
 		attr = &host_ce_config_wlan[pipe_num];
-		completions = 0;
 
 		if (attr->src_nentries) {
 			disable_interrupts = attr->flags & CE_ATTR_DIS_INTR;
 			ath10k_ce_send_cb_register(pipe_info->ce_hdl,
 						   ath10k_pci_ce_send_done,
 						   disable_interrupts);
-			completions += attr->src_nentries;
-			pipe_info->num_sends_allowed = attr->src_nentries - 1;
 		}
 
-		if (attr->dest_nentries) {
+		if (attr->dest_nentries)
 			ath10k_ce_recv_cb_register(pipe_info->ce_hdl,
 						   ath10k_pci_ce_recv_data);
-			completions += attr->dest_nentries;
-		}
-
-		if (completions == 0)
-			continue;
-
-		for (i = 0; i < completions; i++) {
-			compl = kmalloc(sizeof(struct ath10k_pci_compl),
-					GFP_KERNEL);
-			if (!compl) {
-				ath10k_warn("No memory for completion state\n");
-				ath10k_pci_stop_ce(ar);
-				return -ENOMEM;
-			}
-
-			compl->send_or_recv = HIF_CE_COMPLETE_FREE;
-			list_add_tail(&compl->list, &pipe_info->compl_free);
-		}
 	}
 
 	return 0;
 }
 
-static void ath10k_pci_stop_ce(struct ath10k *ar)
+static void ath10k_pci_kill_tasklet(struct ath10k *ar)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct ath10k_pci_compl *compl;
-	struct sk_buff *skb;
 	int i;
 
-	ath10k_ce_disable_interrupts(ar);
-
-	/* Cancel the pending tasklet */
 	tasklet_kill(&ar_pci->intr_tq);
+	tasklet_kill(&ar_pci->msi_fw_err);
+	tasklet_kill(&ar_pci->early_irq_tasklet);
 
 	for (i = 0; i < CE_COUNT; i++)
 		tasklet_kill(&ar_pci->pipe_info[i].intr);
-
-	/* Mark pending completions as aborted, so that upper layers free up
-	 * their associated resources */
-	spin_lock_bh(&ar_pci->compl_lock);
-	list_for_each_entry(compl, &ar_pci->compl_process, list) {
-		skb = (struct sk_buff *)compl->transfer_context;
-		ATH10K_SKB_CB(skb)->is_aborted = true;
-	}
-	spin_unlock_bh(&ar_pci->compl_lock);
-}
-
-static void ath10k_pci_cleanup_ce(struct ath10k *ar)
-{
-	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct ath10k_pci_compl *compl, *tmp;
-	struct hif_ce_pipe_info *pipe_info;
-	struct sk_buff *netbuf;
-	int pipe_num;
-
-	/* Free pending completions. */
-	spin_lock_bh(&ar_pci->compl_lock);
-	if (!list_empty(&ar_pci->compl_process))
-		ath10k_warn("pending completions still present! possible memory leaks.\n");
-
-	list_for_each_entry_safe(compl, tmp, &ar_pci->compl_process, list) {
-		list_del(&compl->list);
-		netbuf = (struct sk_buff *)compl->transfer_context;
-		dev_kfree_skb_any(netbuf);
-		kfree(compl);
-	}
-	spin_unlock_bh(&ar_pci->compl_lock);
-
-	/* Free unused completions for each pipe. */
-	for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
-		pipe_info = &ar_pci->pipe_info[pipe_num];
-
-		spin_lock_bh(&pipe_info->pipe_lock);
-		list_for_each_entry_safe(compl, tmp,
-					 &pipe_info->compl_free, list) {
-			list_del(&compl->list);
-			kfree(compl);
-		}
-		spin_unlock_bh(&pipe_info->pipe_lock);
-	}
-}
-
-static void ath10k_pci_process_ce(struct ath10k *ar)
-{
-	struct ath10k_pci *ar_pci = ar->hif.priv;
-	struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
-	struct ath10k_pci_compl *compl;
-	struct sk_buff *skb;
-	unsigned int nbytes;
-	int ret, send_done = 0;
-
-	/* Upper layers aren't ready to handle tx/rx completions in parallel so
-	 * we must serialize all completion processing. */
-
-	spin_lock_bh(&ar_pci->compl_lock);
-	if (ar_pci->compl_processing) {
-		spin_unlock_bh(&ar_pci->compl_lock);
-		return;
-	}
-	ar_pci->compl_processing = true;
-	spin_unlock_bh(&ar_pci->compl_lock);
-
-	for (;;) {
-		spin_lock_bh(&ar_pci->compl_lock);
-		if (list_empty(&ar_pci->compl_process)) {
-			spin_unlock_bh(&ar_pci->compl_lock);
-			break;
-		}
-		compl = list_first_entry(&ar_pci->compl_process,
-					 struct ath10k_pci_compl, list);
-		list_del(&compl->list);
-		spin_unlock_bh(&ar_pci->compl_lock);
-
-		if (compl->send_or_recv == HIF_CE_COMPLETE_SEND) {
-			cb->tx_completion(ar,
-					  compl->transfer_context,
-					  compl->transfer_id);
-			send_done = 1;
-		} else {
-			ret = ath10k_pci_post_rx_pipe(compl->pipe_info, 1);
-			if (ret) {
-				ath10k_warn("Unable to post recv buffer for pipe: %d\n",
-					    compl->pipe_info->pipe_num);
-				break;
-			}
-
-			skb = (struct sk_buff *)compl->transfer_context;
-			nbytes = compl->nbytes;
-
-			ath10k_dbg(ATH10K_DBG_PCI,
-				   "ath10k_pci_ce_recv_data netbuf=%p  nbytes=%d\n",
-				   skb, nbytes);
-			ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL,
-					"ath10k rx: ", skb->data, nbytes);
-
-			if (skb->len + skb_tailroom(skb) >= nbytes) {
-				skb_trim(skb, 0);
-				skb_put(skb, nbytes);
-				cb->rx_completion(ar, skb,
-						  compl->pipe_info->pipe_num);
-			} else {
-				ath10k_warn("rxed more than expected (nbytes %d, max %d)",
-					    nbytes,
-					    skb->len + skb_tailroom(skb));
-			}
-		}
-
-		compl->send_or_recv = HIF_CE_COMPLETE_FREE;
-
-		/*
-		 * Add completion back to the pipe's free list.
-		 */
-		spin_lock_bh(&compl->pipe_info->pipe_lock);
-		list_add_tail(&compl->list, &compl->pipe_info->compl_free);
-		compl->pipe_info->num_sends_allowed += send_done;
-		spin_unlock_bh(&compl->pipe_info->pipe_lock);
-	}
-
-	spin_lock_bh(&ar_pci->compl_lock);
-	ar_pci->compl_processing = false;
-	spin_unlock_bh(&ar_pci->compl_lock);
 }
 
 /* TODO - temporary mapping while we have too few CE's */
@@ -1031,12 +1005,12 @@
 						 &dl_is_polled);
 }
 
-static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
+static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info,
 				   int num)
 {
 	struct ath10k *ar = pipe_info->hif_ce_state;
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct ce_state *ce_state = pipe_info->ce_hdl;
+	struct ath10k_ce_pipe *ce_state = pipe_info->ce_hdl;
 	struct sk_buff *skb;
 	dma_addr_t ce_data;
 	int i, ret = 0;
@@ -1047,7 +1021,7 @@
 	for (i = 0; i < num; i++) {
 		skb = dev_alloc_skb(pipe_info->buf_sz);
 		if (!skb) {
-			ath10k_warn("could not allocate skbuff for pipe %d\n",
+			ath10k_warn("failed to allocate skbuff for pipe %d\n",
 				    num);
 			ret = -ENOMEM;
 			goto err;
@@ -1060,7 +1034,7 @@
 					 DMA_FROM_DEVICE);
 
 		if (unlikely(dma_mapping_error(ar->dev, ce_data))) {
-			ath10k_warn("could not dma map skbuff\n");
+			ath10k_warn("failed to DMA map sk_buff\n");
 			dev_kfree_skb_any(skb);
 			ret = -EIO;
 			goto err;
@@ -1075,7 +1049,7 @@
 		ret = ath10k_ce_recv_buf_enqueue(ce_state, (void *)skb,
 						 ce_data);
 		if (ret) {
-			ath10k_warn("could not enqueue to pipe %d (%d)\n",
+			ath10k_warn("failed to enqueue to pipe %d: %d\n",
 				    num, ret);
 			goto err;
 		}
@@ -1091,11 +1065,11 @@
 static int ath10k_pci_post_rx(struct ath10k *ar)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct hif_ce_pipe_info *pipe_info;
+	struct ath10k_pci_pipe *pipe_info;
 	const struct ce_attr *attr;
 	int pipe_num, ret = 0;
 
-	for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+	for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
 		pipe_info = &ar_pci->pipe_info[pipe_num];
 		attr = &host_ce_config_wlan[pipe_num];
 
@@ -1105,8 +1079,8 @@
 		ret = ath10k_pci_post_rx_pipe(pipe_info,
 					      attr->dest_nentries - 1);
 		if (ret) {
-			ath10k_warn("Unable to replenish recv buffers for pipe: %d\n",
-				    pipe_num);
+			ath10k_warn("failed to post RX buffer for pipe %d: %d\n",
+				    pipe_num, ret);
 
 			for (; pipe_num >= 0; pipe_num--) {
 				pipe_info = &ar_pci->pipe_info[pipe_num];
@@ -1122,30 +1096,55 @@
 static int ath10k_pci_hif_start(struct ath10k *ar)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	int ret;
+	int ret, ret_early;
 
-	ret = ath10k_pci_start_ce(ar);
+	ath10k_pci_free_early_irq(ar);
+	ath10k_pci_kill_tasklet(ar);
+
+	ret = ath10k_pci_request_irq(ar);
 	if (ret) {
-		ath10k_warn("could not start CE (%d)\n", ret);
-		return ret;
+		ath10k_warn("failed to post RX buffers for all pipes: %d\n",
+			    ret);
+		goto err_early_irq;
+	}
+
+	ret = ath10k_pci_setup_ce_irq(ar);
+	if (ret) {
+		ath10k_warn("failed to setup CE interrupts: %d\n", ret);
+		goto err_stop;
 	}
 
 	/* Post buffers once to start things off. */
 	ret = ath10k_pci_post_rx(ar);
 	if (ret) {
-		ath10k_warn("could not post rx pipes (%d)\n", ret);
-		return ret;
+		ath10k_warn("failed to post RX buffers for all pipes: %d\n",
+			    ret);
+		goto err_stop;
 	}
 
 	ar_pci->started = 1;
 	return 0;
+
+err_stop:
+	ath10k_ce_disable_interrupts(ar);
+	ath10k_pci_free_irq(ar);
+	ath10k_pci_kill_tasklet(ar);
+err_early_irq:
+	/* Though there should be no interrupts (device was reset)
+	 * power_down() expects the early IRQ to be installed as per the
+	 * driver lifecycle. */
+	ret_early = ath10k_pci_request_early_irq(ar);
+	if (ret_early)
+		ath10k_warn("failed to re-enable early irq: %d\n", ret_early);
+
+	return ret;
 }
 
-static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
+static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info)
 {
 	struct ath10k *ar;
 	struct ath10k_pci *ar_pci;
-	struct ce_state *ce_hdl;
+	struct ath10k_ce_pipe *ce_hdl;
 	u32 buf_sz;
 	struct sk_buff *netbuf;
 	u32 ce_data;
@@ -1173,11 +1172,11 @@
 	}
 }
 
-static void ath10k_pci_tx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
+static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info)
 {
 	struct ath10k *ar;
 	struct ath10k_pci *ar_pci;
-	struct ce_state *ce_hdl;
+	struct ath10k_ce_pipe *ce_hdl;
 	struct sk_buff *netbuf;
 	u32 ce_data;
 	unsigned int nbytes;
@@ -1200,12 +1199,10 @@
 
 	while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf,
 					  &ce_data, &nbytes, &id) == 0) {
-		if (netbuf != CE_SENDLIST_ITEM_CTXT)
-			/*
-			 * Indicate the completion to higer layer to free
-			 * the buffer
-			 */
-			ATH10K_SKB_CB(netbuf)->is_aborted = true;
+		/* no need to call tx completion for NULL pointers */
+		if (!netbuf)
+			continue;
+
 			ar_pci->msg_callbacks_current.tx_completion(ar,
 								    netbuf,
 								    id);
@@ -1225,8 +1222,8 @@
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 	int pipe_num;
 
-	for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
-		struct hif_ce_pipe_info *pipe_info;
+	for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
+		struct ath10k_pci_pipe *pipe_info;
 
 		pipe_info = &ar_pci->pipe_info[pipe_num];
 		ath10k_pci_rx_pipe_cleanup(pipe_info);
@@ -1237,10 +1234,10 @@
 static void ath10k_pci_ce_deinit(struct ath10k *ar)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct hif_ce_pipe_info *pipe_info;
+	struct ath10k_pci_pipe *pipe_info;
 	int pipe_num;
 
-	for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+	for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
 		pipe_info = &ar_pci->pipe_info[pipe_num];
 		if (pipe_info->ce_hdl) {
 			ath10k_ce_deinit(pipe_info->ce_hdl);
@@ -1252,18 +1249,36 @@
 
 static void ath10k_pci_hif_stop(struct ath10k *ar)
 {
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ret;
+
 	ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
 
-	ath10k_pci_stop_ce(ar);
+	ret = ath10k_ce_disable_interrupts(ar);
+	if (ret)
+		ath10k_warn("failed to disable CE interrupts: %d\n", ret);
+
+	ath10k_pci_free_irq(ar);
+	ath10k_pci_kill_tasklet(ar);
+
+	ret = ath10k_pci_request_early_irq(ar);
+	if (ret)
+		ath10k_warn("failed to re-enable early irq: %d\n", ret);
 
 	/* At this point, asynchronous threads are stopped, the target should
 	 * not DMA nor interrupt. We process the leftovers and then free
 	 * everything else up. */
 
-	ath10k_pci_process_ce(ar);
-	ath10k_pci_cleanup_ce(ar);
 	ath10k_pci_buffer_cleanup(ar);
-	ath10k_pci_ce_deinit(ar);
+
+	/* Make the sure the device won't access any structures on the host by
+	 * resetting it. The device was fed with PCI CE ringbuffer
+	 * configuration during init. If ringbuffers are freed and the device
+	 * were to access them this could lead to memory corruption on the
+	 * host. */
+	ath10k_pci_warm_reset(ar);
+
+	ar_pci->started = 0;
 }
 
 static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
@@ -1271,14 +1286,18 @@
 					   void *resp, u32 *resp_len)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct ce_state *ce_tx = ar_pci->pipe_info[BMI_CE_NUM_TO_TARG].ce_hdl;
-	struct ce_state *ce_rx = ar_pci->pipe_info[BMI_CE_NUM_TO_HOST].ce_hdl;
+	struct ath10k_pci_pipe *pci_tx = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG];
+	struct ath10k_pci_pipe *pci_rx = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST];
+	struct ath10k_ce_pipe *ce_tx = pci_tx->ce_hdl;
+	struct ath10k_ce_pipe *ce_rx = pci_rx->ce_hdl;
 	dma_addr_t req_paddr = 0;
 	dma_addr_t resp_paddr = 0;
 	struct bmi_xfer xfer = {};
 	void *treq, *tresp = NULL;
 	int ret = 0;
 
+	might_sleep();
+
 	if (resp && !resp_len)
 		return -EINVAL;
 
@@ -1319,14 +1338,12 @@
 	if (ret)
 		goto err_resp;
 
-	ret = wait_for_completion_timeout(&xfer.done,
-					  BMI_COMMUNICATION_TIMEOUT_HZ);
-	if (ret <= 0) {
+	ret = ath10k_pci_bmi_wait(ce_tx, ce_rx, &xfer);
+	if (ret) {
 		u32 unused_buffer;
 		unsigned int unused_nbytes;
 		unsigned int unused_id;
 
-		ret = -ETIMEDOUT;
 		ath10k_ce_cancel_send_next(ce_tx, NULL, &unused_buffer,
 					   &unused_nbytes, &unused_id);
 	} else {
@@ -1356,13 +1373,16 @@
 	return ret;
 }
 
-static void ath10k_pci_bmi_send_done(struct ce_state *ce_state,
-				     void *transfer_context,
-				     u32 data,
-				     unsigned int nbytes,
-				     unsigned int transfer_id)
+static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state)
 {
-	struct bmi_xfer *xfer = transfer_context;
+	struct bmi_xfer *xfer;
+	u32 ce_data;
+	unsigned int nbytes;
+	unsigned int transfer_id;
+
+	if (ath10k_ce_completed_send_next(ce_state, (void **)&xfer, &ce_data,
+					  &nbytes, &transfer_id))
+		return;
 
 	if (xfer->wait_for_resp)
 		return;
@@ -1370,14 +1390,17 @@
 	complete(&xfer->done);
 }
 
-static void ath10k_pci_bmi_recv_data(struct ce_state *ce_state,
-				     void *transfer_context,
-				     u32 data,
-				     unsigned int nbytes,
-				     unsigned int transfer_id,
-				     unsigned int flags)
+static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state)
 {
-	struct bmi_xfer *xfer = transfer_context;
+	struct bmi_xfer *xfer;
+	u32 ce_data;
+	unsigned int nbytes;
+	unsigned int transfer_id;
+	unsigned int flags;
+
+	if (ath10k_ce_completed_recv_next(ce_state, (void **)&xfer, &ce_data,
+					  &nbytes, &transfer_id, &flags))
+		return;
 
 	if (!xfer->wait_for_resp) {
 		ath10k_warn("unexpected: BMI data received; ignoring\n");
@@ -1388,9 +1411,28 @@
 	complete(&xfer->done);
 }
 
-/*
- * Map from service/endpoint to Copy Engine.
- * This table is derived from the CE_PCI TABLE, above.
+static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe,
+			       struct ath10k_ce_pipe *rx_pipe,
+			       struct bmi_xfer *xfer)
+{
+	unsigned long timeout = jiffies + BMI_COMMUNICATION_TIMEOUT_HZ;
+
+	while (time_before_eq(jiffies, timeout)) {
+		ath10k_pci_bmi_send_done(tx_pipe);
+		ath10k_pci_bmi_recv_data(rx_pipe);
+
+		if (completion_done(&xfer->done))
+			return 0;
+
+		schedule();
+	}
+
+	return -ETIMEDOUT;
+}
+
+/*
+ * Map from service/endpoint to Copy Engine.
+ * This table is derived from the CE_PCI TABLE, above.
  * It is passed to the Target at startup for use by firmware.
  */
 static const struct service_to_pipe target_service_to_ce_map_wlan[] = {
@@ -1497,7 +1539,7 @@
 					      CORE_CTRL_ADDRESS,
 					  &core_ctrl);
 	if (ret) {
-		ath10k_warn("Unable to read core ctrl\n");
+		ath10k_warn("failed to read core_ctrl: %d\n", ret);
 		return ret;
 	}
 
@@ -1507,12 +1549,15 @@
 	ret = ath10k_pci_diag_write_access(ar, SOC_CORE_BASE_ADDRESS |
 					       CORE_CTRL_ADDRESS,
 					   core_ctrl);
-	if (ret)
-		ath10k_warn("Unable to set interrupt mask\n");
-
+	if (ret) {
+		ath10k_warn("failed to set target CPU interrupt mask: %d\n",
+			    ret);
 	return ret;
 }
 
+	return 0;
+}
+
 static int ath10k_pci_init_config(struct ath10k *ar)
 {
 	u32 interconnect_targ_addr;
@@ -1657,11 +1702,11 @@
 static int ath10k_pci_ce_init(struct ath10k *ar)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	struct hif_ce_pipe_info *pipe_info;
+	struct ath10k_pci_pipe *pipe_info;
 	const struct ce_attr *attr;
 	int pipe_num;
 
-	for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+	for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
 		pipe_info = &ar_pci->pipe_info[pipe_num];
 		pipe_info->pipe_num = pipe_num;
 		pipe_info->hif_ce_state = ar;
@@ -1669,7 +1714,7 @@
 
 		pipe_info->ce_hdl = ath10k_ce_init(ar, pipe_num, attr);
 		if (pipe_info->ce_hdl == NULL) {
-			ath10k_err("Unable to initialize CE for pipe: %d\n",
+			ath10k_err("failed to initialize CE for pipe: %d\n",
 				   pipe_num);
 
 			/* It is safe to call it here. It checks if ce_hdl is
@@ -1678,31 +1723,18 @@
 			return -1;
 		}
 
-		if (pipe_num == ar_pci->ce_count - 1) {
+		if (pipe_num == CE_COUNT - 1) {
 			/*
 			 * Reserve the ultimate CE for
 			 * diagnostic Window support
 			 */
-			ar_pci->ce_diag =
-			ar_pci->pipe_info[ar_pci->ce_count - 1].ce_hdl;
+			ar_pci->ce_diag = pipe_info->ce_hdl;
 			continue;
 		}
 
 		pipe_info->buf_sz = (size_t) (attr->src_sz_max);
 	}
 
-	/*
-	 * Initially, establish CE completion handlers for use with BMI.
-	 * These are overwritten with generic handlers after we exit BMI phase.
-	 */
-	pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG];
-	ath10k_ce_send_cb_register(pipe_info->ce_hdl,
-				   ath10k_pci_bmi_send_done, 0);
-
-	pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST];
-	ath10k_ce_recv_cb_register(pipe_info->ce_hdl,
-				   ath10k_pci_bmi_recv_data);
-
 	return 0;
 }
 
@@ -1735,21 +1767,303 @@
 	ath10k_pci_sleep(ar);
 }
 
+static int ath10k_pci_warm_reset(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ret = 0;
+	u32 val;
+
+	ath10k_dbg(ATH10K_DBG_BOOT, "boot performing warm chip reset\n");
+
+	ret = ath10k_do_pci_wake(ar);
+	if (ret) {
+		ath10k_err("failed to wake up target: %d\n", ret);
+		return ret;
+	}
+
+	/* debug */
+	val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+				PCIE_INTR_CAUSE_ADDRESS);
+	ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val);
+
+	val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+				CPU_INTR_ADDRESS);
+	ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
+		   val);
+
+	/* disable pending irqs */
+	ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
+			   PCIE_INTR_ENABLE_ADDRESS, 0);
+
+	ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
+			   PCIE_INTR_CLR_ADDRESS, ~0);
+
+	msleep(100);
+
+	/* clear fw indicator */
+	ath10k_pci_write32(ar, ar_pci->fw_indicator_address, 0);
+
+	/* clear target LF timer interrupts */
+	val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+				SOC_LF_TIMER_CONTROL0_ADDRESS);
+	ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS +
+			   SOC_LF_TIMER_CONTROL0_ADDRESS,
+			   val & ~SOC_LF_TIMER_CONTROL0_ENABLE_MASK);
+
+	/* reset CE */
+	val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+				SOC_RESET_CONTROL_ADDRESS);
+	ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+			   val | SOC_RESET_CONTROL_CE_RST_MASK);
+	val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+				SOC_RESET_CONTROL_ADDRESS);
+	msleep(10);
+
+	/* unreset CE */
+	ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+			   val & ~SOC_RESET_CONTROL_CE_RST_MASK);
+	val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+				SOC_RESET_CONTROL_ADDRESS);
+	msleep(10);
+
+	/* debug */
+	val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+				PCIE_INTR_CAUSE_ADDRESS);
+	ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val);
+
+	val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+				CPU_INTR_ADDRESS);
+	ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
+		   val);
+
+	/* CPU warm reset */
+	val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+				SOC_RESET_CONTROL_ADDRESS);
+	ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+			   val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK);
+
+	val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+				SOC_RESET_CONTROL_ADDRESS);
+	ath10k_dbg(ATH10K_DBG_BOOT, "boot target reset state: 0x%08x\n", val);
+
+	msleep(100);
+
+	ath10k_dbg(ATH10K_DBG_BOOT, "boot warm reset complete\n");
+
+	ath10k_do_pci_sleep(ar);
+	return ret;
+}
+
+static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	const char *irq_mode;
+	int ret;
+
+	/*
+	 * Bring the target up cleanly.
+	 *
+	 * The target may be in an undefined state with an AUX-powered Target
+	 * and a Host in WoW mode. If the Host crashes, loses power, or is
+	 * restarted (without unloading the driver) then the Target is left
+	 * (aux) powered and running. On a subsequent driver load, the Target
+	 * is in an unexpected state. We try to catch that here in order to
+	 * reset the Target and retry the probe.
+	 */
+	if (cold_reset)
+		ret = ath10k_pci_cold_reset(ar);
+	else
+		ret = ath10k_pci_warm_reset(ar);
+
+	if (ret) {
+		ath10k_err("failed to reset target: %d\n", ret);
+		goto err;
+	}
+
+	if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
+		/* Force AWAKE forever */
+		ath10k_do_pci_wake(ar);
+
+	ret = ath10k_pci_ce_init(ar);
+	if (ret) {
+		ath10k_err("failed to initialize CE: %d\n", ret);
+		goto err_ps;
+	}
+
+	ret = ath10k_ce_disable_interrupts(ar);
+	if (ret) {
+		ath10k_err("failed to disable CE interrupts: %d\n", ret);
+		goto err_ce;
+	}
+
+	ret = ath10k_pci_init_irq(ar);
+	if (ret) {
+		ath10k_err("failed to init irqs: %d\n", ret);
+		goto err_ce;
+	}
+
+	ret = ath10k_pci_request_early_irq(ar);
+	if (ret) {
+		ath10k_err("failed to request early irq: %d\n", ret);
+		goto err_deinit_irq;
+	}
+
+	ret = ath10k_pci_wait_for_target_init(ar);
+	if (ret) {
+		ath10k_err("failed to wait for target to init: %d\n", ret);
+		goto err_free_early_irq;
+	}
+
+	ret = ath10k_pci_init_config(ar);
+	if (ret) {
+		ath10k_err("failed to setup init config: %d\n", ret);
+		goto err_free_early_irq;
+	}
+
+	ret = ath10k_pci_wake_target_cpu(ar);
+	if (ret) {
+		ath10k_err("could not wake up target CPU: %d\n", ret);
+		goto err_free_early_irq;
+	}
+
+	if (ar_pci->num_msi_intrs > 1)
+		irq_mode = "MSI-X";
+	else if (ar_pci->num_msi_intrs == 1)
+		irq_mode = "MSI";
+	else
+		irq_mode = "legacy";
+
+	if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags))
+		ath10k_info("pci irq %s\n", irq_mode);
+
+	return 0;
+
+err_free_early_irq:
+	ath10k_pci_free_early_irq(ar);
+err_deinit_irq:
+	ath10k_pci_deinit_irq(ar);
+err_ce:
+	ath10k_pci_ce_deinit(ar);
+	ath10k_pci_warm_reset(ar);
+err_ps:
+	if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
+		ath10k_do_pci_sleep(ar);
+err:
+	return ret;
+}
+
+static int ath10k_pci_hif_power_up(struct ath10k *ar)
+{
+	int ret;
+
+	/*
+	 * Hardware CUS232 version 2 has some issues with cold reset and the
+	 * preferred (and safer) way to perform a device reset is through a
+	 * warm reset.
+	 *
+	 * Warm reset doesn't always work though (notably after a firmware
+	 * crash) so fall back to cold reset if necessary.
+	 */
+	ret = __ath10k_pci_hif_power_up(ar, false);
+	if (ret) {
+		ath10k_warn("failed to power up target using warm reset (%d), trying cold reset\n",
+			    ret);
+
+		ret = __ath10k_pci_hif_power_up(ar, true);
+		if (ret) {
+			ath10k_err("failed to power up target using cold reset too (%d)\n",
+				   ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void ath10k_pci_hif_power_down(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	ath10k_pci_free_early_irq(ar);
+	ath10k_pci_kill_tasklet(ar);
+	ath10k_pci_deinit_irq(ar);
+	ath10k_pci_warm_reset(ar);
+
+	ath10k_pci_ce_deinit(ar);
+	if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
+		ath10k_do_pci_sleep(ar);
+}
+
+#ifdef CONFIG_PM
+
+#define ATH10K_PCI_PM_CONTROL 0x44
+
+static int ath10k_pci_hif_suspend(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct pci_dev *pdev = ar_pci->pdev;
+	u32 val;
+
+	pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+	if ((val & 0x000000ff) != 0x3) {
+		pci_save_state(pdev);
+		pci_disable_device(pdev);
+		pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+				       (val & 0xffffff00) | 0x03);
+	}
+
+	return 0;
+}
+
+static int ath10k_pci_hif_resume(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct pci_dev *pdev = ar_pci->pdev;
+	u32 val;
+
+	pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+	if ((val & 0x000000ff) != 0) {
+		pci_restore_state(pdev);
+		pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+				       val & 0xffffff00);
+		/*
+		 * Suspend/Resume resets the PCI configuration space,
+		 * so we have to re-disable the RETRY_TIMEOUT register (0x41)
+		 * to keep PCI Tx retries from interfering with C3 CPU state
+		 */
+		pci_read_config_dword(pdev, 0x40, &val);
+
+		if ((val & 0x0000ff00) != 0)
+			pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+	}
+
+	return 0;
+}
+#endif
+
 static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
-	.send_head		= ath10k_pci_hif_send_head,
+	.tx_sg			= ath10k_pci_hif_tx_sg,
 	.exchange_bmi_msg	= ath10k_pci_hif_exchange_bmi_msg,
 	.start			= ath10k_pci_hif_start,
 	.stop			= ath10k_pci_hif_stop,
 	.map_service_to_pipe	= ath10k_pci_hif_map_service_to_pipe,
 	.get_default_pipe	= ath10k_pci_hif_get_default_pipe,
 	.send_complete_check	= ath10k_pci_hif_send_complete_check,
-	.init			= ath10k_pci_hif_post_init,
+	.set_callbacks		= ath10k_pci_hif_set_callbacks,
 	.get_free_queue_number	= ath10k_pci_hif_get_free_queue_number,
+	.power_up		= ath10k_pci_hif_power_up,
+	.power_down		= ath10k_pci_hif_power_down,
+#ifdef CONFIG_PM
+	.suspend		= ath10k_pci_hif_suspend,
+	.resume			= ath10k_pci_hif_resume,
+#endif
 };
 
 static void ath10k_pci_ce_tasklet(unsigned long ptr)
 {
-	struct hif_ce_pipe_info *pipe = (struct hif_ce_pipe_info *)ptr;
+	struct ath10k_pci_pipe *pipe = (struct ath10k_pci_pipe *)ptr;
 	struct ath10k_pci *ar_pci = pipe->ar_pci;
 
 	ath10k_ce_per_engine_service(ar_pci->ar, pipe->pipe_num);
@@ -1809,25 +2123,10 @@
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
 	if (ar_pci->num_msi_intrs == 0) {
-		/*
-		 * IMPORTANT: INTR_CLR regiser has to be set after
-		 * INTR_ENABLE is set to 0, otherwise interrupt can not be
-		 * really cleared.
-		 */
-		iowrite32(0, ar_pci->mem +
-			  (SOC_CORE_BASE_ADDRESS |
-			   PCIE_INTR_ENABLE_ADDRESS));
-		iowrite32(PCIE_INTR_FIRMWARE_MASK |
-			  PCIE_INTR_CE_MASK_ALL,
-			  ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
-					 PCIE_INTR_CLR_ADDRESS));
-		/*
-		 * IMPORTANT: this extra read transaction is required to
-		 * flush the posted write buffer.
-		 */
-		(void) ioread32(ar_pci->mem +
-				(SOC_CORE_BASE_ADDRESS |
-				 PCIE_INTR_ENABLE_ADDRESS));
+		if (!ath10k_pci_irq_pending(ar))
+			return IRQ_NONE;
+
+		ath10k_pci_disable_and_clear_legacy_irq(ar);
 	}
 
 	tasklet_schedule(&ar_pci->intr_tq);
@@ -1835,6 +2134,34 @@
 	return IRQ_HANDLED;
 }
 
+static void ath10k_pci_early_irq_tasklet(unsigned long data)
+{
+	struct ath10k *ar = (struct ath10k *)data;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	u32 fw_ind;
+	int ret;
+
+	ret = ath10k_pci_wake(ar);
+	if (ret) {
+		ath10k_warn("failed to wake target in early irq tasklet: %d\n",
+			    ret);
+		return;
+	}
+
+	fw_ind = ath10k_pci_read32(ar, ar_pci->fw_indicator_address);
+	if (fw_ind & FW_IND_EVENT_PENDING) {
+		ath10k_pci_write32(ar, ar_pci->fw_indicator_address,
+				   fw_ind & ~FW_IND_EVENT_PENDING);
+
+		/* Some structures are unavailable during early boot or at
+		 * driver teardown so just print that the device has crashed. */
+		ath10k_warn("device crashed - no diagnostics available\n");
+	}
+
+	ath10k_pci_sleep(ar);
+	ath10k_pci_enable_legacy_irq(ar);
+}
+
 static void ath10k_pci_tasklet(unsigned long data)
 {
 	struct ath10k *ar = (struct ath10k *)data;
@@ -1843,81 +2170,62 @@
 	ath10k_pci_fw_interrupt_handler(ar); /* FIXME: Handle FW error */
 	ath10k_ce_per_engine_service_any(ar);
 
-	if (ar_pci->num_msi_intrs == 0) {
-		/* Enable Legacy PCI line interrupts */
-		iowrite32(PCIE_INTR_FIRMWARE_MASK |
-			  PCIE_INTR_CE_MASK_ALL,
-			  ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
-					 PCIE_INTR_ENABLE_ADDRESS));
-		/*
-		 * IMPORTANT: this extra read transaction is required to
-		 * flush the posted write buffer
-		 */
-		(void) ioread32(ar_pci->mem +
-				(SOC_CORE_BASE_ADDRESS |
-				 PCIE_INTR_ENABLE_ADDRESS));
-	}
+	/* Re-enable legacy irq that was disabled in the irq handler */
+	if (ar_pci->num_msi_intrs == 0)
+		ath10k_pci_enable_legacy_irq(ar);
 }
 
-static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num)
+static int ath10k_pci_request_irq_msix(struct ath10k *ar)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	int ret;
-	int i;
-
-	ret = pci_enable_msi_block(ar_pci->pdev, num);
-	if (ret)
-		return ret;
+	int ret, i;
 
 	ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW,
 			  ath10k_pci_msi_fw_handler,
 			  IRQF_SHARED, "ath10k_pci", ar);
-	if (ret)
+	if (ret) {
+		ath10k_warn("failed to request MSI-X fw irq %d: %d\n",
+			    ar_pci->pdev->irq + MSI_ASSIGN_FW, ret);
 		return ret;
+	}
 
 	for (i = MSI_ASSIGN_CE_INITIAL; i <= MSI_ASSIGN_CE_MAX; i++) {
 		ret = request_irq(ar_pci->pdev->irq + i,
 				  ath10k_pci_per_engine_handler,
 				  IRQF_SHARED, "ath10k_pci", ar);
 		if (ret) {
-			ath10k_warn("request_irq(%d) failed %d\n",
+			ath10k_warn("failed to request MSI-X ce irq %d: %d\n",
 				    ar_pci->pdev->irq + i, ret);
 
 			for (i--; i >= MSI_ASSIGN_CE_INITIAL; i--)
 				free_irq(ar_pci->pdev->irq + i, ar);
 
 			free_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW, ar);
-			pci_disable_msi(ar_pci->pdev);
 			return ret;
 		}
 	}
 
-	ath10k_info("MSI-X interrupt handling (%d intrs)\n", num);
 	return 0;
 }
 
-static int ath10k_pci_start_intr_msi(struct ath10k *ar)
+static int ath10k_pci_request_irq_msi(struct ath10k *ar)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 	int ret;
 
-	ret = pci_enable_msi(ar_pci->pdev);
-	if (ret < 0)
-		return ret;
-
 	ret = request_irq(ar_pci->pdev->irq,
 			  ath10k_pci_interrupt_handler,
 			  IRQF_SHARED, "ath10k_pci", ar);
-	if (ret < 0) {
-		pci_disable_msi(ar_pci->pdev);
+	if (ret) {
+		ath10k_warn("failed to request MSI irq %d: %d\n",
+			    ar_pci->pdev->irq, ret);
 		return ret;
 	}
 
-	ath10k_info("MSI interrupt handling\n");
 	return 0;
 }
 
-static int ath10k_pci_start_intr_legacy(struct ath10k *ar)
+static int ath10k_pci_request_irq_legacy(struct ath10k *ar)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 	int ret;
@@ -1925,112 +2233,165 @@
 	ret = request_irq(ar_pci->pdev->irq,
 			  ath10k_pci_interrupt_handler,
 			  IRQF_SHARED, "ath10k_pci", ar);
-	if (ret < 0)
+	if (ret) {
+		ath10k_warn("failed to request legacy irq %d: %d\n",
+			    ar_pci->pdev->irq, ret);
 		return ret;
+	}
 
-	/*
-	 * Make sure to wake the Target before enabling Legacy
-	 * Interrupt.
-	 */
-	iowrite32(PCIE_SOC_WAKE_V_MASK,
-		  ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
-		  PCIE_SOC_WAKE_ADDRESS);
+	return 0;
+}
 
-	ath10k_pci_wait(ar);
+static int ath10k_pci_request_irq(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
-	/*
-	 * A potential race occurs here: The CORE_BASE write
-	 * depends on target correctly decoding AXI address but
-	 * host won't know when target writes BAR to CORE_CTRL.
-	 * This write might get lost if target has NOT written BAR.
-	 * For now, fix the race by repeating the write in below
-	 * synchronization checking.
-	 */
-	iowrite32(PCIE_INTR_FIRMWARE_MASK |
-		  PCIE_INTR_CE_MASK_ALL,
-		  ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
-				 PCIE_INTR_ENABLE_ADDRESS));
-	iowrite32(PCIE_SOC_WAKE_RESET,
-		  ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
-		  PCIE_SOC_WAKE_ADDRESS);
+	switch (ar_pci->num_msi_intrs) {
+	case 0:
+		return ath10k_pci_request_irq_legacy(ar);
+	case 1:
+		return ath10k_pci_request_irq_msi(ar);
+	case MSI_NUM_REQUEST:
+		return ath10k_pci_request_irq_msix(ar);
+	}
 
-	ath10k_info("legacy interrupt handling\n");
-	return 0;
+	ath10k_warn("unknown irq configuration upon request\n");
+	return -EINVAL;
 }
 
-static int ath10k_pci_start_intr(struct ath10k *ar)
+static void ath10k_pci_free_irq(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int i;
+
+	/* There's at least one interrupt irregardless whether its legacy INTR
+	 * or MSI or MSI-X */
+	for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
+		free_irq(ar_pci->pdev->irq + i, ar);
+}
+
+static void ath10k_pci_init_irq_tasklets(struct ath10k *ar)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	int num = MSI_NUM_REQUEST;
-	int ret;
 	int i;
 
 	tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long) ar);
 	tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet,
 		     (unsigned long) ar);
+	tasklet_init(&ar_pci->early_irq_tasklet, ath10k_pci_early_irq_tasklet,
+		     (unsigned long)ar);
 
 	for (i = 0; i < CE_COUNT; i++) {
 		ar_pci->pipe_info[i].ar_pci = ar_pci;
-		tasklet_init(&ar_pci->pipe_info[i].intr,
-			     ath10k_pci_ce_tasklet,
+		tasklet_init(&ar_pci->pipe_info[i].intr, ath10k_pci_ce_tasklet,
 			     (unsigned long)&ar_pci->pipe_info[i]);
 	}
+}
 
-	if (!test_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features))
-		num = 1;
+static int ath10k_pci_init_irq(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	bool msix_supported = test_bit(ATH10K_PCI_FEATURE_MSI_X,
+				       ar_pci->features);
+	int ret;
 
-	if (num > 1) {
-		ret = ath10k_pci_start_intr_msix(ar, num);
+	ath10k_pci_init_irq_tasklets(ar);
+
+	if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_AUTO &&
+	    !test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags))
+		ath10k_info("limiting irq mode to: %d\n", ath10k_pci_irq_mode);
+
+	/* Try MSI-X */
+	if (ath10k_pci_irq_mode == ATH10K_PCI_IRQ_AUTO && msix_supported) {
+		ar_pci->num_msi_intrs = MSI_NUM_REQUEST;
+		ret = pci_enable_msi_block(ar_pci->pdev, ar_pci->num_msi_intrs);
 		if (ret == 0)
-			goto exit;
+			return 0;
 
-		ath10k_warn("MSI-X didn't succeed (%d), trying MSI\n", ret);
-		num = 1;
+		/* fall-through */
 	}
 
-	if (num == 1) {
-		ret = ath10k_pci_start_intr_msi(ar);
+	/* Try MSI */
+	if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_LEGACY) {
+		ar_pci->num_msi_intrs = 1;
+		ret = pci_enable_msi(ar_pci->pdev);
 		if (ret == 0)
-			goto exit;
+			return 0;
 
-		ath10k_warn("MSI didn't succeed (%d), trying legacy INTR\n",
-			    ret);
-		num = 0;
+		/* fall-through */
 	}
 
-	ret = ath10k_pci_start_intr_legacy(ar);
+	/* Try legacy irq
+	 *
+	 * A potential race occurs here: The CORE_BASE write
+	 * depends on target correctly decoding AXI address but
+	 * host won't know when target writes BAR to CORE_CTRL.
+	 * This write might get lost if target has NOT written BAR.
+	 * For now, fix the race by repeating the write in below
+	 * synchronization checking. */
+	ar_pci->num_msi_intrs = 0;
 
-exit:
-	ar_pci->num_msi_intrs = num;
-	ar_pci->ce_count = CE_COUNT;
+	ret = ath10k_pci_wake(ar);
+	if (ret) {
+		ath10k_warn("failed to wake target: %d\n", ret);
 	return ret;
 }
 
-static void ath10k_pci_stop_intr(struct ath10k *ar)
+	ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS,
+			   PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL);
+	ath10k_pci_sleep(ar);
+
+	return 0;
+}
+
+static int ath10k_pci_deinit_irq_legacy(struct ath10k *ar)
 {
-	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	int i;
+	int ret;
 
-	/* There's at least one interrupt irregardless whether its legacy INTR
-	 * or MSI or MSI-X */
-	for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
-		free_irq(ar_pci->pdev->irq + i, ar);
+	ret = ath10k_pci_wake(ar);
+	if (ret) {
+		ath10k_warn("failed to wake target: %d\n", ret);
+		return ret;
+	}
+
+	ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS,
+			   0);
+	ath10k_pci_sleep(ar);
+
+	return 0;
+}
 
-	if (ar_pci->num_msi_intrs > 0)
+static int ath10k_pci_deinit_irq(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	switch (ar_pci->num_msi_intrs) {
+	case 0:
+		return ath10k_pci_deinit_irq_legacy(ar);
+	case 1:
+		/* fall-through */
+	case MSI_NUM_REQUEST:
+		pci_disable_msi(ar_pci->pdev);
+		return 0;
+	default:
 		pci_disable_msi(ar_pci->pdev);
 }
 
-static int ath10k_pci_reset_target(struct ath10k *ar)
+	ath10k_warn("unknown irq configuration upon deinit\n");
+	return -EINVAL;
+}
+
+static int ath10k_pci_wait_for_target_init(struct ath10k *ar)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 	int wait_limit = 300; /* 3 sec */
+	int ret;
 
-	/* Wait for Target to finish initialization before we proceed. */
-	iowrite32(PCIE_SOC_WAKE_V_MASK,
-		  ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
-		  PCIE_SOC_WAKE_ADDRESS);
-
-	ath10k_pci_wait(ar);
+	ret = ath10k_pci_wake(ar);
+	if (ret) {
+		ath10k_err("failed to wake up target: %d\n", ret);
+		return ret;
+	}
 
 	while (wait_limit-- &&
 	       !(ioread32(ar_pci->mem + FW_INDICATOR_ADDRESS) &
@@ -2045,48 +2406,35 @@
 	}
 
 	if (wait_limit < 0) {
-		ath10k_err("Target stalled\n");
-		iowrite32(PCIE_SOC_WAKE_RESET,
-			  ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
-			  PCIE_SOC_WAKE_ADDRESS);
-		return -EIO;
+		ath10k_err("target stalled\n");
+		ret = -EIO;
+		goto out;
 	}
 
-	iowrite32(PCIE_SOC_WAKE_RESET,
-		  ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
-		  PCIE_SOC_WAKE_ADDRESS);
-
-	return 0;
+out:
+	ath10k_pci_sleep(ar);
+	return ret;
 }
 
-static void ath10k_pci_device_reset(struct ath10k_pci *ar_pci)
+static int ath10k_pci_cold_reset(struct ath10k *ar)
 {
-	struct ath10k *ar = ar_pci->ar;
-	void __iomem *mem = ar_pci->mem;
-	int i;
+	int i, ret;
 	u32 val;
 
-	if (!SOC_GLOBAL_RESET_ADDRESS)
-		return;
-
-	if (!mem)
-		return;
-
-	ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS,
-			       PCIE_SOC_WAKE_V_MASK);
-	for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
-		if (ath10k_pci_target_is_awake(ar))
-			break;
-		msleep(1);
+	ret = ath10k_do_pci_wake(ar);
+	if (ret) {
+		ath10k_err("failed to wake up target: %d\n",
+			   ret);
+		return ret;
 	}
 
 	/* Put Target, including PCIe, into RESET. */
-	val = ath10k_pci_reg_read32(mem, SOC_GLOBAL_RESET_ADDRESS);
+	val = ath10k_pci_reg_read32(ar, SOC_GLOBAL_RESET_ADDRESS);
 	val |= 1;
-	ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val);
+	ath10k_pci_reg_write32(ar, SOC_GLOBAL_RESET_ADDRESS, val);
 
 	for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
-		if (ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) &
+		if (ath10k_pci_reg_read32(ar, RTC_STATE_ADDRESS) &
 					  RTC_STATE_COLD_RESET_MASK)
 			break;
 		msleep(1);
@@ -2094,16 +2442,17 @@
 
 	/* Pull Target, including PCIe, out of RESET. */
 	val &= ~1;
-	ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val);
+	ath10k_pci_reg_write32(ar, SOC_GLOBAL_RESET_ADDRESS, val);
 
 	for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
-		if (!(ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) &
+		if (!(ath10k_pci_reg_read32(ar, RTC_STATE_ADDRESS) &
 					    RTC_STATE_COLD_RESET_MASK))
 			break;
 		msleep(1);
 	}
 
-	ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET);
+	ath10k_do_pci_sleep(ar);
+	return 0;
 }
 
 static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
@@ -2116,10 +2465,10 @@
 
 		switch (i) {
 		case ATH10K_PCI_FEATURE_MSI_X:
-			ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n");
+			ath10k_dbg(ATH10K_DBG_BOOT, "device supports MSI-X\n");
 			break;
-		case ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND:
-			ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n");
+		case ATH10K_PCI_FEATURE_SOC_POWER_SAVE:
+			ath10k_dbg(ATH10K_DBG_BOOT, "QCA98XX SoC power save enabled\n");
 			break;
 		}
 	}
@@ -2132,7 +2481,7 @@
 	int ret = 0;
 	struct ath10k *ar;
 	struct ath10k_pci *ar_pci;
-	u32 lcr_val;
+	u32 lcr_val, chip_id;
 
 	ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
 
@@ -2144,9 +2493,6 @@
 	ar_pci->dev = &pdev->dev;
 
 	switch (pci_dev->device) {
-	case QCA988X_1_0_DEVICE_ID:
-		set_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features);
-		break;
 	case QCA988X_2_0_DEVICE_ID:
 		set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features);
 		break;
@@ -2156,20 +2502,18 @@
 		goto err_ar_pci;
 	}
 
+	if (ath10k_target_ps)
+		set_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features);
+
 	ath10k_pci_dump_features(ar_pci);
 
-	ar = ath10k_core_create(ar_pci, ar_pci->dev, ATH10K_BUS_PCI,
-				&ath10k_pci_hif_ops);
+	ar = ath10k_core_create(ar_pci, ar_pci->dev, &ath10k_pci_hif_ops);
 	if (!ar) {
-		ath10k_err("ath10k_core_create failed!\n");
+		ath10k_err("failed to create driver core\n");
 		ret = -EINVAL;
 		goto err_ar_pci;
 	}
 
-	/* Enable QCA988X_1.0 HW workarounds */
-	if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features))
-		spin_lock_init(&ar_pci->hw_v1_workaround_lock);
-
 	ar_pci->ar = ar;
 	ar_pci->fw_indicator_address = FW_INDICATOR_ADDRESS;
 	atomic_set(&ar_pci->keep_awake_count, 0);
@@ -2184,20 +2528,20 @@
 	 */
 	ret = pci_assign_resource(pdev, BAR_NUM);
 	if (ret) {
-		ath10k_err("cannot assign PCI space: %d\n", ret);
+		ath10k_err("failed to assign PCI space: %d\n", ret);
 		goto err_ar;
 	}
 
 	ret = pci_enable_device(pdev);
 	if (ret) {
-		ath10k_err("cannot enable PCI device: %d\n", ret);
+		ath10k_err("failed to enable PCI device: %d\n", ret);
 		goto err_ar;
 	}
 
 	/* Request MMIO resources */
 	ret = pci_request_region(pdev, BAR_NUM, "ath");
 	if (ret) {
-		ath10k_err("PCI MMIO reservation error: %d\n", ret);
+		ath10k_err("failed to request MMIO region: %d\n", ret);
 		goto err_device;
 	}
 
@@ -2207,13 +2551,13 @@
 	 */
 	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
 	if (ret) {
-		ath10k_err("32-bit DMA not available: %d\n", ret);
+		ath10k_err("failed to set DMA mask to 32-bit: %d\n", ret);
 		goto err_region;
 	}
 
 	ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
 	if (ret) {
-		ath10k_err("cannot enable 32-bit consistent DMA\n");
+		ath10k_err("failed to set consistent DMA mask to 32-bit\n");
 		goto err_region;
 	}
 
@@ -2230,7 +2574,7 @@
 	/* Arrange for access to Target SoC registers. */
 	mem = pci_iomap(pdev, BAR_NUM, 0);
 	if (!mem) {
-		ath10k_err("PCI iomap error\n");
+		ath10k_err("failed to perform IOMAP for BAR%d\n", BAR_NUM);
 		ret = -EIO;
 		goto err_master;
 	}
@@ -2239,64 +2583,26 @@
 
 	spin_lock_init(&ar_pci->ce_lock);
 
-	ar_pci->cacheline_sz = dma_get_cache_alignment();
-
-	ret = ath10k_pci_start_intr(ar);
+	ret = ath10k_do_pci_wake(ar);
 	if (ret) {
-		ath10k_err("could not start interrupt handling (%d)\n", ret);
+		ath10k_err("Failed to get chip id: %d\n", ret);
 		goto err_iomap;
 	}
 
-	/*
-	 * Bring the target up cleanly.
-	 *
-	 * The target may be in an undefined state with an AUX-powered Target
-	 * and a Host in WoW mode. If the Host crashes, loses power, or is
-	 * restarted (without unloading the driver) then the Target is left
-	 * (aux) powered and running. On a subsequent driver load, the Target
-	 * is in an unexpected state. We try to catch that here in order to
-	 * reset the Target and retry the probe.
-	 */
-	ath10k_pci_device_reset(ar_pci);
+	chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS);
 
-	ret = ath10k_pci_reset_target(ar);
-	if (ret)
-		goto err_intr;
+	ath10k_do_pci_sleep(ar);
 
-	if (ath10k_target_ps) {
-		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
-	} else {
-		/* Force AWAKE forever */
-		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
-		ath10k_do_pci_wake(ar);
-	}
-
-	ret = ath10k_pci_ce_init(ar);
-	if (ret)
-		goto err_intr;
-
-	ret = ath10k_pci_init_config(ar);
-	if (ret)
-		goto err_ce;
-
-	ret = ath10k_pci_wake_target_cpu(ar);
-	if (ret) {
-		ath10k_err("could not wake up target CPU (%d)\n", ret);
-		goto err_ce;
-	}
+	ath10k_dbg(ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem);
 
-	ret = ath10k_core_register(ar);
+	ret = ath10k_core_register(ar, chip_id);
 	if (ret) {
-		ath10k_err("could not register driver core (%d)\n", ret);
-		goto err_ce;
+		ath10k_err("failed to register driver core: %d\n", ret);
+		goto err_iomap;
 	}
 
 	return 0;
 
-err_ce:
-	ath10k_pci_ce_deinit(ar);
-err_intr:
-	ath10k_pci_stop_intr(ar);
 err_iomap:
 	pci_iounmap(pdev, mem);
 err_master:
@@ -2306,7 +2612,6 @@
 err_device:
 	pci_disable_device(pdev);
 err_ar:
-	pci_set_drvdata(pdev, NULL);
 	ath10k_core_destroy(ar);
 err_ar_pci:
 	/* call HIF PCI free here */
@@ -2333,9 +2638,7 @@
 	tasklet_kill(&ar_pci->msi_fw_err);
 
 	ath10k_core_unregister(ar);
-	ath10k_pci_stop_intr(ar);
 
-	pci_set_drvdata(pdev, NULL);
 	pci_iounmap(pdev, ar_pci->mem);
 	pci_release_region(pdev, BAR_NUM);
 	pci_clear_master(pdev);
@@ -2345,128 +2648,6 @@
 	kfree(ar_pci);
 }
 
-#if defined(CONFIG_PM_SLEEP)
-
-#define ATH10K_PCI_PM_CONTROL 0x44
-
-static int ath10k_pci_suspend(struct device *device)
-{
-	struct pci_dev *pdev = to_pci_dev(device);
-	struct ath10k *ar = pci_get_drvdata(pdev);
-	struct ath10k_pci *ar_pci;
-	u32 val;
-	int ret, retval;
-
-	ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
-
-	if (!ar)
-		return -ENODEV;
-
-	ar_pci = ath10k_pci_priv(ar);
-	if (!ar_pci)
-		return -ENODEV;
-
-	if (ath10k_core_target_suspend(ar))
-		return -EBUSY;
-
-	ret = wait_event_interruptible_timeout(ar->event_queue,
-						ar->is_target_paused == true,
-						1 * HZ);
-	if (ret < 0) {
-		ath10k_warn("suspend interrupted (%d)\n", ret);
-		retval = ret;
-		goto resume;
-	} else if (ret == 0) {
-		ath10k_warn("suspend timed out - target pause event never came\n");
-		retval = EIO;
-		goto resume;
-	}
-
-	/*
-	 * reset is_target_paused and host can check that in next time,
-	 * or it will always be TRUE and host just skip the waiting
-	 * condition, it causes target assert due to host already
-	 * suspend
-	 */
-	ar->is_target_paused = false;
-
-	pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
-
-	if ((val & 0x000000ff) != 0x3) {
-		pci_save_state(pdev);
-		pci_disable_device(pdev);
-		pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
-				       (val & 0xffffff00) | 0x03);
-	}
-
-	return 0;
-resume:
-	ret = ath10k_core_target_resume(ar);
-	if (ret)
-		ath10k_warn("could not resume (%d)\n", ret);
-
-	return retval;
-}
-
-static int ath10k_pci_resume(struct device *device)
-{
-	struct pci_dev *pdev = to_pci_dev(device);
-	struct ath10k *ar = pci_get_drvdata(pdev);
-	struct ath10k_pci *ar_pci;
-	int ret;
-	u32 val;
-
-	ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
-
-	if (!ar)
-		return -ENODEV;
-	ar_pci = ath10k_pci_priv(ar);
-
-	if (!ar_pci)
-		return -ENODEV;
-
-	ret = pci_enable_device(pdev);
-	if (ret) {
-		ath10k_warn("cannot enable PCI device: %d\n", ret);
-		return ret;
-	}
-
-	pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
-
-	if ((val & 0x000000ff) != 0) {
-		pci_restore_state(pdev);
-		pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
-				       val & 0xffffff00);
-		/*
-		 * Suspend/Resume resets the PCI configuration space,
-		 * so we have to re-disable the RETRY_TIMEOUT register (0x41)
-		 * to keep PCI Tx retries from interfering with C3 CPU state
-		 */
-		pci_read_config_dword(pdev, 0x40, &val);
-
-		if ((val & 0x0000ff00) != 0)
-			pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
-	}
-
-	ret = ath10k_core_target_resume(ar);
-	if (ret)
-		ath10k_warn("target resume failed: %d\n", ret);
-
-	return ret;
-}
-
-static SIMPLE_DEV_PM_OPS(ath10k_dev_pm_ops,
-			 ath10k_pci_suspend,
-			 ath10k_pci_resume);
-
-#define ATH10K_PCI_PM_OPS (&ath10k_dev_pm_ops)
-
-#else
-
-#define ATH10K_PCI_PM_OPS NULL
-
-#endif /* CONFIG_PM_SLEEP */
-
 MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table);
 
 static struct pci_driver ath10k_pci_driver = {
@@ -2474,7 +2655,6 @@
 	.id_table = ath10k_pci_id_table,
 	.probe = ath10k_pci_probe,
 	.remove = ath10k_pci_remove,
-	.driver.pm = ATH10K_PCI_PM_OPS,
 };
 
 static int __init ath10k_pci_init(void)
@@ -2483,7 +2663,7 @@
 
 	ret = pci_register_driver(&ath10k_pci_driver);
 	if (ret)
-		ath10k_err("pci_register_driver failed [%d]\n", ret);
+		ath10k_err("failed to register PCI driver: %d\n", ret);
 
 	return ret;
 }
@@ -2499,9 +2679,6 @@
 MODULE_AUTHOR("Qualcomm Atheros");
 MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices");
 MODULE_LICENSE("Dual BSD/GPL");
-MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_FW_FILE);
-MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_OTP_FILE);
-MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_BOARD_DATA_FILE);
 MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_FILE);
 MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_OTP_FILE);
 MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE);
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/pci.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/pci.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/pci.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/pci.h	2015-09-29 11:15:06.921740666 +0200
@@ -43,22 +43,6 @@
 	u32 resp_len;
 };
 
-struct ath10k_pci_compl {
-	struct list_head list;
-	int send_or_recv;
-	struct ce_state *ce_state;
-	struct hif_ce_pipe_info *pipe_info;
-	void *transfer_context;
-	unsigned int nbytes;
-	unsigned int transfer_id;
-	unsigned int flags;
-};
-
-/* compl_state.send_or_recv */
-#define HIF_CE_COMPLETE_FREE 0
-#define HIF_CE_COMPLETE_SEND 1
-#define HIF_CE_COMPLETE_RECV 2
-
 /*
  * PCI-specific Target state
  *
@@ -152,16 +136,16 @@
 
 enum ath10k_pci_features {
 	ATH10K_PCI_FEATURE_MSI_X		= 0,
-	ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND	= 1,
+	ATH10K_PCI_FEATURE_SOC_POWER_SAVE	= 1,
 
 	/* keep last */
 	ATH10K_PCI_FEATURE_COUNT
 };
 
 /* Per-pipe state. */
-struct hif_ce_pipe_info {
+struct ath10k_pci_pipe {
 	/* Handle of underlying Copy Engine */
-	struct ce_state *ce_hdl;
+	struct ath10k_ce_pipe *ce_hdl;
 
 	/* Our pipe number; facilitiates use of pipe_info ptrs. */
 	u8 pipe_num;
@@ -174,12 +158,6 @@
 	/* protects compl_free and num_send_allowed */
 	spinlock_t pipe_lock;
 
-	/* List of free CE completion slots */
-	struct list_head compl_free;
-
-	/* Limit the number of outstanding send requests. */
-	int num_sends_allowed;
-
 	struct ath10k_pci *ar_pci;
 	struct tasklet_struct intr;
 };
@@ -189,7 +167,6 @@
 	struct device *dev;
 	struct ath10k *ar;
 	void __iomem *mem;
-	int cacheline_sz;
 
 	DECLARE_BITMAP(features, ATH10K_PCI_FEATURE_COUNT);
 
@@ -201,24 +178,14 @@
 
 	struct tasklet_struct intr_tq;
 	struct tasklet_struct msi_fw_err;
-
-	/* Number of Copy Engines supported */
-	unsigned int ce_count;
+	struct tasklet_struct early_irq_tasklet;
 
 	int started;
 
 	atomic_t keep_awake_count;
 	bool verified_awake;
 
-	/* List of CE completions to be processed */
-	struct list_head compl_process;
-
-	/* protects compl_processing and compl_process */
-	spinlock_t compl_lock;
-
-	bool compl_processing;
-
-	struct hif_ce_pipe_info pipe_info[CE_COUNT_MAX];
+	struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX];
 
 	struct ath10k_hif_cb msg_callbacks_current;
 
@@ -226,16 +193,13 @@
 	u32 fw_indicator_address;
 
 	/* Copy Engine used for Diagnostic Accesses */
-	struct ce_state *ce_diag;
+	struct ath10k_ce_pipe *ce_diag;
 
 	/* FIXME: document what this really protects */
 	spinlock_t ce_lock;
 
 	/* Map CE id to ce_state */
-	struct ce_state *ce_id_to_state[CE_COUNT_MAX];
-
-	/* makes sure that dummy reads are atomic */
-	spinlock_t hw_v1_workaround_lock;
+	struct ath10k_ce_pipe ce_states[CE_COUNT_MAX];
 };
 
 static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
@@ -243,14 +207,18 @@
 	return ar->hif.priv;
 }
 
-static inline u32 ath10k_pci_reg_read32(void __iomem *mem, u32 addr)
+static inline u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr)
 {
-	return ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	return ioread32(ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr);
 }
 
-static inline void ath10k_pci_reg_write32(void __iomem *mem, u32 addr, u32 val)
+static inline void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val)
 {
-	iowrite32(val, mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	iowrite32(val, ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr);
 }
 
 #define ATH_PCI_RESET_WAIT_MAX 10 /* ms */
@@ -309,23 +277,8 @@
 				      u32 value)
 {
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-	void __iomem *addr = ar_pci->mem;
 
-	if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
-		unsigned long irq_flags;
-
-		spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags);
-
-		ioread32(addr+offset+4); /* 3rd read prior to write */
-		ioread32(addr+offset+4); /* 2nd read prior to write */
-		ioread32(addr+offset+4); /* 1st read prior to write */
-		iowrite32(value, addr+offset);
-
-		spin_unlock_irqrestore(&ar_pci->hw_v1_workaround_lock,
-				       irq_flags);
-	} else {
-		iowrite32(value, addr+offset);
-	}
+	iowrite32(value, ar_pci->mem + offset);
 }
 
 static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset)
@@ -335,20 +288,34 @@
 	return ioread32(ar_pci->mem + offset);
 }
 
-extern unsigned int ath10k_target_ps;
+static inline u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr)
+{
+	return ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + addr);
+}
 
-void ath10k_do_pci_wake(struct ath10k *ar);
+static inline void ath10k_pci_soc_write32(struct ath10k *ar, u32 addr, u32 val)
+{
+	ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + addr, val);
+}
+
+int ath10k_do_pci_wake(struct ath10k *ar);
 void ath10k_do_pci_sleep(struct ath10k *ar);
 
-static inline void ath10k_pci_wake(struct ath10k *ar)
+static inline int ath10k_pci_wake(struct ath10k *ar)
 {
-	if (ath10k_target_ps)
-		ath10k_do_pci_wake(ar);
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
+		return ath10k_do_pci_wake(ar);
+
+	return 0;
 }
 
 static inline void ath10k_pci_sleep(struct ath10k *ar)
 {
-	if (ath10k_target_ps)
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
 		ath10k_do_pci_sleep(ar);
 }
 
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/rx_desc.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/rx_desc.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/rx_desc.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/rx_desc.h	2015-09-29 11:15:06.921740666 +0200
@@ -422,10 +422,30 @@
 #define RX_MSDU_START_INFO1_IP_FRAG             (1 << 14)
 #define RX_MSDU_START_INFO1_TCP_ONLY_ACK        (1 << 15)
 
+/* The decapped header (rx_hdr_status) contains the following:
+ *  a) 802.11 header
+ *  [padding to 4 bytes]
+ *  b) HW crypto parameter
+ *     - 0 bytes for no security
+ *     - 4 bytes for WEP
+ *     - 8 bytes for TKIP, AES
+ *  [padding to 4 bytes]
+ *  c) A-MSDU subframe header (14 bytes) if appliable
+ *  d) LLC/SNAP (RFC1042, 8 bytes)
+ *
+ * In case of A-MSDU only first frame in sequence contains (a) and (b). */
 enum rx_msdu_decap_format {
 	RX_MSDU_DECAP_RAW           = 0,
+
+	/* Note: QoS frames are reported as non-QoS. The rx_hdr_status in
+	 * htt_rx_desc contains the original decapped 802.11 header. */
 	RX_MSDU_DECAP_NATIVE_WIFI   = 1,
+
+	/* Payload contains an ethernet header (struct ethhdr). */
 	RX_MSDU_DECAP_ETHERNET2_DIX = 2,
+
+	/* Payload contains two 48-bit addresses and 2-byte length (14 bytes
+	 * total), followed by an RFC1042 header (8 bytes). */
 	RX_MSDU_DECAP_8023_SNAP_LLC = 3
 };
 
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/trace.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/trace.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/trace.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/trace.h	2015-09-29 11:15:06.925740695 +0200
@@ -111,26 +111,29 @@
 );
 
 TRACE_EVENT(ath10k_wmi_cmd,
-	TP_PROTO(int id, void *buf, size_t buf_len),
+	TP_PROTO(int id, void *buf, size_t buf_len, int ret),
 
-	TP_ARGS(id, buf, buf_len),
+	TP_ARGS(id, buf, buf_len, ret),
 
 	TP_STRUCT__entry(
 		__field(unsigned int, id)
 		__field(size_t, buf_len)
 		__dynamic_array(u8, buf, buf_len)
+		__field(int, ret)
 	),
 
 	TP_fast_assign(
 		__entry->id = id;
 		__entry->buf_len = buf_len;
+		__entry->ret = ret;
 		memcpy(__get_dynamic_array(buf), buf, buf_len);
 	),
 
 	TP_printk(
-		"id %d len %zu",
+		"id %d len %zu ret %d",
 		__entry->id,
-		__entry->buf_len
+		__entry->buf_len,
+		__entry->ret
 	)
 );
 
@@ -157,6 +160,48 @@
 		__entry->buf_len
 	)
 );
+
+TRACE_EVENT(ath10k_htt_stats,
+	TP_PROTO(void *buf, size_t buf_len),
+
+	TP_ARGS(buf, buf_len),
+
+	TP_STRUCT__entry(
+		__field(size_t, buf_len)
+		__dynamic_array(u8, buf, buf_len)
+	),
+
+	TP_fast_assign(
+		__entry->buf_len = buf_len;
+		memcpy(__get_dynamic_array(buf), buf, buf_len);
+	),
+
+	TP_printk(
+		"len %zu",
+		__entry->buf_len
+	)
+);
+
+TRACE_EVENT(ath10k_wmi_dbglog,
+	TP_PROTO(void *buf, size_t buf_len),
+
+	TP_ARGS(buf, buf_len),
+
+	TP_STRUCT__entry(
+		__field(size_t, buf_len)
+		__dynamic_array(u8, buf, buf_len)
+	),
+
+	TP_fast_assign(
+		__entry->buf_len = buf_len;
+		memcpy(__get_dynamic_array(buf), buf, buf_len);
+	),
+
+	TP_printk(
+		"len %zu",
+		__entry->buf_len
+	)
+);
 
 #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
 
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/txrx.c linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/txrx.c
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/txrx.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/txrx.c	2015-09-29 11:15:06.925740695 +0200
@@ -44,40 +44,41 @@
 	spin_unlock_bh(&ar->data_lock);
 }
 
-void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc)
+void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
+			  const struct htt_tx_done *tx_done)
 {
 	struct device *dev = htt->ar->dev;
 	struct ieee80211_tx_info *info;
-	struct sk_buff *txfrag = ATH10K_SKB_CB(txdesc)->htt.txfrag;
-	struct sk_buff *msdu = ATH10K_SKB_CB(txdesc)->htt.msdu;
-	int ret;
+	struct ath10k_skb_cb *skb_cb;
+	struct sk_buff *msdu;
 
-	if (ATH10K_SKB_CB(txdesc)->htt.refcount == 0)
-		return;
+	lockdep_assert_held(&htt->tx_lock);
 
-	ATH10K_SKB_CB(txdesc)->htt.refcount--;
+	ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n",
+		   tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack);
 
-	if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0)
+	if (tx_done->msdu_id >= htt->max_num_pending_tx) {
+		ath10k_warn("warning: msdu_id %d too big, ignoring\n",
+			    tx_done->msdu_id);
 		return;
+	}
 
-	if (txfrag) {
-		ret = ath10k_skb_unmap(dev, txfrag);
-		if (ret)
-			ath10k_warn("txfrag unmap failed (%d)\n", ret);
+	msdu = htt->pending_tx[tx_done->msdu_id];
+	skb_cb = ATH10K_SKB_CB(msdu);
 
-		dev_kfree_skb_any(txfrag);
-	}
+	dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
 
-	ret = ath10k_skb_unmap(dev, msdu);
-	if (ret)
-		ath10k_warn("data skb unmap failed (%d)\n", ret);
+	if (skb_cb->htt.txbuf)
+		dma_pool_free(htt->tx_pool,
+			      skb_cb->htt.txbuf,
+			      skb_cb->htt.txbuf_paddr);
 
 	ath10k_report_offchan_tx(htt->ar, msdu);
 
 	info = IEEE80211_SKB_CB(msdu);
 	memset(&info->status, 0, sizeof(info->status));
 
-	if (ATH10K_SKB_CB(txdesc)->htt.discard) {
+	if (tx_done->discard) {
 		ieee80211_free_txskb(htt->ar->hw, msdu);
 		goto exit;
 	}
@@ -85,44 +86,18 @@
 	if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
 		info->flags |= IEEE80211_TX_STAT_ACK;
 
-	if (ATH10K_SKB_CB(txdesc)->htt.no_ack)
+	if (tx_done->no_ack)
 		info->flags &= ~IEEE80211_TX_STAT_ACK;
 
 	ieee80211_tx_status(htt->ar->hw, msdu);
 	/* we do not own the msdu anymore */
 
 exit:
-	spin_lock_bh(&htt->tx_lock);
-	htt->pending_tx[ATH10K_SKB_CB(txdesc)->htt.msdu_id] = NULL;
-	ath10k_htt_tx_free_msdu_id(htt, ATH10K_SKB_CB(txdesc)->htt.msdu_id);
+	htt->pending_tx[tx_done->msdu_id] = NULL;
+	ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
 	__ath10k_htt_tx_dec_pending(htt);
-	if (bitmap_empty(htt->used_msdu_ids, htt->max_num_pending_tx))
+	if (htt->num_pending_tx == 0)
 		wake_up(&htt->empty_tx_wq);
-	spin_unlock_bh(&htt->tx_lock);
-
-	dev_kfree_skb_any(txdesc);
-}
-
-void ath10k_txrx_tx_completed(struct ath10k_htt *htt,
-			      const struct htt_tx_done *tx_done)
-{
-	struct sk_buff *txdesc;
-
-	ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n",
-		   tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack);
-
-	if (tx_done->msdu_id >= htt->max_num_pending_tx) {
-		ath10k_warn("warning: msdu_id %d too big, ignoring\n",
-			    tx_done->msdu_id);
-		return;
-	}
-
-	txdesc = htt->pending_tx[tx_done->msdu_id];
-
-	ATH10K_SKB_CB(txdesc)->htt.discard = tx_done->discard;
-	ATH10K_SKB_CB(txdesc)->htt.no_ack = tx_done->no_ack;
-
-	ath10k_txrx_tx_unref(htt, txdesc);
 }
 
 static const u8 rx_legacy_rate_idx[] = {
@@ -208,7 +183,7 @@
 		/* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2
 		   TODO check this */
 		mcs = (info2 >> 4) & 0x0F;
-		nss = (info1 >> 10) & 0x07;
+		nss = ((info1 >> 10) & 0x07) + 1;
 		bw = info1 & 3;
 		sgi = info2 & 1;
 
@@ -255,12 +230,15 @@
 				~IEEE80211_FCTL_PROTECTED);
 	}
 
-	if (info->status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR)
+	if (info->mic_err)
 		status->flag |= RX_FLAG_MMIC_ERROR;
 
 	if (info->fcs_err)
 		status->flag |= RX_FLAG_FAILED_FCS_CRC;
 
+	if (info->amsdu_more)
+		status->flag |= RX_FLAG_AMSDU_MORE;
+
 	status->signal = info->signal;
 
 	spin_lock_bh(&ar->data_lock);
@@ -279,8 +257,14 @@
 	status->band = ch->band;
 	status->freq = ch->center_freq;
 
+	if (info->rate.info0 & HTT_RX_INDICATION_INFO0_END_VALID) {
+		/* TSF available only in 32-bit */
+		status->mactime = info->tsf & 0xffffffff;
+		status->flag |= RX_FLAG_MACTIME_END;
+	}
+
 	ath10k_dbg(ATH10K_DBG_DATA,
-		   "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u\n",
+		   "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i\n",
 		   info->skb,
 		   info->skb->len,
 		   status->flag == 0 ? "legacy" : "",
@@ -292,7 +276,9 @@
 		   status->rate_idx,
 		   status->vht_nss,
 		   status->freq,
-		   status->band);
+		   status->band, status->flag, info->fcs_err);
+	ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
+			info->skb->data, info->skb->len);
 
 	ieee80211_rx(ar->hw, info->skb);
 }
@@ -397,7 +383,8 @@
 	spin_lock_bh(&ar->data_lock);
 	peer = ath10k_peer_find_by_id(ar, ev->peer_id);
 	if (!peer) {
-		ath10k_warn("unknown peer id %d\n", ev->peer_id);
+		ath10k_warn("peer-unmap-event: unknown peer id %d\n",
+			    ev->peer_id);
 		goto exit;
 	}
 
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/txrx.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/txrx.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/txrx.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/txrx.h	2015-09-29 11:15:06.925740695 +0200
@@ -19,8 +19,7 @@
 
 #include "htt.h"
 
-void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc);
-void ath10k_txrx_tx_completed(struct ath10k_htt *htt,
+void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
 			      const struct htt_tx_done *tx_done);
 void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info);
 
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/wmi.c linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/wmi.c
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/wmi.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/wmi.c	2015-09-29 11:15:06.925740695 +0200
@@ -16,6 +16,7 @@
  */
 
 #include <linux/skbuff.h>
+#include <linux/ctype.h>
 
 #include "core.h"
 #include "htc.h"
@@ -23,22 +24,468 @@
 #include "wmi.h"
 #include "mac.h"
 
-void ath10k_wmi_flush_tx(struct ath10k *ar)
-{
-	int ret;
-
-	ret = wait_event_timeout(ar->wmi.wq,
-				 atomic_read(&ar->wmi.pending_tx_count) == 0,
-				 5*HZ);
-	if (atomic_read(&ar->wmi.pending_tx_count) == 0)
-		return;
-
-	if (ret == 0)
-		ret = -ETIMEDOUT;
-
-	if (ret < 0)
-		ath10k_warn("wmi flush failed (%d)\n", ret);
-}
+/* MAIN WMI cmd track */
+static struct wmi_cmd_map wmi_cmd_map = {
+	.init_cmdid = WMI_INIT_CMDID,
+	.start_scan_cmdid = WMI_START_SCAN_CMDID,
+	.stop_scan_cmdid = WMI_STOP_SCAN_CMDID,
+	.scan_chan_list_cmdid = WMI_SCAN_CHAN_LIST_CMDID,
+	.scan_sch_prio_tbl_cmdid = WMI_SCAN_SCH_PRIO_TBL_CMDID,
+	.pdev_set_regdomain_cmdid = WMI_PDEV_SET_REGDOMAIN_CMDID,
+	.pdev_set_channel_cmdid = WMI_PDEV_SET_CHANNEL_CMDID,
+	.pdev_set_param_cmdid = WMI_PDEV_SET_PARAM_CMDID,
+	.pdev_pktlog_enable_cmdid = WMI_PDEV_PKTLOG_ENABLE_CMDID,
+	.pdev_pktlog_disable_cmdid = WMI_PDEV_PKTLOG_DISABLE_CMDID,
+	.pdev_set_wmm_params_cmdid = WMI_PDEV_SET_WMM_PARAMS_CMDID,
+	.pdev_set_ht_cap_ie_cmdid = WMI_PDEV_SET_HT_CAP_IE_CMDID,
+	.pdev_set_vht_cap_ie_cmdid = WMI_PDEV_SET_VHT_CAP_IE_CMDID,
+	.pdev_set_dscp_tid_map_cmdid = WMI_PDEV_SET_DSCP_TID_MAP_CMDID,
+	.pdev_set_quiet_mode_cmdid = WMI_PDEV_SET_QUIET_MODE_CMDID,
+	.pdev_green_ap_ps_enable_cmdid = WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+	.pdev_get_tpc_config_cmdid = WMI_PDEV_GET_TPC_CONFIG_CMDID,
+	.pdev_set_base_macaddr_cmdid = WMI_PDEV_SET_BASE_MACADDR_CMDID,
+	.vdev_create_cmdid = WMI_VDEV_CREATE_CMDID,
+	.vdev_delete_cmdid = WMI_VDEV_DELETE_CMDID,
+	.vdev_start_request_cmdid = WMI_VDEV_START_REQUEST_CMDID,
+	.vdev_restart_request_cmdid = WMI_VDEV_RESTART_REQUEST_CMDID,
+	.vdev_up_cmdid = WMI_VDEV_UP_CMDID,
+	.vdev_stop_cmdid = WMI_VDEV_STOP_CMDID,
+	.vdev_down_cmdid = WMI_VDEV_DOWN_CMDID,
+	.vdev_set_param_cmdid = WMI_VDEV_SET_PARAM_CMDID,
+	.vdev_install_key_cmdid = WMI_VDEV_INSTALL_KEY_CMDID,
+	.peer_create_cmdid = WMI_PEER_CREATE_CMDID,
+	.peer_delete_cmdid = WMI_PEER_DELETE_CMDID,
+	.peer_flush_tids_cmdid = WMI_PEER_FLUSH_TIDS_CMDID,
+	.peer_set_param_cmdid = WMI_PEER_SET_PARAM_CMDID,
+	.peer_assoc_cmdid = WMI_PEER_ASSOC_CMDID,
+	.peer_add_wds_entry_cmdid = WMI_PEER_ADD_WDS_ENTRY_CMDID,
+	.peer_remove_wds_entry_cmdid = WMI_PEER_REMOVE_WDS_ENTRY_CMDID,
+	.peer_mcast_group_cmdid = WMI_PEER_MCAST_GROUP_CMDID,
+	.bcn_tx_cmdid = WMI_BCN_TX_CMDID,
+	.pdev_send_bcn_cmdid = WMI_PDEV_SEND_BCN_CMDID,
+	.bcn_tmpl_cmdid = WMI_BCN_TMPL_CMDID,
+	.bcn_filter_rx_cmdid = WMI_BCN_FILTER_RX_CMDID,
+	.prb_req_filter_rx_cmdid = WMI_PRB_REQ_FILTER_RX_CMDID,
+	.mgmt_tx_cmdid = WMI_MGMT_TX_CMDID,
+	.prb_tmpl_cmdid = WMI_PRB_TMPL_CMDID,
+	.addba_clear_resp_cmdid = WMI_ADDBA_CLEAR_RESP_CMDID,
+	.addba_send_cmdid = WMI_ADDBA_SEND_CMDID,
+	.addba_status_cmdid = WMI_ADDBA_STATUS_CMDID,
+	.delba_send_cmdid = WMI_DELBA_SEND_CMDID,
+	.addba_set_resp_cmdid = WMI_ADDBA_SET_RESP_CMDID,
+	.send_singleamsdu_cmdid = WMI_SEND_SINGLEAMSDU_CMDID,
+	.sta_powersave_mode_cmdid = WMI_STA_POWERSAVE_MODE_CMDID,
+	.sta_powersave_param_cmdid = WMI_STA_POWERSAVE_PARAM_CMDID,
+	.sta_mimo_ps_mode_cmdid = WMI_STA_MIMO_PS_MODE_CMDID,
+	.pdev_dfs_enable_cmdid = WMI_PDEV_DFS_ENABLE_CMDID,
+	.pdev_dfs_disable_cmdid = WMI_PDEV_DFS_DISABLE_CMDID,
+	.roam_scan_mode = WMI_ROAM_SCAN_MODE,
+	.roam_scan_rssi_threshold = WMI_ROAM_SCAN_RSSI_THRESHOLD,
+	.roam_scan_period = WMI_ROAM_SCAN_PERIOD,
+	.roam_scan_rssi_change_threshold = WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+	.roam_ap_profile = WMI_ROAM_AP_PROFILE,
+	.ofl_scan_add_ap_profile = WMI_ROAM_AP_PROFILE,
+	.ofl_scan_remove_ap_profile = WMI_OFL_SCAN_REMOVE_AP_PROFILE,
+	.ofl_scan_period = WMI_OFL_SCAN_PERIOD,
+	.p2p_dev_set_device_info = WMI_P2P_DEV_SET_DEVICE_INFO,
+	.p2p_dev_set_discoverability = WMI_P2P_DEV_SET_DISCOVERABILITY,
+	.p2p_go_set_beacon_ie = WMI_P2P_GO_SET_BEACON_IE,
+	.p2p_go_set_probe_resp_ie = WMI_P2P_GO_SET_PROBE_RESP_IE,
+	.p2p_set_vendor_ie_data_cmdid = WMI_P2P_SET_VENDOR_IE_DATA_CMDID,
+	.ap_ps_peer_param_cmdid = WMI_AP_PS_PEER_PARAM_CMDID,
+	.ap_ps_peer_uapsd_coex_cmdid = WMI_AP_PS_PEER_UAPSD_COEX_CMDID,
+	.peer_rate_retry_sched_cmdid = WMI_PEER_RATE_RETRY_SCHED_CMDID,
+	.wlan_profile_trigger_cmdid = WMI_WLAN_PROFILE_TRIGGER_CMDID,
+	.wlan_profile_set_hist_intvl_cmdid =
+				WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+	.wlan_profile_get_profile_data_cmdid =
+				WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+	.wlan_profile_enable_profile_id_cmdid =
+				WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+	.wlan_profile_list_profile_id_cmdid =
+				WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+	.pdev_suspend_cmdid = WMI_PDEV_SUSPEND_CMDID,
+	.pdev_resume_cmdid = WMI_PDEV_RESUME_CMDID,
+	.add_bcn_filter_cmdid = WMI_ADD_BCN_FILTER_CMDID,
+	.rmv_bcn_filter_cmdid = WMI_RMV_BCN_FILTER_CMDID,
+	.wow_add_wake_pattern_cmdid = WMI_WOW_ADD_WAKE_PATTERN_CMDID,
+	.wow_del_wake_pattern_cmdid = WMI_WOW_DEL_WAKE_PATTERN_CMDID,
+	.wow_enable_disable_wake_event_cmdid =
+				WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+	.wow_enable_cmdid = WMI_WOW_ENABLE_CMDID,
+	.wow_hostwakeup_from_sleep_cmdid = WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+	.rtt_measreq_cmdid = WMI_RTT_MEASREQ_CMDID,
+	.rtt_tsf_cmdid = WMI_RTT_TSF_CMDID,
+	.vdev_spectral_scan_configure_cmdid =
+				WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
+	.vdev_spectral_scan_enable_cmdid = WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
+	.request_stats_cmdid = WMI_REQUEST_STATS_CMDID,
+	.set_arp_ns_offload_cmdid = WMI_SET_ARP_NS_OFFLOAD_CMDID,
+	.network_list_offload_config_cmdid =
+				WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID,
+	.gtk_offload_cmdid = WMI_GTK_OFFLOAD_CMDID,
+	.csa_offload_enable_cmdid = WMI_CSA_OFFLOAD_ENABLE_CMDID,
+	.csa_offload_chanswitch_cmdid = WMI_CSA_OFFLOAD_CHANSWITCH_CMDID,
+	.chatter_set_mode_cmdid = WMI_CHATTER_SET_MODE_CMDID,
+	.peer_tid_addba_cmdid = WMI_PEER_TID_ADDBA_CMDID,
+	.peer_tid_delba_cmdid = WMI_PEER_TID_DELBA_CMDID,
+	.sta_dtim_ps_method_cmdid = WMI_STA_DTIM_PS_METHOD_CMDID,
+	.sta_uapsd_auto_trig_cmdid = WMI_STA_UAPSD_AUTO_TRIG_CMDID,
+	.sta_keepalive_cmd = WMI_STA_KEEPALIVE_CMD,
+	.echo_cmdid = WMI_ECHO_CMDID,
+	.pdev_utf_cmdid = WMI_PDEV_UTF_CMDID,
+	.dbglog_cfg_cmdid = WMI_DBGLOG_CFG_CMDID,
+	.pdev_qvit_cmdid = WMI_PDEV_QVIT_CMDID,
+	.pdev_ftm_intg_cmdid = WMI_PDEV_FTM_INTG_CMDID,
+	.vdev_set_keepalive_cmdid = WMI_VDEV_SET_KEEPALIVE_CMDID,
+	.vdev_get_keepalive_cmdid = WMI_VDEV_GET_KEEPALIVE_CMDID,
+	.force_fw_hang_cmdid = WMI_FORCE_FW_HANG_CMDID,
+	.gpio_config_cmdid = WMI_GPIO_CONFIG_CMDID,
+	.gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID,
+};
+
+/* 10.X WMI cmd track */
+static struct wmi_cmd_map wmi_10x_cmd_map = {
+	.init_cmdid = WMI_10X_INIT_CMDID,
+	.start_scan_cmdid = WMI_10X_START_SCAN_CMDID,
+	.stop_scan_cmdid = WMI_10X_STOP_SCAN_CMDID,
+	.scan_chan_list_cmdid = WMI_10X_SCAN_CHAN_LIST_CMDID,
+	.scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED,
+	.pdev_set_regdomain_cmdid = WMI_10X_PDEV_SET_REGDOMAIN_CMDID,
+	.pdev_set_channel_cmdid = WMI_10X_PDEV_SET_CHANNEL_CMDID,
+	.pdev_set_param_cmdid = WMI_10X_PDEV_SET_PARAM_CMDID,
+	.pdev_pktlog_enable_cmdid = WMI_10X_PDEV_PKTLOG_ENABLE_CMDID,
+	.pdev_pktlog_disable_cmdid = WMI_10X_PDEV_PKTLOG_DISABLE_CMDID,
+	.pdev_set_wmm_params_cmdid = WMI_10X_PDEV_SET_WMM_PARAMS_CMDID,
+	.pdev_set_ht_cap_ie_cmdid = WMI_10X_PDEV_SET_HT_CAP_IE_CMDID,
+	.pdev_set_vht_cap_ie_cmdid = WMI_10X_PDEV_SET_VHT_CAP_IE_CMDID,
+	.pdev_set_dscp_tid_map_cmdid = WMI_10X_PDEV_SET_DSCP_TID_MAP_CMDID,
+	.pdev_set_quiet_mode_cmdid = WMI_10X_PDEV_SET_QUIET_MODE_CMDID,
+	.pdev_green_ap_ps_enable_cmdid = WMI_10X_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+	.pdev_get_tpc_config_cmdid = WMI_10X_PDEV_GET_TPC_CONFIG_CMDID,
+	.pdev_set_base_macaddr_cmdid = WMI_10X_PDEV_SET_BASE_MACADDR_CMDID,
+	.vdev_create_cmdid = WMI_10X_VDEV_CREATE_CMDID,
+	.vdev_delete_cmdid = WMI_10X_VDEV_DELETE_CMDID,
+	.vdev_start_request_cmdid = WMI_10X_VDEV_START_REQUEST_CMDID,
+	.vdev_restart_request_cmdid = WMI_10X_VDEV_RESTART_REQUEST_CMDID,
+	.vdev_up_cmdid = WMI_10X_VDEV_UP_CMDID,
+	.vdev_stop_cmdid = WMI_10X_VDEV_STOP_CMDID,
+	.vdev_down_cmdid = WMI_10X_VDEV_DOWN_CMDID,
+	.vdev_set_param_cmdid = WMI_10X_VDEV_SET_PARAM_CMDID,
+	.vdev_install_key_cmdid = WMI_10X_VDEV_INSTALL_KEY_CMDID,
+	.peer_create_cmdid = WMI_10X_PEER_CREATE_CMDID,
+	.peer_delete_cmdid = WMI_10X_PEER_DELETE_CMDID,
+	.peer_flush_tids_cmdid = WMI_10X_PEER_FLUSH_TIDS_CMDID,
+	.peer_set_param_cmdid = WMI_10X_PEER_SET_PARAM_CMDID,
+	.peer_assoc_cmdid = WMI_10X_PEER_ASSOC_CMDID,
+	.peer_add_wds_entry_cmdid = WMI_10X_PEER_ADD_WDS_ENTRY_CMDID,
+	.peer_remove_wds_entry_cmdid = WMI_10X_PEER_REMOVE_WDS_ENTRY_CMDID,
+	.peer_mcast_group_cmdid = WMI_10X_PEER_MCAST_GROUP_CMDID,
+	.bcn_tx_cmdid = WMI_10X_BCN_TX_CMDID,
+	.pdev_send_bcn_cmdid = WMI_10X_PDEV_SEND_BCN_CMDID,
+	.bcn_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
+	.bcn_filter_rx_cmdid = WMI_10X_BCN_FILTER_RX_CMDID,
+	.prb_req_filter_rx_cmdid = WMI_10X_PRB_REQ_FILTER_RX_CMDID,
+	.mgmt_tx_cmdid = WMI_10X_MGMT_TX_CMDID,
+	.prb_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
+	.addba_clear_resp_cmdid = WMI_10X_ADDBA_CLEAR_RESP_CMDID,
+	.addba_send_cmdid = WMI_10X_ADDBA_SEND_CMDID,
+	.addba_status_cmdid = WMI_10X_ADDBA_STATUS_CMDID,
+	.delba_send_cmdid = WMI_10X_DELBA_SEND_CMDID,
+	.addba_set_resp_cmdid = WMI_10X_ADDBA_SET_RESP_CMDID,
+	.send_singleamsdu_cmdid = WMI_10X_SEND_SINGLEAMSDU_CMDID,
+	.sta_powersave_mode_cmdid = WMI_10X_STA_POWERSAVE_MODE_CMDID,
+	.sta_powersave_param_cmdid = WMI_10X_STA_POWERSAVE_PARAM_CMDID,
+	.sta_mimo_ps_mode_cmdid = WMI_10X_STA_MIMO_PS_MODE_CMDID,
+	.pdev_dfs_enable_cmdid = WMI_10X_PDEV_DFS_ENABLE_CMDID,
+	.pdev_dfs_disable_cmdid = WMI_10X_PDEV_DFS_DISABLE_CMDID,
+	.roam_scan_mode = WMI_10X_ROAM_SCAN_MODE,
+	.roam_scan_rssi_threshold = WMI_10X_ROAM_SCAN_RSSI_THRESHOLD,
+	.roam_scan_period = WMI_10X_ROAM_SCAN_PERIOD,
+	.roam_scan_rssi_change_threshold =
+				WMI_10X_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+	.roam_ap_profile = WMI_10X_ROAM_AP_PROFILE,
+	.ofl_scan_add_ap_profile = WMI_10X_OFL_SCAN_ADD_AP_PROFILE,
+	.ofl_scan_remove_ap_profile = WMI_10X_OFL_SCAN_REMOVE_AP_PROFILE,
+	.ofl_scan_period = WMI_10X_OFL_SCAN_PERIOD,
+	.p2p_dev_set_device_info = WMI_10X_P2P_DEV_SET_DEVICE_INFO,
+	.p2p_dev_set_discoverability = WMI_10X_P2P_DEV_SET_DISCOVERABILITY,
+	.p2p_go_set_beacon_ie = WMI_10X_P2P_GO_SET_BEACON_IE,
+	.p2p_go_set_probe_resp_ie = WMI_10X_P2P_GO_SET_PROBE_RESP_IE,
+	.p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED,
+	.ap_ps_peer_param_cmdid = WMI_10X_AP_PS_PEER_PARAM_CMDID,
+	.ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED,
+	.peer_rate_retry_sched_cmdid = WMI_10X_PEER_RATE_RETRY_SCHED_CMDID,
+	.wlan_profile_trigger_cmdid = WMI_10X_WLAN_PROFILE_TRIGGER_CMDID,
+	.wlan_profile_set_hist_intvl_cmdid =
+				WMI_10X_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+	.wlan_profile_get_profile_data_cmdid =
+				WMI_10X_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+	.wlan_profile_enable_profile_id_cmdid =
+				WMI_10X_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+	.wlan_profile_list_profile_id_cmdid =
+				WMI_10X_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+	.pdev_suspend_cmdid = WMI_10X_PDEV_SUSPEND_CMDID,
+	.pdev_resume_cmdid = WMI_10X_PDEV_RESUME_CMDID,
+	.add_bcn_filter_cmdid = WMI_10X_ADD_BCN_FILTER_CMDID,
+	.rmv_bcn_filter_cmdid = WMI_10X_RMV_BCN_FILTER_CMDID,
+	.wow_add_wake_pattern_cmdid = WMI_10X_WOW_ADD_WAKE_PATTERN_CMDID,
+	.wow_del_wake_pattern_cmdid = WMI_10X_WOW_DEL_WAKE_PATTERN_CMDID,
+	.wow_enable_disable_wake_event_cmdid =
+				WMI_10X_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+	.wow_enable_cmdid = WMI_10X_WOW_ENABLE_CMDID,
+	.wow_hostwakeup_from_sleep_cmdid =
+				WMI_10X_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+	.rtt_measreq_cmdid = WMI_10X_RTT_MEASREQ_CMDID,
+	.rtt_tsf_cmdid = WMI_10X_RTT_TSF_CMDID,
+	.vdev_spectral_scan_configure_cmdid =
+				WMI_10X_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
+	.vdev_spectral_scan_enable_cmdid =
+				WMI_10X_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
+	.request_stats_cmdid = WMI_10X_REQUEST_STATS_CMDID,
+	.set_arp_ns_offload_cmdid = WMI_CMD_UNSUPPORTED,
+	.network_list_offload_config_cmdid = WMI_CMD_UNSUPPORTED,
+	.gtk_offload_cmdid = WMI_CMD_UNSUPPORTED,
+	.csa_offload_enable_cmdid = WMI_CMD_UNSUPPORTED,
+	.csa_offload_chanswitch_cmdid = WMI_CMD_UNSUPPORTED,
+	.chatter_set_mode_cmdid = WMI_CMD_UNSUPPORTED,
+	.peer_tid_addba_cmdid = WMI_CMD_UNSUPPORTED,
+	.peer_tid_delba_cmdid = WMI_CMD_UNSUPPORTED,
+	.sta_dtim_ps_method_cmdid = WMI_CMD_UNSUPPORTED,
+	.sta_uapsd_auto_trig_cmdid = WMI_CMD_UNSUPPORTED,
+	.sta_keepalive_cmd = WMI_CMD_UNSUPPORTED,
+	.echo_cmdid = WMI_10X_ECHO_CMDID,
+	.pdev_utf_cmdid = WMI_10X_PDEV_UTF_CMDID,
+	.dbglog_cfg_cmdid = WMI_10X_DBGLOG_CFG_CMDID,
+	.pdev_qvit_cmdid = WMI_10X_PDEV_QVIT_CMDID,
+	.pdev_ftm_intg_cmdid = WMI_CMD_UNSUPPORTED,
+	.vdev_set_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
+	.vdev_get_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
+	.force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED,
+	.gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID,
+	.gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID,
+};
+
+/* MAIN WMI VDEV param map */
+static struct wmi_vdev_param_map wmi_vdev_param_map = {
+	.rts_threshold = WMI_VDEV_PARAM_RTS_THRESHOLD,
+	.fragmentation_threshold = WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+	.beacon_interval = WMI_VDEV_PARAM_BEACON_INTERVAL,
+	.listen_interval = WMI_VDEV_PARAM_LISTEN_INTERVAL,
+	.multicast_rate = WMI_VDEV_PARAM_MULTICAST_RATE,
+	.mgmt_tx_rate = WMI_VDEV_PARAM_MGMT_TX_RATE,
+	.slot_time = WMI_VDEV_PARAM_SLOT_TIME,
+	.preamble = WMI_VDEV_PARAM_PREAMBLE,
+	.swba_time = WMI_VDEV_PARAM_SWBA_TIME,
+	.wmi_vdev_stats_update_period = WMI_VDEV_STATS_UPDATE_PERIOD,
+	.wmi_vdev_pwrsave_ageout_time = WMI_VDEV_PWRSAVE_AGEOUT_TIME,
+	.wmi_vdev_host_swba_interval = WMI_VDEV_HOST_SWBA_INTERVAL,
+	.dtim_period = WMI_VDEV_PARAM_DTIM_PERIOD,
+	.wmi_vdev_oc_scheduler_air_time_limit =
+					WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+	.wds = WMI_VDEV_PARAM_WDS,
+	.atim_window = WMI_VDEV_PARAM_ATIM_WINDOW,
+	.bmiss_count_max = WMI_VDEV_PARAM_BMISS_COUNT_MAX,
+	.bmiss_first_bcnt = WMI_VDEV_PARAM_BMISS_FIRST_BCNT,
+	.bmiss_final_bcnt = WMI_VDEV_PARAM_BMISS_FINAL_BCNT,
+	.feature_wmm = WMI_VDEV_PARAM_FEATURE_WMM,
+	.chwidth = WMI_VDEV_PARAM_CHWIDTH,
+	.chextoffset = WMI_VDEV_PARAM_CHEXTOFFSET,
+	.disable_htprotection =	WMI_VDEV_PARAM_DISABLE_HTPROTECTION,
+	.sta_quickkickout = WMI_VDEV_PARAM_STA_QUICKKICKOUT,
+	.mgmt_rate = WMI_VDEV_PARAM_MGMT_RATE,
+	.protection_mode = WMI_VDEV_PARAM_PROTECTION_MODE,
+	.fixed_rate = WMI_VDEV_PARAM_FIXED_RATE,
+	.sgi = WMI_VDEV_PARAM_SGI,
+	.ldpc = WMI_VDEV_PARAM_LDPC,
+	.tx_stbc = WMI_VDEV_PARAM_TX_STBC,
+	.rx_stbc = WMI_VDEV_PARAM_RX_STBC,
+	.intra_bss_fwd = WMI_VDEV_PARAM_INTRA_BSS_FWD,
+	.def_keyid = WMI_VDEV_PARAM_DEF_KEYID,
+	.nss = WMI_VDEV_PARAM_NSS,
+	.bcast_data_rate = WMI_VDEV_PARAM_BCAST_DATA_RATE,
+	.mcast_data_rate = WMI_VDEV_PARAM_MCAST_DATA_RATE,
+	.mcast_indicate = WMI_VDEV_PARAM_MCAST_INDICATE,
+	.dhcp_indicate = WMI_VDEV_PARAM_DHCP_INDICATE,
+	.unknown_dest_indicate = WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+	.ap_keepalive_min_idle_inactive_time_secs =
+			WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+	.ap_keepalive_max_idle_inactive_time_secs =
+			WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+	.ap_keepalive_max_unresponsive_time_secs =
+			WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+	.ap_enable_nawds = WMI_VDEV_PARAM_AP_ENABLE_NAWDS,
+	.mcast2ucast_set = WMI_VDEV_PARAM_UNSUPPORTED,
+	.enable_rtscts = WMI_VDEV_PARAM_ENABLE_RTSCTS,
+	.txbf = WMI_VDEV_PARAM_TXBF,
+	.packet_powersave = WMI_VDEV_PARAM_PACKET_POWERSAVE,
+	.drop_unencry = WMI_VDEV_PARAM_DROP_UNENCRY,
+	.tx_encap_type = WMI_VDEV_PARAM_TX_ENCAP_TYPE,
+	.ap_detect_out_of_sync_sleeping_sta_time_secs =
+					WMI_VDEV_PARAM_UNSUPPORTED,
+};
+
+/* 10.X WMI VDEV param map */
+static struct wmi_vdev_param_map wmi_10x_vdev_param_map = {
+	.rts_threshold = WMI_10X_VDEV_PARAM_RTS_THRESHOLD,
+	.fragmentation_threshold = WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+	.beacon_interval = WMI_10X_VDEV_PARAM_BEACON_INTERVAL,
+	.listen_interval = WMI_10X_VDEV_PARAM_LISTEN_INTERVAL,
+	.multicast_rate = WMI_10X_VDEV_PARAM_MULTICAST_RATE,
+	.mgmt_tx_rate = WMI_10X_VDEV_PARAM_MGMT_TX_RATE,
+	.slot_time = WMI_10X_VDEV_PARAM_SLOT_TIME,
+	.preamble = WMI_10X_VDEV_PARAM_PREAMBLE,
+	.swba_time = WMI_10X_VDEV_PARAM_SWBA_TIME,
+	.wmi_vdev_stats_update_period = WMI_10X_VDEV_STATS_UPDATE_PERIOD,
+	.wmi_vdev_pwrsave_ageout_time = WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME,
+	.wmi_vdev_host_swba_interval = WMI_10X_VDEV_HOST_SWBA_INTERVAL,
+	.dtim_period = WMI_10X_VDEV_PARAM_DTIM_PERIOD,
+	.wmi_vdev_oc_scheduler_air_time_limit =
+				WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+	.wds = WMI_10X_VDEV_PARAM_WDS,
+	.atim_window = WMI_10X_VDEV_PARAM_ATIM_WINDOW,
+	.bmiss_count_max = WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX,
+	.bmiss_first_bcnt = WMI_VDEV_PARAM_UNSUPPORTED,
+	.bmiss_final_bcnt = WMI_VDEV_PARAM_UNSUPPORTED,
+	.feature_wmm = WMI_10X_VDEV_PARAM_FEATURE_WMM,
+	.chwidth = WMI_10X_VDEV_PARAM_CHWIDTH,
+	.chextoffset = WMI_10X_VDEV_PARAM_CHEXTOFFSET,
+	.disable_htprotection = WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION,
+	.sta_quickkickout = WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT,
+	.mgmt_rate = WMI_10X_VDEV_PARAM_MGMT_RATE,
+	.protection_mode = WMI_10X_VDEV_PARAM_PROTECTION_MODE,
+	.fixed_rate = WMI_10X_VDEV_PARAM_FIXED_RATE,
+	.sgi = WMI_10X_VDEV_PARAM_SGI,
+	.ldpc = WMI_10X_VDEV_PARAM_LDPC,
+	.tx_stbc = WMI_10X_VDEV_PARAM_TX_STBC,
+	.rx_stbc = WMI_10X_VDEV_PARAM_RX_STBC,
+	.intra_bss_fwd = WMI_10X_VDEV_PARAM_INTRA_BSS_FWD,
+	.def_keyid = WMI_10X_VDEV_PARAM_DEF_KEYID,
+	.nss = WMI_10X_VDEV_PARAM_NSS,
+	.bcast_data_rate = WMI_10X_VDEV_PARAM_BCAST_DATA_RATE,
+	.mcast_data_rate = WMI_10X_VDEV_PARAM_MCAST_DATA_RATE,
+	.mcast_indicate = WMI_10X_VDEV_PARAM_MCAST_INDICATE,
+	.dhcp_indicate = WMI_10X_VDEV_PARAM_DHCP_INDICATE,
+	.unknown_dest_indicate = WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+	.ap_keepalive_min_idle_inactive_time_secs =
+		WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+	.ap_keepalive_max_idle_inactive_time_secs =
+		WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+	.ap_keepalive_max_unresponsive_time_secs =
+		WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+	.ap_enable_nawds = WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS,
+	.mcast2ucast_set = WMI_10X_VDEV_PARAM_MCAST2UCAST_SET,
+	.enable_rtscts = WMI_10X_VDEV_PARAM_ENABLE_RTSCTS,
+	.txbf = WMI_VDEV_PARAM_UNSUPPORTED,
+	.packet_powersave = WMI_VDEV_PARAM_UNSUPPORTED,
+	.drop_unencry = WMI_VDEV_PARAM_UNSUPPORTED,
+	.tx_encap_type = WMI_VDEV_PARAM_UNSUPPORTED,
+	.ap_detect_out_of_sync_sleeping_sta_time_secs =
+		WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
+};
+
+static struct wmi_pdev_param_map wmi_pdev_param_map = {
+	.tx_chain_mask = WMI_PDEV_PARAM_TX_CHAIN_MASK,
+	.rx_chain_mask = WMI_PDEV_PARAM_RX_CHAIN_MASK,
+	.txpower_limit2g = WMI_PDEV_PARAM_TXPOWER_LIMIT2G,
+	.txpower_limit5g = WMI_PDEV_PARAM_TXPOWER_LIMIT5G,
+	.txpower_scale = WMI_PDEV_PARAM_TXPOWER_SCALE,
+	.beacon_gen_mode = WMI_PDEV_PARAM_BEACON_GEN_MODE,
+	.beacon_tx_mode = WMI_PDEV_PARAM_BEACON_TX_MODE,
+	.resmgr_offchan_mode = WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+	.protection_mode = WMI_PDEV_PARAM_PROTECTION_MODE,
+	.dynamic_bw = WMI_PDEV_PARAM_DYNAMIC_BW,
+	.non_agg_sw_retry_th = WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+	.agg_sw_retry_th = WMI_PDEV_PARAM_AGG_SW_RETRY_TH,
+	.sta_kickout_th = WMI_PDEV_PARAM_STA_KICKOUT_TH,
+	.ac_aggrsize_scaling = WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+	.ltr_enable = WMI_PDEV_PARAM_LTR_ENABLE,
+	.ltr_ac_latency_be = WMI_PDEV_PARAM_LTR_AC_LATENCY_BE,
+	.ltr_ac_latency_bk = WMI_PDEV_PARAM_LTR_AC_LATENCY_BK,
+	.ltr_ac_latency_vi = WMI_PDEV_PARAM_LTR_AC_LATENCY_VI,
+	.ltr_ac_latency_vo = WMI_PDEV_PARAM_LTR_AC_LATENCY_VO,
+	.ltr_ac_latency_timeout = WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+	.ltr_sleep_override = WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+	.ltr_rx_override = WMI_PDEV_PARAM_LTR_RX_OVERRIDE,
+	.ltr_tx_activity_timeout = WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+	.l1ss_enable = WMI_PDEV_PARAM_L1SS_ENABLE,
+	.dsleep_enable = WMI_PDEV_PARAM_DSLEEP_ENABLE,
+	.pcielp_txbuf_flush = WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH,
+	.pcielp_txbuf_watermark = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
+	.pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
+	.pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE,
+	.pdev_stats_update_period = WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+	.vdev_stats_update_period = WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+	.peer_stats_update_period = WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+	.bcnflt_stats_update_period = WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+	.pmf_qos = WMI_PDEV_PARAM_PMF_QOS,
+	.arp_ac_override = WMI_PDEV_PARAM_ARP_AC_OVERRIDE,
+	.dcs = WMI_PDEV_PARAM_DCS,
+	.ani_enable = WMI_PDEV_PARAM_ANI_ENABLE,
+	.ani_poll_period = WMI_PDEV_PARAM_ANI_POLL_PERIOD,
+	.ani_listen_period = WMI_PDEV_PARAM_ANI_LISTEN_PERIOD,
+	.ani_ofdm_level = WMI_PDEV_PARAM_ANI_OFDM_LEVEL,
+	.ani_cck_level = WMI_PDEV_PARAM_ANI_CCK_LEVEL,
+	.dyntxchain = WMI_PDEV_PARAM_DYNTXCHAIN,
+	.proxy_sta = WMI_PDEV_PARAM_PROXY_STA,
+	.idle_ps_config = WMI_PDEV_PARAM_IDLE_PS_CONFIG,
+	.power_gating_sleep = WMI_PDEV_PARAM_POWER_GATING_SLEEP,
+	.fast_channel_reset = WMI_PDEV_PARAM_UNSUPPORTED,
+	.burst_dur = WMI_PDEV_PARAM_UNSUPPORTED,
+	.burst_enable = WMI_PDEV_PARAM_UNSUPPORTED,
+};
+
+static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+	.tx_chain_mask = WMI_10X_PDEV_PARAM_TX_CHAIN_MASK,
+	.rx_chain_mask = WMI_10X_PDEV_PARAM_RX_CHAIN_MASK,
+	.txpower_limit2g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G,
+	.txpower_limit5g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G,
+	.txpower_scale = WMI_10X_PDEV_PARAM_TXPOWER_SCALE,
+	.beacon_gen_mode = WMI_10X_PDEV_PARAM_BEACON_GEN_MODE,
+	.beacon_tx_mode = WMI_10X_PDEV_PARAM_BEACON_TX_MODE,
+	.resmgr_offchan_mode = WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+	.protection_mode = WMI_10X_PDEV_PARAM_PROTECTION_MODE,
+	.dynamic_bw = WMI_10X_PDEV_PARAM_DYNAMIC_BW,
+	.non_agg_sw_retry_th = WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+	.agg_sw_retry_th = WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH,
+	.sta_kickout_th = WMI_10X_PDEV_PARAM_STA_KICKOUT_TH,
+	.ac_aggrsize_scaling = WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+	.ltr_enable = WMI_10X_PDEV_PARAM_LTR_ENABLE,
+	.ltr_ac_latency_be = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE,
+	.ltr_ac_latency_bk = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK,
+	.ltr_ac_latency_vi = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI,
+	.ltr_ac_latency_vo = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO,
+	.ltr_ac_latency_timeout = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+	.ltr_sleep_override = WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+	.ltr_rx_override = WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE,
+	.ltr_tx_activity_timeout = WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+	.l1ss_enable = WMI_10X_PDEV_PARAM_L1SS_ENABLE,
+	.dsleep_enable = WMI_10X_PDEV_PARAM_DSLEEP_ENABLE,
+	.pcielp_txbuf_flush = WMI_PDEV_PARAM_UNSUPPORTED,
+	.pcielp_txbuf_watermark = WMI_PDEV_PARAM_UNSUPPORTED,
+	.pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_UNSUPPORTED,
+	.pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_UNSUPPORTED,
+	.pdev_stats_update_period = WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+	.vdev_stats_update_period = WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+	.peer_stats_update_period = WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+	.bcnflt_stats_update_period =
+				WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+	.pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS,
+	.arp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE,
+	.dcs = WMI_10X_PDEV_PARAM_DCS,
+	.ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE,
+	.ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD,
+	.ani_listen_period = WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD,
+	.ani_ofdm_level = WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL,
+	.ani_cck_level = WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL,
+	.dyntxchain = WMI_10X_PDEV_PARAM_DYNTXCHAIN,
+	.proxy_sta = WMI_PDEV_PARAM_UNSUPPORTED,
+	.idle_ps_config = WMI_PDEV_PARAM_UNSUPPORTED,
+	.power_gating_sleep = WMI_PDEV_PARAM_UNSUPPORTED,
+	.fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET,
+	.burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR,
+	.burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE,
+};
 
 int ath10k_wmi_wait_for_service_ready(struct ath10k *ar)
 {
@@ -78,18 +525,14 @@
 static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
 {
 	dev_kfree_skb(skb);
-
-	if (atomic_sub_return(1, &ar->wmi.pending_tx_count) == 0)
-		wake_up(&ar->wmi.wq);
 }
 
-/* WMI command API */
-static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
-			       enum wmi_cmd_id cmd_id)
+static int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
+				      u32 cmd_id)
 {
 	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
 	struct wmi_cmd_hdr *cmd_hdr;
-	int status;
+	int ret;
 	u32 cmd = 0;
 
 	if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
@@ -100,25 +543,141 @@
 	cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
 	cmd_hdr->cmd_id = __cpu_to_le32(cmd);
 
-	if (atomic_add_return(1, &ar->wmi.pending_tx_count) >
-	    WMI_MAX_PENDING_TX_COUNT) {
-		/* avoid using up memory when FW hangs */
-		atomic_dec(&ar->wmi.pending_tx_count);
-		return -EBUSY;
+	memset(skb_cb, 0, sizeof(*skb_cb));
+	ret = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb);
+	trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len, ret);
+
+	if (ret)
+		goto err_pull;
+
+	return 0;
+
+err_pull:
+	skb_pull(skb, sizeof(struct wmi_cmd_hdr));
+	return ret;
 	}
 
-	memset(skb_cb, 0, sizeof(*skb_cb));
+static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
+{
+	int ret;
 
-	trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len);
+	lockdep_assert_held(&arvif->ar->data_lock);
 
-	status = ath10k_htc_send(ar->htc, ar->wmi.eid, skb);
-	if (status) {
+	if (arvif->beacon == NULL)
+		return;
+
+	if (arvif->beacon_sent)
+		return;
+
+	ret = ath10k_wmi_beacon_send_ref_nowait(arvif);
+	if (ret)
+		return;
+
+	/* We need to retain the arvif->beacon reference for DMA unmapping and
+	 * freeing the skbuff later. */
+	arvif->beacon_sent = true;
+}
+
+static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac,
+				       struct ieee80211_vif *vif)
+{
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+	ath10k_wmi_tx_beacon_nowait(arvif);
+}
+
+static void ath10k_wmi_tx_beacons_nowait(struct ath10k *ar)
+{
+	spin_lock_bh(&ar->data_lock);
+	ieee80211_iterate_active_interfaces_atomic(ar->hw,
+						   IEEE80211_IFACE_ITER_NORMAL,
+						   ath10k_wmi_tx_beacons_iter,
+						   NULL);
+	spin_unlock_bh(&ar->data_lock);
+}
+
+static void ath10k_wmi_op_ep_tx_credits(struct ath10k *ar)
+{
+	/* try to send pending beacons first. they take priority */
+	ath10k_wmi_tx_beacons_nowait(ar);
+
+	wake_up(&ar->wmi.tx_credits_wq);
+}
+
+static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
+			       u32 cmd_id)
+{
+	int ret = -EOPNOTSUPP;
+
+	might_sleep();
+
+	if (cmd_id == WMI_CMD_UNSUPPORTED) {
+		ath10k_warn("wmi command %d is not supported by firmware\n",
+			    cmd_id);
+		return ret;
+	}
+
+	wait_event_timeout(ar->wmi.tx_credits_wq, ({
+		/* try to send pending beacons first. they take priority */
+		ath10k_wmi_tx_beacons_nowait(ar);
+
+		ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id);
+		(ret != -EAGAIN);
+	}), 3*HZ);
+
+	if (ret)
 		dev_kfree_skb_any(skb);
-		atomic_dec(&ar->wmi.pending_tx_count);
-		return status;
+
+	return ret;
 	}
 
-	return 0;
+int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
+{
+	int ret = 0;
+	struct wmi_mgmt_tx_cmd *cmd;
+	struct ieee80211_hdr *hdr;
+	struct sk_buff *wmi_skb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int len;
+	u16 fc;
+
+	hdr = (struct ieee80211_hdr *)skb->data;
+	fc = le16_to_cpu(hdr->frame_control);
+
+	if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control)))
+		return -EINVAL;
+
+	len = sizeof(cmd->hdr) + skb->len;
+	len = round_up(len, 4);
+
+	wmi_skb = ath10k_wmi_alloc_skb(len);
+	if (!wmi_skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_mgmt_tx_cmd *)wmi_skb->data;
+
+	cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id);
+	cmd->hdr.tx_rate = 0;
+	cmd->hdr.tx_power = 0;
+	cmd->hdr.buf_len = __cpu_to_le32((u32)(skb->len));
+
+	memcpy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr), ETH_ALEN);
+	memcpy(cmd->buf, skb->data, skb->len);
+
+	ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n",
+		   wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE,
+		   fc & IEEE80211_FCTL_STYPE);
+
+	/* Send the management frame buffer to the target */
+	ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid);
+	if (ret)
+		return ret;
+
+	/* TODO: report tx status to mac80211 - temporary just ACK */
+	info->flags |= IEEE80211_TX_STAT_ACK;
+	ieee80211_tx_status_irqsafe(ar->hw, skb);
+
+	return ret;
 }
 
 static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
@@ -308,8 +867,11 @@
 
 static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
 {
-	struct wmi_mgmt_rx_event *event = (struct wmi_mgmt_rx_event *)skb->data;
+	struct wmi_mgmt_rx_event_v1 *ev_v1;
+	struct wmi_mgmt_rx_event_v2 *ev_v2;
+	struct wmi_mgmt_rx_hdr_v1 *ev_hdr;
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+	struct ieee80211_channel *ch;
 	struct ieee80211_hdr *hdr;
 	u32 rx_status;
 	u32 channel;
@@ -318,19 +880,35 @@
 	u32 rate;
 	u32 buf_len;
 	u16 fc;
+	int pull_len;
 
-	channel   = __le32_to_cpu(event->hdr.channel);
-	buf_len   = __le32_to_cpu(event->hdr.buf_len);
-	rx_status = __le32_to_cpu(event->hdr.status);
-	snr       = __le32_to_cpu(event->hdr.snr);
-	phy_mode  = __le32_to_cpu(event->hdr.phy_mode);
-	rate	  = __le32_to_cpu(event->hdr.rate);
+	if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) {
+		ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data;
+		ev_hdr = &ev_v2->hdr.v1;
+		pull_len = sizeof(*ev_v2);
+	} else {
+		ev_v1 = (struct wmi_mgmt_rx_event_v1 *)skb->data;
+		ev_hdr = &ev_v1->hdr;
+		pull_len = sizeof(*ev_v1);
+	}
+
+	channel   = __le32_to_cpu(ev_hdr->channel);
+	buf_len   = __le32_to_cpu(ev_hdr->buf_len);
+	rx_status = __le32_to_cpu(ev_hdr->status);
+	snr       = __le32_to_cpu(ev_hdr->snr);
+	phy_mode  = __le32_to_cpu(ev_hdr->phy_mode);
+	rate	  = __le32_to_cpu(ev_hdr->rate);
 
 	memset(status, 0, sizeof(*status));
 
 	ath10k_dbg(ATH10K_DBG_MGMT,
 		   "event mgmt rx status %08x\n", rx_status);
 
+	if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
 	if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) {
 		dev_kfree_skb(skb);
 		return 0;
@@ -346,17 +924,39 @@
 	if (rx_status & WMI_RX_STATUS_ERR_MIC)
 		status->flag |= RX_FLAG_MMIC_ERROR;
 
+	/* HW can Rx CCK rates on 5GHz. In that case phy_mode is set to
+	 * MODE_11B. This means phy_mode is not a reliable source for the band
+	 * of mgmt rx. */
+
+	ch = ar->scan_channel;
+	if (!ch)
+		ch = ar->rx_channel;
+
+	if (ch) {
+		status->band = ch->band;
+
+		if (phy_mode == MODE_11B &&
+		    status->band == IEEE80211_BAND_5GHZ)
+			ath10k_dbg(ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n");
+	} else {
+		ath10k_warn("using (unreliable) phy_mode to extract band for mgmt rx\n");
 	status->band = phy_mode_to_band(phy_mode);
+	}
+
 	status->freq = ieee80211_channel_to_frequency(channel, status->band);
 	status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR;
 	status->rate_idx = get_rate_idx(rate, status->band);
 
-	skb_pull(skb, sizeof(event->hdr));
+	skb_pull(skb, pull_len);
 
 	hdr = (struct ieee80211_hdr *)skb->data;
 	fc = le16_to_cpu(hdr->frame_control);
 
-	if (fc & IEEE80211_FCTL_PROTECTED) {
+	/* FW delivers WEP Shared Auth frame with Protected Bit set and
+	 * encrypted payload. However in case of PMF it delivers decrypted
+	 * frames with Protected Bit set. */
+	if (ieee80211_has_protected(hdr->frame_control) &&
+	    !ieee80211_is_auth(hdr->frame_control)) {
 		status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED |
 				RX_FLAG_MMIC_STRIPPED;
 		hdr->frame_control = __cpu_to_le16(fc &
@@ -383,9 +983,82 @@
 	return 0;
 }
 
+static int freq_to_idx(struct ath10k *ar, int freq)
+{
+	struct ieee80211_supported_band *sband;
+	int band, ch, idx = 0;
+
+	for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
+		sband = ar->hw->wiphy->bands[band];
+		if (!sband)
+			continue;
+
+		for (ch = 0; ch < sband->n_channels; ch++, idx++)
+			if (sband->channels[ch].center_freq == freq)
+				goto exit;
+	}
+
+exit:
+	return idx;
+}
+
 static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
 {
-	ath10k_dbg(ATH10K_DBG_WMI, "WMI_CHAN_INFO_EVENTID\n");
+	struct wmi_chan_info_event *ev;
+	struct survey_info *survey;
+	u32 err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count;
+	int idx;
+
+	ev = (struct wmi_chan_info_event *)skb->data;
+
+	err_code = __le32_to_cpu(ev->err_code);
+	freq = __le32_to_cpu(ev->freq);
+	cmd_flags = __le32_to_cpu(ev->cmd_flags);
+	noise_floor = __le32_to_cpu(ev->noise_floor);
+	rx_clear_count = __le32_to_cpu(ev->rx_clear_count);
+	cycle_count = __le32_to_cpu(ev->cycle_count);
+
+	ath10k_dbg(ATH10K_DBG_WMI,
+		   "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n",
+		   err_code, freq, cmd_flags, noise_floor, rx_clear_count,
+		   cycle_count);
+
+	spin_lock_bh(&ar->data_lock);
+
+	if (!ar->scan.in_progress) {
+		ath10k_warn("chan info event without a scan request?\n");
+		goto exit;
+	}
+
+	idx = freq_to_idx(ar, freq);
+	if (idx >= ARRAY_SIZE(ar->survey)) {
+		ath10k_warn("chan info: invalid frequency %d (idx %d out of bounds)\n",
+			    freq, idx);
+		goto exit;
+	}
+
+	if (cmd_flags & WMI_CHAN_INFO_FLAG_COMPLETE) {
+		/* During scanning chan info is reported twice for each
+		 * visited channel. The reported cycle count is global
+		 * and per-channel cycle count must be calculated */
+
+		cycle_count -= ar->survey_last_cycle_count;
+		rx_clear_count -= ar->survey_last_rx_clear_count;
+
+		survey = &ar->survey[idx];
+		survey->channel_time = WMI_CHAN_INFO_MSEC(cycle_count);
+		survey->channel_time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count);
+		survey->noise = noise_floor;
+		survey->filled = SURVEY_INFO_CHANNEL_TIME |
+				 SURVEY_INFO_CHANNEL_TIME_RX |
+				 SURVEY_INFO_NOISE_DBM;
+	}
+
+	ar->survey_last_rx_clear_count = rx_clear_count;
+	ar->survey_last_cycle_count = cycle_count;
+
+exit:
+	spin_unlock_bh(&ar->data_lock);
 }
 
 static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb)
@@ -393,9 +1066,14 @@
 	ath10k_dbg(ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n");
 }
 
-static void ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
+static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
 {
-	ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_MESG_EVENTID\n");
+	ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug mesg len %d\n",
+		   skb->len);
+
+	trace_ath10k_wmi_dbglog(skb->data, skb->len);
+
+	return 0;
 }
 
 static void ath10k_wmi_event_update_stats(struct ath10k *ar,
@@ -433,7 +1111,27 @@
 static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar,
 					      struct sk_buff *skb)
 {
-	ath10k_dbg(ATH10K_DBG_WMI, "WMI_PEER_STA_KICKOUT_EVENTID\n");
+	struct wmi_peer_sta_kickout_event *ev;
+	struct ieee80211_sta *sta;
+
+	ev = (struct wmi_peer_sta_kickout_event *)skb->data;
+
+	ath10k_dbg(ATH10K_DBG_WMI, "wmi event peer sta kickout %pM\n",
+		   ev->peer_macaddr.addr);
+
+	rcu_read_lock();
+
+	sta = ieee80211_find_sta_by_ifaddr(ar->hw, ev->peer_macaddr.addr, NULL);
+	if (!sta) {
+		ath10k_warn("Spurious quick kickout for STA %pM\n",
+			    ev->peer_macaddr.addr);
+		goto exit;
+	}
+
+	ieee80211_report_low_ack(sta, 10);
+
+exit:
+	rcu_read_unlock();
 }
 
 /*
@@ -501,7 +1199,7 @@
 	ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies,
 				    (u8 *)skb_tail_pointer(bcn) - ies);
 	if (!ie) {
-		/* highly unlikely for mac80211 */
+		if (arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
 		ath10k_warn("no tim ie found;\n");
 		return;
 	}
@@ -534,6 +1232,13 @@
 	tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast);
 	memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len);
 
+	if (tim->dtim_count == 0) {
+		ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true;
+
+		if (__le32_to_cpu(bcn_info->tim_info.tim_mcast) == 1)
+			ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true;
+	}
+
 	ath10k_dbg(ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n",
 		   tim->dtim_count, tim->dtim_period,
 		   tim->bitmap_ctrl, pvm_len);
@@ -654,10 +1359,8 @@
 	int i = -1;
 	struct wmi_bcn_info *bcn_info;
 	struct ath10k_vif *arvif;
-	struct wmi_bcn_tx_arg arg;
 	struct sk_buff *bcn;
-	int vdev_id = 0;
-	int ret;
+	int ret, vdev_id = 0;
 
 	ath10k_dbg(ATH10K_DBG_MGMT, "WMI_HOST_SWBA_EVENTID\n");
 
@@ -704,6 +1407,17 @@
 			continue;
 		}
 
+		/* There are no completions for beacons so wait for next SWBA
+		 * before telling mac80211 to decrement CSA counter
+		 *
+		 * Once CSA counter is completed stop sending beacons until
+		 * actual channel switch is done */
+		if (arvif->vif->csa_active &&
+		    ieee80211_csa_is_complete(arvif->vif)) {
+			ieee80211_csa_finish(arvif->vif);
+			continue;
+		}
+
 		bcn = ieee80211_beacon_get(ar->hw, arvif->vif);
 		if (!bcn) {
 			ath10k_warn("could not get mac80211 beacon\n");
@@ -714,17 +1428,35 @@
 		ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info);
 		ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info);
 
-		arg.vdev_id = arvif->vdev_id;
-		arg.tx_rate = 0;
-		arg.tx_power = 0;
-		arg.bcn = bcn->data;
-		arg.bcn_len = bcn->len;
+		spin_lock_bh(&ar->data_lock);
 
-		ret = ath10k_wmi_beacon_send(ar, &arg);
-		if (ret)
-			ath10k_warn("could not send beacon (%d)\n", ret);
+		if (arvif->beacon) {
+			if (!arvif->beacon_sent)
+				ath10k_warn("SWBA overrun on vdev %d\n",
+					    arvif->vdev_id);
+
+			dma_unmap_single(arvif->ar->dev,
+					 ATH10K_SKB_CB(arvif->beacon)->paddr,
+					 arvif->beacon->len, DMA_TO_DEVICE);
+			dev_kfree_skb_any(arvif->beacon);
+		}
+
+		ATH10K_SKB_CB(bcn)->paddr = dma_map_single(arvif->ar->dev,
+							   bcn->data, bcn->len,
+							   DMA_TO_DEVICE);
+		ret = dma_mapping_error(arvif->ar->dev,
+					ATH10K_SKB_CB(bcn)->paddr);
+		if (ret) {
+			ath10k_warn("failed to map beacon: %d\n", ret);
+			goto skip;
+		}
+
+		arvif->beacon = bcn;
+		arvif->beacon_sent = false;
 
-		dev_kfree_skb_any(bcn);
+		ath10k_wmi_tx_beacon_nowait(arvif);
+skip:
+		spin_unlock_bh(&ar->data_lock);
 	}
 }
 
@@ -734,9 +1466,259 @@
 	ath10k_dbg(ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n");
 }
 
+static void ath10k_dfs_radar_report(struct ath10k *ar,
+				    struct wmi_single_phyerr_rx_event *event,
+				    struct phyerr_radar_report *rr,
+				    u64 tsf)
+{
+	u32 reg0, reg1, tsf32l;
+	struct pulse_event pe;
+	u64 tsf64;
+	u8 rssi, width;
+
+	reg0 = __le32_to_cpu(rr->reg0);
+	reg1 = __le32_to_cpu(rr->reg1);
+
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "wmi phyerr radar report chirp %d max_width %d agc_total_gain %d pulse_delta_diff %d\n",
+		   MS(reg0, RADAR_REPORT_REG0_PULSE_IS_CHIRP),
+		   MS(reg0, RADAR_REPORT_REG0_PULSE_IS_MAX_WIDTH),
+		   MS(reg0, RADAR_REPORT_REG0_AGC_TOTAL_GAIN),
+		   MS(reg0, RADAR_REPORT_REG0_PULSE_DELTA_DIFF));
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "wmi phyerr radar report pulse_delta_pean %d pulse_sidx %d fft_valid %d agc_mb_gain %d subchan_mask %d\n",
+		   MS(reg0, RADAR_REPORT_REG0_PULSE_DELTA_PEAK),
+		   MS(reg0, RADAR_REPORT_REG0_PULSE_SIDX),
+		   MS(reg1, RADAR_REPORT_REG1_PULSE_SRCH_FFT_VALID),
+		   MS(reg1, RADAR_REPORT_REG1_PULSE_AGC_MB_GAIN),
+		   MS(reg1, RADAR_REPORT_REG1_PULSE_SUBCHAN_MASK));
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "wmi phyerr radar report pulse_tsf_offset 0x%X pulse_dur: %d\n",
+		   MS(reg1, RADAR_REPORT_REG1_PULSE_TSF_OFFSET),
+		   MS(reg1, RADAR_REPORT_REG1_PULSE_DUR));
+
+	if (!ar->dfs_detector)
+		return;
+
+	/* report event to DFS pattern detector */
+	tsf32l = __le32_to_cpu(event->hdr.tsf_timestamp);
+	tsf64 = tsf & (~0xFFFFFFFFULL);
+	tsf64 |= tsf32l;
+
+	width = MS(reg1, RADAR_REPORT_REG1_PULSE_DUR);
+	rssi = event->hdr.rssi_combined;
+
+	/* hardware store this as 8 bit signed value,
+	 * set to zero if negative number
+	 */
+	if (rssi & 0x80)
+		rssi = 0;
+
+	pe.ts = tsf64;
+	pe.freq = ar->hw->conf.chandef.chan->center_freq;
+	pe.width = width;
+	pe.rssi = rssi;
+
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "dfs add pulse freq: %d, width: %d, rssi %d, tsf: %llX\n",
+		   pe.freq, pe.width, pe.rssi, pe.ts);
+
+	ATH10K_DFS_STAT_INC(ar, pulses_detected);
+
+	if (!ar->dfs_detector->add_pulse(ar->dfs_detector, &pe)) {
+		ath10k_dbg(ATH10K_DBG_REGULATORY,
+			   "dfs no pulse pattern detected, yet\n");
+		return;
+	}
+
+	ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs radar detected\n");
+	ATH10K_DFS_STAT_INC(ar, radar_detected);
+
+	/* Control radar events reporting in debugfs file
+	   dfs_block_radar_events */
+	if (ar->dfs_block_radar_events) {
+		ath10k_info("DFS Radar detected, but ignored as requested\n");
+		return;
+	}
+
+	ieee80211_radar_detected(ar->hw);
+}
+
+static int ath10k_dfs_fft_report(struct ath10k *ar,
+				 struct wmi_single_phyerr_rx_event *event,
+				 struct phyerr_fft_report *fftr,
+				 u64 tsf)
+{
+	u32 reg0, reg1;
+	u8 rssi, peak_mag;
+
+	reg0 = __le32_to_cpu(fftr->reg0);
+	reg1 = __le32_to_cpu(fftr->reg1);
+	rssi = event->hdr.rssi_combined;
+
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "wmi phyerr fft report total_gain_db %d base_pwr_db %d fft_chn_idx %d peak_sidx %d\n",
+		   MS(reg0, SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB),
+		   MS(reg0, SEARCH_FFT_REPORT_REG0_BASE_PWR_DB),
+		   MS(reg0, SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX),
+		   MS(reg0, SEARCH_FFT_REPORT_REG0_PEAK_SIDX));
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "wmi phyerr fft report rel_pwr_db %d avgpwr_db %d peak_mag %d num_store_bin %d\n",
+		   MS(reg1, SEARCH_FFT_REPORT_REG1_RELPWR_DB),
+		   MS(reg1, SEARCH_FFT_REPORT_REG1_AVGPWR_DB),
+		   MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG),
+		   MS(reg1, SEARCH_FFT_REPORT_REG1_NUM_STR_BINS_IB));
+
+	peak_mag = MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG);
+
+	/* false event detection */
+	if (rssi == DFS_RSSI_POSSIBLY_FALSE &&
+	    peak_mag < 2 * DFS_PEAK_MAG_THOLD_POSSIBLY_FALSE) {
+		ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs false pulse detected\n");
+		ATH10K_DFS_STAT_INC(ar, pulses_discarded);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void ath10k_wmi_event_dfs(struct ath10k *ar,
+				 struct wmi_single_phyerr_rx_event *event,
+				 u64 tsf)
+{
+	int buf_len, tlv_len, res, i = 0;
+	struct phyerr_tlv *tlv;
+	struct phyerr_radar_report *rr;
+	struct phyerr_fft_report *fftr;
+	u8 *tlv_buf;
+
+	buf_len = __le32_to_cpu(event->hdr.buf_len);
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "wmi event dfs err_code %d rssi %d tsfl 0x%X tsf64 0x%llX len %d\n",
+		   event->hdr.phy_err_code, event->hdr.rssi_combined,
+		   __le32_to_cpu(event->hdr.tsf_timestamp), tsf, buf_len);
+
+	/* Skip event if DFS disabled */
+	if (!config_enabled(CONFIG_ATH10K_DFS_CERTIFIED))
+		return;
+
+	ATH10K_DFS_STAT_INC(ar, pulses_total);
+
+	while (i < buf_len) {
+		if (i + sizeof(*tlv) > buf_len) {
+			ath10k_warn("too short buf for tlv header (%d)\n", i);
+			return;
+		}
+
+		tlv = (struct phyerr_tlv *)&event->bufp[i];
+		tlv_len = __le16_to_cpu(tlv->len);
+		tlv_buf = &event->bufp[i + sizeof(*tlv)];
+		ath10k_dbg(ATH10K_DBG_REGULATORY,
+			   "wmi event dfs tlv_len %d tlv_tag 0x%02X tlv_sig 0x%02X\n",
+			   tlv_len, tlv->tag, tlv->sig);
+
+		switch (tlv->tag) {
+		case PHYERR_TLV_TAG_RADAR_PULSE_SUMMARY:
+			if (i + sizeof(*tlv) + sizeof(*rr) > buf_len) {
+				ath10k_warn("too short radar pulse summary (%d)\n",
+					    i);
+				return;
+			}
+
+			rr = (struct phyerr_radar_report *)tlv_buf;
+			ath10k_dfs_radar_report(ar, event, rr, tsf);
+			break;
+		case PHYERR_TLV_TAG_SEARCH_FFT_REPORT:
+			if (i + sizeof(*tlv) + sizeof(*fftr) > buf_len) {
+				ath10k_warn("too short fft report (%d)\n", i);
+				return;
+			}
+
+			fftr = (struct phyerr_fft_report *)tlv_buf;
+			res = ath10k_dfs_fft_report(ar, event, fftr, tsf);
+			if (res)
+				return;
+			break;
+		}
+
+		i += sizeof(*tlv) + tlv_len;
+	}
+}
+
+static void ath10k_wmi_event_spectral_scan(struct ath10k *ar,
+				struct wmi_single_phyerr_rx_event *event,
+				u64 tsf)
+{
+	ath10k_dbg(ATH10K_DBG_WMI, "wmi event spectral scan\n");
+}
+
 static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
 {
-	ath10k_dbg(ATH10K_DBG_WMI, "WMI_PHYERR_EVENTID\n");
+	struct wmi_comb_phyerr_rx_event *comb_event;
+	struct wmi_single_phyerr_rx_event *event;
+	u32 count, i, buf_len, phy_err_code;
+	u64 tsf;
+	int left_len = skb->len;
+
+	ATH10K_DFS_STAT_INC(ar, phy_errors);
+
+	/* Check if combined event available */
+	if (left_len < sizeof(*comb_event)) {
+		ath10k_warn("wmi phyerr combined event wrong len\n");
+		return;
+	}
+
+	left_len -= sizeof(*comb_event);
+
+	/* Check number of included events */
+	comb_event = (struct wmi_comb_phyerr_rx_event *)skb->data;
+	count = __le32_to_cpu(comb_event->hdr.num_phyerr_events);
+
+	tsf = __le32_to_cpu(comb_event->hdr.tsf_u32);
+	tsf <<= 32;
+	tsf |= __le32_to_cpu(comb_event->hdr.tsf_l32);
+
+	ath10k_dbg(ATH10K_DBG_WMI,
+		   "wmi event phyerr count %d tsf64 0x%llX\n",
+		   count, tsf);
+
+	event = (struct wmi_single_phyerr_rx_event *)comb_event->bufp;
+	for (i = 0; i < count; i++) {
+		/* Check if we can read event header */
+		if (left_len < sizeof(*event)) {
+			ath10k_warn("single event (%d) wrong head len\n", i);
+			return;
+		}
+
+		left_len -= sizeof(*event);
+
+		buf_len = __le32_to_cpu(event->hdr.buf_len);
+		phy_err_code = event->hdr.phy_err_code;
+
+		if (left_len < buf_len) {
+			ath10k_warn("single event (%d) wrong buf len\n", i);
+			return;
+		}
+
+		left_len -= buf_len;
+
+		switch (phy_err_code) {
+		case PHY_ERROR_RADAR:
+			ath10k_wmi_event_dfs(ar, event, tsf);
+			break;
+		case PHY_ERROR_SPECTRAL_SCAN:
+			ath10k_wmi_event_spectral_scan(ar, event, tsf);
+			break;
+		case PHY_ERROR_FALSE_RADAR_EXT:
+			ath10k_wmi_event_dfs(ar, event, tsf);
+			ath10k_wmi_event_spectral_scan(ar, event, tsf);
+			break;
+		default:
+			break;
+		}
+
+		event += sizeof(*event) + buf_len;
+	}
 }
 
 static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb)
@@ -753,7 +1735,35 @@
 static void ath10k_wmi_event_debug_print(struct ath10k *ar,
 				  struct sk_buff *skb)
 {
-	ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_PRINT_EVENTID\n");
+	char buf[101], c;
+	int i;
+
+	for (i = 0; i < sizeof(buf) - 1; i++) {
+		if (i >= skb->len)
+			break;
+
+		c = skb->data[i];
+
+		if (c == '\0')
+			break;
+
+		if (isascii(c) && isprint(c))
+			buf[i] = c;
+		else
+			buf[i] = '.';
+	}
+
+	if (i == sizeof(buf) - 1)
+		ath10k_warn("wmi debug print truncated: %d\n", skb->len);
+
+	/* for some reason the debug prints end with \n, remove that */
+	if (skb->data[i - 1] == '\n')
+		i--;
+
+	/* the last byte is always reserved for the null character */
+	buf[i] = '\0';
+
+	ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug print '%s'\n", buf);
 }
 
 static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
@@ -839,6 +1849,55 @@
 	ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n");
 }
 
+static void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar,
+					     struct sk_buff *skb)
+{
+	ath10k_dbg(ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n");
+}
+
+static void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar,
+					      struct sk_buff *skb)
+{
+	ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n");
+}
+
+static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar,
+					     struct sk_buff *skb)
+{
+	ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n");
+}
+
+static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id,
+				      u32 num_units, u32 unit_len)
+{
+	dma_addr_t paddr;
+	u32 pool_size;
+	int idx = ar->wmi.num_mem_chunks;
+
+	pool_size = num_units * round_up(unit_len, 4);
+
+	if (!pool_size)
+		return -EINVAL;
+
+	ar->wmi.mem_chunks[idx].vaddr = dma_alloc_coherent(ar->dev,
+							   pool_size,
+							   &paddr,
+							   GFP_ATOMIC);
+	if (!ar->wmi.mem_chunks[idx].vaddr) {
+		ath10k_warn("failed to allocate memory chunk\n");
+		return -ENOMEM;
+	}
+
+	memset(ar->wmi.mem_chunks[idx].vaddr, 0, pool_size);
+
+	ar->wmi.mem_chunks[idx].paddr = paddr;
+	ar->wmi.mem_chunks[idx].len = pool_size;
+	ar->wmi.mem_chunks[idx].req_id = req_id;
+	ar->wmi.num_mem_chunks++;
+
+	return 0;
+}
+
 static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
 					      struct sk_buff *skb)
 {
@@ -861,6 +1920,17 @@
 		(__le32_to_cpu(ev->sw_version_1) & 0xffff0000) >> 16;
 	ar->fw_version_build = (__le32_to_cpu(ev->sw_version_1) & 0x0000ffff);
 	ar->phy_capability = __le32_to_cpu(ev->phy_capability);
+	ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains);
+
+	/* only manually set fw features when not using FW IE format */
+	if (ar->fw_api == 1 && ar->fw_version_build > 636)
+		set_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features);
+
+	if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) {
+		ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n",
+			    ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM);
+		ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM;
+	}
 
 	ar->ath_common.regulatory.current_rd =
 		__le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
@@ -885,7 +1955,7 @@
 	}
 
 	ath10k_dbg(ATH10K_DBG_WMI,
-		   "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u\n",
+		   "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n",
 		   __le32_to_cpu(ev->sw_version),
 		   __le32_to_cpu(ev->sw_version_1),
 		   __le32_to_cpu(ev->abi_version),
@@ -894,7 +1964,110 @@
 		   __le32_to_cpu(ev->vht_cap_info),
 		   __le32_to_cpu(ev->vht_supp_mcs),
 		   __le32_to_cpu(ev->sys_cap_info),
-		   __le32_to_cpu(ev->num_mem_reqs));
+		   __le32_to_cpu(ev->num_mem_reqs),
+		   __le32_to_cpu(ev->num_rf_chains));
+
+	complete(&ar->wmi.service_ready);
+}
+
+static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar,
+						  struct sk_buff *skb)
+{
+	u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
+	int ret;
+	struct wmi_service_ready_event_10x *ev = (void *)skb->data;
+
+	if (skb->len < sizeof(*ev)) {
+		ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n",
+			    skb->len, sizeof(*ev));
+		return;
+	}
+
+	ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power);
+	ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power);
+	ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info);
+	ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info);
+	ar->fw_version_major =
+		(__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24;
+	ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff);
+	ar->phy_capability = __le32_to_cpu(ev->phy_capability);
+	ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains);
+
+	if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) {
+		ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n",
+			    ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM);
+		ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM;
+	}
+
+	ar->ath_common.regulatory.current_rd =
+		__le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
+
+	ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap,
+				      sizeof(ev->wmi_service_bitmap));
+
+	if (strlen(ar->hw->wiphy->fw_version) == 0) {
+		snprintf(ar->hw->wiphy->fw_version,
+			 sizeof(ar->hw->wiphy->fw_version),
+			 "%u.%u",
+			 ar->fw_version_major,
+			 ar->fw_version_minor);
+	}
+
+	num_mem_reqs = __le32_to_cpu(ev->num_mem_reqs);
+
+	if (num_mem_reqs > ATH10K_MAX_MEM_REQS) {
+		ath10k_warn("requested memory chunks number (%d) exceeds the limit\n",
+			    num_mem_reqs);
+		return;
+	}
+
+	if (!num_mem_reqs)
+		goto exit;
+
+	ath10k_dbg(ATH10K_DBG_WMI, "firmware has requested %d memory chunks\n",
+		   num_mem_reqs);
+
+	for (i = 0; i < num_mem_reqs; ++i) {
+		req_id = __le32_to_cpu(ev->mem_reqs[i].req_id);
+		num_units = __le32_to_cpu(ev->mem_reqs[i].num_units);
+		unit_size = __le32_to_cpu(ev->mem_reqs[i].unit_size);
+		num_unit_info = __le32_to_cpu(ev->mem_reqs[i].num_unit_info);
+
+		if (num_unit_info & NUM_UNITS_IS_NUM_PEERS)
+			/* number of units to allocate is number of
+			 * peers, 1 extra for self peer on target */
+			/* this needs to be tied, host and target
+			 * can get out of sync */
+			num_units = TARGET_10X_NUM_PEERS + 1;
+		else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS)
+			num_units = TARGET_10X_NUM_VDEVS + 1;
+
+		ath10k_dbg(ATH10K_DBG_WMI,
+			   "wmi mem_req_id %d num_units %d num_unit_info %d unit size %d actual units %d\n",
+			   req_id,
+			   __le32_to_cpu(ev->mem_reqs[i].num_units),
+			   num_unit_info,
+			   unit_size,
+			   num_units);
+
+		ret = ath10k_wmi_alloc_host_mem(ar, req_id, num_units,
+						unit_size);
+		if (ret)
+			return;
+	}
+
+exit:
+	ath10k_dbg(ATH10K_DBG_WMI,
+		   "wmi event service ready sw_ver 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n",
+		   __le32_to_cpu(ev->sw_version),
+		   __le32_to_cpu(ev->abi_version),
+		   __le32_to_cpu(ev->phy_capability),
+		   __le32_to_cpu(ev->ht_cap_info),
+		   __le32_to_cpu(ev->vht_cap_info),
+		   __le32_to_cpu(ev->vht_supp_mcs),
+		   __le32_to_cpu(ev->sys_cap_info),
+		   __le32_to_cpu(ev->num_mem_reqs),
+		   __le32_to_cpu(ev->num_rf_chains));
 
 	complete(&ar->wmi.service_ready);
 }
@@ -909,17 +2082,17 @@
 	memcpy(ar->mac_addr, ev->mac_addr.addr, ETH_ALEN);
 
 	ath10k_dbg(ATH10K_DBG_WMI,
-		   "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n",
+		   "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d skb->len %i ev-sz %zu\n",
 		   __le32_to_cpu(ev->sw_version),
 		   __le32_to_cpu(ev->abi_version),
 		   ev->mac_addr.addr,
-		   __le32_to_cpu(ev->status));
+		   __le32_to_cpu(ev->status), skb->len, sizeof(*ev));
 
 	complete(&ar->wmi.unified_ready);
 	return 0;
 }
 
-static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb)
+static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb)
 {
 	struct wmi_cmd_hdr *cmd_hdr;
 	enum wmi_event_id id;
@@ -1038,64 +2211,158 @@
 	dev_kfree_skb(skb);
 }
 
-static void ath10k_wmi_event_work(struct work_struct *work)
+static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb)
 {
-	struct ath10k *ar = container_of(work, struct ath10k,
-					 wmi.wmi_event_work);
-	struct sk_buff *skb;
+	struct wmi_cmd_hdr *cmd_hdr;
+	enum wmi_10x_event_id id;
+	u16 len;
 
-	for (;;) {
-		skb = skb_dequeue(&ar->wmi.wmi_event_list);
-		if (!skb)
-			break;
+	cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+	id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
 
-		ath10k_wmi_event_process(ar, skb);
-	}
-}
+	if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
+		return;
 
-static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
-{
-	struct wmi_cmd_hdr *cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
-	enum wmi_event_id event_id;
+	len = skb->len;
 
-	event_id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
+	trace_ath10k_wmi_event(id, skb->data, skb->len);
 
-	/* some events require to be handled ASAP
-	 * thus can't be defered to a worker thread */
-	switch (event_id) {
-	case WMI_HOST_SWBA_EVENTID:
-	case WMI_MGMT_RX_EVENTID:
-		ath10k_wmi_event_process(ar, skb);
+	switch (id) {
+	case WMI_10X_MGMT_RX_EVENTID:
+		ath10k_wmi_event_mgmt_rx(ar, skb);
+		/* mgmt_rx() owns the skb now! */
 		return;
+	case WMI_10X_SCAN_EVENTID:
+		ath10k_wmi_event_scan(ar, skb);
+		break;
+	case WMI_10X_CHAN_INFO_EVENTID:
+		ath10k_wmi_event_chan_info(ar, skb);
+		break;
+	case WMI_10X_ECHO_EVENTID:
+		ath10k_wmi_event_echo(ar, skb);
+		break;
+	case WMI_10X_DEBUG_MESG_EVENTID:
+		ath10k_wmi_event_debug_mesg(ar, skb);
+		break;
+	case WMI_10X_UPDATE_STATS_EVENTID:
+		ath10k_wmi_event_update_stats(ar, skb);
+		break;
+	case WMI_10X_VDEV_START_RESP_EVENTID:
+		ath10k_wmi_event_vdev_start_resp(ar, skb);
+		break;
+	case WMI_10X_VDEV_STOPPED_EVENTID:
+		ath10k_wmi_event_vdev_stopped(ar, skb);
+		break;
+	case WMI_10X_PEER_STA_KICKOUT_EVENTID:
+		ath10k_wmi_event_peer_sta_kickout(ar, skb);
+		break;
+	case WMI_10X_HOST_SWBA_EVENTID:
+		ath10k_wmi_event_host_swba(ar, skb);
+		break;
+	case WMI_10X_TBTTOFFSET_UPDATE_EVENTID:
+		ath10k_wmi_event_tbttoffset_update(ar, skb);
+		break;
+	case WMI_10X_PHYERR_EVENTID:
+		ath10k_wmi_event_phyerr(ar, skb);
+		break;
+	case WMI_10X_ROAM_EVENTID:
+		ath10k_wmi_event_roam(ar, skb);
+		break;
+	case WMI_10X_PROFILE_MATCH:
+		ath10k_wmi_event_profile_match(ar, skb);
+		break;
+	case WMI_10X_DEBUG_PRINT_EVENTID:
+		ath10k_wmi_event_debug_print(ar, skb);
+		break;
+	case WMI_10X_PDEV_QVIT_EVENTID:
+		ath10k_wmi_event_pdev_qvit(ar, skb);
+		break;
+	case WMI_10X_WLAN_PROFILE_DATA_EVENTID:
+		ath10k_wmi_event_wlan_profile_data(ar, skb);
+		break;
+	case WMI_10X_RTT_MEASUREMENT_REPORT_EVENTID:
+		ath10k_wmi_event_rtt_measurement_report(ar, skb);
+		break;
+	case WMI_10X_TSF_MEASUREMENT_REPORT_EVENTID:
+		ath10k_wmi_event_tsf_measurement_report(ar, skb);
+		break;
+	case WMI_10X_RTT_ERROR_REPORT_EVENTID:
+		ath10k_wmi_event_rtt_error_report(ar, skb);
+		break;
+	case WMI_10X_WOW_WAKEUP_HOST_EVENTID:
+		ath10k_wmi_event_wow_wakeup_host(ar, skb);
+		break;
+	case WMI_10X_DCS_INTERFERENCE_EVENTID:
+		ath10k_wmi_event_dcs_interference(ar, skb);
+		break;
+	case WMI_10X_PDEV_TPC_CONFIG_EVENTID:
+		ath10k_wmi_event_pdev_tpc_config(ar, skb);
+		break;
+	case WMI_10X_INST_RSSI_STATS_EVENTID:
+		ath10k_wmi_event_inst_rssi_stats(ar, skb);
+		break;
+	case WMI_10X_VDEV_STANDBY_REQ_EVENTID:
+		ath10k_wmi_event_vdev_standby_req(ar, skb);
+		break;
+	case WMI_10X_VDEV_RESUME_REQ_EVENTID:
+		ath10k_wmi_event_vdev_resume_req(ar, skb);
+		break;
+	case WMI_10X_SERVICE_READY_EVENTID:
+		ath10k_wmi_10x_service_ready_event_rx(ar, skb);
+		break;
+	case WMI_10X_READY_EVENTID:
+		ath10k_wmi_ready_event_rx(ar, skb);
+		break;
 	default:
+		ath10k_warn("Unknown eventid: %d\n", id);
 		break;
 	}
 
-	skb_queue_tail(&ar->wmi.wmi_event_list, skb);
-	queue_work(ar->workqueue, &ar->wmi.wmi_event_work);
+	dev_kfree_skb(skb);
+}
+
+
+static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+		ath10k_wmi_10x_process_rx(ar, skb);
+	else
+		ath10k_wmi_main_process_rx(ar, skb);
 }
 
 /* WMI Initialization functions */
 int ath10k_wmi_attach(struct ath10k *ar)
 {
+	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+		ar->wmi.cmd = &wmi_10x_cmd_map;
+		ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
+		ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
+	} else {
+		ar->wmi.cmd = &wmi_cmd_map;
+		ar->wmi.vdev_param = &wmi_vdev_param_map;
+		ar->wmi.pdev_param = &wmi_pdev_param_map;
+	}
+
 	init_completion(&ar->wmi.service_ready);
 	init_completion(&ar->wmi.unified_ready);
-	init_waitqueue_head(&ar->wmi.wq);
-
-	skb_queue_head_init(&ar->wmi.wmi_event_list);
-	INIT_WORK(&ar->wmi.wmi_event_work, ath10k_wmi_event_work);
+	init_waitqueue_head(&ar->wmi.tx_credits_wq);
 
 	return 0;
 }
 
 void ath10k_wmi_detach(struct ath10k *ar)
 {
-	/* HTC should've drained the packets already */
-	if (WARN_ON(atomic_read(&ar->wmi.pending_tx_count) > 0))
-		ath10k_warn("there are still pending packets\n");
+	int i;
 
-	cancel_work_sync(&ar->wmi.wmi_event_work);
-	skb_queue_purge(&ar->wmi.wmi_event_list);
+	/* free the host memory chunks requested by firmware */
+	for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+		dma_free_coherent(ar->dev,
+				  ar->wmi.mem_chunks[i].len,
+				  ar->wmi.mem_chunks[i].vaddr,
+				  ar->wmi.mem_chunks[i].paddr);
+	}
+
+	ar->wmi.num_mem_chunks = 0;
 }
 
 int ath10k_wmi_connect_htc_service(struct ath10k *ar)
@@ -1110,11 +2377,12 @@
 	/* these fields are the same for all service endpoints */
 	conn_req.ep_ops.ep_tx_complete = ath10k_wmi_htc_tx_complete;
 	conn_req.ep_ops.ep_rx_complete = ath10k_wmi_process_rx;
+	conn_req.ep_ops.ep_tx_credits = ath10k_wmi_op_ep_tx_credits;
 
 	/* connect to control service */
 	conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
 
-	status = ath10k_htc_connect_service(ar->htc, &conn_req, &conn_resp);
+	status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp);
 	if (status) {
 		ath10k_warn("failed to connect to WMI CONTROL service status: %d\n",
 			    status);
@@ -1146,7 +2414,8 @@
 		   "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n",
 		   rd, rd2g, rd5g, ctl2g, ctl5g);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_REGDOMAIN_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->pdev_set_regdomain_cmdid);
 }
 
 int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
@@ -1154,6 +2423,7 @@
 {
 	struct wmi_set_channel_cmd *cmd;
 	struct sk_buff *skb;
+	u32 ch_flags = 0;
 
 	if (arg->passive)
 		return -EINVAL;
@@ -1162,10 +2432,14 @@
 	if (!skb)
 		return -ENOMEM;
 
+	if (arg->chan_radar)
+		ch_flags |= WMI_CHAN_FLAG_DFS;
+
 	cmd = (struct wmi_set_channel_cmd *)skb->data;
 	cmd->chan.mhz               = __cpu_to_le32(arg->freq);
 	cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq);
 	cmd->chan.mode              = arg->mode;
+	cmd->chan.flags		   |= __cpu_to_le32(ch_flags);
 	cmd->chan.min_power         = arg->min_power;
 	cmd->chan.max_power         = arg->max_power;
 	cmd->chan.reg_power         = arg->max_reg_power;
@@ -1176,10 +2450,11 @@
 		   "wmi set channel mode %d freq %d\n",
 		   arg->mode, arg->freq);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_CHANNEL_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->pdev_set_channel_cmdid);
 }
 
-int ath10k_wmi_pdev_suspend_target(struct ath10k *ar)
+int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt)
 {
 	struct wmi_pdev_suspend_cmd *cmd;
 	struct sk_buff *skb;
@@ -1189,9 +2464,9 @@
 		return -ENOMEM;
 
 	cmd = (struct wmi_pdev_suspend_cmd *)skb->data;
-	cmd->suspend_opt = WMI_PDEV_SUSPEND;
+	cmd->suspend_opt = __cpu_to_le32(suspend_opt);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SUSPEND_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid);
 }
 
 int ath10k_wmi_pdev_resume_target(struct ath10k *ar)
@@ -1202,15 +2477,19 @@
 	if (skb == NULL)
 		return -ENOMEM;
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_RESUME_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid);
 }
 
-int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
-			      u32 value)
+int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value)
 {
 	struct wmi_pdev_set_param_cmd *cmd;
 	struct sk_buff *skb;
 
+	if (id == WMI_PDEV_PARAM_UNSUPPORTED) {
+		ath10k_warn("pdev param %d not supported by firmware\n", id);
+		return -EOPNOTSUPP;
+	}
+
 	skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
 	if (!skb)
 		return -ENOMEM;
@@ -1221,15 +2500,16 @@
 
 	ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n",
 		   id, value);
-	return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_PARAM_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid);
 }
 
-int ath10k_wmi_cmd_init(struct ath10k *ar)
+static int ath10k_wmi_main_cmd_init(struct ath10k *ar)
 {
 	struct wmi_init_cmd *cmd;
 	struct sk_buff *buf;
 	struct wmi_resource_config config = {};
-	u32 val;
+	u32 len, val;
+	int i;
 
 	config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS);
 	config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS);
@@ -1282,22 +2562,157 @@
 	config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC);
 	config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES);
 
-	buf = ath10k_wmi_alloc_skb(sizeof(*cmd));
+	len = sizeof(*cmd) +
+	      (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks);
+
+	buf = ath10k_wmi_alloc_skb(len);
 	if (!buf)
 		return -ENOMEM;
 
 	cmd = (struct wmi_init_cmd *)buf->data;
+
+	if (ar->wmi.num_mem_chunks == 0) {
 	cmd->num_host_mem_chunks = 0;
+		goto out;
+	}
+
+	ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
+		   ar->wmi.num_mem_chunks);
+
+	cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
+
+	for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+		cmd->host_mem_chunks[i].ptr =
+			__cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
+		cmd->host_mem_chunks[i].size =
+			__cpu_to_le32(ar->wmi.mem_chunks[i].len);
+		cmd->host_mem_chunks[i].req_id =
+			__cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
+
+		ath10k_dbg(ATH10K_DBG_WMI,
+			   "wmi chunk %d len %d requested, addr 0x%llx\n",
+			   i,
+			   ar->wmi.mem_chunks[i].len,
+			   (unsigned long long)ar->wmi.mem_chunks[i].paddr);
+	}
+out:
 	memcpy(&cmd->resource_config, &config, sizeof(config));
 
 	ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n");
-	return ath10k_wmi_cmd_send(ar, buf, WMI_INIT_CMDID);
+	return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
+}
+
+static int ath10k_wmi_10x_cmd_init(struct ath10k *ar)
+{
+	struct wmi_init_cmd_10x *cmd;
+	struct sk_buff *buf;
+	struct wmi_resource_config_10x config = {};
+	u32 len, val;
+	int i;
+
+	config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS);
+	config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS);
+	config.num_peer_keys = __cpu_to_le32(TARGET_10X_NUM_PEER_KEYS);
+	config.num_tids = __cpu_to_le32(TARGET_10X_NUM_TIDS);
+	config.ast_skid_limit = __cpu_to_le32(TARGET_10X_AST_SKID_LIMIT);
+	config.tx_chain_mask = __cpu_to_le32(TARGET_10X_TX_CHAIN_MASK);
+	config.rx_chain_mask = __cpu_to_le32(TARGET_10X_RX_CHAIN_MASK);
+	config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
+	config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
+	config.rx_timeout_pri_be = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
+	config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_HI_PRI);
+	config.rx_decap_mode = __cpu_to_le32(TARGET_10X_RX_DECAP_MODE);
+
+	config.scan_max_pending_reqs =
+		__cpu_to_le32(TARGET_10X_SCAN_MAX_PENDING_REQS);
+
+	config.bmiss_offload_max_vdev =
+		__cpu_to_le32(TARGET_10X_BMISS_OFFLOAD_MAX_VDEV);
+
+	config.roam_offload_max_vdev =
+		__cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_VDEV);
+
+	config.roam_offload_max_ap_profiles =
+		__cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES);
+
+	config.num_mcast_groups = __cpu_to_le32(TARGET_10X_NUM_MCAST_GROUPS);
+	config.num_mcast_table_elems =
+		__cpu_to_le32(TARGET_10X_NUM_MCAST_TABLE_ELEMS);
+
+	config.mcast2ucast_mode = __cpu_to_le32(TARGET_10X_MCAST2UCAST_MODE);
+	config.tx_dbg_log_size = __cpu_to_le32(TARGET_10X_TX_DBG_LOG_SIZE);
+	config.num_wds_entries = __cpu_to_le32(TARGET_10X_NUM_WDS_ENTRIES);
+	config.dma_burst_size = __cpu_to_le32(TARGET_10X_DMA_BURST_SIZE);
+	config.mac_aggr_delim = __cpu_to_le32(TARGET_10X_MAC_AGGR_DELIM);
+
+	val = TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK;
+	config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val);
+
+	config.vow_config = __cpu_to_le32(TARGET_10X_VOW_CONFIG);
+
+	config.num_msdu_desc = __cpu_to_le32(TARGET_10X_NUM_MSDU_DESC);
+	config.max_frag_entries = __cpu_to_le32(TARGET_10X_MAX_FRAG_ENTRIES);
+
+	len = sizeof(*cmd) +
+	      (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks);
+
+	buf = ath10k_wmi_alloc_skb(len);
+	if (!buf)
+		return -ENOMEM;
+
+	cmd = (struct wmi_init_cmd_10x *)buf->data;
+
+	if (ar->wmi.num_mem_chunks == 0) {
+		cmd->num_host_mem_chunks = 0;
+		goto out;
+	}
+
+	ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
+		   ar->wmi.num_mem_chunks);
+
+	cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
+
+	for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+		cmd->host_mem_chunks[i].ptr =
+			__cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
+		cmd->host_mem_chunks[i].size =
+			__cpu_to_le32(ar->wmi.mem_chunks[i].len);
+		cmd->host_mem_chunks[i].req_id =
+			__cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
+
+		ath10k_dbg(ATH10K_DBG_WMI,
+			   "wmi chunk %d len %d requested, addr 0x%llx\n",
+			   i,
+			   ar->wmi.mem_chunks[i].len,
+			   (unsigned long long)ar->wmi.mem_chunks[i].paddr);
+	}
+out:
+	memcpy(&cmd->resource_config, &config, sizeof(config));
+
+	ath10k_dbg(ATH10K_DBG_WMI, "wmi init 10x\n");
+	return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
+}
+
+int ath10k_wmi_cmd_init(struct ath10k *ar)
+{
+	int ret;
+
+	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+		ret = ath10k_wmi_10x_cmd_init(ar);
+	else
+		ret = ath10k_wmi_main_cmd_init(ar);
+
+	return ret;
 }
 
-static int ath10k_wmi_start_scan_calc_len(const struct wmi_start_scan_arg *arg)
+static int ath10k_wmi_start_scan_calc_len(struct ath10k *ar,
+					  const struct wmi_start_scan_arg *arg)
 {
 	int len;
 
+	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+		len = sizeof(struct wmi_start_scan_cmd_10x);
+	else
 	len = sizeof(struct wmi_start_scan_cmd);
 
 	if (arg->ie_len) {
@@ -1358,7 +2773,7 @@
 	int len = 0;
 	int i;
 
-	len = ath10k_wmi_start_scan_calc_len(arg);
+	len = ath10k_wmi_start_scan_calc_len(ar, arg);
 	if (len < 0)
 		return len; /* len contains error code here */
 
@@ -1390,7 +2805,14 @@
 	cmd->scan_ctrl_flags    = __cpu_to_le32(arg->scan_ctrl_flags);
 
 	/* TLV list starts after fields included in the struct */
-	off = sizeof(*cmd);
+	/* There's just one filed that differes the two start_scan
+	 * structures - burst_duration, which we are not using btw,
+	   no point to make the split here, just shift the buffer to fit with
+	   given FW */
+	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+		off = sizeof(struct wmi_start_scan_cmd_10x);
+	else
+		off = sizeof(struct wmi_start_scan_cmd);
 
 	if (arg->n_channels) {
 		channels = (void *)skb->data + off;
@@ -1452,7 +2874,7 @@
 	}
 
 	ath10k_dbg(ATH10K_DBG_WMI, "wmi start scan\n");
-	return ath10k_wmi_cmd_send(ar, skb, WMI_START_SCAN_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid);
 }
 
 void ath10k_wmi_start_scan_init(struct ath10k *ar,
@@ -1468,7 +2890,7 @@
 	arg->repeat_probe_time = 0;
 	arg->probe_spacing_time = 0;
 	arg->idle_time = 0;
-	arg->max_scan_time = 5000;
+	arg->max_scan_time = 20000;
 	arg->probe_delay = 5;
 	arg->notify_scan_events = WMI_SCAN_EVENT_STARTED
 		| WMI_SCAN_EVENT_COMPLETED
@@ -1512,7 +2934,7 @@
 	ath10k_dbg(ATH10K_DBG_WMI,
 		   "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n",
 		   arg->req_id, arg->req_type, arg->u.scan_id);
-	return ath10k_wmi_cmd_send(ar, skb, WMI_STOP_SCAN_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid);
 }
 
 int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
@@ -1537,7 +2959,7 @@
 		   "WMI vdev create: id %d type %d subtype %d macaddr %pM\n",
 		   vdev_id, type, subtype, macaddr);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_CREATE_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid);
 }
 
 int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id)
@@ -1555,20 +2977,21 @@
 	ath10k_dbg(ATH10K_DBG_WMI,
 		   "WMI vdev delete id %d\n", vdev_id);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DELETE_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid);
 }
 
 static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
 				const struct wmi_vdev_start_request_arg *arg,
-				enum wmi_cmd_id cmd_id)
+				u32 cmd_id)
 {
 	struct wmi_vdev_start_request_cmd *cmd;
 	struct sk_buff *skb;
 	const char *cmdname;
 	u32 flags = 0;
+	u32 ch_flags = 0;
 
-	if (cmd_id != WMI_VDEV_START_REQUEST_CMDID &&
-	    cmd_id != WMI_VDEV_RESTART_REQUEST_CMDID)
+	if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid &&
+	    cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid)
 		return -EINVAL;
 	if (WARN_ON(arg->ssid && arg->ssid_len == 0))
 		return -EINVAL;
@@ -1577,9 +3000,9 @@
 	if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid)))
 		return -EINVAL;
 
-	if (cmd_id == WMI_VDEV_START_REQUEST_CMDID)
+	if (cmd_id == ar->wmi.cmd->vdev_start_request_cmdid)
 		cmdname = "start";
-	else if (cmd_id == WMI_VDEV_RESTART_REQUEST_CMDID)
+	else if (cmd_id == ar->wmi.cmd->vdev_restart_request_cmdid)
 		cmdname = "restart";
 	else
 		return -EINVAL; /* should not happen, we already check cmd_id */
@@ -1592,6 +3015,8 @@
 		flags |= WMI_VDEV_START_HIDDEN_SSID;
 	if (arg->pmf_enabled)
 		flags |= WMI_VDEV_START_PMF_ENABLED;
+	if (arg->channel.chan_radar)
+		ch_flags |= WMI_CHAN_FLAG_DFS;
 
 	cmd = (struct wmi_vdev_start_request_cmd *)skb->data;
 	cmd->vdev_id         = __cpu_to_le32(arg->vdev_id);
@@ -1613,6 +3038,7 @@
 		__cpu_to_le32(arg->channel.band_center_freq1);
 
 	cmd->chan.mode = arg->channel.mode;
+	cmd->chan.flags |= __cpu_to_le32(ch_flags);
 	cmd->chan.min_power = arg->channel.min_power;
 	cmd->chan.max_power = arg->channel.max_power;
 	cmd->chan.reg_power = arg->channel.max_reg_power;
@@ -1620,9 +3046,10 @@
 	cmd->chan.antenna_max = arg->channel.max_antenna_gain;
 
 	ath10k_dbg(ATH10K_DBG_WMI,
-		   "wmi vdev %s id 0x%x freq %d, mode %d, ch_flags: 0x%0X,"
-		   "max_power: %d\n", cmdname, arg->vdev_id, arg->channel.freq,
-		   arg->channel.mode, flags, arg->channel.max_power);
+		   "wmi vdev %s id 0x%x flags: 0x%0X, freq %d, mode %d, "
+		   "ch_flags: 0x%0X, max_power: %d\n", cmdname, arg->vdev_id,
+		   flags, arg->channel.freq, arg->channel.mode,
+		   cmd->chan.flags, arg->channel.max_power);
 
 	return ath10k_wmi_cmd_send(ar, skb, cmd_id);
 }
@@ -1630,15 +3057,17 @@
 int ath10k_wmi_vdev_start(struct ath10k *ar,
 			  const struct wmi_vdev_start_request_arg *arg)
 {
-	return ath10k_wmi_vdev_start_restart(ar, arg,
-					     WMI_VDEV_START_REQUEST_CMDID);
+	u32 cmd_id = ar->wmi.cmd->vdev_start_request_cmdid;
+
+	return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id);
 }
 
 int ath10k_wmi_vdev_restart(struct ath10k *ar,
 		     const struct wmi_vdev_start_request_arg *arg)
 {
-	return ath10k_wmi_vdev_start_restart(ar, arg,
-					     WMI_VDEV_RESTART_REQUEST_CMDID);
+	u32 cmd_id = ar->wmi.cmd->vdev_restart_request_cmdid;
+
+	return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id);
 }
 
 int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id)
@@ -1655,7 +3084,7 @@
 
 	ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_STOP_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid);
 }
 
 int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid)
@@ -1676,7 +3105,7 @@
 		   "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n",
 		   vdev_id, aid, bssid);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_UP_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid);
 }
 
 int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id)
@@ -1694,15 +3123,22 @@
 	ath10k_dbg(ATH10K_DBG_WMI,
 		   "wmi mgmt vdev down id 0x%x\n", vdev_id);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DOWN_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid);
 }
 
 int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
-			      enum wmi_vdev_param param_id, u32 param_value)
+			      u32 param_id, u32 param_value)
 {
 	struct wmi_vdev_set_param_cmd *cmd;
 	struct sk_buff *skb;
 
+	if (param_id == WMI_VDEV_PARAM_UNSUPPORTED) {
+		ath10k_dbg(ATH10K_DBG_WMI,
+			   "vdev param %d not supported by firmware\n",
+			    param_id);
+		return -EOPNOTSUPP;
+	}
+
 	skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
 	if (!skb)
 		return -ENOMEM;
@@ -1716,7 +3152,7 @@
 		   "wmi vdev id 0x%x set param %d value %d\n",
 		   vdev_id, param_id, param_value);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_SET_PARAM_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid);
 }
 
 int ath10k_wmi_vdev_install_key(struct ath10k *ar,
@@ -1748,7 +3184,11 @@
 	if (arg->key_data)
 		memcpy(cmd->key_data, arg->key_data, arg->key_len);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID);
+	ath10k_dbg(ATH10K_DBG_WMI,
+		   "wmi vdev install key idx %d cipher %d len %d\n",
+		   arg->key_idx, arg->key_cipher, arg->key_len);
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->vdev_install_key_cmdid);
 }
 
 int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
@@ -1768,7 +3208,7 @@
 	ath10k_dbg(ATH10K_DBG_WMI,
 		   "wmi peer create vdev_id %d peer_addr %pM\n",
 		   vdev_id, peer_addr);
-	return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_CREATE_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid);
 }
 
 int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
@@ -1788,7 +3228,7 @@
 	ath10k_dbg(ATH10K_DBG_WMI,
 		   "wmi peer delete vdev_id %d peer_addr %pM\n",
 		   vdev_id, peer_addr);
-	return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_DELETE_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid);
 }
 
 int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
@@ -1809,7 +3249,7 @@
 	ath10k_dbg(ATH10K_DBG_WMI,
 		   "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n",
 		   vdev_id, peer_addr, tid_bitmap);
-	return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_FLUSH_TIDS_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid);
 }
 
 int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
@@ -1833,7 +3273,7 @@
 		   "wmi vdev %d peer 0x%pM set param %d value %d\n",
 		   vdev_id, peer_addr, param_id, param_value);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_SET_PARAM_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid);
 }
 
 int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
@@ -1854,7 +3294,8 @@
 		   "wmi set powersave id 0x%x mode %d\n",
 		   vdev_id, psmode);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_MODE_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->sta_powersave_mode_cmdid);
 }
 
 int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
@@ -1876,7 +3317,8 @@
 	ath10k_dbg(ATH10K_DBG_WMI,
 		   "wmi sta ps param vdev_id 0x%x param %d value %d\n",
 		   vdev_id, param_id, value);
-	return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_PARAM_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->sta_powersave_param_cmdid);
 }
 
 int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
@@ -1902,7 +3344,8 @@
 		   "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n",
 		   vdev_id, param_id, value, mac);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_AP_PS_PEER_PARAM_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->ap_ps_peer_param_cmdid);
 }
 
 int ath10k_wmi_scan_chan_list(struct ath10k *ar,
@@ -1940,6 +3383,8 @@
 			flags |= WMI_CHAN_FLAG_ALLOW_VHT;
 		if (ch->ht40plus)
 			flags |= WMI_CHAN_FLAG_HT40_PLUS;
+		if (ch->chan_radar)
+			flags |= WMI_CHAN_FLAG_DFS;
 
 		ci->mhz               = __cpu_to_le32(ch->freq);
 		ci->band_center_freq1 = __cpu_to_le32(ch->freq);
@@ -1948,14 +3393,13 @@
 		ci->max_power         = ch->max_power;
 		ci->reg_power         = ch->max_reg_power;
 		ci->antenna_max       = ch->max_antenna_gain;
-		ci->antenna_max       = 0;
 
 		/* mode & flags share storage */
 		ci->mode              = ch->mode;
 		ci->flags            |= __cpu_to_le32(flags);
 	}
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_SCAN_CHAN_LIST_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid);
 }
 
 int ath10k_wmi_peer_assoc(struct ath10k *ar,
@@ -2011,26 +3455,51 @@
 	cmd->peer_vht_rates.tx_mcs_set =
 		__cpu_to_le32(arg->peer_vht_rates.tx_mcs_set);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID);
+	ath10k_dbg(ATH10K_DBG_WMI,
+		   "wmi peer assoc vdev %d addr %pM\n",
+		   arg->vdev_id, arg->addr);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid);
 }
 
-int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg)
+/* This function assumes the beacon is already DMA mapped */
+int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif)
 {
-	struct wmi_bcn_tx_cmd *cmd;
+	struct wmi_bcn_tx_ref_cmd *cmd;
 	struct sk_buff *skb;
+	struct sk_buff *beacon = arvif->beacon;
+	struct ath10k *ar = arvif->ar;
+	struct ieee80211_hdr *hdr;
+	int ret;
+	u16 fc;
 
-	skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->bcn_len);
+	skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
 	if (!skb)
 		return -ENOMEM;
 
-	cmd = (struct wmi_bcn_tx_cmd *)skb->data;
-	cmd->hdr.vdev_id  = __cpu_to_le32(arg->vdev_id);
-	cmd->hdr.tx_rate  = __cpu_to_le32(arg->tx_rate);
-	cmd->hdr.tx_power = __cpu_to_le32(arg->tx_power);
-	cmd->hdr.bcn_len  = __cpu_to_le32(arg->bcn_len);
-	memcpy(cmd->bcn, arg->bcn, arg->bcn_len);
+	hdr = (struct ieee80211_hdr *)beacon->data;
+	fc = le16_to_cpu(hdr->frame_control);
+
+	cmd = (struct wmi_bcn_tx_ref_cmd *)skb->data;
+	cmd->vdev_id = __cpu_to_le32(arvif->vdev_id);
+	cmd->data_len = __cpu_to_le32(beacon->len);
+	cmd->data_ptr = __cpu_to_le32(ATH10K_SKB_CB(beacon)->paddr);
+	cmd->msdu_id = 0;
+	cmd->frame_control = __cpu_to_le32(fc);
+	cmd->flags = 0;
+
+	if (ATH10K_SKB_CB(beacon)->bcn.dtim_zero)
+		cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO);
+
+	if (ATH10K_SKB_CB(beacon)->bcn.deliver_cab)
+		cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB);
 
-	return ath10k_wmi_cmd_send(ar, skb, WMI_BCN_TX_CMDID);
+	ret = ath10k_wmi_cmd_send_nowait(ar, skb,
+					 ar->wmi.cmd->pdev_send_bcn_cmdid);
+
+	if (ret)
+		dev_kfree_skb(skb);
+
+	return ret;
 }
 
 static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,
@@ -2061,7 +3530,8 @@
 	ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo);
 
 	ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set wmm params\n");
-	return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_WMM_PARAMS_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->pdev_set_wmm_params_cmdid);
 }
 
 int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
@@ -2077,5 +3547,61 @@
 	cmd->stats_id = __cpu_to_le32(stats_id);
 
 	ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
-	return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid);
+}
+
+int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+			     enum wmi_force_fw_hang_type type, u32 delay_ms)
+{
+	struct wmi_force_fw_hang_cmd *cmd;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_force_fw_hang_cmd *)skb->data;
+	cmd->type = __cpu_to_le32(type);
+	cmd->delay_ms = __cpu_to_le32(delay_ms);
+
+	ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
+		   type, delay_ms);
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid);
+}
+
+int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable)
+{
+	struct wmi_dbglog_cfg_cmd *cmd;
+	struct sk_buff *skb;
+	u32 cfg;
+
+	skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct wmi_dbglog_cfg_cmd *)skb->data;
+
+	if (module_enable) {
+		cfg = SM(ATH10K_DBGLOG_LEVEL_VERBOSE,
+			 ATH10K_DBGLOG_CFG_LOG_LVL);
+	} else {
+		/* set back defaults, all modules with WARN level */
+		cfg = SM(ATH10K_DBGLOG_LEVEL_WARN,
+			 ATH10K_DBGLOG_CFG_LOG_LVL);
+		module_enable = ~0;
+	}
+
+	cmd->module_enable = __cpu_to_le32(module_enable);
+	cmd->module_valid = __cpu_to_le32(~0);
+	cmd->config_enable = __cpu_to_le32(cfg);
+	cmd->config_valid = __cpu_to_le32(ATH10K_DBGLOG_CFG_LOG_LVL_MASK);
+
+	ath10k_dbg(ATH10K_DBG_WMI,
+		   "wmi dbglog cfg modules %08x %08x config %08x %08x\n",
+		   __le32_to_cpu(cmd->module_enable),
+		   __le32_to_cpu(cmd->module_valid),
+		   __le32_to_cpu(cmd->config_enable),
+		   __le32_to_cpu(cmd->config_valid));
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid);
 }
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath10k/wmi.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/wmi.h
--- linux-3.11.10/drivers/net/wireless/ath/ath10k/wmi.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath10k/wmi.h	2015-09-29 11:15:06.925740695 +0200
@@ -208,6 +208,118 @@
 	(c_macaddr)[5] = (((pwmi_mac_addr)->word1) >> 8) & 0xff; \
 	} while (0)
 
+struct wmi_cmd_map {
+	u32 init_cmdid;
+	u32 start_scan_cmdid;
+	u32 stop_scan_cmdid;
+	u32 scan_chan_list_cmdid;
+	u32 scan_sch_prio_tbl_cmdid;
+	u32 pdev_set_regdomain_cmdid;
+	u32 pdev_set_channel_cmdid;
+	u32 pdev_set_param_cmdid;
+	u32 pdev_pktlog_enable_cmdid;
+	u32 pdev_pktlog_disable_cmdid;
+	u32 pdev_set_wmm_params_cmdid;
+	u32 pdev_set_ht_cap_ie_cmdid;
+	u32 pdev_set_vht_cap_ie_cmdid;
+	u32 pdev_set_dscp_tid_map_cmdid;
+	u32 pdev_set_quiet_mode_cmdid;
+	u32 pdev_green_ap_ps_enable_cmdid;
+	u32 pdev_get_tpc_config_cmdid;
+	u32 pdev_set_base_macaddr_cmdid;
+	u32 vdev_create_cmdid;
+	u32 vdev_delete_cmdid;
+	u32 vdev_start_request_cmdid;
+	u32 vdev_restart_request_cmdid;
+	u32 vdev_up_cmdid;
+	u32 vdev_stop_cmdid;
+	u32 vdev_down_cmdid;
+	u32 vdev_set_param_cmdid;
+	u32 vdev_install_key_cmdid;
+	u32 peer_create_cmdid;
+	u32 peer_delete_cmdid;
+	u32 peer_flush_tids_cmdid;
+	u32 peer_set_param_cmdid;
+	u32 peer_assoc_cmdid;
+	u32 peer_add_wds_entry_cmdid;
+	u32 peer_remove_wds_entry_cmdid;
+	u32 peer_mcast_group_cmdid;
+	u32 bcn_tx_cmdid;
+	u32 pdev_send_bcn_cmdid;
+	u32 bcn_tmpl_cmdid;
+	u32 bcn_filter_rx_cmdid;
+	u32 prb_req_filter_rx_cmdid;
+	u32 mgmt_tx_cmdid;
+	u32 prb_tmpl_cmdid;
+	u32 addba_clear_resp_cmdid;
+	u32 addba_send_cmdid;
+	u32 addba_status_cmdid;
+	u32 delba_send_cmdid;
+	u32 addba_set_resp_cmdid;
+	u32 send_singleamsdu_cmdid;
+	u32 sta_powersave_mode_cmdid;
+	u32 sta_powersave_param_cmdid;
+	u32 sta_mimo_ps_mode_cmdid;
+	u32 pdev_dfs_enable_cmdid;
+	u32 pdev_dfs_disable_cmdid;
+	u32 roam_scan_mode;
+	u32 roam_scan_rssi_threshold;
+	u32 roam_scan_period;
+	u32 roam_scan_rssi_change_threshold;
+	u32 roam_ap_profile;
+	u32 ofl_scan_add_ap_profile;
+	u32 ofl_scan_remove_ap_profile;
+	u32 ofl_scan_period;
+	u32 p2p_dev_set_device_info;
+	u32 p2p_dev_set_discoverability;
+	u32 p2p_go_set_beacon_ie;
+	u32 p2p_go_set_probe_resp_ie;
+	u32 p2p_set_vendor_ie_data_cmdid;
+	u32 ap_ps_peer_param_cmdid;
+	u32 ap_ps_peer_uapsd_coex_cmdid;
+	u32 peer_rate_retry_sched_cmdid;
+	u32 wlan_profile_trigger_cmdid;
+	u32 wlan_profile_set_hist_intvl_cmdid;
+	u32 wlan_profile_get_profile_data_cmdid;
+	u32 wlan_profile_enable_profile_id_cmdid;
+	u32 wlan_profile_list_profile_id_cmdid;
+	u32 pdev_suspend_cmdid;
+	u32 pdev_resume_cmdid;
+	u32 add_bcn_filter_cmdid;
+	u32 rmv_bcn_filter_cmdid;
+	u32 wow_add_wake_pattern_cmdid;
+	u32 wow_del_wake_pattern_cmdid;
+	u32 wow_enable_disable_wake_event_cmdid;
+	u32 wow_enable_cmdid;
+	u32 wow_hostwakeup_from_sleep_cmdid;
+	u32 rtt_measreq_cmdid;
+	u32 rtt_tsf_cmdid;
+	u32 vdev_spectral_scan_configure_cmdid;
+	u32 vdev_spectral_scan_enable_cmdid;
+	u32 request_stats_cmdid;
+	u32 set_arp_ns_offload_cmdid;
+	u32 network_list_offload_config_cmdid;
+	u32 gtk_offload_cmdid;
+	u32 csa_offload_enable_cmdid;
+	u32 csa_offload_chanswitch_cmdid;
+	u32 chatter_set_mode_cmdid;
+	u32 peer_tid_addba_cmdid;
+	u32 peer_tid_delba_cmdid;
+	u32 sta_dtim_ps_method_cmdid;
+	u32 sta_uapsd_auto_trig_cmdid;
+	u32 sta_keepalive_cmd;
+	u32 echo_cmdid;
+	u32 pdev_utf_cmdid;
+	u32 dbglog_cfg_cmdid;
+	u32 pdev_qvit_cmdid;
+	u32 pdev_ftm_intg_cmdid;
+	u32 vdev_set_keepalive_cmdid;
+	u32 vdev_get_keepalive_cmdid;
+	u32 force_fw_hang_cmdid;
+	u32 gpio_config_cmdid;
+	u32 gpio_output_cmdid;
+};
+
 /*
  * wmi command groups.
  */
@@ -247,7 +359,9 @@
 #define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1)
 #define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1)
 
-/* Command IDs and commande events. */
+#define WMI_CMD_UNSUPPORTED 0
+
+/* Command IDs and command events for MAIN FW. */
 enum wmi_cmd_id {
 	WMI_INIT_CMDID = 0x1,
 
@@ -416,6 +530,7 @@
 	WMI_PDEV_FTM_INTG_CMDID,
 	WMI_VDEV_SET_KEEPALIVE_CMDID,
 	WMI_VDEV_GET_KEEPALIVE_CMDID,
+	WMI_FORCE_FW_HANG_CMDID,
 
 	/* GPIO Configuration */
 	WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO),
@@ -487,6 +602,217 @@
 	WMI_GPIO_INPUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GPIO),
 };
 
+/* Command IDs and command events for 10.X firmware */
+enum wmi_10x_cmd_id {
+	WMI_10X_START_CMDID = 0x9000,
+	WMI_10X_END_CMDID = 0x9FFF,
+
+	/* initialize the wlan sub system */
+	WMI_10X_INIT_CMDID,
+
+	/* Scan specific commands */
+
+	WMI_10X_START_SCAN_CMDID = WMI_10X_START_CMDID,
+	WMI_10X_STOP_SCAN_CMDID,
+	WMI_10X_SCAN_CHAN_LIST_CMDID,
+	WMI_10X_ECHO_CMDID,
+
+	/* PDEV(physical device) specific commands */
+	WMI_10X_PDEV_SET_REGDOMAIN_CMDID,
+	WMI_10X_PDEV_SET_CHANNEL_CMDID,
+	WMI_10X_PDEV_SET_PARAM_CMDID,
+	WMI_10X_PDEV_PKTLOG_ENABLE_CMDID,
+	WMI_10X_PDEV_PKTLOG_DISABLE_CMDID,
+	WMI_10X_PDEV_SET_WMM_PARAMS_CMDID,
+	WMI_10X_PDEV_SET_HT_CAP_IE_CMDID,
+	WMI_10X_PDEV_SET_VHT_CAP_IE_CMDID,
+	WMI_10X_PDEV_SET_BASE_MACADDR_CMDID,
+	WMI_10X_PDEV_SET_DSCP_TID_MAP_CMDID,
+	WMI_10X_PDEV_SET_QUIET_MODE_CMDID,
+	WMI_10X_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+	WMI_10X_PDEV_GET_TPC_CONFIG_CMDID,
+
+	/* VDEV(virtual device) specific commands */
+	WMI_10X_VDEV_CREATE_CMDID,
+	WMI_10X_VDEV_DELETE_CMDID,
+	WMI_10X_VDEV_START_REQUEST_CMDID,
+	WMI_10X_VDEV_RESTART_REQUEST_CMDID,
+	WMI_10X_VDEV_UP_CMDID,
+	WMI_10X_VDEV_STOP_CMDID,
+	WMI_10X_VDEV_DOWN_CMDID,
+	WMI_10X_VDEV_STANDBY_RESPONSE_CMDID,
+	WMI_10X_VDEV_RESUME_RESPONSE_CMDID,
+	WMI_10X_VDEV_SET_PARAM_CMDID,
+	WMI_10X_VDEV_INSTALL_KEY_CMDID,
+
+	/* peer specific commands */
+	WMI_10X_PEER_CREATE_CMDID,
+	WMI_10X_PEER_DELETE_CMDID,
+	WMI_10X_PEER_FLUSH_TIDS_CMDID,
+	WMI_10X_PEER_SET_PARAM_CMDID,
+	WMI_10X_PEER_ASSOC_CMDID,
+	WMI_10X_PEER_ADD_WDS_ENTRY_CMDID,
+	WMI_10X_PEER_REMOVE_WDS_ENTRY_CMDID,
+	WMI_10X_PEER_MCAST_GROUP_CMDID,
+
+	/* beacon/management specific commands */
+
+	WMI_10X_BCN_TX_CMDID,
+	WMI_10X_BCN_PRB_TMPL_CMDID,
+	WMI_10X_BCN_FILTER_RX_CMDID,
+	WMI_10X_PRB_REQ_FILTER_RX_CMDID,
+	WMI_10X_MGMT_TX_CMDID,
+
+	/* commands to directly control ba negotiation directly from host. */
+	WMI_10X_ADDBA_CLEAR_RESP_CMDID,
+	WMI_10X_ADDBA_SEND_CMDID,
+	WMI_10X_ADDBA_STATUS_CMDID,
+	WMI_10X_DELBA_SEND_CMDID,
+	WMI_10X_ADDBA_SET_RESP_CMDID,
+	WMI_10X_SEND_SINGLEAMSDU_CMDID,
+
+	/* Station power save specific config */
+	WMI_10X_STA_POWERSAVE_MODE_CMDID,
+	WMI_10X_STA_POWERSAVE_PARAM_CMDID,
+	WMI_10X_STA_MIMO_PS_MODE_CMDID,
+
+	/* set debug log config */
+	WMI_10X_DBGLOG_CFG_CMDID,
+
+	/* DFS-specific commands */
+	WMI_10X_PDEV_DFS_ENABLE_CMDID,
+	WMI_10X_PDEV_DFS_DISABLE_CMDID,
+
+	/* QVIT specific command id */
+	WMI_10X_PDEV_QVIT_CMDID,
+
+	/* Offload Scan and Roaming related  commands */
+	WMI_10X_ROAM_SCAN_MODE,
+	WMI_10X_ROAM_SCAN_RSSI_THRESHOLD,
+	WMI_10X_ROAM_SCAN_PERIOD,
+	WMI_10X_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+	WMI_10X_ROAM_AP_PROFILE,
+	WMI_10X_OFL_SCAN_ADD_AP_PROFILE,
+	WMI_10X_OFL_SCAN_REMOVE_AP_PROFILE,
+	WMI_10X_OFL_SCAN_PERIOD,
+
+	/* P2P specific commands */
+	WMI_10X_P2P_DEV_SET_DEVICE_INFO,
+	WMI_10X_P2P_DEV_SET_DISCOVERABILITY,
+	WMI_10X_P2P_GO_SET_BEACON_IE,
+	WMI_10X_P2P_GO_SET_PROBE_RESP_IE,
+
+	/* AP power save specific config */
+	WMI_10X_AP_PS_PEER_PARAM_CMDID,
+	WMI_10X_AP_PS_PEER_UAPSD_COEX_CMDID,
+
+	/* Rate-control specific commands */
+	WMI_10X_PEER_RATE_RETRY_SCHED_CMDID,
+
+	/* WLAN Profiling commands. */
+	WMI_10X_WLAN_PROFILE_TRIGGER_CMDID,
+	WMI_10X_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+	WMI_10X_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+	WMI_10X_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+	WMI_10X_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+
+	/* Suspend resume command Ids */
+	WMI_10X_PDEV_SUSPEND_CMDID,
+	WMI_10X_PDEV_RESUME_CMDID,
+
+	/* Beacon filter commands */
+	WMI_10X_ADD_BCN_FILTER_CMDID,
+	WMI_10X_RMV_BCN_FILTER_CMDID,
+
+	/* WOW Specific WMI commands*/
+	WMI_10X_WOW_ADD_WAKE_PATTERN_CMDID,
+	WMI_10X_WOW_DEL_WAKE_PATTERN_CMDID,
+	WMI_10X_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+	WMI_10X_WOW_ENABLE_CMDID,
+	WMI_10X_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+
+	/* RTT measurement related cmd */
+	WMI_10X_RTT_MEASREQ_CMDID,
+	WMI_10X_RTT_TSF_CMDID,
+
+	/* transmit beacon by value */
+	WMI_10X_PDEV_SEND_BCN_CMDID,
+
+	/* F/W stats */
+	WMI_10X_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
+	WMI_10X_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
+	WMI_10X_REQUEST_STATS_CMDID,
+
+	/* GPIO Configuration */
+	WMI_10X_GPIO_CONFIG_CMDID,
+	WMI_10X_GPIO_OUTPUT_CMDID,
+
+	WMI_10X_PDEV_UTF_CMDID = WMI_10X_END_CMDID - 1,
+};
+
+enum wmi_10x_event_id {
+	WMI_10X_SERVICE_READY_EVENTID = 0x8000,
+	WMI_10X_READY_EVENTID,
+	WMI_10X_START_EVENTID = 0x9000,
+	WMI_10X_END_EVENTID = 0x9FFF,
+
+	/* Scan specific events */
+	WMI_10X_SCAN_EVENTID = WMI_10X_START_EVENTID,
+	WMI_10X_ECHO_EVENTID,
+	WMI_10X_DEBUG_MESG_EVENTID,
+	WMI_10X_UPDATE_STATS_EVENTID,
+
+	/* Instantaneous RSSI event */
+	WMI_10X_INST_RSSI_STATS_EVENTID,
+
+	/* VDEV specific events */
+	WMI_10X_VDEV_START_RESP_EVENTID,
+	WMI_10X_VDEV_STANDBY_REQ_EVENTID,
+	WMI_10X_VDEV_RESUME_REQ_EVENTID,
+	WMI_10X_VDEV_STOPPED_EVENTID,
+
+	/* peer  specific events */
+	WMI_10X_PEER_STA_KICKOUT_EVENTID,
+
+	/* beacon/mgmt specific events */
+	WMI_10X_HOST_SWBA_EVENTID,
+	WMI_10X_TBTTOFFSET_UPDATE_EVENTID,
+	WMI_10X_MGMT_RX_EVENTID,
+
+	/* Channel stats event */
+	WMI_10X_CHAN_INFO_EVENTID,
+
+	/* PHY Error specific WMI event */
+	WMI_10X_PHYERR_EVENTID,
+
+	/* Roam event to trigger roaming on host */
+	WMI_10X_ROAM_EVENTID,
+
+	/* matching AP found from list of profiles */
+	WMI_10X_PROFILE_MATCH,
+
+	/* debug print message used for tracing FW code while debugging */
+	WMI_10X_DEBUG_PRINT_EVENTID,
+	/* VI spoecific event */
+	WMI_10X_PDEV_QVIT_EVENTID,
+	/* FW code profile data in response to profile request */
+	WMI_10X_WLAN_PROFILE_DATA_EVENTID,
+
+	/*RTT related event ID*/
+	WMI_10X_RTT_MEASUREMENT_REPORT_EVENTID,
+	WMI_10X_TSF_MEASUREMENT_REPORT_EVENTID,
+	WMI_10X_RTT_ERROR_REPORT_EVENTID,
+
+	WMI_10X_WOW_WAKEUP_HOST_EVENTID,
+	WMI_10X_DCS_INTERFERENCE_EVENTID,
+
+	/* TPC config for the current operating channel */
+	WMI_10X_PDEV_TPC_CONFIG_EVENTID,
+
+	WMI_10X_GPIO_INPUT_EVENTID,
+	WMI_10X_PDEV_UTF_EVENTID = WMI_10X_END_EVENTID-1,
+};
+
 enum wmi_phy_mode {
 	MODE_11A        = 0,   /* 11a Mode */
 	MODE_11G        = 1,   /* 11b/g Mode */
@@ -507,6 +833,48 @@
 	MODE_MAX        = 14
 };
 
+static inline const char *ath10k_wmi_phymode_str(enum wmi_phy_mode mode)
+{
+	switch (mode) {
+	case MODE_11A:
+		return "11a";
+	case MODE_11G:
+		return "11g";
+	case MODE_11B:
+		return "11b";
+	case MODE_11GONLY:
+		return "11gonly";
+	case MODE_11NA_HT20:
+		return "11na-ht20";
+	case MODE_11NG_HT20:
+		return "11ng-ht20";
+	case MODE_11NA_HT40:
+		return "11na-ht40";
+	case MODE_11NG_HT40:
+		return "11ng-ht40";
+	case MODE_11AC_VHT20:
+		return "11ac-vht20";
+	case MODE_11AC_VHT40:
+		return "11ac-vht40";
+	case MODE_11AC_VHT80:
+		return "11ac-vht80";
+	case MODE_11AC_VHT20_2G:
+		return "11ac-vht20-2g";
+	case MODE_11AC_VHT40_2G:
+		return "11ac-vht40-2g";
+	case MODE_11AC_VHT80_2G:
+		return "11ac-vht80-2g";
+	case MODE_UNKNOWN:
+		/* skip */
+		break;
+
+		/* no default handler to allow compiler to check that the
+		 * enum is fully handled */
+	};
+
+	return "<unknown>";
+}
+
 #define WMI_CHAN_LIST_TAG	0x1
 #define WMI_SSID_LIST_TAG	0x2
 #define WMI_BSSID_LIST_TAG	0x3
@@ -525,6 +893,7 @@
 	union {
 		__le32 reginfo0;
 		struct {
+			/* note: power unit is 0.5 dBm */
 			u8 min_power;
 			u8 max_power;
 			u8 reg_power;
@@ -547,7 +916,8 @@
 	bool allow_ht;
 	bool allow_vht;
 	bool ht40plus;
-	/* note: power unit is 1/4th of dBm */
+	bool chan_radar;
+	/* note: power unit is 0.5 dBm */
 	u32 min_power;
 	u32 max_power;
 	u32 max_reg_power;
@@ -762,13 +1132,45 @@
 	struct wlan_host_mem_req mem_reqs[1];
 } __packed;
 
+/* This is the definition from 10.X firmware branch */
+struct wmi_service_ready_event_10x {
+	__le32 sw_version;
+	__le32 abi_version;
+
+	/* WMI_PHY_CAPABILITY */
+	__le32 phy_capability;
+
+	/* Maximum number of frag table entries that SW will populate less 1 */
+	__le32 max_frag_entry;
+	__le32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
+	__le32 num_rf_chains;
+
+	/*
+	 * The following field is only valid for service type
+	 * WMI_SERVICE_11AC
+	 */
+	__le32 ht_cap_info; /* WMI HT Capability */
+	__le32 vht_cap_info; /* VHT capability info field of 802.11ac */
+	__le32 vht_supp_mcs; /* VHT Supported MCS Set field Rx/Tx same */
+	__le32 hw_min_tx_power;
+	__le32 hw_max_tx_power;
+
+	struct hal_reg_capabilities hal_reg_capabilities;
+
+	__le32 sys_cap_info;
+	__le32 min_pkt_size_enable; /* Enterprise mode short pkt enable */
+
 /*
- * status consists of  upper 16 bits fo int status and lower 16 bits of
- * module ID that retuned status
+	 * request to host to allocate a chuck of memory and pss it down to FW
+	 * via WM_INIT. FW uses this as FW extesnsion memory for saving its
+	 * data structures. Only valid for low latency interfaces like PCIE
+	 * where FW can access this memory directly (or) by DMA.
  */
-#define WLAN_INIT_STATUS_SUCCESS   0x0
-#define WLAN_GET_INIT_STATUS_REASON(status)    ((status) & 0xffff)
-#define WLAN_GET_INIT_STATUS_MODULE_ID(status) (((status) >> 16) & 0xffff)
+	__le32 num_mem_reqs;
+
+	struct wlan_host_mem_req mem_reqs[1];
+} __packed;
+
 
 #define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ)
 #define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ)
@@ -977,6 +1379,192 @@
 	__le32 max_frag_entries;
 } __packed;
 
+struct wmi_resource_config_10x {
+	/* number of virtual devices (VAPs) to support */
+	__le32 num_vdevs;
+
+	/* number of peer nodes to support */
+	__le32 num_peers;
+
+	/* number of keys per peer */
+	__le32 num_peer_keys;
+
+	/* total number of TX/RX data TIDs */
+	__le32 num_tids;
+
+	/*
+	 * max skid for resolving hash collisions
+	 *
+	 *   The address search table is sparse, so that if two MAC addresses
+	 *   result in the same hash value, the second of these conflicting
+	 *   entries can slide to the next index in the address search table,
+	 *   and use it, if it is unoccupied.  This ast_skid_limit parameter
+	 *   specifies the upper bound on how many subsequent indices to search
+	 *   over to find an unoccupied space.
+	 */
+	__le32 ast_skid_limit;
+
+	/*
+	 * the nominal chain mask for transmit
+	 *
+	 *   The chain mask may be modified dynamically, e.g. to operate AP
+	 *   tx with a reduced number of chains if no clients are associated.
+	 *   This configuration parameter specifies the nominal chain-mask that
+	 *   should be used when not operating with a reduced set of tx chains.
+	 */
+	__le32 tx_chain_mask;
+
+	/*
+	 * the nominal chain mask for receive
+	 *
+	 *   The chain mask may be modified dynamically, e.g. for a client
+	 *   to use a reduced number of chains for receive if the traffic to
+	 *   the client is low enough that it doesn't require downlink MIMO
+	 *   or antenna diversity.
+	 *   This configuration parameter specifies the nominal chain-mask that
+	 *   should be used when not operating with a reduced set of rx chains.
+	 */
+	__le32 rx_chain_mask;
+
+	/*
+	 * what rx reorder timeout (ms) to use for the AC
+	 *
+	 *   Each WMM access class (voice, video, best-effort, background) will
+	 *   have its own timeout value to dictate how long to wait for missing
+	 *   rx MPDUs to arrive before flushing subsequent MPDUs that have
+	 *   already been received.
+	 *   This parameter specifies the timeout in milliseconds for each
+	 *   class.
+	 */
+	__le32 rx_timeout_pri_vi;
+	__le32 rx_timeout_pri_vo;
+	__le32 rx_timeout_pri_be;
+	__le32 rx_timeout_pri_bk;
+
+	/*
+	 * what mode the rx should decap packets to
+	 *
+	 *   MAC can decap to RAW (no decap), native wifi or Ethernet types
+	 *   THis setting also determines the default TX behavior, however TX
+	 *   behavior can be modified on a per VAP basis during VAP init
+	 */
+	__le32 rx_decap_mode;
+
+	/* what is the maximum scan requests than can be queued */
+	__le32 scan_max_pending_reqs;
+
+	/* maximum VDEV that could use BMISS offload */
+	__le32 bmiss_offload_max_vdev;
+
+	/* maximum VDEV that could use offload roaming */
+	__le32 roam_offload_max_vdev;
+
+	/* maximum AP profiles that would push to offload roaming */
+	__le32 roam_offload_max_ap_profiles;
+
+	/*
+	 * how many groups to use for mcast->ucast conversion
+	 *
+	 *   The target's WAL maintains a table to hold information regarding
+	 *   which peers belong to a given multicast group, so that if
+	 *   multicast->unicast conversion is enabled, the target can convert
+	 *   multicast tx frames to a series of unicast tx frames, to each
+	 *   peer within the multicast group.
+	     This num_mcast_groups configuration parameter tells the target how
+	 *   many multicast groups to provide storage for within its multicast
+	 *   group membership table.
+	 */
+	__le32 num_mcast_groups;
+
+	/*
+	 * size to alloc for the mcast membership table
+	 *
+	 *   This num_mcast_table_elems configuration parameter tells the
+	 *   target how many peer elements it needs to provide storage for in
+	 *   its multicast group membership table.
+	 *   These multicast group membership table elements are shared by the
+	 *   multicast groups stored within the table.
+	 */
+	__le32 num_mcast_table_elems;
+
+	/*
+	 * whether/how to do multicast->unicast conversion
+	 *
+	 *   This configuration parameter specifies whether the target should
+	 *   perform multicast --> unicast conversion on transmit, and if so,
+	 *   what to do if it finds no entries in its multicast group
+	 *   membership table for the multicast IP address in the tx frame.
+	 *   Configuration value:
+	 *   0 -> Do not perform multicast to unicast conversion.
+	 *   1 -> Convert multicast frames to unicast, if the IP multicast
+	 *        address from the tx frame is found in the multicast group
+	 *        membership table.  If the IP multicast address is not found,
+	 *        drop the frame.
+	 *   2 -> Convert multicast frames to unicast, if the IP multicast
+	 *        address from the tx frame is found in the multicast group
+	 *        membership table.  If the IP multicast address is not found,
+	 *        transmit the frame as multicast.
+	 */
+	__le32 mcast2ucast_mode;
+
+	/*
+	 * how much memory to allocate for a tx PPDU dbg log
+	 *
+	 *   This parameter controls how much memory the target will allocate
+	 *   to store a log of tx PPDU meta-information (how large the PPDU
+	 *   was, when it was sent, whether it was successful, etc.)
+	 */
+	__le32 tx_dbg_log_size;
+
+	/* how many AST entries to be allocated for WDS */
+	__le32 num_wds_entries;
+
+	/*
+	 * MAC DMA burst size, e.g., For target PCI limit can be
+	 * 0 -default, 1 256B
+	 */
+	__le32 dma_burst_size;
+
+	/*
+	 * Fixed delimiters to be inserted after every MPDU to
+	 * account for interface latency to avoid underrun.
+	 */
+	__le32 mac_aggr_delim;
+
+	/*
+	 *   determine whether target is responsible for detecting duplicate
+	 *   non-aggregate MPDU and timing out stale fragments.
+	 *
+	 *   A-MPDU reordering is always performed on the target.
+	 *
+	 *   0: target responsible for frag timeout and dup checking
+	 *   1: host responsible for frag timeout and dup checking
+	 */
+	__le32 rx_skip_defrag_timeout_dup_detection_check;
+
+	/*
+	 * Configuration for VoW :
+	 * No of Video Nodes to be supported
+	 * and Max no of descriptors for each Video link (node).
+	 */
+	__le32 vow_config;
+
+	/* Number of msdu descriptors target should use */
+	__le32 num_msdu_desc;
+
+	/*
+	 * Max. number of Tx fragments per MSDU
+	 *  This parameter controls the max number of Tx fragments per MSDU.
+	 *  This is sent by the target as part of the WMI_SERVICE_READY event
+	 *  and is overriden by the OS shim as required.
+	 */
+	__le32 max_frag_entries;
+} __packed;
+
+
+#define NUM_UNITS_IS_NUM_VDEVS   0x1
+#define NUM_UNITS_IS_NUM_PEERS   0x2
+
 /* strucutre describing host memory chunk. */
 struct host_memory_chunk {
 	/* id of the request that is passed up in service ready */
@@ -998,6 +1586,18 @@
 	struct host_memory_chunk host_mem_chunks[1];
 } __packed;
 
+/* _10x stucture is from 10.X FW API */
+struct wmi_init_cmd_10x {
+	struct wmi_resource_config_10x resource_config;
+	__le32 num_host_mem_chunks;
+
+	/*
+	 * variable number of host memory chunks.
+	 * This should be the last element in the structure
+	 */
+	struct host_memory_chunk host_mem_chunks[1];
+} __packed;
+
 /* TLV for channel list */
 struct wmi_chan_list {
 	__le32 tag; /* WMI_CHAN_LIST_TAG */
@@ -1022,53 +1622,130 @@
 	u8 ssid[32];
 } __packed;
 
-struct wmi_ssid_list {
-	__le32 tag; /* WMI_SSID_LIST_TAG */
-	__le32 num_ssids;
-	struct wmi_ssid ssids[0];
+struct wmi_ssid_list {
+	__le32 tag; /* WMI_SSID_LIST_TAG */
+	__le32 num_ssids;
+	struct wmi_ssid ssids[0];
+} __packed;
+
+/* prefix used by scan requestor ids on the host */
+#define WMI_HOST_SCAN_REQUESTOR_ID_PREFIX 0xA000
+
+/* prefix used by scan request ids generated on the host */
+/* host cycles through the lower 12 bits to generate ids */
+#define WMI_HOST_SCAN_REQ_ID_PREFIX 0xA000
+
+#define WLAN_SCAN_PARAMS_MAX_SSID    16
+#define WLAN_SCAN_PARAMS_MAX_BSSID   4
+#define WLAN_SCAN_PARAMS_MAX_IE_LEN  256
+
+/* Scan priority numbers must be sequential, starting with 0 */
+enum wmi_scan_priority {
+	WMI_SCAN_PRIORITY_VERY_LOW = 0,
+	WMI_SCAN_PRIORITY_LOW,
+	WMI_SCAN_PRIORITY_MEDIUM,
+	WMI_SCAN_PRIORITY_HIGH,
+	WMI_SCAN_PRIORITY_VERY_HIGH,
+	WMI_SCAN_PRIORITY_COUNT   /* number of priorities supported */
+};
+
+struct wmi_start_scan_cmd {
+	/* Scan ID */
+	__le32 scan_id;
+	/* Scan requestor ID */
+	__le32 scan_req_id;
+	/* VDEV id(interface) that is requesting scan */
+	__le32 vdev_id;
+	/* Scan Priority, input to scan scheduler */
+	__le32 scan_priority;
+	/* Scan events subscription */
+	__le32 notify_scan_events;
+	/* dwell time in msec on active channels */
+	__le32 dwell_time_active;
+	/* dwell time in msec on passive channels */
+	__le32 dwell_time_passive;
+	/*
+	 * min time in msec on the BSS channel,only valid if atleast one
+	 * VDEV is active
+	 */
+	__le32 min_rest_time;
+	/*
+	 * max rest time in msec on the BSS channel,only valid if at least
+	 * one VDEV is active
+	 */
+	/*
+	 * the scanner will rest on the bss channel at least min_rest_time
+	 * after min_rest_time the scanner will start checking for tx/rx
+	 * activity on all VDEVs. if there is no activity the scanner will
+	 * switch to off channel. if there is activity the scanner will let
+	 * the radio on the bss channel until max_rest_time expires.at
+	 * max_rest_time scanner will switch to off channel irrespective of
+	 * activity. activity is determined by the idle_time parameter.
+	 */
+	__le32 max_rest_time;
+	/*
+	 * time before sending next set of probe requests.
+	 * The scanner keeps repeating probe requests transmission with
+	 * period specified by repeat_probe_time.
+	 * The number of probe requests specified depends on the ssid_list
+	 * and bssid_list
+	 */
+	__le32 repeat_probe_time;
+	/* time in msec between 2 consequetive probe requests with in a set. */
+	__le32 probe_spacing_time;
+	/*
+	 * data inactivity time in msec on bss channel that will be used by
+	 * scanner for measuring the inactivity.
+	 */
+	__le32 idle_time;
+	/* maximum time in msec allowed for scan  */
+	__le32 max_scan_time;
+	/*
+	 * delay in msec before sending first probe request after switching
+	 * to a channel
+	 */
+	__le32 probe_delay;
+	/* Scan control flags */
+	__le32 scan_ctrl_flags;
+
+	/* Burst duration time in msecs */
+	__le32 burst_duration;
+	/*
+	 * TLV (tag length value )  paramerters follow the scan_cmd structure.
+	 * TLV can contain channel list, bssid list, ssid list and
+	 * ie. the TLV tags are defined above;
+	 */
 } __packed;
 
-/* prefix used by scan requestor ids on the host */
-#define WMI_HOST_SCAN_REQUESTOR_ID_PREFIX 0xA000
-
-/* prefix used by scan request ids generated on the host */
-/* host cycles through the lower 12 bits to generate ids */
-#define WMI_HOST_SCAN_REQ_ID_PREFIX 0xA000
-
-#define WLAN_SCAN_PARAMS_MAX_SSID    16
-#define WLAN_SCAN_PARAMS_MAX_BSSID   4
-#define WLAN_SCAN_PARAMS_MAX_IE_LEN  256
-
-/* Scan priority numbers must be sequential, starting with 0 */
-enum wmi_scan_priority {
-	WMI_SCAN_PRIORITY_VERY_LOW = 0,
-	WMI_SCAN_PRIORITY_LOW,
-	WMI_SCAN_PRIORITY_MEDIUM,
-	WMI_SCAN_PRIORITY_HIGH,
-	WMI_SCAN_PRIORITY_VERY_HIGH,
-	WMI_SCAN_PRIORITY_COUNT   /* number of priorities supported */
-};
-
-struct wmi_start_scan_cmd {
+/* This is the definition from 10.X firmware branch */
+struct wmi_start_scan_cmd_10x {
 	/* Scan ID */
 	__le32 scan_id;
+
 	/* Scan requestor ID */
 	__le32 scan_req_id;
+
 	/* VDEV id(interface) that is requesting scan */
 	__le32 vdev_id;
+
 	/* Scan Priority, input to scan scheduler */
 	__le32 scan_priority;
+
 	/* Scan events subscription */
 	__le32 notify_scan_events;
+
 	/* dwell time in msec on active channels */
 	__le32 dwell_time_active;
+
 	/* dwell time in msec on passive channels */
 	__le32 dwell_time_passive;
+
 	/*
 	 * min time in msec on the BSS channel,only valid if atleast one
 	 * VDEV is active
 	 */
 	__le32 min_rest_time;
+
 	/*
 	 * max rest time in msec on the BSS channel,only valid if at least
 	 * one VDEV is active
@@ -1083,6 +1760,7 @@
 	 * activity. activity is determined by the idle_time parameter.
 	 */
 	__le32 max_rest_time;
+
 	/*
 	 * time before sending next set of probe requests.
 	 * The scanner keeps repeating probe requests transmission with
@@ -1091,25 +1769,28 @@
 	 * and bssid_list
 	 */
 	__le32 repeat_probe_time;
+
 	/* time in msec between 2 consequetive probe requests with in a set. */
 	__le32 probe_spacing_time;
+
 	/*
 	 * data inactivity time in msec on bss channel that will be used by
 	 * scanner for measuring the inactivity.
 	 */
 	__le32 idle_time;
+
 	/* maximum time in msec allowed for scan  */
 	__le32 max_scan_time;
+
 	/*
 	 * delay in msec before sending first probe request after switching
 	 * to a channel
 	 */
 	__le32 probe_delay;
+
 	/* Scan control flags */
 	__le32 scan_ctrl_flags;
 
-	/* Burst duration time in msecs */
-	__le32 burst_duration;
 	/*
 	 * TLV (tag length value )  paramerters follow the scan_cmd structure.
 	 * TLV can contain channel list, bssid list, ssid list and
@@ -1117,6 +1798,7 @@
 	 */
 } __packed;
 
+
 struct wmi_ssid_arg {
 	int len;
 	const u8 *ssid;
@@ -1267,7 +1949,7 @@
  * good idea to pass all the fields in the RX status
  * descriptor up to the host.
  */
-struct wmi_mgmt_rx_hdr {
+struct wmi_mgmt_rx_hdr_v1 {
 	__le32 channel;
 	__le32 snr;
 	__le32 rate;
@@ -1276,8 +1958,18 @@
 	__le32 status; /* %WMI_RX_STATUS_ */
 } __packed;
 
-struct wmi_mgmt_rx_event {
-	struct wmi_mgmt_rx_hdr hdr;
+struct wmi_mgmt_rx_hdr_v2 {
+	struct wmi_mgmt_rx_hdr_v1 v1;
+	__le32 rssi_ctl[4];
+} __packed;
+
+struct wmi_mgmt_rx_event_v1 {
+	struct wmi_mgmt_rx_hdr_v1 hdr;
+	u8 buf[0];
+} __packed;
+
+struct wmi_mgmt_rx_event_v2 {
+	struct wmi_mgmt_rx_hdr_v2 hdr;
 	u8 buf[0];
 } __packed;
 
@@ -1287,6 +1979,10 @@
 #define WMI_RX_STATUS_ERR_MIC			0x10
 #define WMI_RX_STATUS_ERR_KEY_CACHE_MISS	0x20
 
+#define PHY_ERROR_SPECTRAL_SCAN		0x26
+#define PHY_ERROR_FALSE_RADAR_EXT		0x24
+#define PHY_ERROR_RADAR				0x05
+
 struct wmi_single_phyerr_rx_hdr {
 	/* TSF timestamp */
 	__le32 tsf_timestamp;
@@ -1378,6 +2074,87 @@
 	u8 bufp[0];
 } __packed;
 
+#define PHYERR_TLV_SIG				0xBB
+#define PHYERR_TLV_TAG_SEARCH_FFT_REPORT	0xFB
+#define PHYERR_TLV_TAG_RADAR_PULSE_SUMMARY	0xF8
+
+struct phyerr_radar_report {
+	__le32 reg0; /* RADAR_REPORT_REG0_* */
+	__le32 reg1; /* REDAR_REPORT_REG1_* */
+} __packed;
+
+#define RADAR_REPORT_REG0_PULSE_IS_CHIRP_MASK		0x80000000
+#define RADAR_REPORT_REG0_PULSE_IS_CHIRP_LSB		31
+
+#define RADAR_REPORT_REG0_PULSE_IS_MAX_WIDTH_MASK	0x40000000
+#define RADAR_REPORT_REG0_PULSE_IS_MAX_WIDTH_LSB	30
+
+#define RADAR_REPORT_REG0_AGC_TOTAL_GAIN_MASK		0x3FF00000
+#define RADAR_REPORT_REG0_AGC_TOTAL_GAIN_LSB		20
+
+#define RADAR_REPORT_REG0_PULSE_DELTA_DIFF_MASK		0x000F0000
+#define RADAR_REPORT_REG0_PULSE_DELTA_DIFF_LSB		16
+
+#define RADAR_REPORT_REG0_PULSE_DELTA_PEAK_MASK		0x0000FC00
+#define RADAR_REPORT_REG0_PULSE_DELTA_PEAK_LSB		10
+
+#define RADAR_REPORT_REG0_PULSE_SIDX_MASK		0x000003FF
+#define RADAR_REPORT_REG0_PULSE_SIDX_LSB		0
+
+#define RADAR_REPORT_REG1_PULSE_SRCH_FFT_VALID_MASK	0x80000000
+#define RADAR_REPORT_REG1_PULSE_SRCH_FFT_VALID_LSB	31
+
+#define RADAR_REPORT_REG1_PULSE_AGC_MB_GAIN_MASK	0x7F000000
+#define RADAR_REPORT_REG1_PULSE_AGC_MB_GAIN_LSB		24
+
+#define RADAR_REPORT_REG1_PULSE_SUBCHAN_MASK_MASK	0x00FF0000
+#define RADAR_REPORT_REG1_PULSE_SUBCHAN_MASK_LSB	16
+
+#define RADAR_REPORT_REG1_PULSE_TSF_OFFSET_MASK		0x0000FF00
+#define RADAR_REPORT_REG1_PULSE_TSF_OFFSET_LSB		8
+
+#define RADAR_REPORT_REG1_PULSE_DUR_MASK		0x000000FF
+#define RADAR_REPORT_REG1_PULSE_DUR_LSB			0
+
+struct phyerr_fft_report {
+	__le32 reg0; /* SEARCH_FFT_REPORT_REG0_ * */
+	__le32 reg1; /* SEARCH_FFT_REPORT_REG1_ * */
+} __packed;
+
+#define SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB_MASK	0xFF800000
+#define SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB_LSB	23
+
+#define SEARCH_FFT_REPORT_REG0_BASE_PWR_DB_MASK		0x007FC000
+#define SEARCH_FFT_REPORT_REG0_BASE_PWR_DB_LSB		14
+
+#define SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX_MASK		0x00003000
+#define SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX_LSB		12
+
+#define SEARCH_FFT_REPORT_REG0_PEAK_SIDX_MASK		0x00000FFF
+#define SEARCH_FFT_REPORT_REG0_PEAK_SIDX_LSB		0
+
+#define SEARCH_FFT_REPORT_REG1_RELPWR_DB_MASK		0xFC000000
+#define SEARCH_FFT_REPORT_REG1_RELPWR_DB_LSB		26
+
+#define SEARCH_FFT_REPORT_REG1_AVGPWR_DB_MASK		0x03FC0000
+#define SEARCH_FFT_REPORT_REG1_AVGPWR_DB_LSB		18
+
+#define SEARCH_FFT_REPORT_REG1_PEAK_MAG_MASK		0x0003FF00
+#define SEARCH_FFT_REPORT_REG1_PEAK_MAG_LSB		8
+
+#define SEARCH_FFT_REPORT_REG1_NUM_STR_BINS_IB_MASK	0x000000FF
+#define SEARCH_FFT_REPORT_REG1_NUM_STR_BINS_IB_LSB	0
+
+
+struct phyerr_tlv {
+	__le16 len;
+	u8 tag;
+	u8 sig;
+} __packed;
+
+#define DFS_RSSI_POSSIBLY_FALSE			50
+#define DFS_PEAK_MAG_THOLD_POSSIBLY_FALSE	40
+
 struct wmi_mgmt_tx_hdr {
 	__le32 vdev_id;
 	struct wmi_mac_addr peer_macaddr;
@@ -1464,6 +2241,59 @@
 #define VDEV_DEFAULT_STATS_UPDATE_PERIOD    500
 #define PEER_DEFAULT_STATS_UPDATE_PERIOD    500
 
+struct wmi_pdev_param_map {
+	u32 tx_chain_mask;
+	u32 rx_chain_mask;
+	u32 txpower_limit2g;
+	u32 txpower_limit5g;
+	u32 txpower_scale;
+	u32 beacon_gen_mode;
+	u32 beacon_tx_mode;
+	u32 resmgr_offchan_mode;
+	u32 protection_mode;
+	u32 dynamic_bw;
+	u32 non_agg_sw_retry_th;
+	u32 agg_sw_retry_th;
+	u32 sta_kickout_th;
+	u32 ac_aggrsize_scaling;
+	u32 ltr_enable;
+	u32 ltr_ac_latency_be;
+	u32 ltr_ac_latency_bk;
+	u32 ltr_ac_latency_vi;
+	u32 ltr_ac_latency_vo;
+	u32 ltr_ac_latency_timeout;
+	u32 ltr_sleep_override;
+	u32 ltr_rx_override;
+	u32 ltr_tx_activity_timeout;
+	u32 l1ss_enable;
+	u32 dsleep_enable;
+	u32 pcielp_txbuf_flush;
+	u32 pcielp_txbuf_watermark;
+	u32 pcielp_txbuf_tmo_en;
+	u32 pcielp_txbuf_tmo_value;
+	u32 pdev_stats_update_period;
+	u32 vdev_stats_update_period;
+	u32 peer_stats_update_period;
+	u32 bcnflt_stats_update_period;
+	u32 pmf_qos;
+	u32 arp_ac_override;
+	u32 dcs;
+	u32 ani_enable;
+	u32 ani_poll_period;
+	u32 ani_listen_period;
+	u32 ani_ofdm_level;
+	u32 ani_cck_level;
+	u32 dyntxchain;
+	u32 proxy_sta;
+	u32 idle_ps_config;
+	u32 power_gating_sleep;
+	u32 fast_channel_reset;
+	u32 burst_dur;
+	u32 burst_enable;
+};
+
+#define WMI_PDEV_PARAM_UNSUPPORTED 0
+
 enum wmi_pdev_param {
 	/* TX chian mask */
 	WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
@@ -1489,7 +2319,12 @@
 	 * 0: no protection 1:use CTS-to-self 2: use RTS/CTS
 	 */
 	WMI_PDEV_PARAM_PROTECTION_MODE,
-	/* Dynamic bandwidth 0: disable 1: enable */
+	/*
+	 * Dynamic bandwidth - 0: disable, 1: enable
+	 *
+	 * When enabled HW rate control tries different bandwidths when
+	 * retransmitting frames.
+	 */
 	WMI_PDEV_PARAM_DYNAMIC_BW,
 	/* Non aggregrate/ 11g sw retry threshold.0-disable */
 	WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
@@ -1563,6 +2398,97 @@
 	WMI_PDEV_PARAM_POWER_GATING_SLEEP,
 };
 
+enum wmi_10x_pdev_param {
+	/* TX chian mask */
+	WMI_10X_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
+	/* RX chian mask */
+	WMI_10X_PDEV_PARAM_RX_CHAIN_MASK,
+	/* TX power limit for 2G Radio */
+	WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G,
+	/* TX power limit for 5G Radio */
+	WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G,
+	/* TX power scale */
+	WMI_10X_PDEV_PARAM_TXPOWER_SCALE,
+	/* Beacon generation mode . 0: host, 1: target   */
+	WMI_10X_PDEV_PARAM_BEACON_GEN_MODE,
+	/* Beacon generation mode . 0: staggered 1: bursted   */
+	WMI_10X_PDEV_PARAM_BEACON_TX_MODE,
+	/*
+	 * Resource manager off chan mode .
+	 * 0: turn off off chan mode. 1: turn on offchan mode
+	 */
+	WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+	/*
+	 * Protection mode:
+	 * 0: no protection 1:use CTS-to-self 2: use RTS/CTS
+	 */
+	WMI_10X_PDEV_PARAM_PROTECTION_MODE,
+	/* Dynamic bandwidth 0: disable 1: enable */
+	WMI_10X_PDEV_PARAM_DYNAMIC_BW,
+	/* Non aggregrate/ 11g sw retry threshold.0-disable */
+	WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+	/* aggregrate sw retry threshold. 0-disable*/
+	WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH,
+	/* Station kickout threshold (non of consecutive failures).0-disable */
+	WMI_10X_PDEV_PARAM_STA_KICKOUT_TH,
+	/* Aggerate size scaling configuration per AC */
+	WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+	/* LTR enable */
+	WMI_10X_PDEV_PARAM_LTR_ENABLE,
+	/* LTR latency for BE, in us */
+	WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE,
+	/* LTR latency for BK, in us */
+	WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK,
+	/* LTR latency for VI, in us */
+	WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI,
+	/* LTR latency for VO, in us  */
+	WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO,
+	/* LTR AC latency timeout, in ms */
+	WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+	/* LTR platform latency override, in us */
+	WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+	/* LTR-RX override, in us */
+	WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE,
+	/* Tx activity timeout for LTR, in us */
+	WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+	/* L1SS state machine enable */
+	WMI_10X_PDEV_PARAM_L1SS_ENABLE,
+	/* Deep sleep state machine enable */
+	WMI_10X_PDEV_PARAM_DSLEEP_ENABLE,
+	/* pdev level stats update period in ms */
+	WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+	/* vdev level stats update period in ms */
+	WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+	/* peer level stats update period in ms */
+	WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+	/* beacon filter status update period */
+	WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+	/* QOS Mgmt frame protection MFP/PMF 0: disable, 1: enable */
+	WMI_10X_PDEV_PARAM_PMF_QOS,
+	/* Access category on which ARP and DHCP frames are sent */
+	WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE,
+	/* DCS configuration */
+	WMI_10X_PDEV_PARAM_DCS,
+	/* Enable/Disable ANI on target */
+	WMI_10X_PDEV_PARAM_ANI_ENABLE,
+	/* configure the ANI polling period */
+	WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD,
+	/* configure the ANI listening period */
+	WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD,
+	/* configure OFDM immunity level */
+	WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL,
+	/* configure CCK immunity level */
+	WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL,
+	/* Enable/Disable CDD for 1x1 STAs in rate control module */
+	WMI_10X_PDEV_PARAM_DYNTXCHAIN,
+	/* Enable/Disable Fast channel reset*/
+	WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET,
+	/* Set Bursting DUR */
+	WMI_10X_PDEV_PARAM_BURST_DUR,
+	/* Set Bursting Enable*/
+	WMI_10X_PDEV_PARAM_BURST_ENABLE,
+};
+
 struct wmi_pdev_set_param_cmd {
 	__le32 param_id;
 	__le32 param_value;
@@ -2076,6 +3002,18 @@
 	const void *key_data;
 };
 
+/*
+ * vdev fixed rate format:
+ * - preamble - b7:b6 - see WMI_RATE_PREMABLE_
+ * - nss      - b5:b4 - ss number (0 mean 1ss)
+ * - rate_mcs - b3:b0 - as below
+ *    CCK:  0 - 11Mbps, 1 - 5,5Mbps, 2 - 2Mbps, 3 - 1Mbps,
+ *          4 - 11Mbps (s), 5 - 5,5Mbps (s), 6 - 2Mbps (s)
+ *    OFDM: 0 - 48Mbps, 1 - 24Mbps, 2 - 12Mbps, 3 - 6Mbps,
+ *          4 - 54Mbps, 5 - 36Mbps, 6 - 18Mbps, 7 - 9Mbps
+ *    HT/VHT: MCS index
+ */
+
 /* Preamble types to be used with VDEV fixed rate configuration */
 enum wmi_rate_preamble {
 	WMI_RATE_PREAMBLE_OFDM,
@@ -2087,6 +3025,61 @@
 /* Value to disable fixed rate setting */
 #define WMI_FIXED_RATE_NONE    (0xff)
 
+struct wmi_vdev_param_map {
+	u32 rts_threshold;
+	u32 fragmentation_threshold;
+	u32 beacon_interval;
+	u32 listen_interval;
+	u32 multicast_rate;
+	u32 mgmt_tx_rate;
+	u32 slot_time;
+	u32 preamble;
+	u32 swba_time;
+	u32 wmi_vdev_stats_update_period;
+	u32 wmi_vdev_pwrsave_ageout_time;
+	u32 wmi_vdev_host_swba_interval;
+	u32 dtim_period;
+	u32 wmi_vdev_oc_scheduler_air_time_limit;
+	u32 wds;
+	u32 atim_window;
+	u32 bmiss_count_max;
+	u32 bmiss_first_bcnt;
+	u32 bmiss_final_bcnt;
+	u32 feature_wmm;
+	u32 chwidth;
+	u32 chextoffset;
+	u32 disable_htprotection;
+	u32 sta_quickkickout;
+	u32 mgmt_rate;
+	u32 protection_mode;
+	u32 fixed_rate;
+	u32 sgi;
+	u32 ldpc;
+	u32 tx_stbc;
+	u32 rx_stbc;
+	u32 intra_bss_fwd;
+	u32 def_keyid;
+	u32 nss;
+	u32 bcast_data_rate;
+	u32 mcast_data_rate;
+	u32 mcast_indicate;
+	u32 dhcp_indicate;
+	u32 unknown_dest_indicate;
+	u32 ap_keepalive_min_idle_inactive_time_secs;
+	u32 ap_keepalive_max_idle_inactive_time_secs;
+	u32 ap_keepalive_max_unresponsive_time_secs;
+	u32 ap_enable_nawds;
+	u32 mcast2ucast_set;
+	u32 enable_rtscts;
+	u32 txbf;
+	u32 packet_powersave;
+	u32 drop_unencry;
+	u32 tx_encap_type;
+	u32 ap_detect_out_of_sync_sleeping_sta_time_secs;
+};
+
+#define WMI_VDEV_PARAM_UNSUPPORTED 0
+
 /* the definition of different VDEV parameters */
 enum wmi_vdev_param {
 	/* RTS Threshold */
@@ -2218,6 +3211,121 @@
 	WMI_VDEV_PARAM_TX_ENCAP_TYPE,
 };
 
+/* the definition of different VDEV parameters */
+enum wmi_10x_vdev_param {
+	/* RTS Threshold */
+	WMI_10X_VDEV_PARAM_RTS_THRESHOLD = 0x1,
+	/* Fragmentation threshold */
+	WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+	/* beacon interval in TUs */
+	WMI_10X_VDEV_PARAM_BEACON_INTERVAL,
+	/* Listen interval in TUs */
+	WMI_10X_VDEV_PARAM_LISTEN_INTERVAL,
+	/* muticast rate in Mbps */
+	WMI_10X_VDEV_PARAM_MULTICAST_RATE,
+	/* management frame rate in Mbps */
+	WMI_10X_VDEV_PARAM_MGMT_TX_RATE,
+	/* slot time (long vs short) */
+	WMI_10X_VDEV_PARAM_SLOT_TIME,
+	/* preamble (long vs short) */
+	WMI_10X_VDEV_PARAM_PREAMBLE,
+	/* SWBA time (time before tbtt in msec) */
+	WMI_10X_VDEV_PARAM_SWBA_TIME,
+	/* time period for updating VDEV stats */
+	WMI_10X_VDEV_STATS_UPDATE_PERIOD,
+	/* age out time in msec for frames queued for station in power save */
+	WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME,
+	/*
+	 * Host SWBA interval (time in msec before tbtt for SWBA event
+	 * generation).
+	 */
+	WMI_10X_VDEV_HOST_SWBA_INTERVAL,
+	/* DTIM period (specified in units of num beacon intervals) */
+	WMI_10X_VDEV_PARAM_DTIM_PERIOD,
+	/*
+	 * scheduler air time limit for this VDEV. used by off chan
+	 * scheduler.
+	 */
+	WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+	/* enable/dsiable WDS for this VDEV  */
+	WMI_10X_VDEV_PARAM_WDS,
+	/* ATIM Window */
+	WMI_10X_VDEV_PARAM_ATIM_WINDOW,
+	/* BMISS max */
+	WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX,
+	/* WMM enables/disabled */
+	WMI_10X_VDEV_PARAM_FEATURE_WMM,
+	/* Channel width */
+	WMI_10X_VDEV_PARAM_CHWIDTH,
+	/* Channel Offset */
+	WMI_10X_VDEV_PARAM_CHEXTOFFSET,
+	/* Disable HT Protection */
+	WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION,
+	/* Quick STA Kickout */
+	WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT,
+	/* Rate to be used with Management frames */
+	WMI_10X_VDEV_PARAM_MGMT_RATE,
+	/* Protection Mode */
+	WMI_10X_VDEV_PARAM_PROTECTION_MODE,
+	/* Fixed rate setting */
+	WMI_10X_VDEV_PARAM_FIXED_RATE,
+	/* Short GI Enable/Disable */
+	WMI_10X_VDEV_PARAM_SGI,
+	/* Enable LDPC */
+	WMI_10X_VDEV_PARAM_LDPC,
+	/* Enable Tx STBC */
+	WMI_10X_VDEV_PARAM_TX_STBC,
+	/* Enable Rx STBC */
+	WMI_10X_VDEV_PARAM_RX_STBC,
+	/* Intra BSS forwarding  */
+	WMI_10X_VDEV_PARAM_INTRA_BSS_FWD,
+	/* Setting Default xmit key for Vdev */
+	WMI_10X_VDEV_PARAM_DEF_KEYID,
+	/* NSS width */
+	WMI_10X_VDEV_PARAM_NSS,
+	/* Set the custom rate for the broadcast data frames */
+	WMI_10X_VDEV_PARAM_BCAST_DATA_RATE,
+	/* Set the custom rate (rate-code) for multicast data frames */
+	WMI_10X_VDEV_PARAM_MCAST_DATA_RATE,
+	/* Tx multicast packet indicate Enable/Disable */
+	WMI_10X_VDEV_PARAM_MCAST_INDICATE,
+	/* Tx DHCP packet indicate Enable/Disable */
+	WMI_10X_VDEV_PARAM_DHCP_INDICATE,
+	/* Enable host inspection of Tx unicast packet to unknown destination */
+	WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+
+	/* The minimum amount of time AP begins to consider STA inactive */
+	WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+
+	/*
+	 * An associated STA is considered inactive when there is no recent
+	 * TX/RX activity and no downlink frames are buffered for it. Once a
+	 * STA exceeds the maximum idle inactive time, the AP will send an
+	 * 802.11 data-null as a keep alive to verify the STA is still
+	 * associated. If the STA does ACK the data-null, or if the data-null
+	 * is buffered and the STA does not retrieve it, the STA will be
+	 * considered unresponsive
+	 * (see WMI_10X_VDEV_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS).
+	 */
+	WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+
+	/*
+	 * An associated STA is considered unresponsive if there is no recent
+	 * TX/RX activity and downlink frames are buffered for it. Once a STA
+	 * exceeds the maximum unresponsive time, the AP will send a
+	 * WMI_10X_STA_KICKOUT event to the host so the STA can be deleted. */
+	WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+
+	/* Enable NAWDS : MCAST INSPECT Enable, NAWDS Flag set */
+	WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS,
+
+	WMI_10X_VDEV_PARAM_MCAST2UCAST_SET,
+	/* Enable/Disable RTS-CTS */
+	WMI_10X_VDEV_PARAM_ENABLE_RTSCTS,
+
+	WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
+};
+
 /* slot time long */
 #define WMI_VDEV_SLOT_TIME_LONG		0x1
 /* slot time short */
@@ -2294,6 +3402,24 @@
 	const void *bcn;
 };
 
+enum wmi_bcn_tx_ref_flags {
+	WMI_BCN_TX_REF_FLAG_DTIM_ZERO = 0x1,
+	WMI_BCN_TX_REF_FLAG_DELIVER_CAB = 0x2,
+};
+
+struct wmi_bcn_tx_ref_cmd {
+	__le32 vdev_id;
+	__le32 data_len;
+	/* physical address of the frame - dma pointer */
+	__le32 data_ptr;
+	/* id for host to track */
+	__le32 msdu_id;
+	/* frame ctrl to setup PPDU desc */
+	__le32 frame_control;
+	/* to control CABQ traffic: WMI_BCN_TX_REF_FLAG_ */
+	__le32 flags;
+} __packed;
+
 /* Beacon filter */
 #define WMI_BCN_FILTER_ALL   0 /* Filter all beacons */
 #define WMI_BCN_FILTER_NONE  1 /* Pass all beacons */
@@ -2750,6 +3876,12 @@
 	WMI_PEER_SMPS_DYNAMIC = 0x2
 };
 
+enum wmi_peer_chwidth {
+	WMI_PEER_CHWIDTH_20MHZ = 0,
+	WMI_PEER_CHWIDTH_40MHZ = 1,
+	WMI_PEER_CHWIDTH_80MHZ = 2,
+};
+
 enum wmi_peer_param {
 	WMI_PEER_SMPS_STATE = 0x1, /* see %wmi_peer_smps_state */
 	WMI_PEER_AMPDU      = 0x2,
@@ -2930,6 +4062,15 @@
 	__le32 cycle_count;
 } __packed;
 
+struct wmi_peer_sta_kickout_event {
+	struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+#define WMI_CHAN_INFO_FLAG_COMPLETE BIT(0)
+
+/* FIXME: empirically extrapolated */
+#define WMI_CHAN_INFO_MSEC(x) ((x) / 76595)
+
 /* Beacon filter wmi command info */
 #define BCN_FLT_MAX_SUPPORTED_IES	256
 #define BCN_FLT_MAX_ELEMS_IE_LIST	(BCN_FLT_MAX_SUPPORTED_IES / 32)
@@ -2972,13 +4113,76 @@
 	struct wmi_sta_keepalive_arp_resp arp_resp;
 } __packed;
 
+enum wmi_force_fw_hang_type {
+	WMI_FORCE_FW_HANG_ASSERT = 1,
+	WMI_FORCE_FW_HANG_NO_DETECT,
+	WMI_FORCE_FW_HANG_CTRL_EP_FULL,
+	WMI_FORCE_FW_HANG_EMPTY_POINT,
+	WMI_FORCE_FW_HANG_STACK_OVERFLOW,
+	WMI_FORCE_FW_HANG_INFINITE_LOOP,
+};
+
+#define WMI_FORCE_FW_HANG_RANDOM_TIME 0xFFFFFFFF
+
+struct wmi_force_fw_hang_cmd {
+	__le32 type;
+	__le32 delay_ms;
+} __packed;
+
+enum ath10k_dbglog_level {
+	ATH10K_DBGLOG_LEVEL_VERBOSE = 0,
+	ATH10K_DBGLOG_LEVEL_INFO = 1,
+	ATH10K_DBGLOG_LEVEL_WARN = 2,
+	ATH10K_DBGLOG_LEVEL_ERR = 3,
+};
+
+/* VAP ids to enable dbglog */
+#define ATH10K_DBGLOG_CFG_VAP_LOG_LSB		0
+#define ATH10K_DBGLOG_CFG_VAP_LOG_MASK		0x0000ffff
+
+/* to enable dbglog in the firmware */
+#define ATH10K_DBGLOG_CFG_REPORTING_ENABLE_LSB	16
+#define ATH10K_DBGLOG_CFG_REPORTING_ENABLE_MASK	0x00010000
+
+/* timestamp resolution */
+#define ATH10K_DBGLOG_CFG_RESOLUTION_LSB	17
+#define ATH10K_DBGLOG_CFG_RESOLUTION_MASK	0x000E0000
+
+/* number of queued messages before sending them to the host */
+#define ATH10K_DBGLOG_CFG_REPORT_SIZE_LSB	20
+#define ATH10K_DBGLOG_CFG_REPORT_SIZE_MASK	0x0ff00000
+
+/*
+ * Log levels to enable. This defines the minimum level to enable, this is
+ * not a bitmask. See enum ath10k_dbglog_level for the values.
+ */
+#define ATH10K_DBGLOG_CFG_LOG_LVL_LSB		28
+#define ATH10K_DBGLOG_CFG_LOG_LVL_MASK		0x70000000
+
+/*
+ * Note: this is a cleaned up version of a struct firmware uses. For
+ * example, config_valid was hidden inside an array.
+ */
+struct wmi_dbglog_cfg_cmd {
+	/* bitmask to hold mod id config*/
+	__le32 module_enable;
+
+	/* see ATH10K_DBGLOG_CFG_ */
+	__le32 config_enable;
+
+	/* mask of module id bits to be changed */
+	__le32 module_valid;
+
+	/* mask of config bits to be changed, see ATH10K_DBGLOG_CFG_ */
+	__le32 config_valid;
+} __packed;
+
 #define ATH10K_RTS_MAX		2347
 #define ATH10K_FRAGMT_THRESHOLD_MIN	540
 #define ATH10K_FRAGMT_THRESHOLD_MAX	2346
 
 #define WMI_MAX_EVENT 0x1000
 /* Maximum number of pending TXed WMI packets */
-#define WMI_MAX_PENDING_TX_COUNT 128
 #define WMI_SKB_HEADROOM sizeof(struct wmi_cmd_hdr)
 
 /* By default disable power save for IBSS */
@@ -2991,17 +4195,15 @@
 void ath10k_wmi_detach(struct ath10k *ar);
 int ath10k_wmi_wait_for_service_ready(struct ath10k *ar);
 int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar);
-void ath10k_wmi_flush_tx(struct ath10k *ar);
 
 int ath10k_wmi_connect_htc_service(struct ath10k *ar);
 int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
 				const struct wmi_channel_arg *);
-int ath10k_wmi_pdev_suspend_target(struct ath10k *ar);
+int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt);
 int ath10k_wmi_pdev_resume_target(struct ath10k *ar);
 int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
 				  u16 rd5g, u16 ctl2g, u16 ctl5g);
-int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
-			      u32 value);
+int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value);
 int ath10k_wmi_cmd_init(struct ath10k *ar);
 int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *);
 void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *);
@@ -3021,7 +4223,7 @@
 		       const u8 *bssid);
 int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id);
 int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
-			      enum wmi_vdev_param param_id, u32 param_value);
+			      u32 param_id, u32 param_value);
 int ath10k_wmi_vdev_install_key(struct ath10k *ar,
 				const struct wmi_vdev_install_key_arg *arg);
 int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
@@ -3044,9 +4246,13 @@
 			       enum wmi_ap_ps_peer_param param_id, u32 value);
 int ath10k_wmi_scan_chan_list(struct ath10k *ar,
 			      const struct wmi_scan_chan_list_arg *arg);
-int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg);
+int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif);
 int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
 			const struct wmi_pdev_set_wmm_params_arg *arg);
 int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
+int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+			     enum wmi_force_fw_hang_type type, u32 delay_ms);
+int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb);
+int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable);
 
 #endif /* _WMI_H_ */
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath9k/Makefile linux-3.11.10-fbx/drivers/net/wireless/ath/ath9k/Makefile
--- linux-3.11.10/drivers/net/wireless/ath/ath9k/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath9k/Makefile	2015-09-29 11:15:06.937740787 +0200
@@ -14,9 +14,7 @@
 ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
 ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o
 ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += \
-		dfs.o \
-		dfs_pattern_detector.o \
-		dfs_pri_detector.o
+		dfs.o
 ath9k-$(CONFIG_PM_SLEEP) += wow.o
 
 obj-$(CONFIG_ATH9K) += ath9k.o
diff -ruw linux-3.11.10/drivers/net/wireless/ath/ath.h linux-3.11.10-fbx/drivers/net/wireless/ath/ath.h
--- linux-3.11.10/drivers/net/wireless/ath/ath.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/ath.h	2015-09-29 11:15:06.917740641 +0200
@@ -62,7 +62,7 @@
 };
 
 struct reg_dmn_pair_mapping {
-	u16 regDmnEnum;
+	u16 reg_domain;
 	u16 reg_5ghz_ctl;
 	u16 reg_2ghz_ctl;
 };
diff -ruw linux-3.11.10/drivers/net/wireless/ath/Makefile linux-3.11.10-fbx/drivers/net/wireless/ath/Makefile
--- linux-3.11.10/drivers/net/wireless/ath/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/Makefile	2015-09-29 11:15:06.917740641 +0200
@@ -11,7 +11,9 @@
 ath-objs :=	main.o \
 		regd.o \
 		hw.o \
-		key.o
+		key.o \
+		dfs_pattern_detector.o \
+		dfs_pri_detector.o
 
 ath-$(CONFIG_ATH_DEBUG) += debug.o
 ccflags-y += -D__CHECK_ENDIAN__
diff -ruw linux-3.11.10/drivers/net/wireless/ath/regd.c linux-3.11.10-fbx/drivers/net/wireless/ath/regd.c
--- linux-3.11.10/drivers/net/wireless/ath/regd.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/regd.c	2015-09-29 11:15:06.961740969 +0200
@@ -37,17 +37,18 @@
 
 /* We enable active scan on these a case by case basis by regulatory domain */
 #define ATH9K_2GHZ_CH12_13	REG_RULE(2467-10, 2472+10, 40, 0, 20,\
-					NL80211_RRF_PASSIVE_SCAN)
+					 NL80211_RRF_NO_IR)
 #define ATH9K_2GHZ_CH14		REG_RULE(2484-10, 2484+10, 40, 0, 20,\
-				NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM)
+					 NL80211_RRF_NO_IR | \
+					 NL80211_RRF_NO_OFDM)
 
 /* We allow IBSS on these on a case by case basis by regulatory domain */
 #define ATH9K_5GHZ_5150_5350	REG_RULE(5150-10, 5350+10, 80, 0, 30,\
-				NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
+					 NL80211_RRF_NO_IR)
 #define ATH9K_5GHZ_5470_5850	REG_RULE(5470-10, 5850+10, 80, 0, 30,\
-				NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
+					 NL80211_RRF_NO_IR)
 #define ATH9K_5GHZ_5725_5850	REG_RULE(5725-10, 5850+10, 80, 0, 30,\
-				NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
+					 NL80211_RRF_NO_IR)
 
 #define ATH9K_2GHZ_ALL		ATH9K_2GHZ_CH01_11, \
 				ATH9K_2GHZ_CH12_13, \
@@ -140,7 +141,7 @@
 static const struct
 ieee80211_regdomain *ath_world_regdomain(struct ath_regulatory *reg)
 {
-	switch (reg->regpair->regDmnEnum) {
+	switch (reg->regpair->reg_domain) {
 	case 0x60:
 	case 0x61:
 	case 0x62:
@@ -223,18 +224,11 @@
 				 * default during init, prior to calling our
 				 * regulatory_hint().
 				 */
-				if (!(reg_rule->flags &
-				    NL80211_RRF_NO_IBSS))
-					ch->flags &=
-					  ~IEEE80211_CHAN_NO_IBSS;
-				if (!(reg_rule->flags &
-				    NL80211_RRF_PASSIVE_SCAN))
-					ch->flags &=
-					  ~IEEE80211_CHAN_PASSIVE_SCAN;
+				if (!(reg_rule->flags & NL80211_RRF_NO_IR))
+					ch->flags &= ~IEEE80211_CHAN_NO_IR;
 			} else {
 				if (ch->beacon_found)
-					ch->flags &= ~(IEEE80211_CHAN_NO_IBSS |
-					  IEEE80211_CHAN_PASSIVE_SCAN);
+					ch->flags &= ~IEEE80211_CHAN_NO_IR;
 			}
 		}
 	}
@@ -260,11 +254,11 @@
 	 */
 	if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
 		ch = &sband->channels[11]; /* CH 12 */
-		if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
-			ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
+		if (ch->flags & IEEE80211_CHAN_NO_IR)
+			ch->flags &= ~IEEE80211_CHAN_NO_IR;
 		ch = &sband->channels[12]; /* CH 13 */
-		if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
-			ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
+		if (ch->flags & IEEE80211_CHAN_NO_IR)
+			ch->flags &= ~IEEE80211_CHAN_NO_IR;
 		return;
 	}
 
@@ -278,17 +272,17 @@
 	ch = &sband->channels[11]; /* CH 12 */
 	reg_rule = freq_reg_info(wiphy, ch->center_freq);
 	if (!IS_ERR(reg_rule)) {
-		if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
-			if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
-				ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
+		if (!(reg_rule->flags & NL80211_RRF_NO_IR))
+			if (ch->flags & IEEE80211_CHAN_NO_IR)
+				ch->flags &= ~IEEE80211_CHAN_NO_IR;
 	}
 
 	ch = &sband->channels[12]; /* CH 13 */
 	reg_rule = freq_reg_info(wiphy, ch->center_freq);
 	if (!IS_ERR(reg_rule)) {
-		if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
-			if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
-				ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
+		if (!(reg_rule->flags & NL80211_RRF_NO_IR))
+			if (ch->flags & IEEE80211_CHAN_NO_IR)
+				ch->flags &= ~IEEE80211_CHAN_NO_IR;
 	}
 }
 
@@ -320,8 +314,7 @@
 		 */
 		if (!(ch->flags & IEEE80211_CHAN_DISABLED))
 			ch->flags |= IEEE80211_CHAN_RADAR |
-				     IEEE80211_CHAN_NO_IBSS |
-				     IEEE80211_CHAN_PASSIVE_SCAN;
+				     IEEE80211_CHAN_NO_IR;
 	}
 }
 
@@ -329,7 +322,7 @@
 				      enum nl80211_reg_initiator initiator,
 				      struct ath_regulatory *reg)
 {
-	switch (reg->regpair->regDmnEnum) {
+	switch (reg->regpair->reg_domain) {
 	case 0x60:
 	case 0x63:
 	case 0x66:
@@ -432,7 +425,7 @@
 			printk(KERN_DEBUG "ath: EEPROM indicates we "
 			       "should expect a direct regpair map\n");
 		for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
-			if (regDomainPairs[i].regDmnEnum == rd)
+			if (regDomainPairs[i].reg_domain == rd)
 				return true;
 	}
 	printk(KERN_DEBUG
@@ -489,7 +482,7 @@
 	if (regdmn == NO_ENUMRD)
 		return NULL;
 	for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
-		if (regDomainPairs[i].regDmnEnum == regdmn)
+		if (regDomainPairs[i].reg_domain == regdmn)
 			return &regDomainPairs[i];
 	}
 	return NULL;
@@ -566,7 +559,7 @@
 	    regdmn == CTRY_DEFAULT) {
 		printk(KERN_DEBUG "ath: EEPROM indicates default "
 		       "country code should be used\n");
-		reg->country_code = CTRY_UNITED_STATES;
+		reg->country_code = CTRY_FRANCE;
 	}
 
 	if (reg->country_code == CTRY_DEFAULT) {
@@ -611,7 +604,7 @@
 	printk(KERN_DEBUG "ath: Country alpha2 being used: %c%c\n",
 		reg->alpha2[0], reg->alpha2[1]);
 	printk(KERN_DEBUG "ath: Regpair used: 0x%0x\n",
-		reg->regpair->regDmnEnum);
+		reg->regpair->reg_domain);
 
 	return 0;
 }
diff -ruw linux-3.11.10/drivers/net/wireless/Kconfig linux-3.11.10-fbx/drivers/net/wireless/Kconfig
--- linux-3.11.10/drivers/net/wireless/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/Kconfig	2015-09-29 11:15:06.913740604 +0200
@@ -264,6 +264,13 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called mwl8k.  If unsure, say N.
 
+config MWL8K_MFG
+	tristate "Marvell 88W8xxx Manufacturing mode"
+	# force build as module, only if mwl8k is built as module or
+	# unselected
+	depends on m && (MWL8K_MODULE || !MWL8K)
+	select CFG80211_WEXT
+
 source "drivers/net/wireless/ath/Kconfig"
 source "drivers/net/wireless/b43/Kconfig"
 source "drivers/net/wireless/b43legacy/Kconfig"
diff -ruw linux-3.11.10/drivers/net/wireless/Makefile linux-3.11.10-fbx/drivers/net/wireless/Makefile
--- linux-3.11.10/drivers/net/wireless/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/Makefile	2015-09-29 11:15:06.913740604 +0200
@@ -40,6 +40,7 @@
 obj-$(CONFIG_ADM8211)	+= adm8211.o
 
 obj-$(CONFIG_MWL8K)	+= mwl8k.o
+obj-$(CONFIG_MWL8K_MFG)	+= mwl8k_mfg.o
 
 obj-$(CONFIG_IWLWIFI)	+= iwlwifi/
 obj-$(CONFIG_IWLEGACY)	+= iwlegacy/
diff -ruw linux-3.11.10/drivers/net/wireless/mwl8k.c linux-3.11.10-fbx/drivers/net/wireless/mwl8k.c
--- linux-3.11.10/drivers/net/wireless/mwl8k.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/wireless/mwl8k.c	2015-09-29 11:15:07.061741738 +0200
@@ -25,17 +25,34 @@
 #include <linux/moduleparam.h>
 #include <linux/firmware.h>
 #include <linux/workqueue.h>
+#include <linux/crc32.h>
 
 #define MWL8K_DESC	"Marvell TOPDOG(R) 802.11 Wireless Network Driver"
 #define MWL8K_NAME	KBUILD_MODNAME
-#define MWL8K_VERSION	"0.13"
+#define MWL8K_VERSION	"0.17.0"
 
 /* Module parameters */
-static bool ap_mode_default;
+static bool ap_mode_default = 1;
 module_param(ap_mode_default, bool, 0);
 MODULE_PARM_DESC(ap_mode_default,
 		 "Set to 1 to make ap mode the default instead of sta mode");
 
+static bool disable_life_time_expiry;
+module_param(disable_life_time_expiry, bool, 0);
+MODULE_PARM_DESC(disable_life_time_expiry,
+		 "Set to 1 to to disable the life time expiry feature");
+
+static u8 ap_base_mac_addr[18] = "00:00:00:00:00:00";
+module_param_string(base_mac_addr, ap_base_mac_addr, 18, 0);
+MODULE_PARM_DESC(ap_base_mac_addr,
+		  "Override EEPROM defined base mac address in AP mode");
+
+static bool disable_5g = 0;
+module_param(disable_5g, bool, 0);
+MODULE_PARM_DESC(disable_5g,
+		 "Set to 1 to disable 5G band usage");
+
+
 /* Register definitions */
 #define MWL8K_HIU_GEN_PTR			0x00000c10
 #define  MWL8K_MODE_STA				 0x0000005a
@@ -82,6 +99,8 @@
  */
 
 #define	MWL8K_HW_TIMER_REGISTER			0x0000a600
+#define BBU_RXRDY_CNT_REG			0x0000a860
+#define NOK_CCA_CNT_REG				0x0000a6a0
 
 #define MWL8K_A2H_EVENTS	(MWL8K_A2H_INT_DUMMY | \
 				 MWL8K_A2H_INT_CHNL_SWITCHED | \
@@ -174,6 +193,8 @@
 	u8 idx;
 };
 
+#define MWL8K_NUM_CHANS 18
+
 struct mwl8k_priv {
 	struct ieee80211_hw *hw;
 	struct pci_dev *pdev;
@@ -279,7 +300,12 @@
 
 	/* To perform the task of reloading the firmware */
 	struct work_struct fw_reload;
+	/* To firmware if the firmware is stuck or has crashed */
+	struct work_struct fw_check;
+
 	bool hw_restart_in_progress;
+	/* Set to true after the firmware is loaded */
+	bool booted;
 
 	/* async firmware loading state */
 	unsigned fw_state;
@@ -290,6 +316,14 @@
 
 	/* bitmap of running BSSes */
 	u32 running_bsses;
+
+	unsigned long channel_time;
+	struct ieee80211_channel *acs_chan;
+	bool sw_scan_start;
+	struct survey_info survey[MWL8K_NUM_CHANS];
+
+	unsigned int last_short_preamble;
+	unsigned int last_basic_rates;
 };
 
 #define MAX_WEP_KEY_LEN         13
@@ -317,6 +351,9 @@
 
 	/* A flag to indicate is HW crypto is enabled for this bssid */
 	bool is_hw_crypto_enabled;
+
+	u32 last_beacon_crc;
+	unsigned int last_beacon_int;
 };
 #define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv))
 #define IEEE80211_KEY_CONF(_u8) ((struct ieee80211_key_conf *)(_u8))
@@ -397,6 +434,7 @@
 #define MWL8K_CMD_SET_HW_SPEC		0x0004
 #define MWL8K_CMD_MAC_MULTICAST_ADR	0x0010
 #define MWL8K_CMD_GET_STAT		0x0014
+#define MWL8K_CMD_BBP_REG_ACCESS	0x001a
 #define MWL8K_CMD_RADIO_CONTROL		0x001c
 #define MWL8K_CMD_RF_TX_POWER		0x001e
 #define MWL8K_CMD_TX_POWER		0x001f
@@ -406,6 +444,7 @@
 #define MWL8K_CMD_SET_POST_SCAN		0x0108
 #define MWL8K_CMD_SET_RF_CHANNEL	0x010a
 #define MWL8K_CMD_SET_AID		0x010d
+#define MWL8K_CMD_SET_INFRA_MODE	0x010e
 #define MWL8K_CMD_SET_RATE		0x0110
 #define MWL8K_CMD_SET_FINALIZE_JOIN	0x0111
 #define MWL8K_CMD_RTS_THRESHOLD		0x0113
@@ -998,6 +1037,9 @@
 		}
 	}
 
+	if (le16_to_cpu(rxd->htsig2) & (1 << 7))
+		status->flag |= RX_FLAG_SHORT_GI;
+
 	if (rxd->channel > 14) {
 		status->band = IEEE80211_BAND_5GHZ;
 		if (!(status->flag & RX_FLAG_HT))
@@ -1511,21 +1553,77 @@
 			  fw_owned, drv_owned, unused);
 	}
 }
+#define MWL8K_TX_WAIT_TIMEOUT_MS	5000
+
+static void mwl8k_check_fw_state(struct work_struct *work)
+{
+	DECLARE_COMPLETION_ONSTACK(tx_wait);
+	struct mwl8k_priv *priv =
+		container_of(work, struct mwl8k_priv, fw_check);
+	struct ieee80211_hw *hw = priv->hw;
+	int retry;
+	int rc;
+
+	if (priv->hw_restart_in_progress)
+		return;
+
+	might_sleep();
+
+
+	retry = 1;
+	rc = 0;
+
+	spin_lock_bh(&priv->tx_lock);
+	if (!priv->pending_tx_pkts) {
+		spin_unlock_bh(&priv->tx_lock);
+		return;
+	}
+	priv->tx_wait = &tx_wait;
+	while (!rc) {
+		unsigned long timeout;
+
+		spin_unlock_bh(&priv->tx_lock);
+		timeout = wait_for_completion_timeout(&tx_wait,
+			    msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS));
+
+		spin_lock_bh(&priv->tx_lock);
+
+		if (timeout || !priv->pending_tx_pkts)
+			break;
+
+		if (retry) {
+			mwl8k_tx_start(priv);
+			retry = 0;
+			continue;
+		}
+
+		wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n",
+			  MWL8K_TX_WAIT_TIMEOUT_MS);
+		mwl8k_dump_tx_rings(hw);
+
+		priv->tx_wait = NULL;
+		priv->hw_restart_in_progress = true;
+		ieee80211_queue_work(hw, &priv->fw_reload);
+		rc = -ETIMEDOUT;
+	}
+
+	priv->tx_wait = NULL;
+	spin_unlock_bh(&priv->tx_lock);
+
+	return;
+}
 
 /*
  * Must be called with priv->fw_mutex held and tx queues stopped.
  */
-#define MWL8K_TX_WAIT_TIMEOUT_MS	5000
 
 static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
 {
 	struct mwl8k_priv *priv = hw->priv;
-	DECLARE_COMPLETION_ONSTACK(tx_wait);
+	DECLARE_COMPLETION_ONSTACK(tx_wait_old);
 	int retry;
 	int rc;
 
-	might_sleep();
-
 	/* Since fw restart is in progress, allow only the firmware
 	 * commands from the restart code and block the other
 	 * commands since they are going to fail in any case since
@@ -1538,6 +1636,19 @@
 			return -EBUSY;
 	}
 
+	/* For 8764, cmd and data need not be multiplexed, however to
+	 * detect a situation where the fw has crashed, schedule a work
+	 * queue to check the firmware state and take the necessary action
+	 */
+
+	if (priv->is_8764 && priv->booted == true) {
+		if (!priv->hw_restart_in_progress)
+			ieee80211_queue_work(hw, &priv->fw_check);
+		return 0;
+	}
+
+	might_sleep();
+
 	if (atomic_read(&priv->watchdog_event_pending))
 		return 0;
 
@@ -1552,7 +1663,7 @@
 	rc = 0;
 
 	spin_lock_bh(&priv->tx_lock);
-	priv->tx_wait = &tx_wait;
+	priv->tx_wait = &tx_wait_old;
 	while (!rc) {
 		int oldcount;
 		unsigned long timeout;
@@ -1560,7 +1671,7 @@
 		oldcount = priv->pending_tx_pkts;
 
 		spin_unlock_bh(&priv->tx_lock);
-		timeout = wait_for_completion_timeout(&tx_wait,
+		timeout = wait_for_completion_timeout(&tx_wait_old,
 			    msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS));
 
 		if (atomic_read(&priv->watchdog_event_pending)) {
@@ -1647,6 +1758,8 @@
  */
 
 #define RI_FORMAT(a)		  (a & 0x0001)
+#define RI_SHORT_GI(a)		 (a & 0x0002)
+#define RI_40MHZ(a)		 (a & 0x0004)
 #define RI_RATE_ID_MCS(a)	 ((a & 0x01f8) >> 3)
 
 static int
@@ -1704,6 +1817,8 @@
 		tx_desc->pkt_len = 0;
 
 		info = IEEE80211_SKB_CB(skb);
+		rate_info = le16_to_cpu(tx_desc->rate_info);
+
 		if (ieee80211_is_data(wh->frame_control)) {
 			rcu_read_lock();
 			sta = ieee80211_find_sta_by_ifaddr(hw, wh->addr1,
@@ -1711,7 +1826,6 @@
 			if (sta) {
 				sta_info = MWL8K_STA(sta);
 				BUG_ON(sta_info == NULL);
-				rate_info = le16_to_cpu(tx_desc->rate_info);
 				/* If rate is < 6.5 Mpbs for an ht station
 				 * do not form an ampdu. If the station is a
 				 * legacy station (format = 0), do not form an
@@ -1725,18 +1839,25 @@
 				}
 			}
 			rcu_read_unlock();
-		}
 
-		ieee80211_tx_info_clear_status(info);
-
-		/* Rate control is happening in the firmware.
-		 * Ensure no tx rate is being reported.
-		 */
+			info->status.rates[0].idx = RI_RATE_ID_MCS(rate_info);
+			info->status.rates[0].flags = 0;
+			if (RI_FORMAT(rate_info))
+				info->status.rates[0].flags |= IEEE80211_TX_RC_MCS;
+			if (RI_SHORT_GI(rate_info))
+				info->status.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI;
+			if (RI_40MHZ(rate_info))
+				info->status.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+			info->status.rates[1].idx = -1;
+		} else
 		info->status.rates[0].idx = -1;
-		info->status.rates[0].count = 1;
+
+		ieee80211_tx_info_clear_status(info);
 
 		if (MWL8K_TXD_SUCCESS(status))
 			info->flags |= IEEE80211_TX_STAT_ACK;
+		if (index >= MWL8K_TX_WMM_QUEUES)
+			info->flags |= IEEE80211_TX_STAT_AMPDU;
 
 		ieee80211_tx_status_irqsafe(hw, skb);
 
@@ -2139,10 +2260,18 @@
 		int rc;
 
 		mutex_lock(&priv->fw_mutex);
+		if (!priv->is_8764 || priv->hw_restart_in_progress)
 		ieee80211_stop_queues(hw);
 
 		rc = mwl8k_tx_wait_empty(hw);
 		if (rc) {
+			/* rc will be non-zero
+			 * if hw_restart is in progress and cmd has come from
+			 * routine other than restart (may be old pending cmd or
+			 * AMPDU)
+			 *
+			 * Above is also true for 8764.
+			 */
 			if (!priv->hw_restart_in_progress)
 				ieee80211_wake_queues(hw);
 
@@ -2164,7 +2293,8 @@
 	struct mwl8k_priv *priv = hw->priv;
 
 	if (!--priv->fw_mutex_depth) {
-		if (!priv->hw_restart_in_progress)
+		if (!priv->hw_restart_in_progress &&
+			!priv->is_8764)
 			ieee80211_wake_queues(hw);
 
 		priv->fw_mutex_owner = NULL;
@@ -2425,7 +2555,7 @@
 			mwl8k_set_ht_caps(hw, &priv->band_24, caps);
 	}
 
-	if (caps & MWL8K_CAP_5GHZ) {
+	if (!disable_5g && (caps & MWL8K_CAP_5GHZ)) {
 		mwl8k_setup_5ghz_band(hw);
 		if (caps & MWL8K_CAP_MIMO)
 			mwl8k_set_ht_caps(hw, &priv->band_50, caps);
@@ -2520,6 +2650,7 @@
 
 	if (!rc) {
 		int off;
+		u8 ap_base_mac[ETH_ALEN];
 
 		api_version = le32_to_cpu(cmd->fw_api_version);
 		if (priv->device_info->fw_api_ap != api_version) {
@@ -2531,7 +2662,13 @@
 			rc = -EINVAL;
 			goto done;
 		}
+
+		if (mac_pton(ap_base_mac_addr, ap_base_mac) &&
+		    !is_zero_ether_addr(ap_base_mac))
+			SET_IEEE80211_PERM_ADDR(hw, ap_base_mac);
+		else
 		SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr);
+
 		priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
 		priv->fw_rev = le32_to_cpu(cmd->fw_rev);
 		priv->hw_rev = cmd->hw_rev;
@@ -2605,6 +2742,7 @@
 {
 	struct mwl8k_priv *priv = hw->priv;
 	struct mwl8k_cmd_set_hw_spec *cmd;
+	__le32 flags;
 	int rc;
 	int i;
 
@@ -2630,11 +2768,15 @@
 		cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[j].txd_dma);
 	}
 
-	cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT |
+	flags = MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT |
 				 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP |
 				 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON |
-				 MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY |
-				 MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR);
+		MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR;
+
+	if (!disable_life_time_expiry)
+		flags |= MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY;
+
+	cmd->flags = cpu_to_le32(flags);
 	cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
 	cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
 
@@ -2769,6 +2911,9 @@
 	if (cmd == NULL)
 		return -ENOMEM;
 
+	if (priv->is_8764)
+		ieee80211_stop_queues(hw);
+
 	cmd->header.code = cpu_to_le16(MWL8K_CMD_RADIO_CONTROL);
 	cmd->header.length = cpu_to_le16(sizeof(*cmd));
 	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
@@ -2778,6 +2923,9 @@
 	rc = mwl8k_post_cmd(hw, &cmd->header);
 	kfree(cmd);
 
+	if (priv->is_8764)
+		ieee80211_wake_queues(hw);
+
 	if (!rc)
 		priv->radio_on = enable;
 
@@ -2915,12 +3063,16 @@
 mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask)
 {
 	struct mwl8k_cmd_rf_antenna *cmd;
+	struct mwl8k_priv *priv = hw->priv;
 	int rc;
 
 	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
 	if (cmd == NULL)
 		return -ENOMEM;
 
+	if (priv->is_8764)
+		ieee80211_stop_queues(hw);
+
 	cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_ANTENNA);
 	cmd->header.length = cpu_to_le16(sizeof(*cmd));
 	cmd->antenna = cpu_to_le16(antenna);
@@ -2929,6 +3081,9 @@
 	rc = mwl8k_post_cmd(hw, &cmd->header);
 	kfree(cmd);
 
+	if (priv->is_8764)
+		ieee80211_wake_queues(hw);
+
 	return rc;
 }
 
@@ -3018,6 +3173,65 @@
 }
 
 /*
+ * CMD_BBP_REG_ACCESS.
+ */
+struct mwl8k_cmd_bbp_reg_access {
+	struct mwl8k_cmd_pkt header;
+	__le16 action;
+	__le16 offset;
+	u8 value;
+	u8 rsrv[3];
+} __packed;
+
+static int
+mwl8k_cmd_bbp_reg_access(struct ieee80211_hw *hw,
+			 u16 action,
+			 u16 offset,
+			 u8 *value)
+{
+	struct mwl8k_cmd_bbp_reg_access *cmd;
+	int rc;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (cmd == NULL)
+		return -ENOMEM;
+
+	cmd->header.code = cpu_to_le16(MWL8K_CMD_BBP_REG_ACCESS);
+	cmd->header.length = cpu_to_le16(sizeof(*cmd));
+	cmd->action = cpu_to_le16(action);
+	cmd->offset = cpu_to_le16(offset);
+
+	rc = mwl8k_post_cmd(hw, &cmd->header);
+
+	if (!rc)
+		*value = cmd->value;
+	else
+		*value = 0;
+
+	return rc;
+}
+
+static int freq_to_idx(struct mwl8k_priv *priv, int freq)
+{
+	struct ieee80211_supported_band *sband;
+	int band, ch, idx = 0;
+
+	for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
+		sband = priv->hw->wiphy->bands[band];
+		if (!sband)
+			continue;
+
+		for (ch = 0; ch < sband->n_channels; ch++, idx++)
+			if (sband->channels[ch].center_freq == freq)
+				goto exit;
+	}
+
+exit:
+	return idx;
+}
+
+
+/*
  * CMD_SET_RF_CHANNEL.
  */
 struct mwl8k_cmd_set_rf_channel {
@@ -3027,6 +3241,40 @@
 	__le32 channel_flags;
 } __packed;
 
+void mwl8k_update_survey(struct mwl8k_priv *priv,
+			 struct ieee80211_channel *channel)
+{
+	u32 cca_cnt, rx_rdy;
+	s8 nf = 0;
+	u8 idx = freq_to_idx(priv, priv->acs_chan->center_freq);
+	struct survey_info *survey = &priv->survey[idx];
+
+	cca_cnt = le32_to_cpu(ioread32(priv->regs + NOK_CCA_CNT_REG));
+	cca_cnt /= 1000; /* uSecs to mSecs */
+	survey->channel_time_busy = (u64) cca_cnt;
+
+	rx_rdy = le32_to_cpu(ioread32(priv->regs + BBU_RXRDY_CNT_REG));
+	rx_rdy /= 1000; /* uSecs to mSecs */
+	survey->channel_time_rx = (u64) rx_rdy;
+
+	survey->channel_time_tx = 0;
+
+	priv->channel_time = jiffies - priv->channel_time;
+	survey->channel_time = jiffies_to_msecs(priv->channel_time);
+
+	survey->channel = channel;
+
+	mwl8k_cmd_bbp_reg_access(priv->hw, 0, 0x67, &nf);
+	survey->noise = -nf;
+
+	survey->filled = SURVEY_INFO_NOISE_DBM |
+			 SURVEY_INFO_CHANNEL_TIME |
+			 SURVEY_INFO_CHANNEL_TIME_BUSY |
+			 SURVEY_INFO_CHANNEL_TIME_RX |
+			 SURVEY_INFO_CHANNEL_TIME_TX;
+}
+
+
 static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,
 				    struct ieee80211_conf *conf)
 {
@@ -3034,12 +3282,16 @@
 	enum nl80211_channel_type channel_type =
 		cfg80211_get_chandef_type(&conf->chandef);
 	struct mwl8k_cmd_set_rf_channel *cmd;
+	struct mwl8k_priv *priv = hw->priv;
 	int rc;
 
 	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
 	if (cmd == NULL)
 		return -ENOMEM;
 
+	if (priv->is_8764)
+		ieee80211_stop_queues(hw);
+
 	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RF_CHANNEL);
 	cmd->header.length = cpu_to_le16(sizeof(*cmd));
 	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
@@ -3050,6 +3302,9 @@
 	else if (channel->band == IEEE80211_BAND_5GHZ)
 		cmd->channel_flags |= cpu_to_le32(0x00000004);
 
+	if (priv->sw_scan_start) {
+		cmd->channel_flags |= cpu_to_le32(0x00000080);
+	} else {
 	if (channel_type == NL80211_CHAN_NO_HT ||
 	    channel_type == NL80211_CHAN_HT20)
 		cmd->channel_flags |= cpu_to_le32(0x00000080);
@@ -3057,10 +3312,25 @@
 		cmd->channel_flags |= cpu_to_le32(0x000001900);
 	else if (channel_type == NL80211_CHAN_HT40PLUS)
 		cmd->channel_flags |= cpu_to_le32(0x000000900);
+	}
+
+	if (priv->sw_scan_start) {
+		/* Store current channel stats
+		 * before switching to newer one
+		 */
+		if (priv->channel_time != 0)
+			mwl8k_update_survey(priv, priv->acs_chan);
+
+		priv->channel_time = jiffies;
+		priv->acs_chan =  channel;
+	}
 
 	rc = mwl8k_post_cmd(hw, &cmd->header);
 	kfree(cmd);
 
+	if (priv->is_8764)
+		ieee80211_wake_queues(hw);
+
 	return rc;
 }
 
@@ -3714,6 +3984,7 @@
 
 	spin_unlock(&priv->stream_lock);
 done:
+	if (!priv->is_8764)
 	atomic_dec(&priv->watchdog_event_pending);
 	status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
 	iowrite32((status | MWL8K_A2H_INT_BA_WATCHDOG),
@@ -3722,6 +3993,32 @@
 	return;
 }
 
+/*
+ * CMD_SET_INFRA_MODE
+ */
+struct mwl8k_cmd_set_infra_mode {
+	struct mwl8k_cmd_pkt header;
+} __packed;
+
+static int mwl8k_cmd_set_infra_mode(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif)
+{
+	struct mwl8k_cmd_set_infra_mode *cmd;
+	int rc;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (cmd == NULL)
+		return -ENOMEM;
+
+	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_INFRA_MODE);
+	cmd->header.length = cpu_to_le16(sizeof(*cmd));
+
+	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
+	kfree(cmd);
+
+	return rc;
+}
+
 
 /*
  * CMD_BSS_START.
@@ -4475,6 +4772,7 @@
 		iowrite32(~MWL8K_A2H_INT_BA_WATCHDOG,
 			  priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
 
+		if (!priv->is_8764)
 		atomic_inc(&priv->watchdog_event_pending);
 		status &= ~MWL8K_A2H_INT_BA_WATCHDOG;
 		ieee80211_queue_work(hw, &priv->watchdog_ba_handle);
@@ -4511,7 +4809,8 @@
 	for (i = 0; i < mwl8k_tx_queues(priv); i++)
 		limit -= mwl8k_txq_reclaim(hw, i, limit, 0);
 
-	if (!priv->pending_tx_pkts && priv->tx_wait != NULL) {
+	if ((!priv->pending_tx_pkts || (priv->is_8764 && limit != 32)) &&
+	    priv->tx_wait != NULL) {
 		complete(priv->tx_wait);
 		priv->tx_wait = NULL;
 	}
@@ -4579,10 +4878,6 @@
 	}
 	priv->irq = priv->pdev->irq;
 
-	/* Enable TX reclaim and RX tasklets.  */
-	tasklet_enable(&priv->poll_tx_task);
-	tasklet_enable(&priv->poll_rx_task);
-
 	/* Enable interrupts */
 	iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
 	iowrite32(MWL8K_A2H_EVENTS,
@@ -4617,12 +4912,15 @@
 		iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
 		free_irq(priv->pdev->irq, hw);
 		priv->irq = -1;
-		tasklet_disable(&priv->poll_tx_task);
-		tasklet_disable(&priv->poll_rx_task);
+		tasklet_kill(&priv->poll_tx_task);
+		tasklet_kill(&priv->poll_rx_task);
 	} else {
 		ieee80211_wake_queues(hw);
 	}
 
+	priv->last_short_preamble = ~0;
+	priv->last_basic_rates = ~0;
+
 	return rc;
 }
 
@@ -4650,8 +4948,8 @@
 		dev_kfree_skb(priv->beacon_skb);
 
 	/* Stop TX reclaim and RX tasklets.  */
-	tasklet_disable(&priv->poll_tx_task);
-	tasklet_disable(&priv->poll_rx_task);
+	tasklet_kill(&priv->poll_tx_task);
+	tasklet_kill(&priv->poll_rx_task);
 
 	/* Return all skbs to mac80211 */
 	for (i = 0; i < mwl8k_tx_queues(priv); i++)
@@ -4683,6 +4981,7 @@
 	di = priv->device_info;
 	switch (vif->type) {
 	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_STATION:
 		if (!priv->ap_fw && di->fw_image_ap) {
 			/* we must load the ap fw to meet this request */
 			if (!list_empty(&priv->vif_list))
@@ -4692,7 +4991,12 @@
 				return rc;
 		}
 		macids_supported = priv->ap_macids_supported;
+
+		if (vif->type == NL80211_IFTYPE_STATION)
+			macids_supported = 0x00000100;
+
 		break;
+#if 0
 	case NL80211_IFTYPE_STATION:
 		if (priv->ap_fw && di->fw_image_sta) {
 			if (!list_empty(&priv->vif_list)) {
@@ -4710,6 +5014,7 @@
 		}
 		macids_supported = priv->sta_macids_supported;
 		break;
+#endif
 	default:
 		return -EINVAL;
 	}
@@ -4726,6 +5031,8 @@
 	mwl8k_vif->seqno = 0;
 	memcpy(mwl8k_vif->bssid, vif->addr, ETH_ALEN);
 	mwl8k_vif->is_hw_crypto_enabled = false;
+	mwl8k_vif->last_beacon_crc = ~0;
+	mwl8k_vif->last_beacon_int = ~0;
 
 	/* Set the mac address.  */
 	mwl8k_cmd_set_mac_addr(hw, vif, vif->addr);
@@ -4799,6 +5106,9 @@
 	 */
 	mwl8k_fw_unlock(hw);
 
+	if (priv->is_8764)
+		ieee80211_wake_queues(hw);
+
 	ieee80211_restart_hw(hw);
 
 	wiphy_err(hw->wiphy, "Firmware restarted successfully\n");
@@ -4843,8 +5153,6 @@
 			if (rc)
 				goto out;
 		}
-
-
 	} else {
 		rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level);
 		if (rc)
@@ -4866,10 +5174,26 @@
 	u32 ap_legacy_rates = 0;
 	u8 ap_mcs_rates[16];
 	int rc;
+	static u32 bitmap = 0;
 
 	if (mwl8k_fw_lock(hw))
 		return;
 
+	if ((changed & BSS_CHANGED_BSSID) &&
+	    (changed & BSS_CHANGED_BEACON_INT)) {
+		/*
+		 * BSS_CHANED_BSSID & BSS_CHANGED_BEACON_INT
+		 * are set before the Association process
+		 */
+
+		/* disable all runing BSSes */
+		bitmap = priv->running_bsses;
+		mwl8k_enable_bsses(hw, false, bitmap);
+
+		/* Start infra mode */
+		mwl8k_cmd_set_infra_mode(hw, vif);
+	}
+
 	/*
 	 * No need to capture a beacon if we're no longer associated.
 	 */
@@ -4961,6 +5285,11 @@
 		memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN);
 		priv->capture_beacon = true;
 	}
+	if (bitmap && (changed & (BSS_CHANGED_IDLE | BSS_CHANGED_ASSOC))) {
+		mwl8k_enable_bsses(hw, true, bitmap);
+		bitmap = 0;
+	}
+
 
 out:
 	mwl8k_fw_unlock(hw);
@@ -4970,19 +5299,27 @@
 mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 			  struct ieee80211_bss_conf *info, u32 changed)
 {
+	struct mwl8k_priv *priv = hw->priv;
+	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
+	struct sk_buff *skb;
+	bool update_beacon;
+	u32 crc;
 	int rc;
 
 	if (mwl8k_fw_lock(hw))
 		return;
 
-	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+	if ((changed & BSS_CHANGED_ERP_PREAMBLE) &&
+	    priv->last_short_preamble != vif->bss_conf.use_short_preamble) {
 		rc = mwl8k_set_radio_preamble(hw,
 				vif->bss_conf.use_short_preamble);
 		if (rc)
 			goto out;
+		priv->last_short_preamble = vif->bss_conf.use_short_preamble;
 	}
 
-	if (changed & BSS_CHANGED_BASIC_RATES) {
+	if ((changed & BSS_CHANGED_BASIC_RATES) &&
+	    priv->last_basic_rates != vif->bss_conf.basic_rates) {
 		int idx;
 		int rate;
 
@@ -5001,17 +5338,32 @@
 			rate = mwl8k_rates_50[idx].hw_value;
 
 		mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate);
+		priv->last_basic_rates = vif->bss_conf.basic_rates;
 	}
 
-	if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) {
-		struct sk_buff *skb;
+	update_beacon = false;
+
+	if ((changed & BSS_CHANGED_BEACON_INT) &&
+	    mwl8k_vif->last_beacon_int != vif->bss_conf.beacon_int)
+		update_beacon = true;
 
 		skb = ieee80211_beacon_get(hw, vif);
-		if (skb != NULL) {
-			mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len);
-			kfree_skb(skb);
+	crc = 0;
+
+	if (changed & BSS_CHANGED_BEACON) {
+		if (skb) {
+			crc = crc32_le(~0, skb->data, skb->len);
+			if (crc != mwl8k_vif->last_beacon_crc)
+				update_beacon = true;
+		}
 		}
+
+	if (skb && update_beacon) {
+		mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len);
+		mwl8k_vif->last_beacon_crc = crc;
+		mwl8k_vif->last_beacon_int = vif->bss_conf.beacon_int;
 	}
+	kfree_skb(skb);
 
 	if (changed & BSS_CHANGED_BEACON_ENABLED)
 		mwl8k_cmd_bss_start(hw, vif, info->enable_beacon);
@@ -5263,14 +5615,22 @@
 				struct survey_info *survey)
 {
 	struct mwl8k_priv *priv = hw->priv;
-	struct ieee80211_conf *conf = &hw->conf;
+	struct ieee80211_supported_band *sband;
 
-	if (idx != 0)
+	sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ];
+	if (sband && idx >= sband->n_channels) {
+		idx -= sband->n_channels;
+		sband = NULL;
+	}
+
+	if (!sband)
+		sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ];
+
+	if (!sband || idx >= sband->n_channels)
 		return -ENOENT;
 
-	survey->channel = conf->chandef.chan;
-	survey->filled = SURVEY_INFO_NOISE_DBM;
-	survey->noise = priv->noise;
+	memcpy(survey, &priv->survey[idx], sizeof(*survey));
+	survey->channel = &sband->channels[idx];
 
 	return 0;
 }
@@ -5407,6 +5767,30 @@
 	return rc;
 }
 
+static  void mwl8k_sw_scan_start(struct ieee80211_hw *hw)
+{
+	struct mwl8k_priv *priv = hw->priv;
+
+	/* clear all stats */
+	ioread32(priv->regs + BBU_RXRDY_CNT_REG);
+	ioread32(priv->regs + NOK_CCA_CNT_REG);
+
+	priv->channel_time = 0;
+	priv->sw_scan_start = true;
+}
+
+static 	void mwl8k_sw_scan_complete(struct ieee80211_hw *hw)
+{
+	struct mwl8k_priv *priv = hw->priv;
+
+	priv->sw_scan_start = false;
+
+	/* clear all stats */
+	ioread32(priv->regs + BBU_RXRDY_CNT_REG);
+	ioread32(priv->regs + NOK_CCA_CNT_REG);
+	priv->channel_time = 0;
+}
+
 static const struct ieee80211_ops mwl8k_ops = {
 	.tx			= mwl8k_tx,
 	.start			= mwl8k_start,
@@ -5425,6 +5809,8 @@
 	.get_stats		= mwl8k_get_stats,
 	.get_survey		= mwl8k_get_survey,
 	.ampdu_action		= mwl8k_ampdu_action,
+	.sw_scan_start		= mwl8k_sw_scan_start,
+	.sw_scan_complete	= mwl8k_sw_scan_complete,
 };
 
 static void mwl8k_finalize_join_worker(struct work_struct *work)
@@ -5496,6 +5882,7 @@
 MODULE_FIRMWARE("mwl8k/helper_8366.fw");
 MODULE_FIRMWARE("mwl8k/fmimage_8366.fw");
 MODULE_FIRMWARE(MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API));
+MODULE_FIRMWARE(MWL8K_8764_AP_FW(MWL8K_8764_AP_FW_API));
 
 static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = {
 	{ PCI_VDEVICE(MARVELL, 0x2a0a), .driver_data = MWL8363, },
@@ -5682,9 +6069,11 @@
 		priv->rxd_ops = &rxd_sta_ops;
 	}
 
+	priv->booted = false;
 	priv->sniffer_enabled = false;
 	priv->wmm_enabled = false;
 	priv->pending_tx_pkts = 0;
+	if (!priv->is_8764)
 	atomic_set(&priv->watchdog_event_pending, 0);
 
 	rc = mwl8k_rxq_init(hw, 0);
@@ -5766,14 +6155,20 @@
 	}
 
 	/* Configure Antennas */
+	if (priv->is_8764)
+		rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x4);
+	else
 	rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3);
 	if (rc)
 		wiphy_warn(hw->wiphy, "failed to set # of RX antennas");
+
+	if (priv->is_8764)
+		rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0xf);
+	else
 	rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7);
 	if (rc)
 		wiphy_warn(hw->wiphy, "failed to set # of TX antennas");
 
-
 	/* Disable interrupts */
 	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
 	free_irq(priv->pdev->irq, hw);
@@ -5785,6 +6180,8 @@
 		   (priv->fw_rev >> 24) & 0xff, (priv->fw_rev >> 16) & 0xff,
 		   (priv->fw_rev >> 8) & 0xff, priv->fw_rev & 0xff);
 
+	priv->booted = true;
+
 	return 0;
 
 err_free_irq:
@@ -5923,12 +6320,12 @@
 	INIT_WORK(&priv->watchdog_ba_handle, mwl8k_watchdog_ba_events);
 	/* To reload the firmware if it crashes */
 	INIT_WORK(&priv->fw_reload, mwl8k_hw_restart_work);
+	/* To check if firmware is stuck or has crashed */
+	INIT_WORK(&priv->fw_check, mwl8k_check_fw_state);
 
 	/* TX reclaim and RX tasklets.  */
 	tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw);
-	tasklet_disable(&priv->poll_tx_task);
 	tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw);
-	tasklet_disable(&priv->poll_rx_task);
 
 	/* Power management cookie */
 	priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma);
@@ -5959,6 +6356,9 @@
 		hw->wiphy->n_iface_combinations = 1;
 	}
 
+	/* Set offchannel duration for acs to 100 msecs */
+	hw->wiphy->max_remain_on_channel_duration = 100;
+
 	if (priv->sta_macids_supported || priv->device_info->fw_image_sta)
 		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
 
diff -ruw linux-3.11.10/drivers/pci/pci.c linux-3.11.10-fbx/drivers/pci/pci.c
--- linux-3.11.10/drivers/pci/pci.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/pci/pci.c	2015-09-29 11:15:07.133742286 +0200
@@ -1119,6 +1119,8 @@
 static int do_pci_enable_device(struct pci_dev *dev, int bars)
 {
 	int err;
+	u16 cmd;
+	u8 pin;
 
 	err = pci_set_power_state(dev, PCI_D0);
 	if (err < 0 && err != -EIO)
@@ -1128,6 +1130,17 @@
 		return err;
 	pci_fixup_device(pci_fixup_enable, dev);
 
+	if (dev->msi_enabled || dev->msix_enabled)
+		return 0;
+
+	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+	if (pin) {
+		pci_read_config_word(dev, PCI_COMMAND, &cmd);
+		if (cmd & PCI_COMMAND_INTX_DISABLE)
+			pci_write_config_word(dev, PCI_COMMAND,
+					      cmd & ~PCI_COMMAND_INTX_DISABLE);
+	}
+
 	return 0;
 }
 
@@ -3656,7 +3669,7 @@
 	u16 cmd;
 	int rc;
 
-	WARN_ON((flags & PCI_VGA_STATE_CHANGE_DECODES) & (command_bits & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY)));
+	WARN_ON((flags & PCI_VGA_STATE_CHANGE_DECODES) && (command_bits & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY)));
 
 	/* ARCH specific VGA enables */
 	rc = pci_set_vga_state_arch(dev, decode, command_bits, flags);
diff -ruw linux-3.11.10/drivers/pci/pci-driver.c linux-3.11.10-fbx/drivers/pci/pci-driver.c
--- linux-3.11.10/drivers/pci/pci-driver.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/pci/pci-driver.c	2015-09-29 11:15:07.129742249 +0200
@@ -19,6 +19,7 @@
 #include <linux/cpu.h>
 #include <linux/pm_runtime.h>
 #include <linux/suspend.h>
+#include <linux/kexec.h>
 #include "pci.h"
 
 struct pci_dynid {
@@ -388,12 +389,17 @@
 	pci_msi_shutdown(pci_dev);
 	pci_msix_shutdown(pci_dev);
 
+#ifdef CONFIG_KEXEC
 	/*
-	 * Turn off Bus Master bit on the device to tell it to not
-	 * continue to do DMA. Don't touch devices in D3cold or unknown states.
+	 * If this is a kexec reboot, turn off Bus Master bit on the
+	 * device to tell it to not continue to do DMA. Don't touch
+	 * devices in D3cold or unknown states.
+	 * If it is not a kexec reboot, firmware will hit the PCI
+	 * devices with big hammer and stop their DMA any way.
 	 */
-	if (pci_dev->current_state <= PCI_D3hot)
+	if (kexec_in_progress && (pci_dev->current_state <= PCI_D3hot))
 		pci_clear_master(pci_dev);
+#endif
 }
 
 #ifdef CONFIG_PM
diff -ruw linux-3.11.10/drivers/pci/pcie/portdrv_pci.c linux-3.11.10-fbx/drivers/pci/pcie/portdrv_pci.c
--- linux-3.11.10/drivers/pci/pcie/portdrv_pci.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/pci/pcie/portdrv_pci.c	2015-09-29 11:15:07.133742286 +0200
@@ -223,7 +223,6 @@
 static void pcie_portdrv_remove(struct pci_dev *dev)
 {
 	pcie_port_device_remove(dev);
-	pci_disable_device(dev);
 }
 
 static int error_detected_iter(struct device *device, void *data)
diff -ruw linux-3.11.10/drivers/pci/quirks.c linux-3.11.10-fbx/drivers/pci/quirks.c
--- linux-3.11.10/drivers/pci/quirks.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/pci/quirks.c	2015-09-29 11:15:07.133742286 +0200
@@ -2635,6 +2635,8 @@
 }
 
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_HINT, 0x0020, quirk_hotplug_bridge);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PERICOM, PCI_DEVICE_ID_PI7C9X20303SL,
+			 quirk_hotplug_bridge);
 
 /*
  * This is a quirk for the Ricoh MMC controller found as a part of
@@ -2953,6 +2955,7 @@
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq);
 
 /*
  * Some devices may pass our check in pci_intx_mask_supported if
diff -ruw linux-3.11.10/drivers/pinctrl/core.c linux-3.11.10-fbx/drivers/pinctrl/core.c
--- linux-3.11.10/drivers/pinctrl/core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/pinctrl/core.c	2015-09-29 11:15:07.145742377 +0200
@@ -827,7 +827,9 @@
 	kref_init(&p->users);
 
 	/* Add the pinctrl handle to the global list */
+	mutex_lock(&pinctrl_list_mutex);
 	list_add_tail(&p->node, &pinctrl_list);
+	mutex_unlock(&pinctrl_list_mutex);
 
 	return p;
 }
diff -ruw linux-3.11.10/drivers/platform/Kconfig linux-3.11.10-fbx/drivers/platform/Kconfig
--- linux-3.11.10/drivers/platform/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/platform/Kconfig	2015-09-29 11:15:07.169742560 +0200
@@ -5,3 +5,10 @@
 source "drivers/platform/goldfish/Kconfig"
 endif
 
+if TANGO2
+source "drivers/platform/tango2/Kconfig"
+endif
+
+if X86_INTEL_CE
+source "drivers/platform/intelce/Kconfig"
+endif
diff -ruw linux-3.11.10/drivers/platform/Makefile linux-3.11.10-fbx/drivers/platform/Makefile
--- linux-3.11.10/drivers/platform/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/platform/Makefile	2015-09-29 11:15:07.169742560 +0200
@@ -5,3 +5,5 @@
 obj-$(CONFIG_X86)		+= x86/
 obj-$(CONFIG_OLPC)		+= olpc/
 obj-$(CONFIG_GOLDFISH)		+= goldfish/
+obj-$(CONFIG_TANGO2)		+= tango2/
+obj-$(CONFIG_X86_INTEL_CE)	+= intelce/
diff -ruw linux-3.11.10/drivers/scsi/hosts.c linux-3.11.10-fbx/drivers/scsi/hosts.c
--- linux-3.11.10/drivers/scsi/hosts.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/scsi/hosts.c	2015-09-29 11:15:07.285743437 +0200
@@ -388,6 +388,7 @@
 	shost->unchecked_isa_dma = sht->unchecked_isa_dma;
 	shost->use_clustering = sht->use_clustering;
 	shost->ordered_tag = sht->ordered_tag;
+	shost->no_write_same = sht->no_write_same;
 
 	if (sht->supported_mode == MODE_UNKNOWN)
 		/* means we didn't set it ... default to INITIATOR */
diff -ruw linux-3.11.10/drivers/scsi/scsi_error.c linux-3.11.10-fbx/drivers/scsi/scsi_error.c
--- linux-3.11.10/drivers/scsi/scsi_error.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/scsi/scsi_error.c	2015-09-29 11:15:07.353743956 +0200
@@ -933,6 +933,15 @@
 		    SCSI_SENSE_VALID(scmd))
 			continue;
 
+		if (status_byte(scmd->result) != CHECK_CONDITION)
+			/*
+			 * don't request sense if there's no check condition
+			 * status because the error we're processing isn't one
+			 * that has a sense code (and some devices get
+			 * confused by sense requests out of the blue)
+			 */
+			continue;
+
 		SCSI_LOG_ERROR_RECOVERY(2, scmd_printk(KERN_INFO, scmd,
 						  "%s: requesting sense\n",
 						  current->comm));
diff -ruw linux-3.11.10/drivers/scsi/scsi_scan.c linux-3.11.10-fbx/drivers/scsi/scsi_scan.c
--- linux-3.11.10/drivers/scsi/scsi_scan.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/scsi/scsi_scan.c	2015-09-29 11:15:07.357743985 +0200
@@ -320,6 +320,7 @@
 	struct Scsi_Host *shost = dev_to_shost(dev->parent);
 	unsigned long flags;
 
+	starget->state = STARGET_DEL;
 	transport_destroy_device(dev);
 	spin_lock_irqsave(shost->host_lock, flags);
 	if (shost->hostt->target_destroy)
@@ -371,6 +372,37 @@
 }
 
 /**
+ * scsi_target_reap_ref_release - remove target from visibility
+ * @kref: the reap_ref in the target being released
+ *
+ * Called on last put of reap_ref, which is the indication that no device
+ * under this target is visible anymore, so render the target invisible in
+ * sysfs.  Note: we have to be in user context here because the target reaps
+ * should be done in places where the scsi device visibility is being removed.
+ */
+static void scsi_target_reap_ref_release(struct kref *kref)
+{
+	struct scsi_target *starget
+		= container_of(kref, struct scsi_target, reap_ref);
+
+	/*
+	 * if we get here and the target is still in the CREATED state that
+	 * means it was allocated but never made visible (because a scan
+	 * turned up no LUNs), so don't call device_del() on it.
+	 */
+	if (starget->state != STARGET_CREATED) {
+		transport_remove_device(&starget->dev);
+		device_del(&starget->dev);
+	}
+	scsi_target_destroy(starget);
+}
+
+static void scsi_target_reap_ref_put(struct scsi_target *starget)
+{
+	kref_put(&starget->reap_ref, scsi_target_reap_ref_release);
+}
+
+/**
  * scsi_alloc_target - allocate a new or find an existing target
  * @parent:	parent of the target (need not be a scsi host)
  * @channel:	target channel number (zero if no channels)
@@ -392,7 +424,7 @@
 		+ shost->transportt->target_size;
 	struct scsi_target *starget;
 	struct scsi_target *found_target;
-	int error;
+	int error, ref_got;
 
 	starget = kzalloc(size, GFP_KERNEL);
 	if (!starget) {
@@ -401,7 +433,7 @@
 	}
 	dev = &starget->dev;
 	device_initialize(dev);
-	starget->reap_ref = 1;
+	kref_init(&starget->reap_ref);
 	dev->parent = get_device(parent);
 	dev_set_name(dev, "target%d:%d:%d", shost->host_no, channel, id);
 	dev->bus = &scsi_bus_type;
@@ -441,29 +473,36 @@
 	return starget;
 
  found:
-	found_target->reap_ref++;
+	/*
+	 * release routine already fired if kref is zero, so if we can still
+	 * take the reference, the target must be alive.  If we can't, it must
+	 * be dying and we need to wait for a new target
+	 */
+	ref_got = kref_get_unless_zero(&found_target->reap_ref);
+
 	spin_unlock_irqrestore(shost->host_lock, flags);
-	if (found_target->state != STARGET_DEL) {
+	if (ref_got) {
 		put_device(dev);
 		return found_target;
 	}
-	/* Unfortunately, we found a dying target; need to
-	 * wait until it's dead before we can get a new one */
+	/*
+	 * Unfortunately, we found a dying target; need to wait until it's
+	 * dead before we can get a new one.  There is an anomaly here.  We
+	 * *should* call scsi_target_reap() to balance the kref_get() of the
+	 * reap_ref above.  However, since the target being released, it's
+	 * already invisible and the reap_ref is irrelevant.  If we call
+	 * scsi_target_reap() we might spuriously do another device_del() on
+	 * an already invisible target.
+	 */
 	put_device(&found_target->dev);
-	flush_scheduled_work();
+	/*
+	 * length of time is irrelevant here, we just want to yield the CPU
+	 * for a tick to avoid busy waiting for the target to die.
+	 */
+	msleep(1);
 	goto retry;
 }
 
-static void scsi_target_reap_usercontext(struct work_struct *work)
-{
-	struct scsi_target *starget =
-		container_of(work, struct scsi_target, ew.work);
-
-	transport_remove_device(&starget->dev);
-	device_del(&starget->dev);
-	scsi_target_destroy(starget);
-}
-
 /**
  * scsi_target_reap - check to see if target is in use and destroy if not
  * @starget: target to be checked
@@ -474,28 +513,13 @@
  */
 void scsi_target_reap(struct scsi_target *starget)
 {
-	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
-	unsigned long flags;
-	enum scsi_target_state state;
-	int empty = 0;
-
-	spin_lock_irqsave(shost->host_lock, flags);
-	state = starget->state;
-	if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
-		empty = 1;
-		starget->state = STARGET_DEL;
-	}
-	spin_unlock_irqrestore(shost->host_lock, flags);
-
-	if (!empty)
-		return;
-
-	BUG_ON(state == STARGET_DEL);
-	if (state == STARGET_CREATED)
-		scsi_target_destroy(starget);
-	else
-		execute_in_process_context(scsi_target_reap_usercontext,
-					   &starget->ew);
+	/*
+	 * serious problem if this triggers: STARGET_DEL is only set in the if
+	 * the reap_ref drops to zero, so we're trying to do another final put
+	 * on an already released kref
+	 */
+	BUG_ON(starget->state == STARGET_DEL);
+	scsi_target_reap_ref_put(starget);
 }
 
 /**
@@ -1532,6 +1556,10 @@
 	}
 	mutex_unlock(&shost->scan_mutex);
 	scsi_autopm_put_target(starget);
+	/*
+	 * paired with scsi_alloc_target().  Target will be destroyed unless
+	 * scsi_probe_and_add_lun made an underlying device visible
+	 */
 	scsi_target_reap(starget);
 	put_device(&starget->dev);
 
@@ -1612,8 +1640,10 @@
 
  out_reap:
 	scsi_autopm_put_target(starget);
-	/* now determine if the target has any children at all
-	 * and if not, nuke it */
+	/*
+	 * paired with scsi_alloc_target(): determine if the target has
+	 * any children at all and if not, nuke it
+	 */
 	scsi_target_reap(starget);
 
 	put_device(&starget->dev);
diff -ruw linux-3.11.10/drivers/scsi/scsi_sysfs.c linux-3.11.10-fbx/drivers/scsi/scsi_sysfs.c
--- linux-3.11.10/drivers/scsi/scsi_sysfs.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/scsi/scsi_sysfs.c	2015-09-29 11:15:07.357743985 +0200
@@ -332,17 +332,14 @@
 {
 	struct scsi_device *sdev;
 	struct device *parent;
-	struct scsi_target *starget;
 	struct list_head *this, *tmp;
 	unsigned long flags;
 
 	sdev = container_of(work, struct scsi_device, ew.work);
 
 	parent = sdev->sdev_gendev.parent;
-	starget = to_scsi_target(parent);
 
 	spin_lock_irqsave(sdev->host->host_lock, flags);
-	starget->reap_ref++;
 	list_del(&sdev->siblings);
 	list_del(&sdev->same_target_siblings);
 	list_del(&sdev->starved_entry);
@@ -362,8 +359,6 @@
 	/* NULL queue means the device can't be used */
 	sdev->request_queue = NULL;
 
-	scsi_target_reap(scsi_target(sdev));
-
 	kfree(sdev->inquiry);
 	kfree(sdev);
 
@@ -1008,6 +1003,13 @@
 		sdev->host->hostt->slave_destroy(sdev);
 	transport_destroy_device(dev);
 
+	/*
+	 * Paired with the kref_get() in scsi_sysfs_initialize().  We have
+	 * remoed sysfs visibility from the device, so make the target
+	 * invisible if this was the last device underneath it.
+	 */
+	scsi_target_reap(scsi_target(sdev));
+
 	put_device(dev);
 }
 
@@ -1070,7 +1072,7 @@
 			continue;
 		if (starget->dev.parent == dev || &starget->dev == dev) {
 			/* assuming new targets arrive at the end */
-			starget->reap_ref++;
+			kref_get(&starget->reap_ref);
 			spin_unlock_irqrestore(shost->host_lock, flags);
 			if (last)
 				scsi_target_reap(last);
@@ -1154,6 +1156,12 @@
 	list_add_tail(&sdev->same_target_siblings, &starget->devices);
 	list_add_tail(&sdev->siblings, &shost->__devices);
 	spin_unlock_irqrestore(shost->host_lock, flags);
+	/*
+	 * device can now only be removed via __scsi_remove_device() so hold
+	 * the target.  Target will be held in CREATED state until something
+	 * beneath it becomes visible (in which case it moves to RUNNING)
+	 */
+	kref_get(&starget->reap_ref);
 }
 
 int scsi_is_sdev_device(const struct device *dev)
diff -ruw linux-3.11.10/drivers/scsi/sd.c linux-3.11.10-fbx/drivers/scsi/sd.c
--- linux-3.11.10/drivers/scsi/sd.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/scsi/sd.c	2015-09-29 11:15:07.361744022 +0200
@@ -2637,14 +2637,23 @@
 {
 	struct scsi_device *sdev = sdkp->device;
 
+	if (sdev->host->no_write_same) {
+		sdev->no_write_same = 1;
+
+		return;
+	}
+
 	if (scsi_report_opcode(sdev, buffer, SD_BUF_SIZE, INQUIRY) < 0) {
+		/* too large values might cause issues with arcmsr */
+		int vpd_buf_len = 64;
+
 		sdev->no_report_opcodes = 1;
 
 		/* Disable WRITE SAME if REPORT SUPPORTED OPERATION
 		 * CODES is unsupported and the device has an ATA
 		 * Information VPD page (SAT).
 		 */
-		if (!scsi_get_vpd_page(sdev, 0x89, buffer, SD_BUF_SIZE))
+		if (!scsi_get_vpd_page(sdev, 0x89, buffer, vpd_buf_len))
 			sdev->no_write_same = 1;
 	}
 
diff -ruw linux-3.11.10/drivers/spi/Kconfig linux-3.11.10-fbx/drivers/spi/Kconfig
--- linux-3.11.10/drivers/spi/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/spi/Kconfig	2015-09-29 11:15:07.369744077 +0200
@@ -327,6 +327,12 @@
 	def_bool y
 	depends on SPI_PXA2XX && !SPI_PXA2XX_PXADMA
 
+config SPI_TDM_ORION
+	tristate "Orion TDM SPI master"
+	depends on PLAT_ORION
+	help
+	  This enables using the TDM SPI master controller on the Orion chips.
+
 config SPI_PXA2XX
 	tristate "PXA2xx SSP SPI master"
 	depends on (ARCH_PXA || PCI || ACPI) && GENERIC_HARDIRQS
diff -ruw linux-3.11.10/drivers/spi/Makefile linux-3.11.10-fbx/drivers/spi/Makefile
--- linux-3.11.10/drivers/spi/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/spi/Makefile	2015-09-29 11:15:07.369744077 +0200
@@ -47,6 +47,7 @@
 obj-$(CONFIG_SPI_OMAP_100K)		+= spi-omap-100k.o
 obj-$(CONFIG_SPI_OMAP24XX)		+= spi-omap2-mcspi.o
 obj-$(CONFIG_SPI_ORION)			+= spi-orion.o
+obj-$(CONFIG_SPI_TDM_ORION)		+= orion_tdm_spi.o
 obj-$(CONFIG_SPI_PL022)			+= spi-pl022.o
 obj-$(CONFIG_SPI_PPC4xx)		+= spi-ppc4xx.o
 spi-pxa2xx-platform-objs		:= spi-pxa2xx.o
diff -ruw linux-3.11.10/drivers/staging/tidspbridge/Kconfig linux-3.11.10-fbx/drivers/staging/tidspbridge/Kconfig
--- linux-3.11.10/drivers/staging/tidspbridge/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/staging/tidspbridge/Kconfig	2015-09-29 11:15:07.601745850 +0200
@@ -4,7 +4,7 @@
 
 menuconfig TIDSPBRIDGE
 	tristate "DSP Bridge driver"
-	depends on ARCH_OMAP3 && !ARCH_MULTIPLATFORM
+	depends on ARCH_OMAP3 && !ARCH_MULTIPLATFORM && BROKEN
 	select MAILBOX
 	select OMAP2PLUS_MBOX
 	help
diff -ruw linux-3.11.10/drivers/tty/n_tty.c linux-3.11.10-fbx/drivers/tty/n_tty.c
--- linux-3.11.10/drivers/tty/n_tty.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/tty/n_tty.c	2015-09-29 11:15:07.685746490 +0200
@@ -2066,8 +2066,12 @@
 			if (tty->ops->flush_chars)
 				tty->ops->flush_chars(tty);
 		} else {
+			struct n_tty_data *ldata = tty->disc_data;
+
 			while (nr > 0) {
+				mutex_lock(&ldata->output_lock);
 				c = tty->ops->write(tty, b, nr);
+				mutex_unlock(&ldata->output_lock);
 				if (c < 0) {
 					retval = c;
 					goto break_out;
diff -ruw linux-3.11.10/drivers/tty/serial/8250/8250_core.c linux-3.11.10-fbx/drivers/tty/serial/8250/8250_core.c
--- linux-3.11.10/drivers/tty/serial/8250/8250_core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/tty/serial/8250/8250_core.c	2015-09-29 11:15:07.689746515 +0200
@@ -42,6 +42,12 @@
 #include <linux/sunserialcore.h>
 #endif
 
+#ifdef CONFIG_TANGO2
+#include <asm/mach-tango2/tango2_gbus.h>
+
+extern unsigned long em8xxx_sys_frequency;
+#endif
+
 #include <asm/io.h>
 #include <asm/irq.h>
 
@@ -325,20 +331,30 @@
 	},
 };
 
+
 /* Uart divisor latch read */
 static int default_serial_dl_read(struct uart_8250_port *up)
 {
+#ifndef CONFIG_TANGO2
 	return serial_in(up, UART_DLL) | serial_in(up, UART_DLM) << 8;
+#else
+	BUG();
+	return 0;
+#endif
 }
 
 /* Uart divisor latch write */
 static void default_serial_dl_write(struct uart_8250_port *up, int value)
 {
+#ifndef CONFIG_TANGO2
 	serial_out(up, UART_DLL, value & 0xff);
 	serial_out(up, UART_DLM, value >> 8 & 0xff);
+#else
+	BUG();
+#endif
 }
 
-#if defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_SERIAL_8250_RT288X)
+#if defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_SERIAL_8250_RT288X) || defined(CONFIG_WINTEGRA_WINPATH3)
 
 /* Au1x00/RT288x UART hardware has a weird register layout */
 static const u8 au_io_in_map[] = {
@@ -400,14 +416,44 @@
 
 static unsigned int mem_serial_in(struct uart_port *p, int offset)
 {
+#ifdef CONFIG_TANGO2
+	unsigned long v;
+
+	/* no EFR on tango2 */
+	if (offset == UART_EFR)
+		v = 0;
+	else
+		v = gbus_readl((unsigned long)p->membase +
+			       (offset << p->regshift));
+	return v;
+#else
 	offset = offset << p->regshift;
 	return readb(p->membase + offset);
+#endif
 }
 
 static void mem_serial_out(struct uart_port *p, int offset, int value)
 {
+#ifdef CONFIG_TANGO2
+	/*
+	 * we add a special case for UART_DL register, since
+	 * register content has a different meaning for us.
+	 */
+	if (offset == UART_DL) {
+		/* select right clock source */
+		value = (em8xxx_sys_frequency / p->uartclk);
+	}
+
+	/* no EFR on tango2 */
+	if (offset != UART_EFR) {
+		offset = offset << p->regshift;
+		gbus_writel((unsigned long)p->membase + offset,
+			    value);
+	}
+#else
 	offset = offset << p->regshift;
 	writeb(value, p->membase + offset);
+#endif
 }
 
 static void mem32_serial_out(struct uart_port *p, int offset, int value)
@@ -461,7 +507,7 @@
 		p->serial_out = mem32_serial_out;
 		break;
 
-#if defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_SERIAL_8250_RT288X)
+#if defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_SERIAL_8250_RT288X) || defined(CONFIG_WINTEGRA_WINPATH3)
 	case UPIO_AU:
 		p->serial_in = au_serial_in;
 		p->serial_out = au_serial_out;
@@ -555,7 +601,7 @@
 	 */
 	if ((p->port.type == PORT_XR17V35X) ||
 	   (p->port.type == PORT_XR17D15X)) {
-		serial_out(p, UART_EXAR_SLEEP, 0xff);
+		serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0);
 		return;
 	}
 
@@ -650,7 +696,11 @@
 static int size_fifo(struct uart_8250_port *up)
 {
 	unsigned char old_fcr, old_mcr, old_lcr;
+#ifdef CONFIG_TANGO2
+	unsigned short old_dll, old_dlm;
+#else
 	unsigned short old_dl;
+#endif
 	int count;
 
 	old_lcr = serial_in(up, UART_LCR);
@@ -661,8 +711,14 @@
 		    UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
 	serial_out(up, UART_MCR, UART_MCR_LOOP);
 	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+#ifdef CONFIG_TANGO2
+	old_dll = serial_in(up, UART_DL) & 0xff;
+	old_dlm = serial_in(up, UART_DL) >> 8;
+	serial_out(up, UART_DL, 0x01);
+#else
 	old_dl = serial_dl_read(up);
 	serial_dl_write(up, 0x0001);
+#endif
 	serial_out(up, UART_LCR, 0x03);
 	for (count = 0; count < 256; count++)
 		serial_out(up, UART_TX, count);
@@ -673,7 +729,11 @@
 	serial_out(up, UART_FCR, old_fcr);
 	serial_out(up, UART_MCR, old_mcr);
 	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+#ifdef CONFIG_TANGO2
+	serial_out(up, UART_DL, (old_dlm << 8) | old_dll);
+#else
 	serial_dl_write(up, old_dl);
+#endif
 	serial_out(up, UART_LCR, old_lcr);
 
 	return count;
@@ -692,6 +752,16 @@
 	old_lcr = serial_in(p, UART_LCR);
 	serial_out(p, UART_LCR, UART_LCR_CONF_MODE_A);
 
+#ifdef CONFIG_TANGO2
+	old_dll = serial_in(p, UART_DL) & 0xff;
+	old_dlm = serial_in(p, UART_DL) >> 8;
+
+	serial_out(p, UART_DL, 0);
+
+	id = serial_in(p, UART_DL);
+
+	serial_out(p, UART_DL, (old_dlm << 8) | old_dll);
+#else
 	old_dll = serial_in(p, UART_DLL);
 	old_dlm = serial_in(p, UART_DLM);
 
@@ -702,6 +772,7 @@
 
 	serial_out(p, UART_DLL, old_dll);
 	serial_out(p, UART_DLM, old_dlm);
+#endif
 	serial_out(p, UART_LCR, old_lcr);
 
 	return id;
@@ -941,11 +1012,19 @@
 
 			serial_out(up, UART_LCR, 0xE0);
 
+#ifdef CONFIG_TANGO2
+			quot = serial_in(up, UART_DL);
+#else
 			quot = serial_dl_read(up);
+#endif
 			quot <<= 3;
 
 			if (ns16550a_goto_highspeed(up))
+#ifdef CONFIG_TANGO2
+				serial_out(up, UART_DL, quot);
+#else
 				serial_dl_write(up, quot);
+#endif
 
 			serial_out(up, UART_LCR, 0);
 
@@ -1520,7 +1599,7 @@
 			status = serial8250_rx_chars(up, status);
 	}
 	serial8250_modem_status(up);
-	if (status & UART_LSR_THRE)
+	if (!up->dma && (status & UART_LSR_THRE))
 		serial8250_tx_chars(up);
 
 	spin_unlock_irqrestore(&port->lock, flags);
@@ -2430,7 +2509,11 @@
 	else
 		serial_port_out(port, UART_LCR, cval | UART_LCR_DLAB);
 
+#ifdef CONFIG_TANGO2
+	serial_out(up, UART_DL, quot);
+#else
 	serial_dl_write(up, quot);
+#endif
 
 	/*
 	 * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
@@ -2670,6 +2753,10 @@
 	if (port->type == PORT_16550A && port->iotype == UPIO_AU)
 		up->bugs |= UART_BUG_NOMSR;
 
+	/* HW bugs may trigger IRQ while IIR == NO_INT */
+	if (port->type == PORT_TEGRA)
+		up->bugs |= UART_BUG_NOMSR;
+
 	if (port->type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
 		autoconfig_irq(up);
 
diff -ruw linux-3.11.10/drivers/tty/serial/8250/8250_early.c linux-3.11.10-fbx/drivers/tty/serial/8250/8250_early.c
--- linux-3.11.10/drivers/tty/serial/8250/8250_early.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/tty/serial/8250/8250_early.c	2015-09-29 11:15:07.689746515 +0200
@@ -121,8 +121,13 @@
 
 	lcr = serial8250_early_in(port, UART_LCR);
 	serial8250_early_out(port, UART_LCR, lcr | UART_LCR_DLAB);
+#ifdef CONFIG_TANGO2
+	dll = serial8250_early_in(port, UART_DL) & 0xff;
+	dlm = serial8250_early_in(port, UART_DL) >> 8;
+#else
 	dll = serial8250_early_in(port, UART_DLL);
 	dlm = serial8250_early_in(port, UART_DLM);
+#endif
 	serial8250_early_out(port, UART_LCR, lcr);
 
 	quot = (dlm << 8) | dll;
@@ -143,8 +148,12 @@
 	divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud);
 	c = serial8250_early_in(port, UART_LCR);
 	serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB);
+#ifdef CONFIG_TANGO2
+	serial8250_early_out(port, UART_DL, divisor & 0xffff);
+#else
 	serial8250_early_out(port, UART_DLL, divisor & 0xff);
 	serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff);
+#endif
 	serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB);
 }
 
diff -ruw linux-3.11.10/drivers/tty/serial/8250/8250_pci.c linux-3.11.10-fbx/drivers/tty/serial/8250/8250_pci.c
--- linux-3.11.10/drivers/tty/serial/8250/8250_pci.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/tty/serial/8250/8250_pci.c	2015-09-29 11:15:07.689746515 +0200
@@ -1260,10 +1260,10 @@
 		unsigned long base = pci_resource_start(dev, 0);
 		if (base) {
 			u32 tmp;
-			outl(inl(base + 0x38), base + 0x38);
+			outl(inl(base + 0x38) | 0x00002000, base + 0x38);
 			tmp = inl(base + 0x3c);
 			outl(tmp | 0x01000000, base + 0x3c);
-			outl(tmp, base + 0x3c);
+			outl(tmp &= ~0x01000000, base + 0x3c);
 		}
 	}
 	return 0;
@@ -1545,6 +1545,7 @@
 #define PCI_DEVICE_ID_TITAN_800E	0xA014
 #define PCI_DEVICE_ID_TITAN_200EI	0xA016
 #define PCI_DEVICE_ID_TITAN_200EISI	0xA017
+#define PCI_DEVICE_ID_TITAN_200V3	0xA306
 #define PCI_DEVICE_ID_TITAN_400V3	0xA310
 #define PCI_DEVICE_ID_TITAN_410V3	0xA312
 #define PCI_DEVICE_ID_TITAN_800V3	0xA314
@@ -4139,6 +4140,9 @@
 	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_oxsemi_2_4000000 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200V3,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_921600 },
 	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400V3,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_b0_4_921600 },
diff -ruw linux-3.11.10/drivers/tty/tty_io.c linux-3.11.10-fbx/drivers/tty/tty_io.c
--- linux-3.11.10/drivers/tty/tty_io.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/tty/tty_io.c	2015-09-29 11:15:07.713746698 +0200
@@ -1267,12 +1267,13 @@
  *
  *	Locking: None
  */
-static void tty_line_name(struct tty_driver *driver, int index, char *p)
+static ssize_t tty_line_name(struct tty_driver *driver, int index, char *p)
 {
 	if (driver->flags & TTY_DRIVER_UNNUMBERED_NODE)
-		strcpy(p, driver->name);
+		return sprintf(p, "%s", driver->name);
 	else
-		sprintf(p, "%s%d", driver->name, index + driver->name_base);
+		return sprintf(p, "%s%d", driver->name,
+			       index + driver->name_base);
 }
 
 /**
@@ -3539,9 +3540,19 @@
 		if (i >= ARRAY_SIZE(cs))
 			break;
 	}
-	while (i--)
-		count += sprintf(buf + count, "%s%d%c",
-				 cs[i]->name, cs[i]->index, i ? ' ':'\n');
+	while (i--) {
+		int index = cs[i]->index;
+		struct tty_driver *drv = cs[i]->device(cs[i], &index);
+
+		/* don't resolve tty0 as some programs depend on it */
+		if (drv && (cs[i]->index > 0 || drv->major != TTY_MAJOR))
+			count += tty_line_name(drv, index, buf + count);
+		else
+			count += sprintf(buf + count, "%s%d",
+					 cs[i]->name, cs[i]->index);
+
+		count += sprintf(buf + count, "%c", i ? ' ':'\n');
+	}
 	console_unlock();
 
 	return count;
diff -ruw linux-3.11.10/drivers/tty/vt/vt.c linux-3.11.10-fbx/drivers/tty/vt/vt.c
--- linux-3.11.10/drivers/tty/vt/vt.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/tty/vt/vt.c	2015-09-29 11:15:07.713746698 +0200
@@ -1164,6 +1164,8 @@
 			scr_memsetw(vc->vc_screenbuf, vc->vc_video_erase_char,
 				    vc->vc_screenbuf_size >> 1);
 			set_origin(vc);
+			if (CON_IS_VISIBLE(vc))
+				update_screen(vc);
 			/* fall through */
 		case 2: /* erase whole display */
 			count = vc->vc_cols * vc->vc_rows;
diff -ruw linux-3.11.10/drivers/usb/class/cdc-acm.c linux-3.11.10-fbx/drivers/usb/class/cdc-acm.c
--- linux-3.11.10/drivers/usb/class/cdc-acm.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/class/cdc-acm.c	2015-09-29 11:15:07.721746764 +0200
@@ -122,13 +122,23 @@
 static int acm_ctrl_msg(struct acm *acm, int request, int value,
 							void *buf, int len)
 {
-	int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
+	int retval;
+
+	retval = usb_autopm_get_interface(acm->control);
+	if (retval)
+		return retval;
+
+	retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
 		request, USB_RT_ACM, value,
 		acm->control->altsetting[0].desc.bInterfaceNumber,
 		buf, len, 5000);
+
 	dev_dbg(&acm->control->dev,
 			"%s - rq 0x%02x, val %#x, len %#x, result %d\n",
 			__func__, request, value, len, retval);
+
+	usb_autopm_put_interface(acm->control);
+
 	return retval < 0 ? retval : 0;
 }
 
@@ -484,6 +494,7 @@
 {
 	struct acm *acm = container_of(port, struct acm, port);
 	int retval = -ENODEV;
+	int i;
 
 	dev_dbg(&acm->control->dev, "%s\n", __func__);
 
@@ -532,6 +543,8 @@
 	return 0;
 
 error_submit_read_urbs:
+	for (i = 0; i < acm->rx_buflimit; i++)
+		usb_kill_urb(acm->read_urbs[i]);
 	acm->ctrlout = 0;
 	acm_set_control(acm, acm->ctrlout);
 error_set_control:
@@ -559,20 +572,34 @@
 static void acm_port_shutdown(struct tty_port *port)
 {
 	struct acm *acm = container_of(port, struct acm, port);
+	struct urb *urb;
+	struct acm_wb *wb;
 	int i;
+	int pm_err;
 
 	dev_dbg(&acm->control->dev, "%s\n", __func__);
 
 	mutex_lock(&acm->mutex);
 	if (!acm->disconnected) {
-		usb_autopm_get_interface(acm->control);
+		pm_err = usb_autopm_get_interface(acm->control);
 		acm_set_control(acm, acm->ctrlout = 0);
+
+		for (;;) {
+			urb = usb_get_from_anchor(&acm->delayed);
+			if (!urb)
+				break;
+			wb = urb->context;
+			wb->use = 0;
+			usb_autopm_put_interface_async(acm->control);
+		}
+
 		usb_kill_urb(acm->ctrlurb);
 		for (i = 0; i < ACM_NW; i++)
 			usb_kill_urb(acm->wb[i].urb);
 		for (i = 0; i < acm->rx_buflimit; i++)
 			usb_kill_urb(acm->read_urbs[i]);
 		acm->control->needs_remote_wakeup = 0;
+		if (!pm_err)
 		usb_autopm_put_interface(acm->control);
 	}
 	mutex_unlock(&acm->mutex);
@@ -632,14 +659,17 @@
 	memcpy(wb->buf, buf, count);
 	wb->len = count;
 
-	usb_autopm_get_interface_async(acm->control);
+	stat = usb_autopm_get_interface_async(acm->control);
+	if (stat) {
+		wb->use = 0;
+		spin_unlock_irqrestore(&acm->write_lock, flags);
+		return stat;
+	}
+
 	if (acm->susp_count) {
-		if (!acm->delayed_wb)
-			acm->delayed_wb = wb;
-		else
-			usb_autopm_put_interface_async(acm->control);
+		usb_anchor_urb(wb->urb, &acm->delayed);
 		spin_unlock_irqrestore(&acm->write_lock, flags);
-		return count;	/* A white lie */
+		return count;
 	}
 	usb_mark_last_busy(acm->dev);
 
@@ -1176,6 +1206,7 @@
 		acm->bInterval = epread->bInterval;
 	tty_port_init(&acm->port);
 	acm->port.ops = &acm_port_ops;
+	init_usb_anchor(&acm->delayed);
 
 	buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
 	if (!buf) {
@@ -1420,18 +1451,15 @@
 	struct acm *acm = usb_get_intfdata(intf);
 	int cnt;
 
+	spin_lock_irq(&acm->read_lock);
+	spin_lock(&acm->write_lock);
 	if (PMSG_IS_AUTO(message)) {
-		int b;
-
-		spin_lock_irq(&acm->write_lock);
-		b = acm->transmitting;
-		spin_unlock_irq(&acm->write_lock);
-		if (b)
+		if (acm->transmitting) {
+			spin_unlock(&acm->write_lock);
+			spin_unlock_irq(&acm->read_lock);
 			return -EBUSY;
 	}
-
-	spin_lock_irq(&acm->read_lock);
-	spin_lock(&acm->write_lock);
+	}
 	cnt = acm->susp_count++;
 	spin_unlock(&acm->write_lock);
 	spin_unlock_irq(&acm->read_lock);
@@ -1439,7 +1467,6 @@
 	if (cnt)
 		return 0;
 
-	if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags))
 		stop_data_traffic(acm);
 
 	return 0;
@@ -1448,29 +1475,24 @@
 static int acm_resume(struct usb_interface *intf)
 {
 	struct acm *acm = usb_get_intfdata(intf);
-	struct acm_wb *wb;
+	struct urb *urb;
 	int rv = 0;
-	int cnt;
 
 	spin_lock_irq(&acm->read_lock);
-	acm->susp_count -= 1;
-	cnt = acm->susp_count;
-	spin_unlock_irq(&acm->read_lock);
+	spin_lock(&acm->write_lock);
 
-	if (cnt)
-		return 0;
+	if (--acm->susp_count)
+		goto out;
 
 	if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
-		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
+		rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
 
-		spin_lock_irq(&acm->write_lock);
-		if (acm->delayed_wb) {
-			wb = acm->delayed_wb;
-			acm->delayed_wb = NULL;
-			spin_unlock_irq(&acm->write_lock);
-			acm_start_wb(acm, wb);
-		} else {
-			spin_unlock_irq(&acm->write_lock);
+		for (;;) {
+			urb = usb_get_from_anchor(&acm->delayed);
+			if (!urb)
+				break;
+
+			acm_start_wb(acm, urb->context);
 		}
 
 		/*
@@ -1478,12 +1500,14 @@
 		 * do the write path at all cost
 		 */
 		if (rv < 0)
-			goto err_out;
+			goto out;
 
-		rv = acm_submit_read_urbs(acm, GFP_NOIO);
+		rv = acm_submit_read_urbs(acm, GFP_ATOMIC);
 	}
+out:
+	spin_unlock(&acm->write_lock);
+	spin_unlock_irq(&acm->read_lock);
 
-err_out:
 	return rv;
 }
 
@@ -1515,6 +1539,8 @@
 
 static const struct usb_device_id acm_ids[] = {
 	/* quirky and broken devices */
+	{ USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */
+	.driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
 	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
 	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
 	},
@@ -1558,13 +1584,27 @@
 	},
 	/* Motorola H24 HSPA module: */
 	{ USB_DEVICE(0x22b8, 0x2d91) }, /* modem                                */
-	{ USB_DEVICE(0x22b8, 0x2d92) }, /* modem           + diagnostics        */
-	{ USB_DEVICE(0x22b8, 0x2d93) }, /* modem + AT port                      */
-	{ USB_DEVICE(0x22b8, 0x2d95) }, /* modem + AT port + diagnostics        */
-	{ USB_DEVICE(0x22b8, 0x2d96) }, /* modem                         + NMEA */
-	{ USB_DEVICE(0x22b8, 0x2d97) }, /* modem           + diagnostics + NMEA */
-	{ USB_DEVICE(0x22b8, 0x2d99) }, /* modem + AT port               + NMEA */
-	{ USB_DEVICE(0x22b8, 0x2d9a) }, /* modem + AT port + diagnostics + NMEA */
+	{ USB_DEVICE(0x22b8, 0x2d92),   /* modem           + diagnostics        */
+	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+	},
+	{ USB_DEVICE(0x22b8, 0x2d93),   /* modem + AT port                      */
+	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+	},
+	{ USB_DEVICE(0x22b8, 0x2d95),   /* modem + AT port + diagnostics        */
+	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+	},
+	{ USB_DEVICE(0x22b8, 0x2d96),   /* modem                         + NMEA */
+	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+	},
+	{ USB_DEVICE(0x22b8, 0x2d97),   /* modem           + diagnostics + NMEA */
+	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+	},
+	{ USB_DEVICE(0x22b8, 0x2d99),   /* modem + AT port               + NMEA */
+	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+	},
+	{ USB_DEVICE(0x22b8, 0x2d9a),   /* modem + AT port + diagnostics + NMEA */
+	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+	},
 
 	{ USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
 	.driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
diff -ruw linux-3.11.10/drivers/usb/class/cdc-acm.h linux-3.11.10-fbx/drivers/usb/class/cdc-acm.h
--- linux-3.11.10/drivers/usb/class/cdc-acm.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/class/cdc-acm.h	2015-09-29 11:15:07.721746764 +0200
@@ -117,7 +117,7 @@
 	unsigned int throttled:1;			/* actually throttled */
 	unsigned int throttle_req:1;			/* throttle requested */
 	u8 bInterval;
-	struct acm_wb *delayed_wb;			/* write queued for a device about to be woken */
+	struct usb_anchor delayed;			/* writes queued for a device about to be woken */
 };
 
 #define CDC_DATA_INTERFACE_TYPE	0x0a
diff -ruw linux-3.11.10/drivers/usb/core/config.c linux-3.11.10-fbx/drivers/usb/core/config.c
--- linux-3.11.10/drivers/usb/core/config.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/core/config.c	2015-09-29 11:15:07.721746764 +0200
@@ -651,10 +651,6 @@
  *
  * hub-only!! ... and only in reset path, or usb_new_device()
  * (used by real hubs and virtual root hubs)
- *
- * NOTE: if this is a WUSB device and is not authorized, we skip the
- *       whole thing. A non-authorized USB device has no
- *       configurations.
  */
 int usb_get_configuration(struct usb_device *dev)
 {
@@ -666,8 +662,6 @@
 	struct usb_config_descriptor *desc;
 
 	cfgno = 0;
-	if (dev->authorized == 0)	/* Not really an error */
-		goto out_not_authorized;
 	result = -ENOMEM;
 	if (ncfg > USB_MAXCONFIG) {
 		dev_warn(ddev, "too many configurations: %d, "
@@ -724,6 +718,10 @@
 			result = -ENOMEM;
 			goto err;
 		}
+
+		if (dev->quirks & USB_QUIRK_DELAY_INIT)
+			msleep(100);
+
 		result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
 		    bigbuffer, length);
 		if (result < 0) {
@@ -751,7 +749,6 @@
 
 err:
 	kfree(desc);
-out_not_authorized:
 	dev->descriptor.bNumConfigurations = cfgno;
 err2:
 	if (result == -ENOMEM)
diff -ruw linux-3.11.10/drivers/usb/core/driver.c linux-3.11.10-fbx/drivers/usb/core/driver.c
--- linux-3.11.10/drivers/usb/core/driver.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/core/driver.c	2015-09-29 11:15:07.721746764 +0200
@@ -953,8 +953,7 @@
  * it doesn't support pre_reset/post_reset/reset_resume or
  * because it doesn't support suspend/resume.
  *
- * The caller must hold @intf's device's lock, but not its pm_mutex
- * and not @intf->dev.sem.
+ * The caller must hold @intf's device's lock, but not @intf's lock.
  */
 void usb_forced_unbind_intf(struct usb_interface *intf)
 {
@@ -967,16 +966,37 @@
 	intf->needs_binding = 1;
 }
 
+/*
+ * Unbind drivers for @udev's marked interfaces.  These interfaces have
+ * the needs_binding flag set, for example by usb_resume_interface().
+ *
+ * The caller must hold @udev's device lock.
+ */
+static void unbind_marked_interfaces(struct usb_device *udev)
+{
+	struct usb_host_config	*config;
+	int			i;
+	struct usb_interface	*intf;
+
+	config = udev->actconfig;
+	if (config) {
+		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+			intf = config->interface[i];
+			if (intf->dev.driver && intf->needs_binding)
+				usb_forced_unbind_intf(intf);
+		}
+	}
+}
+
 /* Delayed forced unbinding of a USB interface driver and scan
  * for rebinding.
  *
- * The caller must hold @intf's device's lock, but not its pm_mutex
- * and not @intf->dev.sem.
+ * The caller must hold @intf's device's lock, but not @intf's lock.
  *
  * Note: Rebinds will be skipped if a system sleep transition is in
  * progress and the PM "complete" callback hasn't occurred yet.
  */
-void usb_rebind_intf(struct usb_interface *intf)
+static void usb_rebind_intf(struct usb_interface *intf)
 {
 	int rc;
 
@@ -993,68 +1013,66 @@
 	}
 }
 
-#ifdef CONFIG_PM
-
-/* Unbind drivers for @udev's interfaces that don't support suspend/resume
- * There is no check for reset_resume here because it can be determined
- * only during resume whether reset_resume is needed.
+/*
+ * Rebind drivers to @udev's marked interfaces.  These interfaces have
+ * the needs_binding flag set.
  *
  * The caller must hold @udev's device lock.
  */
-static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
+static void rebind_marked_interfaces(struct usb_device *udev)
 {
 	struct usb_host_config	*config;
 	int			i;
 	struct usb_interface	*intf;
-	struct usb_driver	*drv;
 
 	config = udev->actconfig;
 	if (config) {
 		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
 			intf = config->interface[i];
-
-			if (intf->dev.driver) {
-				drv = to_usb_driver(intf->dev.driver);
-				if (!drv->suspend || !drv->resume)
-					usb_forced_unbind_intf(intf);
-			}
+			if (intf->needs_binding)
+				usb_rebind_intf(intf);
 		}
 	}
 }
 
-/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
- * These interfaces have the needs_binding flag set by usb_resume_interface().
+/*
+ * Unbind all of @udev's marked interfaces and then rebind all of them.
+ * This ordering is necessary because some drivers claim several interfaces
+ * when they are first probed.
  *
  * The caller must hold @udev's device lock.
  */
-static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
+void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev)
 {
-	struct usb_host_config	*config;
-	int			i;
-	struct usb_interface	*intf;
-
-	config = udev->actconfig;
-	if (config) {
-		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
-			intf = config->interface[i];
-			if (intf->dev.driver && intf->needs_binding)
-				usb_forced_unbind_intf(intf);
-		}
-	}
+	unbind_marked_interfaces(udev);
+	rebind_marked_interfaces(udev);
 }
 
-static void do_rebind_interfaces(struct usb_device *udev)
+#ifdef CONFIG_PM
+
+/* Unbind drivers for @udev's interfaces that don't support suspend/resume
+ * There is no check for reset_resume here because it can be determined
+ * only during resume whether reset_resume is needed.
+ *
+ * The caller must hold @udev's device lock.
+ */
+static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
 {
 	struct usb_host_config	*config;
 	int			i;
 	struct usb_interface	*intf;
+	struct usb_driver	*drv;
 
 	config = udev->actconfig;
 	if (config) {
 		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
 			intf = config->interface[i];
-			if (intf->needs_binding)
-				usb_rebind_intf(intf);
+
+			if (intf->dev.driver) {
+				drv = to_usb_driver(intf->dev.driver);
+				if (!drv->suspend || !drv->resume)
+					usb_forced_unbind_intf(intf);
+			}
 		}
 	}
 }
@@ -1379,7 +1397,7 @@
 	 * whose needs_binding flag is set
 	 */
 	if (udev->state != USB_STATE_NOTATTACHED)
-		do_rebind_interfaces(udev);
+		rebind_marked_interfaces(udev);
 	return 0;
 }
 
@@ -1401,7 +1419,7 @@
 		pm_runtime_disable(dev);
 		pm_runtime_set_active(dev);
 		pm_runtime_enable(dev);
-		unbind_no_reset_resume_drivers_interfaces(udev);
+		unbind_marked_interfaces(udev);
 	}
 
 	/* Avoid PM error messages for devices disconnected while suspended
@@ -1736,10 +1754,13 @@
 	if (status == -EAGAIN || status == -EBUSY)
 		usb_mark_last_busy(udev);
 
-	/* The PM core reacts badly unless the return code is 0,
-	 * -EAGAIN, or -EBUSY, so always return -EBUSY on an error.
+	/*
+	 * The PM core reacts badly unless the return code is 0,
+	 * -EAGAIN, or -EBUSY, so always return -EBUSY on an error
+	 * (except for root hubs, because they don't suspend through
+	 * an upstream port like other USB devices).
 	 */
-	if (status != 0)
+	if (status != 0 && udev->parent)
 		return -EBUSY;
 	return status;
 }
@@ -1774,6 +1795,9 @@
 	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
 	int ret = -EPERM;
 
+	if (enable && !udev->usb2_hw_lpm_allowed)
+		return 0;
+
 	if (hcd->driver->set_usb2_hw_lpm) {
 		ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable);
 		if (!ret)
diff -ruw linux-3.11.10/drivers/usb/core/hcd.c linux-3.11.10-fbx/drivers/usb/core/hcd.c
--- linux-3.11.10/drivers/usb/core/hcd.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/core/hcd.c	2015-09-29 11:15:07.721746764 +0200
@@ -1035,7 +1035,6 @@
 					dev_name(&usb_dev->dev), retval);
 			return retval;
 		}
-		usb_dev->lpm_capable = usb_device_supports_lpm(usb_dev);
 	}
 
 	retval = usb_new_device (usb_dev);
diff -ruw linux-3.11.10/drivers/usb/core/hcd-pci.c linux-3.11.10-fbx/drivers/usb/core/hcd-pci.c
--- linux-3.11.10/drivers/usb/core/hcd-pci.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/core/hcd-pci.c	2015-09-29 11:15:07.721746764 +0200
@@ -75,7 +75,7 @@
 				PCI_SLOT(companion->devfn) != slot)
 			continue;
 		companion_hcd = pci_get_drvdata(companion);
-		if (!companion_hcd)
+		if (!companion_hcd || !companion_hcd->self.root_hub)
 			continue;
 		fn(pdev, hcd, companion, companion_hcd);
 	}
diff -ruw linux-3.11.10/drivers/usb/core/hub.c linux-3.11.10-fbx/drivers/usb/core/hub.c
--- linux-3.11.10/drivers/usb/core/hub.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/core/hub.c	2015-09-29 11:15:07.725746790 +0200
@@ -135,7 +135,7 @@
 	return usb_get_intfdata(hdev->actconfig->interface[0]);
 }
 
-int usb_device_supports_lpm(struct usb_device *udev)
+static int usb_device_supports_lpm(struct usb_device *udev)
 {
 	/* USB 2.1 (and greater) devices indicate LPM support through
 	 * their USB 2.0 Extended Capabilities BOS descriptor.
@@ -156,11 +156,6 @@
 				"Power management will be impacted.\n");
 		return 0;
 	}
-
-	/* udev is root hub */
-	if (!udev->parent)
-		return 1;
-
 	if (udev->parent->lpm_capable)
 		return 1;
 
@@ -1129,6 +1124,11 @@
 			usb_clear_port_feature(hub->hdev, port1,
 					USB_PORT_FEAT_C_ENABLE);
 		}
+		if (portchange & USB_PORT_STAT_C_RESET) {
+			need_debounce_delay = true;
+			usb_clear_port_feature(hub->hdev, port1,
+					USB_PORT_FEAT_C_RESET);
+		}
 		if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
 				hub_is_superspeed(hub->hdev)) {
 			need_debounce_delay = true;
@@ -1600,7 +1600,7 @@
 {
 	struct usb_hub *hub = usb_get_intfdata(intf);
 	struct usb_device *hdev = interface_to_usbdev(intf);
-	int i;
+	int port1;
 
 	/* Take the hub off the event list and don't let it be added again */
 	spin_lock_irq(&hub_event_lock);
@@ -1615,11 +1615,15 @@
 	hub->error = 0;
 	hub_quiesce(hub, HUB_DISCONNECT);
 
+	/* Avoid races with recursively_mark_NOTATTACHED() */
+	spin_lock_irq(&device_state_lock);
+	port1 = hdev->maxchild;
+	hdev->maxchild = 0;
 	usb_set_intfdata (intf, NULL);
+	spin_unlock_irq(&device_state_lock);
 
-	for (i = 0; i < hdev->maxchild; i++)
-		usb_hub_remove_port_device(hub, i + 1);
-	hub->hdev->maxchild = 0;
+	for (; port1 > 0; --port1)
+		usb_hub_remove_port_device(hub, port1);
 
 	if (hub->hdev->speed == USB_SPEED_HIGH)
 		highspeed_hubs--;
@@ -1679,8 +1683,19 @@
 	 */
 	pm_runtime_set_autosuspend_delay(&hdev->dev, 0);
 
-	/* Hubs have proper suspend/resume support. */
+	/*
+	 * Hubs have proper suspend/resume support, except for root hubs
+	 * where the controller driver doesn't have bus_suspend and
+	 * bus_resume methods.
+	 */
+	if (hdev->parent) {		/* normal device */
 	usb_enable_autosuspend(hdev);
+	} else {			/* root hub */
+		const struct hc_driver *drv = bus_to_hcd(hdev->bus)->driver;
+
+		if (drv->bus_suspend && drv->bus_resume)
+			usb_enable_autosuspend(hdev);
+	}
 
 	if (hdev->level == MAX_TOPO_LEVEL) {
 		dev_err(&intf->dev,
@@ -2224,18 +2239,13 @@
 			return err;
 		}
 	}
-	if (udev->wusb == 1 && udev->authorized == 0) {
-		udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-		udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-		udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-	}
-	else {
+
 		/* read the standard strings and cache them if present */
 		udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
 		udev->manufacturer = usb_cache_string(udev,
 						      udev->descriptor.iManufacturer);
 		udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
-	}
+
 	err = usb_enumerate_device_otg(udev);
 	if (err < 0)
 		return err;
@@ -2414,16 +2424,6 @@
 	usb_dev->authorized = 0;
 	usb_set_configuration(usb_dev, -1);
 
-	kfree(usb_dev->product);
-	usb_dev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-	kfree(usb_dev->manufacturer);
-	usb_dev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-	kfree(usb_dev->serial);
-	usb_dev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-
-	usb_destroy_configuration(usb_dev);
-	usb_dev->descriptor.bNumConfigurations = 0;
-
 out_unauthorized:
 	usb_unlock_device(usb_dev);
 	return 0;
@@ -2451,17 +2451,7 @@
 		goto error_device_descriptor;
 	}
 
-	kfree(usb_dev->product);
-	usb_dev->product = NULL;
-	kfree(usb_dev->manufacturer);
-	usb_dev->manufacturer = NULL;
-	kfree(usb_dev->serial);
-	usb_dev->serial = NULL;
-
 	usb_dev->authorized = 1;
-	result = usb_enumerate_device(usb_dev);
-	if (result < 0)
-		goto error_enumerate;
 	/* Choose and set the configuration.  This registers the interfaces
 	 * with the driver core and lets interface drivers bind to them.
 	 */
@@ -2477,7 +2467,6 @@
 	}
 	dev_info(&usb_dev->dev, "authorized to connect\n");
 
-error_enumerate:
 error_device_descriptor:
 	usb_autosuspend_device(usb_dev);
 error_autoresume:
@@ -3952,6 +3941,32 @@
 	return retval;
 }
 
+/*
+ * There are reports of USB 3.0 devices that say they support USB 2.0 Link PM
+ * when they're plugged into a USB 2.0 port, but they don't work when LPM is
+ * enabled.
+ *
+ * Only enable USB 2.0 Link PM if the port is internal (hardwired), or the
+ * device says it supports the new USB 2.0 Link PM errata by setting the BESL
+ * support bit in the BOS descriptor.
+ */
+static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev)
+{
+	int connect_type;
+
+	if (!udev->usb2_hw_lpm_capable)
+		return;
+
+	connect_type = usb_get_hub_port_connect_type(udev->parent,
+			udev->portnum);
+
+	if ((udev->bos->ext_cap->bmAttributes & USB_BESL_SUPPORT) ||
+			connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
+		udev->usb2_hw_lpm_allowed = 1;
+		usb_set_usb2_hardware_lpm(udev, 1);
+	}
+}
+
 /* Reset device, (re)assign address, get device descriptor.
  * Device connection must be stable, no more debouncing needed.
  * Returns device in USB_STATE_ADDRESS, except on error.
@@ -4245,6 +4260,7 @@
 	/* notify HCD that we have a device connected and addressed */
 	if (hcd->driver->update_device)
 		hcd->driver->update_device(hcd, udev);
+	hub_set_initial_usb2_lpm_policy(udev);
 fail:
 	if (retval) {
 		hub_port_disable(hub, port1, 0);
@@ -4805,8 +4821,9 @@
 					hub->ports[i - 1]->child;
 
 				dev_dbg(hub_dev, "warm reset port %d\n", i);
-				if (!udev || !(portstatus &
-						USB_PORT_STAT_CONNECTION)) {
+				if (!udev ||
+				    !(portstatus & USB_PORT_STAT_CONNECTION) ||
+				    udev->state == USB_STATE_NOTATTACHED) {
 					status = hub_port_reset(hub, i,
 							NULL, HUB_BH_RESET_TIME,
 							true);
@@ -5079,6 +5096,12 @@
 	}
 	parent_hub = usb_hub_to_struct_hub(parent_hdev);
 
+	/* Disable USB2 hardware LPM.
+	 * It will be re-enabled by the enumeration process.
+	 */
+	if (udev->usb2_hw_lpm_enabled == 1)
+		usb_set_usb2_hardware_lpm(udev, 0);
+
 	/* Disable LPM and LTM while we reset the device and reinstall the alt
 	 * settings.  Device-initiated LPM settings, and system exit latency
 	 * settings are cleared when the device is reset, so we have to set
@@ -5183,6 +5206,7 @@
 
 done:
 	/* Now that the alt settings are re-installed, enable LTM and LPM. */
+	usb_set_usb2_hardware_lpm(udev, 1);
 	usb_unlocked_enable_lpm(udev);
 	usb_enable_ltm(udev);
 	return 0;
@@ -5275,10 +5299,11 @@
 				else if (cintf->condition ==
 						USB_INTERFACE_BOUND)
 					rebind = 1;
+				if (rebind)
+					cintf->needs_binding = 1;
 			}
-			if (ret == 0 && rebind)
-				usb_rebind_intf(cintf);
 		}
+		usb_unbind_and_rebind_marked_interfaces(udev);
 	}
 
 	usb_autosuspend_device(udev);
diff -ruw linux-3.11.10/drivers/usb/core/quirks.c linux-3.11.10-fbx/drivers/usb/core/quirks.c
--- linux-3.11.10/drivers/usb/core/quirks.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/core/quirks.c	2015-09-29 11:15:07.725746790 +0200
@@ -46,6 +46,10 @@
 	/* Microsoft LifeCam-VX700 v2.0 */
 	{ USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME },
 
+	/* Logitech HD Pro Webcams C920 and C930e */
+	{ USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT },
+	{ USB_DEVICE(0x046d, 0x0843), .driver_info = USB_QUIRK_DELAY_INIT },
+
 	/* Logitech Quickcam Fusion */
 	{ USB_DEVICE(0x046d, 0x08c1), .driver_info = USB_QUIRK_RESET_RESUME },
 
diff -ruw linux-3.11.10/drivers/usb/core/sysfs.c linux-3.11.10-fbx/drivers/usb/core/sysfs.c
--- linux-3.11.10/drivers/usb/core/sysfs.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/core/sysfs.c	2015-09-29 11:15:07.725746790 +0200
@@ -463,7 +463,7 @@
 	struct usb_device *udev = to_usb_device(dev);
 	const char *p;
 
-	if (udev->usb2_hw_lpm_enabled == 1)
+	if (udev->usb2_hw_lpm_allowed == 1)
 		p = "enabled";
 	else
 		p = "disabled";
@@ -483,8 +483,10 @@
 
 	ret = strtobool(buf, &value);
 
-	if (!ret)
+	if (!ret) {
+		udev->usb2_hw_lpm_allowed = value;
 		ret = usb_set_usb2_hardware_lpm(udev, value);
+	}
 
 	usb_unlock_device(udev);
 
diff -ruw linux-3.11.10/drivers/usb/core/usb.h linux-3.11.10-fbx/drivers/usb/core/usb.h
--- linux-3.11.10/drivers/usb/core/usb.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/core/usb.h	2015-09-29 11:15:07.725746790 +0200
@@ -35,7 +35,6 @@
 		unsigned int size);
 extern int usb_get_bos_descriptor(struct usb_device *dev);
 extern void usb_release_bos_descriptor(struct usb_device *dev);
-extern int usb_device_supports_lpm(struct usb_device *udev);
 extern char *usb_cache_string(struct usb_device *udev, int index);
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
 extern int usb_choose_configuration(struct usb_device *udev);
@@ -56,7 +55,7 @@
 extern int usb_match_device(struct usb_device *dev,
 			    const struct usb_device_id *id);
 extern void usb_forced_unbind_intf(struct usb_interface *intf);
-extern void usb_rebind_intf(struct usb_interface *intf);
+extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev);
 
 extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port,
 		struct dev_state *owner);
diff -ruw linux-3.11.10/drivers/usb/gadget/Kconfig linux-3.11.10-fbx/drivers/usb/gadget/Kconfig
--- linux-3.11.10/drivers/usb/gadget/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/gadget/Kconfig	2015-09-29 11:15:07.729746819 +0200
@@ -173,7 +173,7 @@
 
 config USB_FSL_USB2
 	tristate "Freescale Highspeed USB DR Peripheral Controller"
-	depends on FSL_SOC || ARCH_MXC
+	depends on FSL_SOC || ARCH_MXC || PLAT_ORION
 	select USB_FSL_MPH_DR_OF if OF
 	help
 	   Some of Freescale PowerPC and i.MX processors have a High Speed
diff -ruw linux-3.11.10/drivers/usb/gadget/Makefile linux-3.11.10-fbx/drivers/usb/gadget/Makefile
--- linux-3.11.10/drivers/usb/gadget/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/gadget/Makefile	2015-09-29 11:15:07.729746819 +0200
@@ -23,6 +23,7 @@
 obj-$(CONFIG_USB_FSL_USB2)	+= fsl_usb2_udc.o
 fsl_usb2_udc-y			:= fsl_udc_core.o
 fsl_usb2_udc-$(CONFIG_ARCH_MXC)	+= fsl_mxc_udc.o
+fsl_usb2_udc-$(CONFIG_PLAT_ORION) += fsl_orion_udc.o
 obj-$(CONFIG_USB_M66592)	+= m66592-udc.o
 obj-$(CONFIG_USB_R8A66597)	+= r8a66597-udc.o
 obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
diff -ruw linux-3.11.10/drivers/usb/host/ehci.h linux-3.11.10-fbx/drivers/usb/host/ehci.h
--- linux-3.11.10/drivers/usb/host/ehci.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/host/ehci.h	2015-09-29 11:15:07.753747001 +0200
@@ -200,6 +200,7 @@
 	unsigned		has_synopsys_hc_bug:1; /* Synopsys HC */
 	unsigned		frame_index_bug:1; /* MosChip (AKA NetMos) */
 	unsigned		need_oc_pp_cycle:1; /* MPC834X port power */
+	unsigned		imx28_write_fix:1; /* For Freescale i.MX28 */
 
 	/* required for usb32 quirk */
 	#define OHCI_CTRL_HCFS          (3 << 6)
@@ -675,6 +676,18 @@
 #endif
 }
 
+#ifdef CONFIG_SOC_IMX28
+static inline void imx28_ehci_writel(const unsigned int val,
+		volatile __u32 __iomem *addr)
+{
+	__asm__ ("swp %0, %0, [%1]" : : "r"(val), "r"(addr));
+}
+#else
+static inline void imx28_ehci_writel(const unsigned int val,
+		volatile __u32 __iomem *addr)
+{
+}
+#endif
 static inline void ehci_writel(const struct ehci_hcd *ehci,
 		const unsigned int val, __u32 __iomem *regs)
 {
@@ -683,6 +696,9 @@
 		writel_be(val, regs) :
 		writel(val, regs);
 #else
+	if (ehci->imx28_write_fix)
+		imx28_ehci_writel(val, regs);
+	else
 	writel(val, regs);
 #endif
 }
diff -ruw linux-3.11.10/drivers/usb/host/ehci-hcd.c linux-3.11.10-fbx/drivers/usb/host/ehci-hcd.c
--- linux-3.11.10/drivers/usb/host/ehci-hcd.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/host/ehci-hcd.c	2015-09-29 11:15:07.749746972 +0200
@@ -689,8 +689,15 @@
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
 	u32			status, masked_status, pcd_status = 0, cmd;
 	int			bh;
+	unsigned long		flags;
 
-	spin_lock (&ehci->lock);
+	/*
+	 * For threadirqs option we use spin_lock_irqsave() variant to prevent
+	 * deadlock with ehci hrtimer callback, because hrtimer callbacks run
+	 * in interrupt context even when threadirqs is specified. We can go
+	 * back to spin_lock() variant when hrtimer callbacks become threaded.
+	 */
+	spin_lock_irqsave(&ehci->lock, flags);
 
 	status = ehci_readl(ehci, &ehci->regs->status);
 
@@ -708,7 +715,7 @@
 
 	/* Shared IRQ? */
 	if (!masked_status || unlikely(ehci->rh_state == EHCI_RH_HALTED)) {
-		spin_unlock(&ehci->lock);
+		spin_unlock_irqrestore(&ehci->lock, flags);
 		return IRQ_NONE;
 	}
 
@@ -826,7 +833,7 @@
 
 	if (bh)
 		ehci_work (ehci);
-	spin_unlock (&ehci->lock);
+	spin_unlock_irqrestore(&ehci->lock, flags);
 	if (pcd_status)
 		usb_hcd_poll_rh_status(hcd);
 	return IRQ_HANDLED;
@@ -1284,6 +1291,16 @@
 #define	PLATFORM_DRIVER		ehci_hcd_sead3_driver
 #endif
 
+#ifdef CONFIG_USB_EHCI_BCM63XX
+#include "ehci-bcm63xx.c"
+#define	PLATFORM_DRIVER		ehci_hcd_bcm63xx_driver
+#endif
+
+#ifdef CONFIG_TANGO2
+#include "ehci-tango2.c"
+#define	PLATFORM_DRIVER		ehci_hcd_tango2_driver
+#endif
+
 static int __init ehci_hcd_init(void)
 {
 	int retval = 0;
diff -ruw linux-3.11.10/drivers/usb/host/ehci-orion.c linux-3.11.10-fbx/drivers/usb/host/ehci-orion.c
--- linux-3.11.10/drivers/usb/host/ehci-orion.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/host/ehci-orion.c	2015-09-29 11:15:07.749746972 +0200
@@ -35,6 +35,7 @@
 #define USB_WINDOW_BASE(i)	(0x324 + ((i) << 4))
 #define USB_IPG			0x360
 #define USB_PHY_PWR_CTRL	0x400
+#define USB_PHY_PLL_CTRL	0x410
 #define USB_PHY_TX_CTRL		0x420
 #define USB_PHY_RX_CTRL		0x430
 #define USB_PHY_IVREF_CTRL	0x440
@@ -116,6 +117,70 @@
 	wrl(USB_MODE, 0x13);
 }
 
+/*
+ * Implement Kirkwood USB controller specification guidelines
+ */
+static void orion_usb_phy_kw_setup(struct usb_hcd *hcd)
+{
+	u32 val;
+
+	/*
+	 * Clear interrupt cause and mask
+	 */
+	wrl(USB_CAUSE, 0);
+	wrl(USB_MASK, 0);
+
+	/*
+	 * Reset controller
+	 */
+	wrl(USB_CMD, rdl(USB_CMD) | 0x2);
+	while (rdl(USB_CMD) & 0x2);
+
+	/*
+	 * GL# USB-10: Set IPG for non start of frame packets
+	 * Bits[14:8]=0xd
+	 */
+	wrl(USB_IPG, (rdl(USB_IPG) & ~0x7f00) | 0xd00);
+
+	val = rdl(USB_PHY_PLL_CTRL);
+	val |= (1 << 21);
+	wrl(USB_PHY_PLL_CTRL, val);
+	udelay(100);
+	val &= ~(1 << 21);
+	wrl(USB_PHY_PLL_CTRL, val);
+
+
+	val = rdl(USB_PHY_TX_CTRL);
+	val |= (1 << 12);
+	wrl(USB_PHY_TX_CTRL, val);
+	udelay(100);
+	val &= ~(1 << 12);
+	wrl(USB_PHY_TX_CTRL, val);
+	val &= ~0x7;
+	val |= 0x4;
+	wrl(USB_PHY_TX_CTRL, val);
+
+
+	val = rdl(USB_PHY_RX_CTRL);
+	val &= ~(0xf << 4);
+	val |= (0x8 << 4);
+	wrl(USB_PHY_RX_CTRL, val);
+
+	/*
+	 * Stop and reset controller
+	 */
+	wrl(USB_CMD, rdl(USB_CMD) & ~0x1);
+	wrl(USB_CMD, rdl(USB_CMD) | 0x2);
+	while (rdl(USB_CMD) & 0x2);
+
+	/*
+	 * GL# USB-5 Streaming disable REG_USB_MODE[4]=1
+	 * TBD: This need to be done after each reset!
+	 * GL# USB-4 Setup USB Host mode
+	 */
+	wrl(USB_MODE, 0x13);
+}
+
 static void
 ehci_orion_conf_mbus_windows(struct usb_hcd *hcd,
 			     const struct mbus_dram_target_info *dram)
@@ -243,8 +308,10 @@
 	case EHCI_PHY_ORION:
 		orion_usb_phy_v1_setup(hcd);
 		break;
-	case EHCI_PHY_DD:
 	case EHCI_PHY_KW:
+		orion_usb_phy_kw_setup(hcd);
+		break;
+	case EHCI_PHY_DD:
 	default:
 		printk(KERN_WARNING "Orion ehci -USB phy version isn't supported.\n");
 	}
diff -ruw linux-3.11.10/drivers/usb/host/Kconfig linux-3.11.10-fbx/drivers/usb/host/Kconfig
--- linux-3.11.10/drivers/usb/host/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/host/Kconfig	2015-09-29 11:15:07.749746972 +0200
@@ -130,6 +130,14 @@
 		support both high speed and full speed devices, or high speed
 		devices only.
 
+config USB_EHCI_BCM63XX
+	bool "Support for Broadcom 63xx on-chip EHCI USB controller"
+	depends on USB_EHCI_HCD && BCM63XX
+	select USB_EHCI_BIG_ENDIAN_MMIO
+	---help---
+	  Enables support for the on-chip EHCI controller on
+	  BCM6358 and later chips.
+
 config USB_EHCI_FSL
 	bool "Support for Freescale PPC on-chip EHCI USB controller"
 	depends on FSL_SOC
@@ -373,6 +381,15 @@
 
 if USB_OHCI_HCD
 
+config USB_OHCI_BCM63XX
+	bool "Support for Broadcom 63xx on-chip OHCI USB controller"
+	depends on BCM63XX
+	select USB_OHCI_BIG_ENDIAN_DESC
+	select USB_OHCI_BIG_ENDIAN_MMIO
+	---help---
+	  Enables support for the on-chip OHCI controller on
+	  BCM63XX chips.
+
 config USB_OHCI_HCD_OMAP1
 	bool "OHCI support for OMAP1/2 chips"
 	depends on ARCH_OMAP1
diff -ruw linux-3.11.10/drivers/usb/host/pci-quirks.c linux-3.11.10-fbx/drivers/usb/host/pci-quirks.c
--- linux-3.11.10/drivers/usb/host/pci-quirks.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/host/pci-quirks.c	2015-09-29 11:15:07.761747064 +0200
@@ -568,6 +568,14 @@
 			DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"),
 		},
 	},
+	{
+		/* HASEE E200 */
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "HASEE"),
+			DMI_MATCH(DMI_BOARD_NAME, "E210"),
+			DMI_MATCH(DMI_BIOS_VERSION, "6.00"),
+		},
+	},
 	{ }
 };
 
@@ -577,9 +585,14 @@
 {
 	int try_handoff = 1, tried_handoff = 0;
 
-	/* The Pegatron Lucid tablet sporadically waits for 98 seconds trying
-	 * the handoff on its unused controller.  Skip it. */
-	if (pdev->vendor == 0x8086 && pdev->device == 0x283a) {
+	/*
+	 * The Pegatron Lucid tablet sporadically waits for 98 seconds trying
+	 * the handoff on its unused controller.  Skip it.
+	 *
+	 * The HASEE E200 hangs when the semaphore is set (bugzilla #77021).
+	 */
+	if (pdev->vendor == 0x8086 && (pdev->device == 0x283a ||
+			pdev->device == 0x27cc)) {
 		if (dmi_check_system(ehci_dmi_nohandoff_table))
 			try_handoff = 0;
 	}
diff -ruw linux-3.11.10/drivers/usb/storage/Kconfig linux-3.11.10-fbx/drivers/usb/storage/Kconfig
--- linux-3.11.10/drivers/usb/storage/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/storage/Kconfig	2015-09-29 11:15:07.797747338 +0200
@@ -18,7 +18,9 @@
 
 	  This option depends on 'SCSI' support being enabled, but you
 	  probably also need 'SCSI device support: SCSI disk support'
-	  (BLK_DEV_SD) for most USB storage devices.
+	  (BLK_DEV_SD) for most USB storage devices.  Some devices also
+	  will require 'Probe all LUNs on each SCSI device'
+	  (SCSI_MULTI_LUN).
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called usb-storage.
diff -ruw linux-3.11.10/drivers/usb/storage/scsiglue.c linux-3.11.10-fbx/drivers/usb/storage/scsiglue.c
--- linux-3.11.10/drivers/usb/storage/scsiglue.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/storage/scsiglue.c	2015-09-29 11:15:07.797747338 +0200
@@ -78,6 +78,8 @@
 
 static int slave_alloc (struct scsi_device *sdev)
 {
+	struct us_data *us = host_to_us(sdev->host);
+
 	/*
 	 * Set the INQUIRY transfer length to 36.  We don't use any of
 	 * the extra data and many devices choke if asked for more or
@@ -102,6 +104,10 @@
 	 */
 	blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1));
 
+	/* Tell the SCSI layer if we know there is more than one LUN */
+	if (us->protocol == USB_PR_BULK && us->max_lun > 0)
+		sdev->sdev_bflags |= BLIST_FORCELUN;
+
 	return 0;
 }
 
diff -ruw linux-3.11.10/drivers/usb/storage/unusual_cypress.h linux-3.11.10-fbx/drivers/usb/storage/unusual_cypress.h
--- linux-3.11.10/drivers/usb/storage/unusual_cypress.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/storage/unusual_cypress.h	2014-07-29 21:29:44.373864410 +0200
@@ -31,7 +31,7 @@
 		"Cypress ISD-300LP",
 		USB_SC_CYP_ATACB, USB_PR_DEVICE, NULL, 0),
 
-UNUSUAL_DEV( 0x14cd, 0x6116, 0x0000, 0x0219,
+UNUSUAL_DEV( 0x14cd, 0x6116, 0x0160, 0x0160,
 		"Super Top",
 		"USB 2.0  SATA BRIDGE",
 		USB_SC_CYP_ATACB, USB_PR_DEVICE, NULL, 0),
diff -ruw linux-3.11.10/drivers/usb/storage/unusual_devs.h linux-3.11.10-fbx/drivers/usb/storage/unusual_devs.h
--- linux-3.11.10/drivers/usb/storage/unusual_devs.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/storage/unusual_devs.h	2015-09-29 11:15:07.801747367 +0200
@@ -234,6 +234,27 @@
 		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
 		US_FL_MAX_SECTORS_64 ),
 
+/* Reported by Daniele Forsi <dforsi@gmail.com> */
+UNUSUAL_DEV(  0x0421, 0x04b9, 0x0350, 0x0350,
+		"Nokia",
+		"5300",
+		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+		US_FL_MAX_SECTORS_64 ),
+
+/* Patch submitted by Victor A. Santos <victoraur.santos@gmail.com> */
+UNUSUAL_DEV(  0x0421, 0x05af, 0x0742, 0x0742,
+		"Nokia",
+		"305",
+		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+		US_FL_MAX_SECTORS_64),
+
+/* Patch submitted by Mikhail Zolotaryov <lebon@lebon.org.ua> */
+UNUSUAL_DEV(  0x0421, 0x06aa, 0x1110, 0x1110,
+		"Nokia",
+		"502",
+		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+		US_FL_MAX_SECTORS_64 ),
+
 #ifdef NO_SDDR09
 UNUSUAL_DEV(  0x0436, 0x0005, 0x0100, 0x0100,
 		"Microtech",
@@ -1448,6 +1469,13 @@
 		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
 		US_FL_FIX_CAPACITY ),
 
+/* Reported by Moritz Moeller-Herrmann <moritz-kernel@moeller-herrmann.de> */
+UNUSUAL_DEV(  0x0fca, 0x8004, 0x0201, 0x0201,
+		"Research In Motion",
+		"BlackBerry Bold 9000",
+		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+		US_FL_MAX_SECTORS_64 ),
+
 /* Reported by Michael Stattmann <michael@stattmann.com> */
 UNUSUAL_DEV(  0x0fce, 0xd008, 0x0000, 0x0000,
 		"Sony Ericsson",
diff -ruw linux-3.11.10/drivers/usb/storage/usb.c linux-3.11.10-fbx/drivers/usb/storage/usb.c
--- linux-3.11.10/drivers/usb/storage/usb.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/usb/storage/usb.c	2015-09-29 11:15:07.801747367 +0200
@@ -78,7 +78,7 @@
 MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
 MODULE_LICENSE("GPL");
 
-static unsigned int delay_use = 1;
+static unsigned int delay_use = 5;
 module_param(delay_use, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
 
diff -ruw linux-3.11.10/drivers/video/Kconfig linux-3.11.10-fbx/drivers/video/Kconfig
--- linux-3.11.10/drivers/video/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/video/Kconfig	2015-09-29 11:15:07.809747429 +0200
@@ -477,6 +477,17 @@
 	  this driver, say Y or M; otherwise say N. You must specify the
 	  GPIO IO address to be used for setting control and data.
 
+config FB_SSD1327
+	tristate "SSD1327 OLED driver"
+	depends on FB
+	select FB_SYS_FILLRECT
+	select FB_SYS_COPYAREA
+	select FB_SYS_IMAGEBLIT
+	select FB_SYS_FOPS
+	select FB_BACKLIGHT
+	select SPI
+	default n
+
 config FB_ATARI
 	bool "Atari native chipset support"
 	depends on (FB = y) && ATARI
diff -ruw linux-3.11.10/drivers/video/Makefile linux-3.11.10-fbx/drivers/video/Makefile
--- linux-3.11.10/drivers/video/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/drivers/video/Makefile	2015-09-29 11:15:07.809747429 +0200
@@ -34,6 +34,7 @@
 # Hardware specific drivers go first
 obj-$(CONFIG_FB_AMIGA)            += amifb.o c2p_planar.o
 obj-$(CONFIG_FB_ARC)              += arcfb.o
+obj-$(CONFIG_FB_SSD1327)          += ssd1327.o
 obj-$(CONFIG_FB_CLPS711X)         += clps711xfb.o
 obj-$(CONFIG_FB_CYBER2000)        += cyber2000fb.o
 obj-$(CONFIG_FB_GRVGA)            += grvga.o
diff -ruw linux-3.11.10/firmware/Makefile linux-3.11.10-fbx/firmware/Makefile
--- linux-3.11.10/firmware/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/firmware/Makefile	2015-09-29 11:15:08.329751388 +0200
@@ -65,6 +65,11 @@
 fw-shipped-$(CONFIG_DVB_TTUSB_BUDGET) += ttusb-budget/dspbootcode.bin
 fw-shipped-$(CONFIG_E100) += e100/d101m_ucode.bin e100/d101s_ucode.bin \
 			     e100/d102e_ucode.bin
+fw-shipped-$(CONFIG_MWL8K) += mwl8k/helper_8366.fw mwl8k/fmimage_8764_ap-1.fw \
+				mwl8k/fmimage_8366_ap-3.fw
+
+fw-shipped-$(CONFIG_MWL8K_MFG) += mwl8k/mfg_fmimage_8764.fw mwl8k/mfg_fmimage_8366.fw
+
 fw-shipped-$(CONFIG_MYRI_SBUS) += myricom/lanai.bin
 fw-shipped-$(CONFIG_PCMCIA_PCNET) += cis/LA-PCM.cis cis/PCMLM28.cis \
 				     cis/DP83903.cis cis/NE2K.cis \
@@ -249,3 +254,10 @@
 obj-n := dummy
 
 hostprogs-y := ihex2fw
+
+# hack for 'make mrproper' and mwl8k .fw files being removed.
+quiet_cmd_shipped = SHIPPED $@
+cmd_shipped = cat $< > $@
+
+%.fw: %.fw_shipped
+	$(call cmd,shipped)
diff -ruw linux-3.11.10/fs/aio.c linux-3.11.10-fbx/fs/aio.c
--- linux-3.11.10/fs/aio.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/aio.c	2015-09-29 11:15:08.337751451 +0200
@@ -312,7 +312,6 @@
 
 		avail = (head <= ctx->tail ? ctx->tail : ctx->nr_events) - head;
 
-		atomic_sub(avail, &ctx->reqs_active);
 		head += avail;
 		head %= ctx->nr_events;
 	}
@@ -425,10 +424,12 @@
  *	when the processes owning a context have all exited to encourage
  *	the rapid destruction of the kioctx.
  */
-static void kill_ioctx(struct kioctx *ctx)
+static void kill_ioctx(struct mm_struct *mm, struct kioctx *ctx)
 {
 	if (!atomic_xchg(&ctx->dead, 1)) {
+		spin_lock(&mm->ioctx_lock);
 		hlist_del_rcu(&ctx->list);
+		spin_unlock(&mm->ioctx_lock);
 
 		/*
 		 * It'd be more correct to do this in free_ioctx(), after all
@@ -496,7 +497,7 @@
 		 */
 		ctx->mmap_size = 0;
 
-		kill_ioctx(ctx);
+		kill_ioctx(mm, ctx);
 	}
 }
 
@@ -678,6 +679,7 @@
 put_rq:
 	/* everything turned out well, dispose of the aiocb. */
 	aio_put_req(iocb);
+	atomic_dec(&ctx->reqs_active);
 
 	/*
 	 * We have to order our ring_info tail store above and test
@@ -717,6 +719,8 @@
 	if (head == ctx->tail)
 		goto out;
 
+	head %= ctx->nr_events;
+
 	while (ret < nr) {
 		long avail;
 		struct io_event *ev;
@@ -755,8 +759,6 @@
 	flush_dcache_page(ctx->ring_pages[0]);
 
 	pr_debug("%li  h%u t%u\n", ret, head, ctx->tail);
-
-	atomic_sub(ret, &ctx->reqs_active);
 out:
 	mutex_unlock(&ctx->ring_lock);
 
@@ -854,7 +856,7 @@
 	if (!IS_ERR(ioctx)) {
 		ret = put_user(ioctx->user_id, ctxp);
 		if (ret)
-			kill_ioctx(ioctx);
+			kill_ioctx(current->mm, ioctx);
 		put_ioctx(ioctx);
 	}
 
@@ -872,7 +874,7 @@
 {
 	struct kioctx *ioctx = lookup_ioctx(ctx);
 	if (likely(NULL != ioctx)) {
-		kill_ioctx(ioctx);
+		kill_ioctx(current->mm, ioctx);
 		put_ioctx(ioctx);
 		return 0;
 	}
diff -ruw linux-3.11.10/fs/attr.c linux-3.11.10-fbx/fs/attr.c
--- linux-3.11.10/fs/attr.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/attr.c	2015-09-29 11:15:08.337751451 +0200
@@ -50,14 +50,14 @@
 	if ((ia_valid & ATTR_UID) &&
 	    (!uid_eq(current_fsuid(), inode->i_uid) ||
 	     !uid_eq(attr->ia_uid, inode->i_uid)) &&
-	    !inode_capable(inode, CAP_CHOWN))
+	    !capable_wrt_inode_uidgid(inode, CAP_CHOWN))
 		return -EPERM;
 
 	/* Make sure caller can chgrp. */
 	if ((ia_valid & ATTR_GID) &&
 	    (!uid_eq(current_fsuid(), inode->i_uid) ||
 	    (!in_group_p(attr->ia_gid) && !gid_eq(attr->ia_gid, inode->i_gid))) &&
-	    !inode_capable(inode, CAP_CHOWN))
+	    !capable_wrt_inode_uidgid(inode, CAP_CHOWN))
 		return -EPERM;
 
 	/* Make sure a caller can chmod. */
@@ -67,7 +67,7 @@
 		/* Also check the setgid bit! */
 		if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
 				inode->i_gid) &&
-		    !inode_capable(inode, CAP_FSETID))
+		    !capable_wrt_inode_uidgid(inode, CAP_FSETID))
 			attr->ia_mode &= ~S_ISGID;
 	}
 
@@ -160,7 +160,7 @@
 		umode_t mode = attr->ia_mode;
 
 		if (!in_group_p(inode->i_gid) &&
-		    !inode_capable(inode, CAP_FSETID))
+		    !capable_wrt_inode_uidgid(inode, CAP_FSETID))
 			mode &= ~S_ISGID;
 		inode->i_mode = mode;
 	}
@@ -182,11 +182,6 @@
 			return -EPERM;
 	}
 
-	if ((ia_valid & ATTR_SIZE) && IS_I_VERSION(inode)) {
-		if (attr->ia_size != inode->i_size)
-			inode_inc_iversion(inode);
-	}
-
 	if ((ia_valid & ATTR_MODE)) {
 		umode_t amode = attr->ia_mode;
 		/* Flag setting protected by i_mutex */
diff -ruw linux-3.11.10/fs/buffer.c linux-3.11.10-fbx/fs/buffer.c
--- linux-3.11.10/fs/buffer.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/buffer.c	2015-09-29 11:15:08.365751662 +0200
@@ -654,14 +654,16 @@
 static void __set_page_dirty(struct page *page,
 		struct address_space *mapping, int warn)
 {
-	spin_lock_irq(&mapping->tree_lock);
+	unsigned long flags;
+
+	spin_lock_irqsave(&mapping->tree_lock, flags);
 	if (page->mapping) {	/* Race with truncate? */
 		WARN_ON_ONCE(warn && !PageUptodate(page));
 		account_page_dirtied(page, mapping);
 		radix_tree_tag_set(&mapping->page_tree,
 				page_index(page), PAGECACHE_TAG_DIRTY);
 	}
-	spin_unlock_irq(&mapping->tree_lock);
+	spin_unlock_irqrestore(&mapping->tree_lock, flags);
 	__mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
 }
 
diff -ruw linux-3.11.10/fs/coredump.c linux-3.11.10-fbx/fs/coredump.c
--- linux-3.11.10/fs/coredump.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/coredump.c	2015-09-29 11:15:08.385751816 +0200
@@ -74,10 +74,15 @@
 static int cn_vprintf(struct core_name *cn, const char *fmt, va_list arg)
 {
 	int free, need;
+	va_list arg_copy;
 
 again:
 	free = cn->size - cn->used;
-	need = vsnprintf(cn->corename + cn->used, free, fmt, arg);
+
+	va_copy(arg_copy, arg);
+	need = vsnprintf(cn->corename + cn->used, free, fmt, arg_copy);
+	va_end(arg_copy);
+
 	if (need < free) {
 		cn->used += need;
 		return 0;
diff -ruw linux-3.11.10/fs/dcache.c linux-3.11.10-fbx/fs/dcache.c
--- linux-3.11.10/fs/dcache.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/dcache.c	2015-09-29 11:15:08.385751816 +0200
@@ -2675,8 +2675,13 @@
 	 * thus don't need to be hashed.  They also don't need a name until a
 	 * user wants to identify the object in /proc/pid/fd/.  The little hack
 	 * below allows us to generate a name for these objects on demand:
+	 *
+	 * Some pseudo inodes are mountable.  When they are mounted
+	 * path->dentry == path->mnt->mnt_root.  In that case don't call d_dname
+	 * and instead have d_path return the mounted path.
 	 */
-	if (path->dentry->d_op && path->dentry->d_op->d_dname)
+	if (path->dentry->d_op && path->dentry->d_op->d_dname &&
+	    (!IS_ROOT(path->dentry) || path->dentry != path->mnt->mnt_root))
 		return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
 
 	get_fs_root(current->fs, &root);
diff -ruw linux-3.11.10/fs/devpts/inode.c linux-3.11.10-fbx/fs/devpts/inode.c
--- linux-3.11.10/fs/devpts/inode.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/devpts/inode.c	2015-09-29 11:15:08.389751845 +0200
@@ -498,6 +498,7 @@
 {
 	struct pts_fs_info *fsi = DEVPTS_SB(sb);
 
+	ida_destroy(&fsi->allocated_ptys);
 	kfree(fsi);
 	kill_litter_super(sb);
 }
diff -ruw linux-3.11.10/fs/exec.c linux-3.11.10-fbx/fs/exec.c
--- linux-3.11.10/fs/exec.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/exec.c	2015-09-29 11:15:08.397751907 +0200
@@ -655,10 +655,10 @@
 	unsigned long rlim_stack;
 
 #ifdef CONFIG_STACK_GROWSUP
-	/* Limit stack size to 1GB */
+	/* Limit stack size */
 	stack_base = rlimit_max(RLIMIT_STACK);
-	if (stack_base > (1 << 30))
-		stack_base = 1 << 30;
+	if (stack_base > STACK_SIZE_MAX)
+		stack_base = STACK_SIZE_MAX;
 
 	/* Make sure we didn't let the argument array grow too large. */
 	if (vma->vm_end - vma->vm_start > stack_base)
@@ -1467,6 +1467,23 @@
 	int retval;
 
 	/*
+	 * handle current->exec_mode:
+	 * - if unlimited, then nothing to do.
+	 * - if once, then set it to denied and continue (next execve
+	 *   after this one will fail).
+	 * - if denied, then effectively fail the execve call with EPERM.
+	 */
+	switch (current->exec_mode) {
+	case EXEC_MODE_UNLIMITED:
+		break;
+	case EXEC_MODE_ONCE:
+		current->exec_mode = EXEC_MODE_DENIED;
+		break;
+	case EXEC_MODE_DENIED:
+		return -EPERM;
+	}
+
+	/*
 	 * We move the actual failure in case of RLIMIT_NPROC excess from
 	 * set*uid() to execve() because too many poorly written programs
 	 * don't check setuid() return code.  Here we additionally recheck
diff -ruw linux-3.11.10/fs/ext2/super.c linux-3.11.10-fbx/fs/ext2/super.c
--- linux-3.11.10/fs/ext2/super.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext2/super.c	2015-09-29 11:15:08.401751936 +0200
@@ -1493,6 +1493,7 @@
 				sb->s_blocksize - offset : towrite;
 
 		tmp_bh.b_state = 0;
+		tmp_bh.b_size = sb->s_blocksize;
 		err = ext2_get_block(inode, blk, &tmp_bh, 1);
 		if (err < 0)
 			goto out;
diff -ruw linux-3.11.10/fs/ext4/ext4.h linux-3.11.10-fbx/fs/ext4/ext4.h
--- linux-3.11.10/fs/ext4/ext4.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/ext4.h	2015-09-29 11:15:08.409751999 +0200
@@ -270,6 +270,16 @@
 /* Translate # of blks to # of clusters */
 #define EXT4_NUM_B2C(sbi, blks)	(((blks) + (sbi)->s_cluster_ratio - 1) >> \
 				 (sbi)->s_cluster_bits)
+/* Mask out the low bits to get the starting block of the cluster */
+#define EXT4_PBLK_CMASK(s, pblk) ((pblk) &				\
+				  ~((ext4_fsblk_t) (s)->s_cluster_ratio - 1))
+#define EXT4_LBLK_CMASK(s, lblk) ((lblk) &				\
+				  ~((ext4_lblk_t) (s)->s_cluster_ratio - 1))
+/* Get the cluster offset */
+#define EXT4_PBLK_COFF(s, pblk) ((pblk) &				\
+				 ((ext4_fsblk_t) (s)->s_cluster_ratio - 1))
+#define EXT4_LBLK_COFF(s, lblk) ((lblk) &				\
+				 ((ext4_lblk_t) (s)->s_cluster_ratio - 1))
 
 /*
  * Structure of a blocks group descriptor
@@ -749,6 +759,8 @@
 	if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime))		       \
 		(einode)->xtime.tv_sec = 				       \
 			(signed)le32_to_cpu((raw_inode)->xtime);	       \
+	else								       \
+		(einode)->xtime.tv_sec = 0;				       \
 	if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra))	       \
 		ext4_decode_extra_time(&(einode)->xtime,		       \
 				       raw_inode->xtime ## _extra);	       \
@@ -2721,7 +2733,8 @@
 extern int ext4_bio_write_page(struct ext4_io_submit *io,
 			       struct page *page,
 			       int len,
-			       struct writeback_control *wbc);
+			       struct writeback_control *wbc,
+			       bool keep_towrite);
 
 /* mmp.c */
 extern int ext4_multi_mount_protect(struct super_block *, ext4_fsblk_t);
diff -ruw linux-3.11.10/fs/ext4/ext4_jbd2.c linux-3.11.10-fbx/fs/ext4/ext4_jbd2.c
--- linux-3.11.10/fs/ext4/ext4_jbd2.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/ext4_jbd2.c	2015-09-29 11:15:08.409751999 +0200
@@ -259,6 +259,15 @@
 		if (WARN_ON_ONCE(err)) {
 			ext4_journal_abort_handle(where, line, __func__, bh,
 						  handle, err);
+			ext4_error_inode(inode, where, line,
+					 bh->b_blocknr,
+					 "journal_dirty_metadata failed: "
+					 "handle type %u started at line %u, "
+					 "credits %u/%u, errcode %d",
+					 handle->h_type,
+					 handle->h_line_no,
+					 handle->h_requested_credits,
+					 handle->h_buffer_credits, err);
 		}
 	} else {
 		if (inode)
diff -ruw linux-3.11.10/fs/ext4/extents.c linux-3.11.10-fbx/fs/ext4/extents.c
--- linux-3.11.10/fs/ext4/extents.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/extents.c	2015-09-29 11:15:08.409751999 +0200
@@ -360,8 +360,10 @@
 {
 	ext4_fsblk_t block = ext4_ext_pblock(ext);
 	int len = ext4_ext_get_actual_len(ext);
+	ext4_lblk_t lblock = le32_to_cpu(ext->ee_block);
+	ext4_lblk_t last = lblock + len - 1;
 
-	if (len == 0)
+	if (lblock > last)
 		return 0;
 	return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len);
 }
@@ -387,11 +389,26 @@
 	if (depth == 0) {
 		/* leaf entries */
 		struct ext4_extent *ext = EXT_FIRST_EXTENT(eh);
+		struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
+		ext4_fsblk_t pblock = 0;
+		ext4_lblk_t lblock = 0;
+		ext4_lblk_t prev = 0;
+		int len = 0;
 		while (entries) {
 			if (!ext4_valid_extent(inode, ext))
 				return 0;
+
+			/* Check for overlapping extents */
+			lblock = le32_to_cpu(ext->ee_block);
+			len = ext4_ext_get_actual_len(ext);
+			if ((lblock <= prev) && prev) {
+				pblock = ext4_ext_pblock(ext);
+				es->s_last_error_block = cpu_to_le64(pblock);
+				return 0;
+			}
 			ext++;
 			entries--;
+			prev = lblock + len - 1;
 		}
 	} else {
 		struct ext4_extent_idx *ext_idx = EXT_FIRST_INDEX(eh);
@@ -1755,8 +1772,7 @@
 	depth = ext_depth(inode);
 	if (!path[depth].p_ext)
 		goto out;
-	b2 = le32_to_cpu(path[depth].p_ext->ee_block);
-	b2 &= ~(sbi->s_cluster_ratio - 1);
+	b2 = EXT4_LBLK_CMASK(sbi, le32_to_cpu(path[depth].p_ext->ee_block));
 
 	/*
 	 * get the next allocated block if the extent in the path
@@ -1766,7 +1782,7 @@
 		b2 = ext4_ext_next_allocated_block(path);
 		if (b2 == EXT_MAX_BLOCKS)
 			goto out;
-		b2 &= ~(sbi->s_cluster_ratio - 1);
+		b2 = EXT4_LBLK_CMASK(sbi, b2);
 	}
 
 	/* check for wrap through zero on extent logical start block*/
@@ -2446,7 +2462,7 @@
 		 * extent, we have to mark the cluster as used (store negative
 		 * cluster number in partial_cluster).
 		 */
-		unaligned = pblk & (sbi->s_cluster_ratio - 1);
+		unaligned = EXT4_PBLK_COFF(sbi, pblk);
 		if (unaligned && (ee_len == num) &&
 		    (*partial_cluster != -((long long)EXT4_B2C(sbi, pblk))))
 			*partial_cluster = EXT4_B2C(sbi, pblk);
@@ -2511,6 +2527,27 @@
 	ex_ee_block = le32_to_cpu(ex->ee_block);
 	ex_ee_len = ext4_ext_get_actual_len(ex);
 
+	/*
+	 * If we're starting with an extent other than the last one in the
+	 * node, we need to see if it shares a cluster with the extent to
+	 * the right (towards the end of the file). If its leftmost cluster
+	 * is this extent's rightmost cluster and it is not cluster aligned,
+	 * we'll mark it as a partial that is not to be deallocated.
+	 */
+
+	if (ex != EXT_LAST_EXTENT(eh)) {
+		ext4_fsblk_t current_pblk, right_pblk;
+		long long current_cluster, right_cluster;
+
+		current_pblk = ext4_ext_pblock(ex) + ex_ee_len - 1;
+		current_cluster = (long long)EXT4_B2C(sbi, current_pblk);
+		right_pblk = ext4_ext_pblock(ex + 1);
+		right_cluster = (long long)EXT4_B2C(sbi, right_pblk);
+		if (current_cluster == right_cluster &&
+			EXT4_PBLK_COFF(sbi, right_pblk))
+			*partial_cluster = -right_cluster;
+	}
+
 	trace_ext4_ext_rm_leaf(inode, start, ex, *partial_cluster);
 
 	while (ex >= EXT_FIRST_EXTENT(eh) &&
@@ -2540,7 +2577,7 @@
 			 * accidentally freeing it later on
 			 */
 			pblk = ext4_ext_pblock(ex);
-			if (pblk & (sbi->s_cluster_ratio - 1))
+			if (EXT4_PBLK_COFF(sbi, pblk))
 				*partial_cluster =
 					-((long long)EXT4_B2C(sbi, pblk));
 			ex--;
@@ -2636,10 +2673,15 @@
 		err = ext4_ext_correct_indexes(handle, inode, path);
 
 	/*
-	 * Free the partial cluster only if the current extent does not
-	 * reference it. Otherwise we might free used cluster.
+	 * If there's a partial cluster and at least one extent remains in
+	 * the leaf, free the partial cluster if it isn't shared with the
+	 * current extent.  If there's a partial cluster and no extents
+	 * remain in the leaf, it can't be freed here.  It can only be
+	 * freed when it's possible to determine if it's not shared with
+	 * any other extent - when the next leaf is processed or when space
+	 * removal is complete.
 	 */
-	if (*partial_cluster > 0 &&
+	if (*partial_cluster > 0 && eh->eh_entries &&
 	    (EXT4_B2C(sbi, ext4_ext_pblock(ex) + ex_ee_len - 1) !=
 	     *partial_cluster)) {
 		int flags = get_default_free_blocks_flags(inode);
@@ -3680,7 +3722,7 @@
 {
 	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
 	ext4_lblk_t lblk_start, lblk_end;
-	lblk_start = lblk & (~(sbi->s_cluster_ratio - 1));
+	lblk_start = EXT4_LBLK_CMASK(sbi, lblk);
 	lblk_end = lblk_start + sbi->s_cluster_ratio - 1;
 
 	return ext4_find_delalloc_range(inode, lblk_start, lblk_end);
@@ -3739,9 +3781,9 @@
 	trace_ext4_get_reserved_cluster_alloc(inode, lblk_start, num_blks);
 
 	/* Check towards left side */
-	c_offset = lblk_start & (sbi->s_cluster_ratio - 1);
+	c_offset = EXT4_LBLK_COFF(sbi, lblk_start);
 	if (c_offset) {
-		lblk_from = lblk_start & (~(sbi->s_cluster_ratio - 1));
+		lblk_from = EXT4_LBLK_CMASK(sbi, lblk_start);
 		lblk_to = lblk_from + c_offset - 1;
 
 		if (ext4_find_delalloc_range(inode, lblk_from, lblk_to))
@@ -3749,7 +3791,7 @@
 	}
 
 	/* Now check towards right. */
-	c_offset = (lblk_start + num_blks) & (sbi->s_cluster_ratio - 1);
+	c_offset = EXT4_LBLK_COFF(sbi, lblk_start + num_blks);
 	if (allocated_clusters && c_offset) {
 		lblk_from = lblk_start + num_blks;
 		lblk_to = lblk_from + (sbi->s_cluster_ratio - c_offset) - 1;
@@ -3817,6 +3859,7 @@
 		} else
 			err = ret;
 		map->m_flags |= EXT4_MAP_MAPPED;
+		map->m_pblk = newblock;
 		if (allocated > map->m_len)
 			allocated = map->m_len;
 		map->m_len = allocated;
@@ -3957,7 +4000,7 @@
 				     struct ext4_ext_path *path)
 {
 	struct ext4_sb_info *sbi = EXT4_SB(sb);
-	ext4_lblk_t c_offset = map->m_lblk & (sbi->s_cluster_ratio-1);
+	ext4_lblk_t c_offset = EXT4_LBLK_COFF(sbi, map->m_lblk);
 	ext4_lblk_t ex_cluster_start, ex_cluster_end;
 	ext4_lblk_t rr_cluster_start;
 	ext4_lblk_t ee_block = le32_to_cpu(ex->ee_block);
@@ -3975,8 +4018,7 @@
 	    (rr_cluster_start == ex_cluster_start)) {
 		if (rr_cluster_start == ex_cluster_end)
 			ee_start += ee_len - 1;
-		map->m_pblk = (ee_start & ~(sbi->s_cluster_ratio - 1)) +
-			c_offset;
+		map->m_pblk = EXT4_PBLK_CMASK(sbi, ee_start) + c_offset;
 		map->m_len = min(map->m_len,
 				 (unsigned) sbi->s_cluster_ratio - c_offset);
 		/*
@@ -4039,7 +4081,7 @@
 	struct ext4_extent newex, *ex, *ex2;
 	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
 	ext4_fsblk_t newblock = 0;
-	int free_on_err = 0, err = 0, depth;
+	int free_on_err = 0, err = 0, depth, ret;
 	unsigned int allocated = 0, offset = 0;
 	unsigned int allocated_clusters = 0;
 	struct ext4_allocation_request ar;
@@ -4100,9 +4142,13 @@
 			if (!ext4_ext_is_uninitialized(ex))
 				goto out;
 
-			allocated = ext4_ext_handle_uninitialized_extents(
+			ret = ext4_ext_handle_uninitialized_extents(
 				handle, inode, map, path, flags,
 				allocated, newblock);
+			if (ret < 0)
+				err = ret;
+			else
+				allocated = ret;
 			goto out3;
 		}
 	}
@@ -4130,7 +4176,7 @@
 	 */
 	map->m_flags &= ~EXT4_MAP_FROM_CLUSTER;
 	newex.ee_block = cpu_to_le32(map->m_lblk);
-	cluster_offset = map->m_lblk & (sbi->s_cluster_ratio-1);
+	cluster_offset = EXT4_LBLK_COFF(sbi, map->m_lblk);
 
 	/*
 	 * If we are doing bigalloc, check to see if the extent returned
@@ -4198,7 +4244,7 @@
 	 * needed so that future calls to get_implied_cluster_alloc()
 	 * work correctly.
 	 */
-	offset = map->m_lblk & (sbi->s_cluster_ratio - 1);
+	offset = EXT4_LBLK_COFF(sbi, map->m_lblk);
 	ar.len = EXT4_NUM_B2C(sbi, offset+allocated);
 	ar.goal -= offset;
 	ar.logical -= offset;
diff -ruw linux-3.11.10/fs/ext4/file.c linux-3.11.10-fbx/fs/ext4/file.c
--- linux-3.11.10/fs/ext4/file.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/file.c	2015-09-29 11:15:08.413752028 +0200
@@ -82,7 +82,7 @@
 	size_t count = iov_length(iov, nr_segs);
 	loff_t final_size = pos + count;
 
-	if (pos >= inode->i_size)
+	if (pos >= i_size_read(inode))
 		return 0;
 
 	if ((pos & blockmask) || (final_size & blockmask))
diff -ruw linux-3.11.10/fs/ext4/ialloc.c linux-3.11.10-fbx/fs/ext4/ialloc.c
--- linux-3.11.10/fs/ext4/ialloc.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/ialloc.c	2015-09-29 11:15:08.413752028 +0200
@@ -781,6 +781,13 @@
 		goto out;
 	}
 
+	BUFFER_TRACE(group_desc_bh, "get_write_access");
+	err = ext4_journal_get_write_access(handle, group_desc_bh);
+	if (err) {
+		ext4_std_error(sb, err);
+		goto out;
+	}
+
 	/* We may have to initialize the block bitmap if it isn't already */
 	if (ext4_has_group_desc_csum(sb) &&
 	    gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
@@ -817,13 +824,6 @@
 		}
 	}
 
-	BUFFER_TRACE(group_desc_bh, "get_write_access");
-	err = ext4_journal_get_write_access(handle, group_desc_bh);
-	if (err) {
-		ext4_std_error(sb, err);
-		goto out;
-	}
-
 	/* Update the relevant bg descriptor fields */
 	if (ext4_has_group_desc_csum(sb)) {
 		int free;
diff -ruw linux-3.11.10/fs/ext4/indirect.c linux-3.11.10-fbx/fs/ext4/indirect.c
--- linux-3.11.10/fs/ext4/indirect.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/indirect.c	2015-09-29 11:15:08.413752028 +0200
@@ -390,7 +390,13 @@
 	return 0;
 failed:
 	for (; i >= 0; i--) {
-		if (i != indirect_blks && branch[i].bh)
+		/*
+		 * We want to ext4_forget() only freshly allocated indirect
+		 * blocks.  Buffer for new_blocks[i-1] is at branch[i].bh and
+		 * buffer at branch[0].bh is indirect block / inode already
+		 * existing before ext4_alloc_branch() was called.
+		 */
+		if (i > 0 && i != indirect_blks && branch[i].bh)
 			ext4_forget(handle, 1, inode, branch[i].bh,
 				    branch[i].bh->b_blocknr);
 		ext4_free_blocks(handle, inode, NULL, new_blocks[i],
@@ -1313,16 +1319,24 @@
 		blk = *i_data;
 		if (level > 0) {
 			ext4_lblk_t first2;
+			ext4_lblk_t count2;
+
 			bh = sb_bread(inode->i_sb, le32_to_cpu(blk));
 			if (!bh) {
 				EXT4_ERROR_INODE_BLOCK(inode, le32_to_cpu(blk),
 						       "Read failure");
 				return -EIO;
 			}
-			first2 = (first > offset) ? first - offset : 0;
+			if (first > offset) {
+				first2 = first - offset;
+				count2 = count;
+			} else {
+				first2 = 0;
+				count2 = count - (offset - first);
+			}
 			ret = free_hole_blocks(handle, inode, bh,
 					       (__le32 *)bh->b_data, level - 1,
-					       first2, count - offset,
+					       first2, count2,
 					       inode->i_sb->s_blocksize >> 2);
 			if (ret) {
 				brelse(bh);
diff -ruw linux-3.11.10/fs/ext4/inline.c linux-3.11.10-fbx/fs/ext4/inline.c
--- linux-3.11.10/fs/ext4/inline.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/inline.c	2015-09-29 11:15:08.413752028 +0200
@@ -1925,9 +1925,11 @@
 		}
 
 		/* Clear the content within i_blocks. */
-		if (i_size < EXT4_MIN_INLINE_DATA_SIZE)
-			memset(ext4_raw_inode(&is.iloc)->i_block + i_size, 0,
+		if (i_size < EXT4_MIN_INLINE_DATA_SIZE) {
+			void *p = (void *) ext4_raw_inode(&is.iloc)->i_block;
+			memset(p + i_size, 0,
 					EXT4_MIN_INLINE_DATA_SIZE - i_size);
+		}
 
 		EXT4_I(inode)->i_inline_size = i_size <
 					EXT4_MIN_INLINE_DATA_SIZE ?
diff -ruw linux-3.11.10/fs/ext4/inode.c linux-3.11.10-fbx/fs/ext4/inode.c
--- linux-3.11.10/fs/ext4/inode.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/inode.c	2015-09-29 11:15:08.413752028 +0200
@@ -38,6 +38,7 @@
 #include <linux/slab.h>
 #include <linux/ratelimit.h>
 #include <linux/aio.h>
+#include <linux/bitops.h>
 
 #include "ext4_jbd2.h"
 #include "xattr.h"
@@ -1201,7 +1202,6 @@
  */
 static int ext4_da_reserve_metadata(struct inode *inode, ext4_lblk_t lblock)
 {
-	int retries = 0;
 	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
 	struct ext4_inode_info *ei = EXT4_I(inode);
 	unsigned int md_needed;
@@ -1213,7 +1213,6 @@
 	 * in order to allocate nrblocks
 	 * worse case is one extent per block
 	 */
-repeat:
 	spin_lock(&ei->i_block_reservation_lock);
 	/*
 	 * ext4_calc_metadata_amount() has side effects, which we have
@@ -1233,10 +1232,6 @@
 		ei->i_da_metadata_calc_len = save_len;
 		ei->i_da_metadata_calc_last_lblock = save_last_lblock;
 		spin_unlock(&ei->i_block_reservation_lock);
-		if (ext4_should_retry_alloc(inode->i_sb, &retries)) {
-			cond_resched();
-			goto repeat;
-		}
 		return -ENOSPC;
 	}
 	ei->i_reserved_meta_blocks += md_needed;
@@ -1250,7 +1245,6 @@
  */
 static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock)
 {
-	int retries = 0;
 	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
 	struct ext4_inode_info *ei = EXT4_I(inode);
 	unsigned int md_needed;
@@ -1272,7 +1266,6 @@
 	 * in order to allocate nrblocks
 	 * worse case is one extent per block
 	 */
-repeat:
 	spin_lock(&ei->i_block_reservation_lock);
 	/*
 	 * ext4_calc_metadata_amount() has side effects, which we have
@@ -1292,10 +1285,6 @@
 		ei->i_da_metadata_calc_len = save_len;
 		ei->i_da_metadata_calc_last_lblock = save_last_lblock;
 		spin_unlock(&ei->i_block_reservation_lock);
-		if (ext4_should_retry_alloc(inode->i_sb, &retries)) {
-			cond_resched();
-			goto repeat;
-		}
 		dquot_release_reservation_block(inode, EXT4_C2B(sbi, 1));
 		return -ENOSPC;
 	}
@@ -1837,6 +1826,7 @@
 	struct buffer_head *page_bufs = NULL;
 	struct inode *inode = page->mapping->host;
 	struct ext4_io_submit io_submit;
+	bool keep_towrite = false;
 
 	trace_ext4_writepage(page);
 	size = i_size_read(inode);
@@ -1867,6 +1857,7 @@
 			unlock_page(page);
 			return 0;
 		}
+		keep_towrite = true;
 	}
 
 	if (PageChecked(page) && ext4_should_journal_data(inode))
@@ -1883,7 +1874,7 @@
 		unlock_page(page);
 		return -ENOMEM;
 	}
-	ret = ext4_bio_write_page(&io_submit, page, len, wbc);
+	ret = ext4_bio_write_page(&io_submit, page, len, wbc, keep_towrite);
 	ext4_io_submit(&io_submit);
 	/* Drop io_end reference we got from init */
 	ext4_put_io_end_defer(io_submit.io_end);
@@ -1902,7 +1893,7 @@
 	else
 		len = PAGE_CACHE_SIZE;
 	clear_page_dirty_for_io(page);
-	err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc);
+	err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc, false);
 	if (!err)
 		mpd->wbc->nr_to_write--;
 	mpd->first_page++;
@@ -3949,18 +3940,20 @@
 void ext4_set_inode_flags(struct inode *inode)
 {
 	unsigned int flags = EXT4_I(inode)->i_flags;
+	unsigned int new_fl = 0;
 
-	inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
 	if (flags & EXT4_SYNC_FL)
-		inode->i_flags |= S_SYNC;
+		new_fl |= S_SYNC;
 	if (flags & EXT4_APPEND_FL)
-		inode->i_flags |= S_APPEND;
+		new_fl |= S_APPEND;
 	if (flags & EXT4_IMMUTABLE_FL)
-		inode->i_flags |= S_IMMUTABLE;
+		new_fl |= S_IMMUTABLE;
 	if (flags & EXT4_NOATIME_FL)
-		inode->i_flags |= S_NOATIME;
+		new_fl |= S_NOATIME;
 	if (flags & EXT4_DIRSYNC_FL)
-		inode->i_flags |= S_DIRSYNC;
+		new_fl |= S_DIRSYNC;
+	set_mask_bits(&inode->i_flags,
+		      S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC, new_fl);
 }
 
 /* Propagate flags from i_flags to EXT4_I(inode)->i_flags */
@@ -4610,6 +4603,10 @@
 			if (attr->ia_size > sbi->s_bitmap_maxbytes)
 				return -EFBIG;
 		}
+
+		if (IS_I_VERSION(inode) && attr->ia_size != inode->i_size)
+			inode_inc_iversion(inode);
+
 		if (S_ISREG(inode->i_mode) &&
 		    (attr->ia_size < inode->i_size)) {
 			if (ext4_should_order_data(inode)) {
diff -ruw linux-3.11.10/fs/ext4/ioctl.c linux-3.11.10-fbx/fs/ext4/ioctl.c
--- linux-3.11.10/fs/ext4/ioctl.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/ioctl.c	2015-09-29 11:15:08.413752028 +0200
@@ -145,7 +145,7 @@
 	handle = ext4_journal_start(inode_bl, EXT4_HT_MOVE_EXTENTS, 2);
 	if (IS_ERR(handle)) {
 		err = -EINVAL;
-		goto swap_boot_out;
+		goto journal_err_out;
 	}
 
 	/* Protect extent tree against block allocations via delalloc */
@@ -203,6 +203,7 @@
 
 	ext4_double_up_write_data_sem(inode, inode_bl);
 
+journal_err_out:
 	ext4_inode_resume_unlocked_dio(inode);
 	ext4_inode_resume_unlocked_dio(inode_bl);
 
diff -ruw linux-3.11.10/fs/ext4/mballoc.c linux-3.11.10-fbx/fs/ext4/mballoc.c
--- linux-3.11.10/fs/ext4/mballoc.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/mballoc.c	2015-09-29 11:15:08.413752028 +0200
@@ -3117,7 +3117,7 @@
 	}
 	BUG_ON(start + size <= ac->ac_o_ex.fe_logical &&
 			start > ac->ac_o_ex.fe_logical);
-	BUG_ON(size <= 0 || size > EXT4_CLUSTERS_PER_GROUP(ac->ac_sb));
+	BUG_ON(size <= 0 || size > EXT4_BLOCKS_PER_GROUP(ac->ac_sb));
 
 	/* now prepare goal request */
 
@@ -3424,6 +3424,9 @@
 {
 	struct ext4_prealloc_space *pa;
 	pa = container_of(head, struct ext4_prealloc_space, u.pa_rcu);
+
+	BUG_ON(atomic_read(&pa->pa_count));
+	BUG_ON(pa->pa_deleted == 0);
 	kmem_cache_free(ext4_pspace_cachep, pa);
 }
 
@@ -3437,11 +3440,13 @@
 	ext4_group_t grp;
 	ext4_fsblk_t grp_blk;
 
-	if (!atomic_dec_and_test(&pa->pa_count) || pa->pa_free != 0)
-		return;
-
 	/* in this short window concurrent discard can set pa_deleted */
 	spin_lock(&pa->pa_lock);
+	if (!atomic_dec_and_test(&pa->pa_count) || pa->pa_free != 0) {
+		spin_unlock(&pa->pa_lock);
+		return;
+	}
+
 	if (pa->pa_deleted == 1) {
 		spin_unlock(&pa->pa_lock);
 		return;
@@ -4103,7 +4108,7 @@
 	ext4_get_group_no_and_offset(sb, goal, &group, &block);
 
 	/* set up allocation goals */
-	ac->ac_b_ex.fe_logical = ar->logical & ~(sbi->s_cluster_ratio - 1);
+	ac->ac_b_ex.fe_logical = EXT4_LBLK_CMASK(sbi, ar->logical);
 	ac->ac_status = AC_STATUS_CONTINUE;
 	ac->ac_sb = sb;
 	ac->ac_inode = ar->inode;
@@ -4644,7 +4649,7 @@
 	 * blocks at the beginning or the end unless we are explicitly
 	 * requested to avoid doing so.
 	 */
-	overflow = block & (sbi->s_cluster_ratio - 1);
+	overflow = EXT4_PBLK_COFF(sbi, block);
 	if (overflow) {
 		if (flags & EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER) {
 			overflow = sbi->s_cluster_ratio - overflow;
@@ -4658,7 +4663,7 @@
 			count += overflow;
 		}
 	}
-	overflow = count & (sbi->s_cluster_ratio - 1);
+	overflow = EXT4_LBLK_COFF(sbi, count);
 	if (overflow) {
 		if (flags & EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER) {
 			if (count > overflow)
@@ -4771,8 +4776,8 @@
 					 " group:%d block:%d count:%lu failed"
 					 " with %d", block_group, bit, count,
 					 err);
-		}
-
+		} else
+			EXT4_MB_GRP_CLEAR_TRIMMED(e4b.bd_info);
 
 		ext4_lock_group(sb, block_group);
 		mb_clear_bits(bitmap_bh->b_data, bit, count_clusters);
diff -ruw linux-3.11.10/fs/ext4/page-io.c linux-3.11.10-fbx/fs/ext4/page-io.c
--- linux-3.11.10/fs/ext4/page-io.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/page-io.c	2015-09-29 11:15:08.417752065 +0200
@@ -323,13 +323,14 @@
 	if (error) {
 		struct inode *inode = io_end->inode;
 
-		ext4_warning(inode->i_sb, "I/O error writing to inode %lu "
+		ext4_warning(inode->i_sb, "I/O error %d writing to inode %lu "
 			     "(offset %llu size %ld starting block %llu)",
-			     inode->i_ino,
+			     error, inode->i_ino,
 			     (unsigned long long) io_end->offset,
 			     (long) io_end->size,
 			     (unsigned long long)
 			     bi_sector >> (inode->i_blkbits - 9));
+		mapping_set_error(inode->i_mapping, error);
 	}
 
 	if (io_end->flag & EXT4_IO_END_UNWRITTEN) {
@@ -415,7 +416,8 @@
 int ext4_bio_write_page(struct ext4_io_submit *io,
 			struct page *page,
 			int len,
-			struct writeback_control *wbc)
+			struct writeback_control *wbc,
+			bool keep_towrite)
 {
 	struct inode *inode = page->mapping->host;
 	unsigned block_start, blocksize;
@@ -428,10 +430,24 @@
 	BUG_ON(!PageLocked(page));
 	BUG_ON(PageWriteback(page));
 
+	if (keep_towrite)
+		set_page_writeback_keepwrite(page);
+	else
 	set_page_writeback(page);
 	ClearPageError(page);
 
 	/*
+	 * Comments copied from block_write_full_page_endio:
+	 *
+	 * The page straddles i_size.  It must be zeroed out on each and every
+	 * writepage invocation because it may be mmapped.  "A file is mapped
+	 * in multiples of the page size.  For a file that is not a multiple of
+	 * the page size, the remaining memory is zeroed when mapped, and
+	 * writes to that region are not written out to the file."
+	 */
+	if (len < PAGE_CACHE_SIZE)
+		zero_user_segment(page, len, PAGE_CACHE_SIZE);
+	/*
 	 * In the first loop we prepare and mark buffers to submit. We have to
 	 * mark all buffers in the page before submitting so that
 	 * end_page_writeback() cannot be called from ext4_bio_end_io() when IO
@@ -442,19 +458,6 @@
 	do {
 		block_start = bh_offset(bh);
 		if (block_start >= len) {
-			/*
-			 * Comments copied from block_write_full_page_endio:
-			 *
-			 * The page straddles i_size.  It must be zeroed out on
-			 * each and every writepage invocation because it may
-			 * be mmapped.  "A file is mapped in multiples of the
-			 * page size.  For a file that is not a multiple of
-			 * the  page size, the remaining memory is zeroed when
-			 * mapped, and writes to that region are not written
-			 * out to the file."
-			 */
-			zero_user_segment(page, block_start,
-					  block_start + blocksize);
 			clear_buffer_dirty(bh);
 			set_buffer_uptodate(bh);
 			continue;
diff -ruw linux-3.11.10/fs/ext4/resize.c linux-3.11.10-fbx/fs/ext4/resize.c
--- linux-3.11.10/fs/ext4/resize.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/resize.c	2015-09-29 11:15:08.417752065 +0200
@@ -243,6 +243,7 @@
 	ext4_group_t group;
 	ext4_group_t last_group;
 	unsigned overhead;
+	__u16 uninit_mask = (flexbg_size > 1) ? ~EXT4_BG_BLOCK_UNINIT : ~0;
 
 	BUG_ON(flex_gd->count == 0 || group_data == NULL);
 
@@ -266,7 +267,7 @@
 	src_group++;
 	for (; src_group <= last_group; src_group++) {
 		overhead = ext4_group_overhead_blocks(sb, src_group);
-		if (overhead != 0)
+		if (overhead == 0)
 			last_blk += group_data[src_group - group].blocks_count;
 		else
 			break;
@@ -280,8 +281,7 @@
 		group = ext4_get_group_number(sb, start_blk - 1);
 		group -= group_data[0].group;
 		group_data[group].free_blocks_count--;
-		if (flexbg_size > 1)
-			flex_gd->bg_flags[group] &= ~EXT4_BG_BLOCK_UNINIT;
+		flex_gd->bg_flags[group] &= uninit_mask;
 	}
 
 	/* Allocate inode bitmaps */
@@ -292,22 +292,30 @@
 		group = ext4_get_group_number(sb, start_blk - 1);
 		group -= group_data[0].group;
 		group_data[group].free_blocks_count--;
-		if (flexbg_size > 1)
-			flex_gd->bg_flags[group] &= ~EXT4_BG_BLOCK_UNINIT;
+		flex_gd->bg_flags[group] &= uninit_mask;
 	}
 
 	/* Allocate inode tables */
 	for (; it_index < flex_gd->count; it_index++) {
-		if (start_blk + EXT4_SB(sb)->s_itb_per_group > last_blk)
+		unsigned int itb = EXT4_SB(sb)->s_itb_per_group;
+		ext4_fsblk_t next_group_start;
+
+		if (start_blk + itb > last_blk)
 			goto next_group;
 		group_data[it_index].inode_table = start_blk;
-		group = ext4_get_group_number(sb, start_blk - 1);
+		group = ext4_get_group_number(sb, start_blk);
+		next_group_start = ext4_group_first_block_no(sb, group + 1);
 		group -= group_data[0].group;
-		group_data[group].free_blocks_count -=
-					EXT4_SB(sb)->s_itb_per_group;
-		if (flexbg_size > 1)
-			flex_gd->bg_flags[group] &= ~EXT4_BG_BLOCK_UNINIT;
 
+		if (start_blk + itb > next_group_start) {
+			flex_gd->bg_flags[group + 1] &= uninit_mask;
+			overhead = start_blk + itb - next_group_start;
+			group_data[group + 1].free_blocks_count -= overhead;
+			itb -= overhead;
+		}
+
+		group_data[group].free_blocks_count -= itb;
+		flex_gd->bg_flags[group] &= uninit_mask;
 		start_blk += EXT4_SB(sb)->s_itb_per_group;
 	}
 
@@ -401,7 +409,7 @@
 		start = ext4_group_first_block_no(sb, group);
 		group -= flex_gd->groups[0].group;
 
-		count2 = sb->s_blocksize * 8 - (block - start);
+		count2 = EXT4_BLOCKS_PER_GROUP(sb) - (block - start);
 		if (count2 > count)
 			count2 = count;
 
@@ -620,7 +628,7 @@
 			if (err)
 				goto out;
 			count = group_table_count[j];
-			start = group_data[i].block_bitmap;
+			start = (&group_data[i].block_bitmap)[j];
 			block = start;
 		}
 
diff -ruw linux-3.11.10/fs/ext4/super.c linux-3.11.10-fbx/fs/ext4/super.c
--- linux-3.11.10/fs/ext4/super.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/super.c	2015-09-29 11:15:08.417752065 +0200
@@ -775,7 +775,7 @@
 	}
 
 	ext4_es_unregister_shrinker(sbi);
-	del_timer(&sbi->s_err_report);
+	del_timer_sync(&sbi->s_err_report);
 	ext4_release_system_zone(sb);
 	ext4_mb_release(sb);
 	ext4_ext_release(sb);
@@ -1501,8 +1501,6 @@
 			arg = JBD2_DEFAULT_MAX_COMMIT_AGE;
 		sbi->s_commit_interval = HZ * arg;
 	} else if (token == Opt_max_batch_time) {
-		if (arg == 0)
-			arg = EXT4_DEF_MAX_BATCH_TIME;
 		sbi->s_max_batch_time = arg;
 	} else if (token == Opt_min_batch_time) {
 		sbi->s_min_batch_time = arg;
@@ -2725,10 +2723,11 @@
 	es = sbi->s_es;
 
 	if (es->s_error_count)
-		ext4_msg(sb, KERN_NOTICE, "error count: %u",
+		/* fsck newer than v1.41.13 is needed to clean this condition. */
+		ext4_msg(sb, KERN_NOTICE, "error count since last fsck: %u",
 			 le32_to_cpu(es->s_error_count));
 	if (es->s_first_error_time) {
-		printk(KERN_NOTICE "EXT4-fs (%s): initial error at %u: %.*s:%d",
+		printk(KERN_NOTICE "EXT4-fs (%s): initial error at time %u: %.*s:%d",
 		       sb->s_id, le32_to_cpu(es->s_first_error_time),
 		       (int) sizeof(es->s_first_error_func),
 		       es->s_first_error_func,
@@ -2742,7 +2741,7 @@
 		printk("\n");
 	}
 	if (es->s_last_error_time) {
-		printk(KERN_NOTICE "EXT4-fs (%s): last error at %u: %.*s:%d",
+		printk(KERN_NOTICE "EXT4-fs (%s): last error at time %u: %.*s:%d",
 		       sb->s_id, le32_to_cpu(es->s_last_error_time),
 		       (int) sizeof(es->s_last_error_func),
 		       es->s_last_error_func,
@@ -3251,11 +3250,19 @@
 }
 
 
-static ext4_fsblk_t ext4_calculate_resv_clusters(struct ext4_sb_info *sbi)
+static ext4_fsblk_t ext4_calculate_resv_clusters(struct super_block *sb)
 {
 	ext4_fsblk_t resv_clusters;
 
 	/*
+	 * There's no need to reserve anything when we aren't using extents.
+	 * The space estimates are exact, there are no unwritten extents,
+	 * hole punching doesn't need new metadata... This is needed especially
+	 * to keep ext2/3 backward compatibility.
+	 */
+	if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_EXTENTS))
+		return 0;
+	/*
 	 * By default we reserve 2% or 4096 clusters, whichever is smaller.
 	 * This should cover the situations where we can not afford to run
 	 * out of space like for example punch hole, or converting
@@ -3263,7 +3270,8 @@
 	 * allocation would require 1, or 2 blocks, higher numbers are
 	 * very rare.
 	 */
-	resv_clusters = ext4_blocks_count(sbi->s_es) >> sbi->s_cluster_bits;
+	resv_clusters = ext4_blocks_count(EXT4_SB(sb)->s_es) >>
+			EXT4_SB(sb)->s_cluster_bits;
 
 	do_div(resv_clusters, 50);
 	resv_clusters = min_t(ext4_fsblk_t, resv_clusters, 4096);
@@ -3621,17 +3629,23 @@
 	for (i = 0; i < 4; i++)
 		sbi->s_hash_seed[i] = le32_to_cpu(es->s_hash_seed[i]);
 	sbi->s_def_hash_version = es->s_def_hash_version;
+	if (EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) {
 	i = le32_to_cpu(es->s_flags);
 	if (i & EXT2_FLAGS_UNSIGNED_HASH)
 		sbi->s_hash_unsigned = 3;
 	else if ((i & EXT2_FLAGS_SIGNED_HASH) == 0) {
 #ifdef __CHAR_UNSIGNED__
-		es->s_flags |= cpu_to_le32(EXT2_FLAGS_UNSIGNED_HASH);
+			if (!(sb->s_flags & MS_RDONLY))
+				es->s_flags |=
+					cpu_to_le32(EXT2_FLAGS_UNSIGNED_HASH);
 		sbi->s_hash_unsigned = 3;
 #else
-		es->s_flags |= cpu_to_le32(EXT2_FLAGS_SIGNED_HASH);
+			if (!(sb->s_flags & MS_RDONLY))
+				es->s_flags |=
+					cpu_to_le32(EXT2_FLAGS_SIGNED_HASH);
 #endif
 	}
+	}
 
 	/* Handle clustersize */
 	clustersize = BLOCK_SIZE << le32_to_cpu(es->s_log_cluster_size);
@@ -4014,10 +4028,10 @@
 			 "available");
 	}
 
-	err = ext4_reserve_clusters(sbi, ext4_calculate_resv_clusters(sbi));
+	err = ext4_reserve_clusters(sbi, ext4_calculate_resv_clusters(sb));
 	if (err) {
 		ext4_msg(sb, KERN_ERR, "failed to reserve %llu clusters for "
-			 "reserved pool", ext4_calculate_resv_clusters(sbi));
+			 "reserved pool", ext4_calculate_resv_clusters(sb));
 		goto failed_mount4a;
 	}
 
@@ -4124,7 +4138,7 @@
 	}
 failed_mount3:
 	ext4_es_unregister_shrinker(sbi);
-	del_timer(&sbi->s_err_report);
+	del_timer_sync(&sbi->s_err_report);
 	if (sbi->s_flex_groups)
 		ext4_kvfree(sbi->s_flex_groups);
 	percpu_counter_destroy(&sbi->s_freeclusters_counter);
diff -ruw linux-3.11.10/fs/ext4/xattr.c linux-3.11.10-fbx/fs/ext4/xattr.c
--- linux-3.11.10/fs/ext4/xattr.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ext4/xattr.c	2015-09-29 11:15:08.417752065 +0200
@@ -517,8 +517,8 @@
 }
 
 /*
- * Release the xattr block BH: If the reference count is > 1, decrement
- * it; otherwise free the block.
+ * Release the xattr block BH: If the reference count is > 1, decrement it;
+ * otherwise free the block.
  */
 static void
 ext4_xattr_release_block(handle_t *handle, struct inode *inode,
@@ -538,16 +538,31 @@
 		if (ce)
 			mb_cache_entry_free(ce);
 		get_bh(bh);
+		unlock_buffer(bh);
 		ext4_free_blocks(handle, inode, bh, 0, 1,
 				 EXT4_FREE_BLOCKS_METADATA |
 				 EXT4_FREE_BLOCKS_FORGET);
-		unlock_buffer(bh);
 	} else {
 		le32_add_cpu(&BHDR(bh)->h_refcount, -1);
 		if (ce)
 			mb_cache_entry_release(ce);
+		/*
+		 * Beware of this ugliness: Releasing of xattr block references
+		 * from different inodes can race and so we have to protect
+		 * from a race where someone else frees the block (and releases
+		 * its journal_head) before we are done dirtying the buffer. In
+		 * nojournal mode this race is harmless and we actually cannot
+		 * call ext4_handle_dirty_xattr_block() with locked buffer as
+		 * that function can call sync_dirty_buffer() so for that case
+		 * we handle the dirtying after unlocking the buffer.
+		 */
+		if (ext4_handle_valid(handle))
+			error = ext4_handle_dirty_xattr_block(handle, inode,
+							      bh);
 		unlock_buffer(bh);
-		error = ext4_handle_dirty_xattr_block(handle, inode, bh);
+		if (!ext4_handle_valid(handle))
+			error = ext4_handle_dirty_xattr_block(handle, inode,
+							      bh);
 		if (IS_SYNC(inode))
 			ext4_handle_sync(handle);
 		dquot_free_block(inode, EXT4_C2B(EXT4_SB(inode->i_sb), 1));
@@ -1352,6 +1367,7 @@
 					new_extra_isize = s_min_extra_isize;
 					kfree(is); is = NULL;
 					kfree(bs); bs = NULL;
+					brelse(bh);
 					goto retry;
 				}
 				error = -1;
diff -ruw linux-3.11.10/fs/file.c linux-3.11.10-fbx/fs/file.c
--- linux-3.11.10/fs/file.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/file.c	2015-09-29 11:15:08.425752119 +0200
@@ -34,7 +34,7 @@
 	 * vmalloc() if the allocation size will be considered "large" by the VM.
 	 */
 	if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) {
-		void *data = kmalloc(size, GFP_KERNEL|__GFP_NOWARN);
+		void *data = kmalloc(size, GFP_KERNEL|__GFP_NOWARN|__GFP_NORETRY);
 		if (data != NULL)
 			return data;
 	}
diff -ruw linux-3.11.10/fs/file_table.c linux-3.11.10-fbx/fs/file_table.c
--- linux-3.11.10/fs/file_table.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/file_table.c	2015-09-29 11:15:08.425752119 +0200
@@ -211,10 +211,10 @@
 	struct dentry *dentry = file->f_path.dentry;
 	struct inode *inode = dentry->d_inode;
 
-	put_write_access(inode);
-
 	if (special_file(inode->i_mode))
 		return;
+
+	put_write_access(inode);
 	if (file_check_writeable(file) != 0)
 		return;
 	__mnt_drop_write(mnt);
diff -ruw linux-3.11.10/fs/fs-writeback.c linux-3.11.10-fbx/fs/fs-writeback.c
--- linux-3.11.10/fs/fs-writeback.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/fs-writeback.c	2015-09-29 11:15:08.425752119 +0200
@@ -88,16 +88,29 @@
 #define CREATE_TRACE_POINTS
 #include <trace/events/writeback.h>
 
+static void bdi_wakeup_thread(struct backing_dev_info *bdi)
+{
+	spin_lock_bh(&bdi->wb_lock);
+	if (test_bit(BDI_registered, &bdi->state))
+		mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0);
+	spin_unlock_bh(&bdi->wb_lock);
+}
+
 static void bdi_queue_work(struct backing_dev_info *bdi,
 			   struct wb_writeback_work *work)
 {
 	trace_writeback_queue(bdi, work);
 
 	spin_lock_bh(&bdi->wb_lock);
+	if (!test_bit(BDI_registered, &bdi->state)) {
+		if (work->done)
+			complete(work->done);
+		goto out_unlock;
+	}
 	list_add_tail(&work->list, &bdi->work_list);
-	spin_unlock_bh(&bdi->wb_lock);
-
 	mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0);
+out_unlock:
+	spin_unlock_bh(&bdi->wb_lock);
 }
 
 static void
@@ -113,7 +126,7 @@
 	work = kzalloc(sizeof(*work), GFP_ATOMIC);
 	if (!work) {
 		trace_writeback_nowork(bdi);
-		mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0);
+		bdi_wakeup_thread(bdi);
 		return;
 	}
 
@@ -160,7 +173,7 @@
 	 * writeback as soon as there is no other work to do.
 	 */
 	trace_writeback_wake_background(bdi);
-	mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0);
+	bdi_wakeup_thread(bdi);
 }
 
 /*
@@ -508,13 +521,16 @@
 	}
 	WARN_ON(inode->i_state & I_SYNC);
 	/*
-	 * Skip inode if it is clean. We don't want to mess with writeback
-	 * lists in this function since flusher thread may be doing for example
-	 * sync in parallel and if we move the inode, it could get skipped. So
-	 * here we make sure inode is on some writeback list and leave it there
-	 * unless we have completely cleaned the inode.
-	 */
-	if (!(inode->i_state & I_DIRTY))
+	 * Skip inode if it is clean and we have no outstanding writeback in
+	 * WB_SYNC_ALL mode. We don't want to mess with writeback lists in this
+	 * function since flusher thread may be doing for example sync in
+	 * parallel and if we move the inode, it could get skipped. So here we
+	 * make sure inode is on some writeback list and leave it there unless
+	 * we have completely cleaned the inode.
+	 */
+	if (!(inode->i_state & I_DIRTY) &&
+	    (wbc->sync_mode != WB_SYNC_ALL ||
+	     !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK)))
 		goto out;
 	inode->i_state |= I_SYNC;
 	spin_unlock(&inode->i_lock);
@@ -1011,7 +1027,7 @@
 	current->flags |= PF_SWAPWRITE;
 
 	if (likely(!current_is_workqueue_rescuer() ||
-		   list_empty(&bdi->bdi_list))) {
+		   !test_bit(BDI_registered, &bdi->state))) {
 		/*
 		 * The normal path.  Keep writing back @bdi until its
 		 * work_list is empty.  Note that this path is also taken
@@ -1033,10 +1049,10 @@
 		trace_writeback_pages_written(pages_written);
 	}
 
-	if (!list_empty(&bdi->work_list) ||
-	    (wb_has_dirty_io(wb) && dirty_writeback_interval))
-		queue_delayed_work(bdi_wq, &wb->dwork,
-			msecs_to_jiffies(dirty_writeback_interval * 10));
+	if (!list_empty(&bdi->work_list))
+		mod_delayed_work(bdi_wq, &wb->dwork, 0);
+	else if (wb_has_dirty_io(wb) && dirty_writeback_interval)
+		bdi_wakeup_thread_delayed(bdi);
 
 	current->flags &= ~PF_SWAPWRITE;
 }
diff -ruw linux-3.11.10/fs/fuse/dev.c linux-3.11.10-fbx/fs/fuse/dev.c
--- linux-3.11.10/fs/fuse/dev.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/fuse/dev.c	2015-09-29 11:15:08.429752156 +0200
@@ -1296,22 +1296,6 @@
 	return fuse_dev_do_read(fc, file, &cs, iov_length(iov, nr_segs));
 }
 
-static int fuse_dev_pipe_buf_steal(struct pipe_inode_info *pipe,
-				   struct pipe_buffer *buf)
-{
-	return 1;
-}
-
-static const struct pipe_buf_operations fuse_dev_pipe_buf_ops = {
-	.can_merge = 0,
-	.map = generic_pipe_buf_map,
-	.unmap = generic_pipe_buf_unmap,
-	.confirm = generic_pipe_buf_confirm,
-	.release = generic_pipe_buf_release,
-	.steal = fuse_dev_pipe_buf_steal,
-	.get = generic_pipe_buf_get,
-};
-
 static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,
 				    struct pipe_inode_info *pipe,
 				    size_t len, unsigned int flags)
@@ -1358,7 +1342,11 @@
 		buf->page = bufs[page_nr].page;
 		buf->offset = bufs[page_nr].offset;
 		buf->len = bufs[page_nr].len;
-		buf->ops = &fuse_dev_pipe_buf_ops;
+		/*
+		 * Need to be careful about this.  Having buf->ops in module
+		 * code can Oops if the buffer persists after module unload.
+		 */
+		buf->ops = &nosteal_pipe_buf_ops;
 
 		pipe->nrbufs++;
 		page_nr++;
diff -ruw linux-3.11.10/fs/hfsplus/btree.c linux-3.11.10-fbx/fs/hfsplus/btree.c
--- linux-3.11.10/fs/hfsplus/btree.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/hfsplus/btree.c	2015-09-29 11:15:08.441752248 +0200
@@ -15,6 +15,118 @@
 #include "hfsplus_fs.h"
 #include "hfsplus_raw.h"
 
+/*
+ * Initial source code of clump size calculation is gotten
+ * from http://opensource.apple.com/tarballs/diskdev_cmds/
+ */
+#define CLUMP_ENTRIES	15
+
+static short clumptbl[CLUMP_ENTRIES * 3] = {
+/*
+ *	    Volume	Attributes	 Catalog	 Extents
+ *	     Size	Clump (MB)	Clump (MB)	Clump (MB)
+ */
+	/*   1GB */	  4,		  4,		 4,
+	/*   2GB */	  6,		  6,		 4,
+	/*   4GB */	  8,		  8,		 4,
+	/*   8GB */	 11,		 11,		 5,
+	/*
+	 * For volumes 16GB and larger, we want to make sure that a full OS
+	 * install won't require fragmentation of the Catalog or Attributes
+	 * B-trees.  We do this by making the clump sizes sufficiently large,
+	 * and by leaving a gap after the B-trees for them to grow into.
+	 *
+	 * For SnowLeopard 10A298, a FullNetInstall with all packages selected
+	 * results in:
+	 * Catalog B-tree Header
+	 *	nodeSize:          8192
+	 *	totalNodes:       31616
+	 *	freeNodes:         1978
+	 * (used = 231.55 MB)
+	 * Attributes B-tree Header
+	 *	nodeSize:          8192
+	 *	totalNodes:       63232
+	 *	freeNodes:          958
+	 * (used = 486.52 MB)
+	 *
+	 * We also want Time Machine backup volumes to have a sufficiently
+	 * large clump size to reduce fragmentation.
+	 *
+	 * The series of numbers for Catalog and Attribute form a geometric
+	 * series. For Catalog (16GB to 512GB), each term is 8**(1/5) times
+	 * the previous term.  For Attributes (16GB to 512GB), each term is
+	 * 4**(1/5) times the previous term.  For 1TB to 16TB, each term is
+	 * 2**(1/5) times the previous term.
+	 */
+	/*  16GB */	 64,		 32,		 5,
+	/*  32GB */	 84,		 49,		 6,
+	/*  64GB */	111,		 74,		 7,
+	/* 128GB */	147,		111,		 8,
+	/* 256GB */	194,		169,		 9,
+	/* 512GB */	256,		256,		11,
+	/*   1TB */	294,		294,		14,
+	/*   2TB */	338,		338,		16,
+	/*   4TB */	388,		388,		20,
+	/*   8TB */	446,		446,		25,
+	/*  16TB */	512,		512,		32
+};
+
+u32 hfsplus_calc_btree_clump_size(u32 block_size, u32 node_size,
+					u64 sectors, int file_id)
+{
+	u32 mod = max(node_size, block_size);
+	u32 clump_size;
+	int column;
+	int i;
+
+	/* Figure out which column of the above table to use for this file. */
+	switch (file_id) {
+	case HFSPLUS_ATTR_CNID:
+		column = 0;
+		break;
+	case HFSPLUS_CAT_CNID:
+		column = 1;
+		break;
+	default:
+		column = 2;
+		break;
+	}
+
+	/*
+	 * The default clump size is 0.8% of the volume size. And
+	 * it must also be a multiple of the node and block size.
+	 */
+	if (sectors < 0x200000) {
+		clump_size = sectors << 2;	/*  0.8 %  */
+		if (clump_size < (8 * node_size))
+			clump_size = 8 * node_size;
+	} else {
+		/* turn exponent into table index... */
+		for (i = 0, sectors = sectors >> 22;
+		     sectors && (i < CLUMP_ENTRIES - 1);
+		     ++i, sectors = sectors >> 1) {
+			/* empty body */
+		}
+
+		clump_size = clumptbl[column + (i) * 3] * 1024 * 1024;
+	}
+
+	/*
+	 * Round the clump size to a multiple of node and block size.
+	 * NOTE: This rounds down.
+	 */
+	clump_size /= mod;
+	clump_size *= mod;
+
+	/*
+	 * Rounding down could have rounded down to 0 if the block size was
+	 * greater than the clump size.  If so, just use one block or node.
+	 */
+	if (clump_size == 0)
+		clump_size = mod;
+
+	return clump_size;
+}
 
 /* Get a reference to a B*Tree and do some initial checks */
 struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id)
diff -ruw linux-3.11.10/fs/hfsplus/hfsplus_fs.h linux-3.11.10-fbx/fs/hfsplus/hfsplus_fs.h
--- linux-3.11.10/fs/hfsplus/hfsplus_fs.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/hfsplus/hfsplus_fs.h	2015-09-29 11:15:08.441752248 +0200
@@ -126,6 +126,14 @@
 #define HFS_BNODE_DELETED	4
 
 /*
+ * Attributes file states
+ */
+#define HFSPLUS_EMPTY_ATTR_TREE		0
+#define HFSPLUS_CREATING_ATTR_TREE	1
+#define HFSPLUS_VALID_ATTR_TREE		2
+#define HFSPLUS_FAILED_ATTR_TREE	3
+
+/*
  * HFS+ superblock info (built from Volume Header on disk)
  */
 
@@ -140,6 +148,7 @@
 	struct hfs_btree *ext_tree;
 	struct hfs_btree *cat_tree;
 	struct hfs_btree *attr_tree;
+	atomic_t attr_tree_state;
 	struct inode *alloc_file;
 	struct inode *hidden_dir;
 	struct nls_table *nls;
@@ -188,6 +197,7 @@
 #define HFSPLUS_SB_HFSX		3
 #define HFSPLUS_SB_CASEFOLD	4
 #define HFSPLUS_SB_NOBARRIER	5
+#define HFSPLUS_SB_NOOWNERS	6
 
 static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb)
 {
@@ -379,6 +389,7 @@
 int hfsplus_block_free(struct super_block *, u32, u32);
 
 /* btree.c */
+u32 hfsplus_calc_btree_clump_size(u32, u32, u64, int);
 struct hfs_btree *hfs_btree_open(struct super_block *, u32);
 void hfs_btree_close(struct hfs_btree *);
 int hfs_btree_write(struct hfs_btree *);
diff -ruw linux-3.11.10/fs/hfsplus/hfsplus_raw.h linux-3.11.10-fbx/fs/hfsplus/hfsplus_raw.h
--- linux-3.11.10/fs/hfsplus/hfsplus_raw.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/hfsplus/hfsplus_raw.h	2015-09-29 11:15:08.441752248 +0200
@@ -187,6 +187,9 @@
 /* HFS+ BTree misc info */
 #define HFSPLUS_TREE_HEAD 0
 #define HFSPLUS_NODE_MXSZ 32768
+#define HFSPLUS_ATTR_TREE_NODE_SIZE		8192
+#define HFSPLUS_BTREE_HDR_NODE_RECS_COUNT	3
+#define HFSPLUS_BTREE_HDR_USER_BYTES		128
 
 /* Some special File ID numbers (stolen from hfs.h) */
 #define HFSPLUS_POR_CNID		1	/* Parent Of the Root */
diff -ruw linux-3.11.10/fs/hfsplus/inode.c linux-3.11.10-fbx/fs/hfsplus/inode.c
--- linux-3.11.10/fs/hfsplus/inode.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/hfsplus/inode.c	2015-09-29 11:15:08.441752248 +0200
@@ -244,11 +244,13 @@
 	mode = be16_to_cpu(perms->mode);
 
 	i_uid_write(inode, be32_to_cpu(perms->owner));
-	if (!i_uid_read(inode) && !mode)
+	if ((!i_uid_read(inode) && !mode) ||
+	    test_bit(HFSPLUS_SB_NOOWNERS, &sbi->flags))
 		inode->i_uid = sbi->uid;
 
 	i_gid_write(inode, be32_to_cpu(perms->group));
-	if (!i_gid_read(inode) && !mode)
+	if ((!i_gid_read(inode) && !mode) ||
+	    test_bit(HFSPLUS_SB_NOOWNERS, &sbi->flags))
 		inode->i_gid = sbi->gid;
 
 	if (dir) {
@@ -301,12 +303,16 @@
 static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr)
 {
 	struct inode *inode = dentry->d_inode;
+	struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
 	int error;
 
 	error = inode_change_ok(inode, attr);
 	if (error)
 		return error;
 
+	if (test_bit(HFSPLUS_SB_NOOWNERS, &sbi->flags))
+		attr->ia_valid &= ~(ATTR_UID | ATTR_GID);
+
 	if ((attr->ia_valid & ATTR_SIZE) &&
 	    attr->ia_size != i_size_read(inode)) {
 		inode_dio_wait(inode);
@@ -410,8 +416,13 @@
 
 	inode->i_ino = sbi->next_cnid++;
 	inode->i_mode = mode;
+	if (test_bit(HFSPLUS_SB_NOOWNERS, &sbi->flags)) {
+		inode->i_uid = sbi->uid;
+		inode->i_gid = sbi->gid;
+	} else {
 	inode->i_uid = current_fsuid();
 	inode->i_gid = current_fsgid();
+	}
 	set_nlink(inode, 1);
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
 
diff -ruw linux-3.11.10/fs/hfsplus/options.c linux-3.11.10-fbx/fs/hfsplus/options.c
--- linux-3.11.10/fs/hfsplus/options.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/hfsplus/options.c	2015-09-29 11:15:08.441752248 +0200
@@ -24,7 +24,7 @@
 	opt_part, opt_session, opt_nls,
 	opt_nodecompose, opt_decompose,
 	opt_barrier, opt_nobarrier,
-	opt_force, opt_err
+	opt_force, opt_noowners, opt_err
 };
 
 static const match_table_t tokens = {
@@ -41,6 +41,7 @@
 	{ opt_barrier, "barrier" },
 	{ opt_nobarrier, "nobarrier" },
 	{ opt_force, "force" },
+	{ opt_noowners, "noowners" },
 	{ opt_err, NULL }
 };
 
@@ -196,6 +197,9 @@
 		case opt_force:
 			set_bit(HFSPLUS_SB_FORCE, &sbi->flags);
 			break;
+		case opt_noowners:
+			set_bit(HFSPLUS_SB_NOOWNERS, &sbi->flags);
+			break;
 		default:
 			return 0;
 		}
@@ -235,5 +239,7 @@
 		seq_printf(seq, ",nodecompose");
 	if (test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags))
 		seq_printf(seq, ",nobarrier");
+	if (test_bit(HFSPLUS_SB_NOOWNERS, &sbi->flags))
+		seq_printf(seq, ",noowners");
 	return 0;
 }
diff -ruw linux-3.11.10/fs/hfsplus/super.c linux-3.11.10-fbx/fs/hfsplus/super.c
--- linux-3.11.10/fs/hfsplus/super.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/hfsplus/super.c	2015-09-29 11:15:08.441752248 +0200
@@ -474,12 +474,14 @@
 		pr_err("failed to load catalog file\n");
 		goto out_close_ext_tree;
 	}
+	atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE);
 	if (vhdr->attr_file.total_blocks != 0) {
 		sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID);
 		if (!sbi->attr_tree) {
 			pr_err("failed to load attributes file\n");
 			goto out_close_cat_tree;
 		}
+		atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE);
 	}
 	sb->s_xattr = hfsplus_xattr_handlers;
 
diff -ruw linux-3.11.10/fs/hfsplus/xattr.c linux-3.11.10-fbx/fs/hfsplus/xattr.c
--- linux-3.11.10/fs/hfsplus/xattr.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/hfsplus/xattr.c	2015-09-29 11:15:08.441752248 +0200
@@ -75,6 +75,208 @@
 	return 0;
 }
 
+static void hfsplus_init_header_node(struct inode *attr_file,
+					u32 clump_size,
+					char *buf, size_t node_size)
+{
+	struct hfs_bnode_desc *desc;
+	struct hfs_btree_header_rec *head;
+	u16 offset;
+	__be16 *rec_offsets;
+	u32 hdr_node_map_rec_bits;
+	char *bmp;
+	u32 used_nodes;
+	u32 used_bmp_bytes;
+
+	hfs_dbg(ATTR_MOD, "init_hdr_attr_file: clump %u, node_size %zu\n",
+				clump_size, node_size);
+
+	/* The end of the node contains list of record offsets */
+	rec_offsets = (__be16 *)(buf + node_size);
+
+	desc = (struct hfs_bnode_desc *)buf;
+	desc->type = HFS_NODE_HEADER;
+	desc->num_recs = cpu_to_be16(HFSPLUS_BTREE_HDR_NODE_RECS_COUNT);
+	offset = sizeof(struct hfs_bnode_desc);
+	*--rec_offsets = cpu_to_be16(offset);
+
+	head = (struct hfs_btree_header_rec *)(buf + offset);
+	head->node_size = cpu_to_be16(node_size);
+	head->node_count = cpu_to_be32(i_size_read(attr_file) / node_size);
+	head->free_nodes = cpu_to_be32(be32_to_cpu(head->node_count) - 1);
+	head->clump_size = cpu_to_be32(clump_size);
+	head->attributes |= cpu_to_be32(HFS_TREE_BIGKEYS | HFS_TREE_VARIDXKEYS);
+	head->max_key_len = cpu_to_be16(HFSPLUS_ATTR_KEYLEN - sizeof(u16));
+	offset += sizeof(struct hfs_btree_header_rec);
+	*--rec_offsets = cpu_to_be16(offset);
+	offset += HFSPLUS_BTREE_HDR_USER_BYTES;
+	*--rec_offsets = cpu_to_be16(offset);
+
+	hdr_node_map_rec_bits = 8 * (node_size - offset - (4 * sizeof(u16)));
+	if (be32_to_cpu(head->node_count) > hdr_node_map_rec_bits) {
+		u32 map_node_bits;
+		u32 map_nodes;
+
+		desc->next = cpu_to_be32(be32_to_cpu(head->leaf_tail) + 1);
+		map_node_bits = 8 * (node_size - sizeof(struct hfs_bnode_desc) -
+					(2 * sizeof(u16)) - 2);
+		map_nodes = (be32_to_cpu(head->node_count) -
+				hdr_node_map_rec_bits +
+				(map_node_bits - 1)) / map_node_bits;
+		be32_add_cpu(&head->free_nodes, 0 - map_nodes);
+	}
+
+	bmp = buf + offset;
+	used_nodes =
+		be32_to_cpu(head->node_count) - be32_to_cpu(head->free_nodes);
+	used_bmp_bytes = used_nodes / 8;
+	if (used_bmp_bytes) {
+		memset(bmp, 0xFF, used_bmp_bytes);
+		bmp += used_bmp_bytes;
+		used_nodes %= 8;
+	}
+	*bmp = ~(0xFF >> used_nodes);
+	offset += hdr_node_map_rec_bits / 8;
+	*--rec_offsets = cpu_to_be16(offset);
+}
+
+static int hfsplus_create_attributes_file(struct super_block *sb)
+{
+	int err = 0;
+	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
+	struct inode *attr_file;
+	struct hfsplus_inode_info *hip;
+	u32 clump_size;
+	u16 node_size = HFSPLUS_ATTR_TREE_NODE_SIZE;
+	char *buf;
+	int index, written;
+	struct address_space *mapping;
+	struct page *page;
+	int old_state = HFSPLUS_EMPTY_ATTR_TREE;
+
+	hfs_dbg(ATTR_MOD, "create_attr_file: ino %d\n", HFSPLUS_ATTR_CNID);
+
+check_attr_tree_state_again:
+	switch (atomic_read(&sbi->attr_tree_state)) {
+	case HFSPLUS_EMPTY_ATTR_TREE:
+		if (old_state != atomic_cmpxchg(&sbi->attr_tree_state,
+						old_state,
+						HFSPLUS_CREATING_ATTR_TREE))
+			goto check_attr_tree_state_again;
+		break;
+	case HFSPLUS_CREATING_ATTR_TREE:
+		/*
+		 * This state means that another thread is in process
+		 * of AttributesFile creation. Theoretically, it is
+		 * possible to be here. But really __setxattr() method
+		 * first of all calls hfs_find_init() for lookup in
+		 * B-tree of CatalogFile. This method locks mutex of
+		 * CatalogFile's B-tree. As a result, if some thread
+		 * is inside AttributedFile creation operation then
+		 * another threads will be waiting unlocking of
+		 * CatalogFile's B-tree's mutex. However, if code will
+		 * change then we will return error code (-EAGAIN) from
+		 * here. Really, it means that first try to set of xattr
+		 * fails with error but second attempt will have success.
+		 */
+		return -EAGAIN;
+	case HFSPLUS_VALID_ATTR_TREE:
+		return 0;
+	case HFSPLUS_FAILED_ATTR_TREE:
+		return -EOPNOTSUPP;
+	default:
+		BUG();
+	}
+
+	attr_file = hfsplus_iget(sb, HFSPLUS_ATTR_CNID);
+	if (IS_ERR(attr_file)) {
+		pr_err("failed to load attributes file\n");
+		return PTR_ERR(attr_file);
+	}
+
+	BUG_ON(i_size_read(attr_file) != 0);
+
+	hip = HFSPLUS_I(attr_file);
+
+	clump_size = hfsplus_calc_btree_clump_size(sb->s_blocksize,
+						    node_size,
+						    sbi->sect_count,
+						    HFSPLUS_ATTR_CNID);
+
+	mutex_lock(&hip->extents_lock);
+	hip->clump_blocks = clump_size >> sbi->alloc_blksz_shift;
+	mutex_unlock(&hip->extents_lock);
+
+	if (sbi->free_blocks <= (hip->clump_blocks << 1)) {
+		err = -ENOSPC;
+		goto end_attr_file_creation;
+	}
+
+	while (hip->alloc_blocks < hip->clump_blocks) {
+		err = hfsplus_file_extend(attr_file);
+		if (unlikely(err)) {
+			pr_err("failed to extend attributes file\n");
+			goto end_attr_file_creation;
+		}
+		hip->phys_size = attr_file->i_size =
+			(loff_t)hip->alloc_blocks << sbi->alloc_blksz_shift;
+		hip->fs_blocks = hip->alloc_blocks << sbi->fs_shift;
+		inode_set_bytes(attr_file, attr_file->i_size);
+	}
+
+	buf = kzalloc(node_size, GFP_NOFS);
+	if (!buf) {
+		pr_err("failed to allocate memory for header node\n");
+		err = -ENOMEM;
+		goto end_attr_file_creation;
+	}
+
+	hfsplus_init_header_node(attr_file, clump_size, buf, node_size);
+
+	mapping = attr_file->i_mapping;
+
+	index = 0;
+	written = 0;
+	for (; written < node_size; index++, written += PAGE_CACHE_SIZE) {
+		void *kaddr;
+
+		page = read_mapping_page(mapping, index, NULL);
+		if (IS_ERR(page)) {
+			err = PTR_ERR(page);
+			goto failed_header_node_init;
+		}
+
+		kaddr = kmap_atomic(page);
+		memcpy(kaddr, buf + written,
+			min_t(size_t, PAGE_CACHE_SIZE, node_size - written));
+		kunmap_atomic(kaddr);
+
+		set_page_dirty(page);
+		page_cache_release(page);
+	}
+
+	hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY);
+
+	sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID);
+	if (!sbi->attr_tree)
+		pr_err("failed to load attributes file\n");
+
+failed_header_node_init:
+	kfree(buf);
+
+end_attr_file_creation:
+	iput(attr_file);
+
+	if (!err)
+		atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE);
+	else if (err == -ENOSPC)
+		atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE);
+	else
+		atomic_set(&sbi->attr_tree_state, HFSPLUS_FAILED_ATTR_TREE);
+
+	return err;
+}
+
 int __hfsplus_setxattr(struct inode *inode, const char *name,
 			const void *value, size_t size, int flags)
 {
@@ -159,7 +361,8 @@
 	}
 
 	if (!HFSPLUS_SB(inode->i_sb)->attr_tree) {
-		err = -EOPNOTSUPP;
+		err = hfsplus_create_attributes_file(inode->i_sb);
+		if (unlikely(err))
 		goto end_setxattr;
 	}
 
diff -ruw linux-3.11.10/fs/inode.c linux-3.11.10-fbx/fs/inode.c
--- linux-3.11.10/fs/inode.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/inode.c	2015-09-29 11:15:08.445752273 +0200
@@ -1839,14 +1839,18 @@
  * inode_owner_or_capable - check current task permissions to inode
  * @inode: inode being checked
  *
- * Return true if current either has CAP_FOWNER to the inode, or
- * owns the file.
+ * Return true if current either has CAP_FOWNER in a namespace with the
+ * inode owner uid mapped, or owns the file.
  */
 bool inode_owner_or_capable(const struct inode *inode)
 {
+	struct user_namespace *ns;
+
 	if (uid_eq(current_fsuid(), inode->i_uid))
 		return true;
-	if (inode_capable(inode, CAP_FOWNER))
+
+	ns = current_user_ns();
+	if (ns_capable(ns, CAP_FOWNER) && kuid_has_mapping(ns, inode->i_uid))
 		return true;
 	return false;
 }
diff -ruw linux-3.11.10/fs/jbd2/transaction.c linux-3.11.10-fbx/fs/jbd2/transaction.c
--- linux-3.11.10/fs/jbd2/transaction.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/jbd2/transaction.c	2015-09-29 11:15:08.449752302 +0200
@@ -514,11 +514,13 @@
 	 * similarly constrained call sites
 	 */
 	ret = start_this_handle(journal, handle, GFP_NOFS);
-	if (ret < 0)
+	if (ret < 0) {
 		jbd2_journal_free_reserved(handle);
+		return ret;
+	}
 	handle->h_type = type;
 	handle->h_line_no = line_no;
-	return ret;
+	return 0;
 }
 EXPORT_SYMBOL(jbd2_journal_start_reserved);
 
@@ -1290,7 +1292,10 @@
 		 * once a transaction -bzzz
 		 */
 		jh->b_modified = 1;
-		J_ASSERT_JH(jh, handle->h_buffer_credits > 0);
+		if (handle->h_buffer_credits <= 0) {
+			ret = -ENOSPC;
+			goto out_unlock_bh;
+		}
 		handle->h_buffer_credits--;
 	}
 
@@ -1373,7 +1378,6 @@
 	jbd2_journal_put_journal_head(jh);
 out:
 	JBUFFER_TRACE(jh, "exit");
-	WARN_ON(ret);	/* All errors are bugs, so dump the stack */
 	return ret;
 }
 
@@ -1586,9 +1590,12 @@
 	 * to perform a synchronous write.  We do this to detect the
 	 * case where a single process is doing a stream of sync
 	 * writes.  No point in waiting for joiners in that case.
+	 *
+	 * Setting max_batch_time to 0 disables this completely.
 	 */
 	pid = current->pid;
-	if (handle->h_sync && journal->j_last_sync_writer != pid) {
+	if (handle->h_sync && journal->j_last_sync_writer != pid &&
+	    journal->j_max_batch_time) {
 		u64 commit_time, trans_time;
 
 		journal->j_last_sync_writer = pid;
diff -ruw linux-3.11.10/fs/Kconfig linux-3.11.10-fbx/fs/Kconfig
--- linux-3.11.10/fs/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/Kconfig	2015-09-29 11:15:08.333751425 +0200
@@ -93,6 +93,7 @@
 
 source "fs/fat/Kconfig"
 source "fs/ntfs/Kconfig"
+source "fs/exfat/Kconfig"
 
 endmenu
 endif # BLOCK
@@ -166,6 +167,17 @@
 config HUGETLB_PAGE
 	def_bool HUGETLBFS
 
+config RAMFS_XATTR
+	bool
+	default n
+ 
+config RAMFS_XATTR_USER
+	bool "Enable user extended attributes on RAMFS filesystem"
+	select RAMFS_XATTR
+	help
+	  Select this to enable extended user attributes on RAMFS
+	  filesystem.
+
 source "fs/configfs/Kconfig"
 
 endmenu
diff -ruw linux-3.11.10/fs/lockd/svc.c linux-3.11.10-fbx/fs/lockd/svc.c
--- linux-3.11.10/fs/lockd/svc.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/lockd/svc.c	2015-09-29 11:15:08.469752456 +0200
@@ -235,6 +235,7 @@
 	if (warned++ == 0)
 		printk(KERN_WARNING
 			"lockd_up: makesock failed, error=%d\n", err);
+	svc_shutdown_net(serv, net);
 	return err;
 }
 
diff -ruw linux-3.11.10/fs/lockd/svclock.c linux-3.11.10-fbx/fs/lockd/svclock.c
--- linux-3.11.10/fs/lockd/svclock.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/lockd/svclock.c	2015-09-29 11:15:08.469752456 +0200
@@ -779,6 +779,7 @@
 	struct nlm_file		*file = block->b_file;
 	struct nlm_lock		*lock = &block->b_call->a_args.lock;
 	int			error;
+	loff_t			fl_start, fl_end;
 
 	dprintk("lockd: grant blocked lock %p\n", block);
 
@@ -796,9 +797,16 @@
 	}
 
 	/* Try the lock operation again */
+	/* vfs_lock_file() can mangle fl_start and fl_end, but we need
+	 * them unchanged for the GRANT_MSG
+	 */
 	lock->fl.fl_flags |= FL_SLEEP;
+	fl_start = lock->fl.fl_start;
+	fl_end = lock->fl.fl_end;
 	error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
 	lock->fl.fl_flags &= ~FL_SLEEP;
+	lock->fl.fl_start = fl_start;
+	lock->fl.fl_end = fl_end;
 
 	switch (error) {
 	case 0:
diff -ruw linux-3.11.10/fs/locks.c linux-3.11.10-fbx/fs/locks.c
--- linux-3.11.10/fs/locks.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/locks.c	2015-09-29 11:15:08.469752456 +0200
@@ -1359,11 +1359,10 @@
 
 restart:
 	break_time = flock->fl_break_time;
-	if (break_time != 0) {
+	if (break_time != 0)
 		break_time -= jiffies;
 		if (break_time == 0)
 			break_time++;
-	}
 	locks_insert_block(flock, new_fl);
 	spin_unlock(&inode->i_lock);
 	error = wait_event_interruptible_timeout(new_fl->fl_wait,
diff -ruw linux-3.11.10/fs/Makefile linux-3.11.10-fbx/fs/Makefile
--- linux-3.11.10/fs/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/Makefile	2015-09-29 11:15:08.333751425 +0200
@@ -126,3 +126,4 @@
 obj-$(CONFIG_CEPH_FS)		+= ceph/
 obj-$(CONFIG_PSTORE)		+= pstore/
 obj-$(CONFIG_EFIVAR_FS)		+= efivarfs/
+obj-$(CONFIG_EXFAT_FS)		+= exfat/
diff -ruw linux-3.11.10/fs/mount.h linux-3.11.10-fbx/fs/mount.h
--- linux-3.11.10/fs/mount.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/mount.h	2015-09-29 11:15:08.473752485 +0200
@@ -73,7 +73,7 @@
 static inline int is_mounted(struct vfsmount *mnt)
 {
 	/* neither detached nor internal? */
-	return !IS_ERR_OR_NULL(real_mount(mnt));
+	return !IS_ERR_OR_NULL(real_mount(mnt)->mnt_ns);
 }
 
 extern struct mount *__lookup_mnt(struct vfsmount *, struct dentry *, int);
diff -ruw linux-3.11.10/fs/namei.c linux-3.11.10-fbx/fs/namei.c
--- linux-3.11.10/fs/namei.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/namei.c	2015-09-29 11:15:08.473752485 +0200
@@ -321,10 +321,11 @@
 
 	if (S_ISDIR(inode->i_mode)) {
 		/* DACs are overridable for directories */
-		if (inode_capable(inode, CAP_DAC_OVERRIDE))
+		if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
 			return 0;
 		if (!(mask & MAY_WRITE))
-			if (inode_capable(inode, CAP_DAC_READ_SEARCH))
+			if (capable_wrt_inode_uidgid(inode,
+						     CAP_DAC_READ_SEARCH))
 				return 0;
 		return -EACCES;
 	}
@@ -334,7 +335,7 @@
 	 * at least one exec bit set.
 	 */
 	if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
-		if (inode_capable(inode, CAP_DAC_OVERRIDE))
+		if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
 			return 0;
 
 	/*
@@ -342,7 +343,7 @@
 	 */
 	mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
 	if (mask == MAY_READ)
-		if (inode_capable(inode, CAP_DAC_READ_SEARCH))
+		if (capable_wrt_inode_uidgid(inode, CAP_DAC_READ_SEARCH))
 			return 0;
 
 	return -EACCES;
@@ -2198,7 +2199,7 @@
 		return 0;
 	if (uid_eq(dir->i_uid, fsuid))
 		return 0;
-	return !inode_capable(inode, CAP_FOWNER);
+	return !capable_wrt_inode_uidgid(inode, CAP_FOWNER);
 }
 
 /*
@@ -2262,6 +2263,7 @@
  */
 static inline int may_create(struct inode *dir, struct dentry *child)
 {
+	audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
 	if (child->d_inode)
 		return -EEXIST;
 	if (IS_DEADDIR(dir))
@@ -3707,6 +3709,7 @@
 out_dput:
 	done_path_create(&new_path, new_dentry);
 	if (retry_estale(error, how)) {
+		path_put(&old_path);
 		how |= LOOKUP_REVAL;
 		goto retry;
 	}
diff -ruw linux-3.11.10/fs/namespace.c linux-3.11.10-fbx/fs/namespace.c
--- linux-3.11.10/fs/namespace.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/namespace.c	2015-09-29 11:15:08.473752485 +0200
@@ -1650,7 +1650,7 @@
 
 static int flags_to_propagation_type(int flags)
 {
-	int type = flags & ~(MS_REC | MS_SILENT);
+	int type = flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE);
 
 	/* Fail if any non-propagation flags are set */
 	if (type & ~(MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
diff -ruw linux-3.11.10/fs/nfs/dir.c linux-3.11.10-fbx/fs/nfs/dir.c
--- linux-3.11.10/fs/nfs/dir.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfs/dir.c	2015-09-29 11:15:08.477752522 +0200
@@ -1813,6 +1813,11 @@
 							GFP_KERNEL)) {
 		SetPageUptodate(page);
 		unlock_page(page);
+		/*
+		 * add_to_page_cache_lru() grabs an extra page refcount.
+		 * Drop it here to avoid leaking this page later.
+		 */
+		page_cache_release(page);
 	} else
 		__free_page(page);
 
diff -ruw linux-3.11.10/fs/nfs/inode.c linux-3.11.10-fbx/fs/nfs/inode.c
--- linux-3.11.10/fs/nfs/inode.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfs/inode.c	2015-09-29 11:15:08.481752547 +0200
@@ -163,17 +163,16 @@
 	if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) {
 		nfs_fscache_invalidate(inode);
 		nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-					| NFS_INO_INVALID_LABEL
 					| NFS_INO_INVALID_DATA
 					| NFS_INO_INVALID_ACCESS
 					| NFS_INO_INVALID_ACL
 					| NFS_INO_REVAL_PAGECACHE;
 	} else
 		nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-					| NFS_INO_INVALID_LABEL
 					| NFS_INO_INVALID_ACCESS
 					| NFS_INO_INVALID_ACL
 					| NFS_INO_REVAL_PAGECACHE;
+	nfs_zap_label_cache_locked(nfsi);
 }
 
 void nfs_zap_caches(struct inode *inode)
@@ -265,6 +264,13 @@
 }
 
 #ifdef CONFIG_NFS_V4_SECURITY_LABEL
+static void nfs_clear_label_invalid(struct inode *inode)
+{
+	spin_lock(&inode->i_lock);
+	NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_LABEL;
+	spin_unlock(&inode->i_lock);
+}
+
 void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
 					struct nfs4_label *label)
 {
@@ -288,6 +294,7 @@
 					__func__,
 					(char *)label->label,
 					label->len, error);
+		nfs_clear_label_invalid(inode);
 	}
 }
 
@@ -1478,18 +1485,20 @@
 			inode->i_version = fattr->change_attr;
 		}
 	} else if (server->caps & NFS_CAP_CHANGE_ATTR)
-		invalid |= save_cache_validity;
+		nfsi->cache_validity |= save_cache_validity;
 
 	if (fattr->valid & NFS_ATTR_FATTR_MTIME) {
 		memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
 	} else if (server->caps & NFS_CAP_MTIME)
-		invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+		nfsi->cache_validity |= save_cache_validity &
+				(NFS_INO_INVALID_ATTR
 				| NFS_INO_REVAL_FORCED);
 
 	if (fattr->valid & NFS_ATTR_FATTR_CTIME) {
 		memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
 	} else if (server->caps & NFS_CAP_CTIME)
-		invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+		nfsi->cache_validity |= save_cache_validity &
+				(NFS_INO_INVALID_ATTR
 				| NFS_INO_REVAL_FORCED);
 
 	/* Check if our cached file size is stale */
@@ -1512,7 +1521,8 @@
 					(long long)new_isize);
 		}
 	} else
-		invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+		nfsi->cache_validity |= save_cache_validity &
+				(NFS_INO_INVALID_ATTR
 				| NFS_INO_REVAL_PAGECACHE
 				| NFS_INO_REVAL_FORCED);
 
@@ -1520,7 +1530,8 @@
 	if (fattr->valid & NFS_ATTR_FATTR_ATIME)
 		memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime));
 	else if (server->caps & NFS_CAP_ATIME)
-		invalid |= save_cache_validity & (NFS_INO_INVALID_ATIME
+		nfsi->cache_validity |= save_cache_validity &
+				(NFS_INO_INVALID_ATIME
 				| NFS_INO_REVAL_FORCED);
 
 	if (fattr->valid & NFS_ATTR_FATTR_MODE) {
@@ -1531,7 +1542,8 @@
 			invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
 		}
 	} else if (server->caps & NFS_CAP_MODE)
-		invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+		nfsi->cache_validity |= save_cache_validity &
+				(NFS_INO_INVALID_ATTR
 				| NFS_INO_INVALID_ACCESS
 				| NFS_INO_INVALID_ACL
 				| NFS_INO_REVAL_FORCED);
@@ -1542,7 +1554,8 @@
 			inode->i_uid = fattr->uid;
 		}
 	} else if (server->caps & NFS_CAP_OWNER)
-		invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+		nfsi->cache_validity |= save_cache_validity &
+				(NFS_INO_INVALID_ATTR
 				| NFS_INO_INVALID_ACCESS
 				| NFS_INO_INVALID_ACL
 				| NFS_INO_REVAL_FORCED);
@@ -1553,7 +1566,8 @@
 			inode->i_gid = fattr->gid;
 		}
 	} else if (server->caps & NFS_CAP_OWNER_GROUP)
-		invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+		nfsi->cache_validity |= save_cache_validity &
+				(NFS_INO_INVALID_ATTR
 				| NFS_INO_INVALID_ACCESS
 				| NFS_INO_INVALID_ACL
 				| NFS_INO_REVAL_FORCED);
@@ -1566,7 +1580,8 @@
 			set_nlink(inode, fattr->nlink);
 		}
 	} else if (server->caps & NFS_CAP_NLINK)
-		invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+		nfsi->cache_validity |= save_cache_validity &
+				(NFS_INO_INVALID_ATTR
 				| NFS_INO_REVAL_FORCED);
 
 	if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
@@ -1579,7 +1594,7 @@
 		inode->i_blocks = fattr->du.nfs2.blocks;
 
 	/* Update attrtimeo value if we're out of the unstable period */
-	if (invalid & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL)) {
+	if (invalid & NFS_INO_INVALID_ATTR) {
 		nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
 		nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
 		nfsi->attrtimeo_timestamp = now;
@@ -1592,7 +1607,6 @@
 		}
 	}
 	invalid &= ~NFS_INO_INVALID_ATTR;
-	invalid &= ~NFS_INO_INVALID_LABEL;
 	/* Don't invalidate the data if we were to blame */
 	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
 				|| S_ISLNK(inode->i_mode)))
diff -ruw linux-3.11.10/fs/nfs/internal.h linux-3.11.10-fbx/fs/nfs/internal.h
--- linux-3.11.10/fs/nfs/internal.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfs/internal.h	2015-09-29 11:15:08.481752547 +0200
@@ -263,6 +263,30 @@
 extern struct rpc_procinfo nfs4_procedures[];
 #endif
 
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+extern struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags);
+static inline void nfs4_label_free(struct nfs4_label *label)
+{
+	if (label) {
+		kfree(label->label);
+		kfree(label);
+	}
+	return;
+}
+
+static inline void nfs_zap_label_cache_locked(struct nfs_inode *nfsi)
+{
+	if (nfs_server_capable(&nfsi->vfs_inode, NFS_CAP_SECURITY_LABEL))
+		nfsi->cache_validity |= NFS_INO_INVALID_LABEL;
+}
+#else
+static inline struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags) { return NULL; }
+static inline void nfs4_label_free(void *label) {}
+static inline void nfs_zap_label_cache_locked(struct nfs_inode *nfsi)
+{
+}
+#endif /* CONFIG_NFS_V4_SECURITY_LABEL */
+
 /* proc.c */
 void nfs_close_context(struct nfs_open_context *ctx, int is_sync);
 extern struct nfs_client *nfs_init_client(struct nfs_client *clp,
diff -ruw linux-3.11.10/fs/nfs/nfs4_fs.h linux-3.11.10-fbx/fs/nfs/nfs4_fs.h
--- linux-3.11.10/fs/nfs/nfs4_fs.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfs/nfs4_fs.h	2015-09-29 11:15:08.481752547 +0200
@@ -9,6 +9,14 @@
 #ifndef __LINUX_FS_NFS_NFS4_FS_H
 #define __LINUX_FS_NFS_NFS4_FS_H
 
+#if defined(CONFIG_NFS_V4_2)
+#define NFS4_MAX_MINOR_VERSION 2
+#elif defined(CONFIG_NFS_V4_1)
+#define NFS4_MAX_MINOR_VERSION 1
+#else
+#define NFS4_MAX_MINOR_VERSION 0
+#endif
+
 #if IS_ENABLED(CONFIG_NFS_V4)
 
 #define NFS4_MAX_LOOP_ON_RECOVER (10)
diff -ruw linux-3.11.10/fs/nfs/super.c linux-3.11.10-fbx/fs/nfs/super.c
--- linux-3.11.10/fs/nfs/super.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfs/super.c	2015-09-29 11:15:08.489752613 +0200
@@ -2178,6 +2178,7 @@
 	data->timeo = 10U * nfss->client->cl_timeout->to_initval / HZ;
 	data->nfs_server.port = nfss->port;
 	data->nfs_server.addrlen = nfss->nfs_client->cl_addrlen;
+	data->net = current->nsproxy->net_ns;
 	memcpy(&data->nfs_server.address, &nfss->nfs_client->cl_addr,
 		data->nfs_server.addrlen);
 
diff -ruw linux-3.11.10/fs/nfs/write.c linux-3.11.10-fbx/fs/nfs/write.c
--- linux-3.11.10/fs/nfs/write.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfs/write.c	2015-09-29 11:15:08.493752639 +0200
@@ -893,19 +893,20 @@
  * extend the write to cover the entire page in order to avoid fragmentation
  * inefficiencies.
  *
- * If the file is opened for synchronous writes or if we have a write delegation
- * from the server then we can just skip the rest of the checks.
+ * If the file is opened for synchronous writes then we can just skip the rest
+ * of the checks.
  */
 static int nfs_can_extend_write(struct file *file, struct page *page, struct inode *inode)
 {
 	if (file->f_flags & O_DSYNC)
 		return 0;
+	if (!nfs_write_pageuptodate(page, inode))
+		return 0;
 	if (NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
 		return 1;
-	if (nfs_write_pageuptodate(page, inode) && (inode->i_flock == NULL ||
-			(inode->i_flock->fl_start == 0 &&
+	if (inode->i_flock == NULL || (inode->i_flock->fl_start == 0 &&
 			inode->i_flock->fl_end == OFFSET_MAX &&
-			inode->i_flock->fl_type != F_RDLCK)))
+			inode->i_flock->fl_type != F_RDLCK))
 		return 1;
 	return 0;
 }
diff -ruw linux-3.11.10/fs/nfsd/export.c linux-3.11.10-fbx/fs/nfsd/export.c
--- linux-3.11.10/fs/nfsd/export.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfsd/export.c	2015-09-29 11:15:08.493752639 +0200
@@ -536,16 +536,12 @@
 		if (err)
 			goto out3;
 		exp.ex_anon_uid= make_kuid(&init_user_ns, an_int);
-		if (!uid_valid(exp.ex_anon_uid))
-			goto out3;
 
 		/* anon gid */
 		err = get_int(&mesg, &an_int);
 		if (err)
 			goto out3;
 		exp.ex_anon_gid= make_kgid(&init_user_ns, an_int);
-		if (!gid_valid(exp.ex_anon_gid))
-			goto out3;
 
 		/* fsid */
 		err = get_int(&mesg, &an_int);
@@ -583,6 +579,17 @@
 				   exp.ex_uuid);
 		if (err)
 			goto out4;
+		/*
+		 * For some reason exportfs has been passing down an
+		 * invalid (-1) uid & gid on the "dummy" export which it
+		 * uses to test export support.  To make sure exportfs
+		 * sees errors from check_export we therefore need to
+		 * delay these checks till after check_export:
+		 */
+		if (!uid_valid(exp.ex_anon_uid))
+			goto out4;
+		if (!gid_valid(exp.ex_anon_gid))
+			goto out4;
 	}
 
 	expp = svc_export_lookup(&exp);
diff -ruw linux-3.11.10/fs/nfsd/nfs4acl.c linux-3.11.10-fbx/fs/nfsd/nfs4acl.c
--- linux-3.11.10/fs/nfsd/nfs4acl.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfsd/nfs4acl.c	2015-09-29 11:15:08.493752639 +0200
@@ -385,8 +385,10 @@
 	 * by uid/gid. */
 	int i, j;
 
-	if (pacl->a_count <= 4)
-		return; /* no users or groups */
+	/* no users or groups */
+	if (!pacl || pacl->a_count <= 4)
+		return;
+
 	i = 1;
 	while (pacl->a_entries[i].e_tag == ACL_USER)
 		i++;
@@ -513,13 +515,12 @@
 
 	/*
 	 * ACLs with no ACEs are treated differently in the inheritable
-	 * and effective cases: when there are no inheritable ACEs, we
-	 * set a zero-length default posix acl:
+	 * and effective cases: when there are no inheritable ACEs,
+	 * calls ->set_acl with a NULL ACL structure.
 	 */
-	if (state->empty && (flags & NFS4_ACL_TYPE_DEFAULT)) {
-		pacl = posix_acl_alloc(0, GFP_KERNEL);
-		return pacl ? pacl : ERR_PTR(-ENOMEM);
-	}
+	if (state->empty && (flags & NFS4_ACL_TYPE_DEFAULT))
+		return NULL;
+
 	/*
 	 * When there are no effective ACEs, the following will end
 	 * up setting a 3-element effective posix ACL with all
diff -ruw linux-3.11.10/fs/nfsd/nfs4callback.c linux-3.11.10-fbx/fs/nfsd/nfs4callback.c
--- linux-3.11.10/fs/nfsd/nfs4callback.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfsd/nfs4callback.c	2015-09-29 11:15:08.493752639 +0200
@@ -637,9 +637,11 @@
 
 static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses)
 {
+	int maxtime = max_cb_time(clp->net);
 	struct rpc_timeout	timeparms = {
-		.to_initval	= max_cb_time(clp->net),
+		.to_initval	= maxtime,
 		.to_retries	= 0,
+		.to_maxval	= maxtime,
 	};
 	struct rpc_create_args args = {
 		.net		= clp->net,
diff -ruw linux-3.11.10/fs/nfsd/nfs4proc.c linux-3.11.10-fbx/fs/nfsd/nfs4proc.c
--- linux-3.11.10/fs/nfsd/nfs4proc.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfsd/nfs4proc.c	2015-09-29 11:15:08.493752639 +0200
@@ -610,15 +610,6 @@
 
 	switch (create->cr_type) {
 	case NF4LNK:
-		/* ugh! we have to null-terminate the linktext, or
-		 * vfs_symlink() will choke.  it is always safe to
-		 * null-terminate by brute force, since at worst we
-		 * will overwrite the first byte of the create namelen
-		 * in the XDR buffer, which has already been extracted
-		 * during XDR decode.
-		 */
-		create->cr_linkname[create->cr_linklen] = 0;
-
 		status = nfsd_symlink(rqstp, &cstate->current_fh,
 				      create->cr_name, create->cr_namelen,
 				      create->cr_linkname, create->cr_linklen,
@@ -1349,6 +1340,12 @@
 		/* If op is non-idempotent */
 		if (opdesc->op_flags & OP_MODIFIES_SOMETHING) {
 			plen = opdesc->op_rsize_bop(rqstp, op);
+			/*
+			 * If there's still another operation, make sure
+			 * we'll have space to at least encode an error:
+			 */
+			if (resp->opcnt < args->opcnt)
+				plen += COMPOUND_ERR_SLACK_SPACE;
 			op->status = nfsd4_check_resp_size(resp, plen);
 		}
 
@@ -1513,7 +1510,8 @@
 
 static inline u32 nfsd4_setclientid_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
 {
-	return (op_encode_hdr_size + 2 + 1024) * sizeof(__be32);
+	return (op_encode_hdr_size + 2 + XDR_QUADLEN(NFS4_VERIFIER_SIZE)) *
+								sizeof(__be32);
 }
 
 static inline u32 nfsd4_write_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
diff -ruw linux-3.11.10/fs/nfsd/nfs4state.c linux-3.11.10-fbx/fs/nfsd/nfs4state.c
--- linux-3.11.10/fs/nfsd/nfs4state.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfsd/nfs4state.c	2015-09-29 11:15:08.497752668 +0200
@@ -1071,6 +1071,18 @@
 		return NULL;
 	}
 	clp->cl_name.len = name.len;
+	INIT_LIST_HEAD(&clp->cl_sessions);
+	idr_init(&clp->cl_stateids);
+	atomic_set(&clp->cl_refcount, 0);
+	clp->cl_cb_state = NFSD4_CB_UNKNOWN;
+	INIT_LIST_HEAD(&clp->cl_idhash);
+	INIT_LIST_HEAD(&clp->cl_openowners);
+	INIT_LIST_HEAD(&clp->cl_delegations);
+	INIT_LIST_HEAD(&clp->cl_lru);
+	INIT_LIST_HEAD(&clp->cl_callbacks);
+	INIT_LIST_HEAD(&clp->cl_revoked);
+	spin_lock_init(&clp->cl_lock);
+	rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
 	return clp;
 }
 
@@ -1088,6 +1100,7 @@
 		WARN_ON_ONCE(atomic_read(&ses->se_ref));
 		free_session(ses);
 	}
+	rpc_destroy_wait_queue(&clp->cl_cb_waitq);
 	free_svc_cred(&clp->cl_cred);
 	kfree(clp->cl_name.data);
 	idr_destroy(&clp->cl_stateids);
@@ -1335,7 +1348,6 @@
 	if (clp == NULL)
 		return NULL;
 
-	INIT_LIST_HEAD(&clp->cl_sessions);
 	ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred);
 	if (ret) {
 		spin_lock(&nn->client_lock);
@@ -1343,20 +1355,9 @@
 		spin_unlock(&nn->client_lock);
 		return NULL;
 	}
-	idr_init(&clp->cl_stateids);
-	atomic_set(&clp->cl_refcount, 0);
-	clp->cl_cb_state = NFSD4_CB_UNKNOWN;
-	INIT_LIST_HEAD(&clp->cl_idhash);
-	INIT_LIST_HEAD(&clp->cl_openowners);
-	INIT_LIST_HEAD(&clp->cl_delegations);
-	INIT_LIST_HEAD(&clp->cl_lru);
-	INIT_LIST_HEAD(&clp->cl_callbacks);
-	INIT_LIST_HEAD(&clp->cl_revoked);
-	spin_lock_init(&clp->cl_lock);
 	nfsd4_init_callback(&clp->cl_cb_null);
 	clp->cl_time = get_seconds();
 	clear_bit(0, &clp->cl_cb_slot_busy);
-	rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
 	copy_verf(clp, verf);
 	rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa);
 	gen_confirm(clp);
@@ -3695,9 +3696,16 @@
 static __be32
 nfsd4_free_lock_stateid(struct nfs4_ol_stateid *stp)
 {
-	if (check_for_locks(stp->st_file, lockowner(stp->st_stateowner)))
+	struct nfs4_lockowner *lo = lockowner(stp->st_stateowner);
+
+	if (check_for_locks(stp->st_file, lo))
 		return nfserr_locks_held;
-	release_lock_stateid(stp);
+	/*
+	 * Currently there's a 1-1 lock stateid<->lockowner
+	 * correspondance, and we have to delete the lockowner when we
+	 * delete the lock stateid:
+	 */
+	release_lockowner(lo);
 	return nfs_ok;
 }
 
@@ -4141,6 +4149,10 @@
 
 	if (!same_owner_str(&lo->lo_owner, owner, clid))
 		return false;
+	if (list_empty(&lo->lo_owner.so_stateids)) {
+		WARN_ON_ONCE(1);
+		return false;
+	}
 	lst = list_first_entry(&lo->lo_owner.so_stateids,
 			       struct nfs4_ol_stateid, st_perstateowner);
 	return lst->st_file->fi_inode == inode;
@@ -5048,7 +5060,6 @@
 	int i;
 	struct nfs4_client *clp = NULL;
 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
-	struct rb_node *node, *tmp;
 
 	for (i = 0; i < CLIENT_HASH_SIZE; i++) {
 		while (!list_empty(&nn->conf_id_hashtbl[i])) {
@@ -5057,14 +5068,12 @@
 		}
 	}
 
-	node = rb_first(&nn->unconf_name_tree);
-	while (node != NULL) {
-		tmp = node;
-		node = rb_next(tmp);
-		clp = rb_entry(tmp, struct nfs4_client, cl_namenode);
-		rb_erase(tmp, &nn->unconf_name_tree);
+	for (i = 0; i < CLIENT_HASH_SIZE; i++) {
+		while (!list_empty(&nn->unconf_id_hashtbl[i])) {
+			clp = list_entry(nn->unconf_id_hashtbl[i].next, struct nfs4_client, cl_idhash);
 		destroy_client(clp);
 	}
+	}
 
 	kfree(nn->sessionid_hashtbl);
 	kfree(nn->lockowner_ino_hashtbl);
diff -ruw linux-3.11.10/fs/nfsd/nfs4xdr.c linux-3.11.10-fbx/fs/nfsd/nfs4xdr.c
--- linux-3.11.10/fs/nfsd/nfs4xdr.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfsd/nfs4xdr.c	2015-09-29 11:15:08.497752668 +0200
@@ -141,8 +141,8 @@
 
 static void next_decode_page(struct nfsd4_compoundargs *argp)
 {
-	argp->pagelist++;
 	argp->p = page_address(argp->pagelist[0]);
+	argp->pagelist++;
 	if (argp->pagelen < PAGE_SIZE) {
 		argp->end = argp->p + (argp->pagelen>>2);
 		argp->pagelen = 0;
@@ -411,6 +411,7 @@
 		label->data = kzalloc(dummy32 + 1, GFP_KERNEL);
 		if (!label->data)
 			return nfserr_jukebox;
+		label->len = dummy32;
 		defer_free(argp, kfree, label->data);
 		memcpy(label->data, buf, dummy32);
 	}
@@ -593,7 +594,18 @@
 		READ_BUF(4);
 		READ32(create->cr_linklen);
 		READ_BUF(create->cr_linklen);
-		SAVEMEM(create->cr_linkname, create->cr_linklen);
+		/*
+		 * The VFS will want a null-terminated string, and
+		 * null-terminating in place isn't safe since this might
+		 * end on a page boundary:
+		 */
+		create->cr_linkname =
+				kmalloc(create->cr_linklen + 1, GFP_KERNEL);
+		if (!create->cr_linkname)
+			return nfserr_jukebox;
+		memcpy(create->cr_linkname, p, create->cr_linklen);
+		create->cr_linkname[create->cr_linklen] = '\0';
+		defer_free(argp, kfree, create->cr_linkname);
 		break;
 	case NF4BLK:
 	case NF4CHR:
@@ -1208,6 +1220,7 @@
 		len -= pages * PAGE_SIZE;
 
 		argp->p = (__be32 *)page_address(argp->pagelist[0]);
+		argp->pagelist++;
 		argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE);
 	}
 	argp->p += XDR_QUADLEN(len);
@@ -2109,8 +2122,8 @@
 	err = vfs_getattr(&path, &stat);
 	if (err)
 		goto out_nfserr;
-	if ((bmval0 & (FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL |
-			FATTR4_WORD0_MAXNAME)) ||
+	if ((bmval0 & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE |
+			FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) ||
 	    (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
 		       FATTR4_WORD1_SPACE_TOTAL))) {
 		err = vfs_statfs(&path, &statfs);
@@ -2498,6 +2511,8 @@
 			goto out;
 	}
 	if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
+		if ((buflen -= 16) < 0)
+			goto out_resource;
 		WRITE32(3);
 		WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD0);
 		WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD1);
@@ -3506,6 +3521,9 @@
 	struct nfsd4_test_stateid_id *stateid, *next;
 	__be32 *p;
 
+	if (nfserr)
+		return nfserr;
+
 	RESERVE_SPACE(4 + (4 * test_stateid->ts_num_ids));
 	*p++ = htonl(test_stateid->ts_num_ids);
 
diff -ruw linux-3.11.10/fs/nfsd/nfscache.c linux-3.11.10-fbx/fs/nfsd/nfscache.c
--- linux-3.11.10/fs/nfsd/nfscache.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfsd/nfscache.c	2015-09-29 11:15:08.497752668 +0200
@@ -221,13 +221,6 @@
 	hlist_add_head(&rp->c_hash, cache_hash + hash_32(rp->c_xid, maskbits));
 }
 
-static inline bool
-nfsd_cache_entry_expired(struct svc_cacherep *rp)
-{
-	return rp->c_state != RC_INPROG &&
-	       time_after(jiffies, rp->c_timestamp + RC_EXPIRE);
-}
-
 /*
  * Walk the LRU list and prune off entries that are older than RC_EXPIRE.
  * Also prune the oldest ones when the total exceeds the max number of entries.
@@ -238,8 +231,14 @@
 	struct svc_cacherep *rp, *tmp;
 
 	list_for_each_entry_safe(rp, tmp, &lru_head, c_lru) {
-		if (!nfsd_cache_entry_expired(rp) &&
-		    num_drc_entries <= max_drc_entries)
+		/*
+		 * Don't free entries attached to calls that are still
+		 * in-progress, but do keep scanning the list.
+		 */
+		if (rp->c_state == RC_INPROG)
+			continue;
+		if (num_drc_entries <= max_drc_entries &&
+		    time_before(jiffies, rp->c_timestamp + RC_EXPIRE))
 			break;
 		nfsd_reply_cache_free_locked(rp);
 	}
@@ -395,22 +394,8 @@
 
 	/*
 	 * Since the common case is a cache miss followed by an insert,
-	 * preallocate an entry. First, try to reuse the first entry on the LRU
-	 * if it works, then go ahead and prune the LRU list.
+	 * preallocate an entry.
 	 */
-	spin_lock(&cache_lock);
-	if (!list_empty(&lru_head)) {
-		rp = list_first_entry(&lru_head, struct svc_cacherep, c_lru);
-		if (nfsd_cache_entry_expired(rp) ||
-		    num_drc_entries >= max_drc_entries) {
-			lru_put_end(rp);
-			prune_cache_entries();
-			goto search_cache;
-		}
-	}
-
-	/* No expired ones available, allocate a new one. */
-	spin_unlock(&cache_lock);
 	rp = nfsd_reply_cache_alloc();
 	spin_lock(&cache_lock);
 	if (likely(rp)) {
@@ -418,7 +403,9 @@
 		drc_mem_usage += sizeof(*rp);
 	}
 
-search_cache:
+	/* go ahead and prune the cache */
+	prune_cache_entries();
+
 	found = nfsd_cache_search(rqstp, csum);
 	if (found) {
 		if (likely(rp))
@@ -432,15 +419,6 @@
 		goto out;
 	}
 
-	/*
-	 * We're keeping the one we just allocated. Are we now over the
-	 * limit? Prune one off the tip of the LRU in trade for the one we
-	 * just allocated if so.
-	 */
-	if (num_drc_entries >= max_drc_entries)
-		nfsd_reply_cache_free_locked(list_first_entry(&lru_head,
-						struct svc_cacherep, c_lru));
-
 	nfsdstats.rcmisses++;
 	rqstp->rq_cacherep = rp;
 	rp->c_state = RC_INPROG;
diff -ruw linux-3.11.10/fs/nfsd/nfsctl.c linux-3.11.10-fbx/fs/nfsd/nfsctl.c
--- linux-3.11.10/fs/nfsd/nfsctl.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfsd/nfsctl.c	2015-09-29 11:15:08.497752668 +0200
@@ -699,6 +699,11 @@
 	if (err != 0 || fd < 0)
 		return -EINVAL;
 
+	if (svc_alien_sock(net, fd)) {
+		printk(KERN_ERR "%s: socket net is different to NFSd's one\n", __func__);
+		return -EINVAL;
+	}
+
 	err = nfsd_create_serv(net);
 	if (err != 0)
 		return err;
diff -ruw linux-3.11.10/fs/nfsd/nfsd.h linux-3.11.10-fbx/fs/nfsd/nfsd.h
--- linux-3.11.10/fs/nfsd/nfsd.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfsd/nfsd.h	2015-09-29 11:15:08.497752668 +0200
@@ -282,7 +282,7 @@
  * reason.
  */
 #define	COMPOUND_SLACK_SPACE		140    /* OP_GETFH */
-#define COMPOUND_ERR_SLACK_SPACE	12     /* OP_SETATTR */
+#define COMPOUND_ERR_SLACK_SPACE	16     /* OP_SETATTR */
 
 #define NFSD_LAUNDROMAT_MINTIMEOUT      1   /* seconds */
 
diff -ruw linux-3.11.10/fs/nfsd/vfs.c linux-3.11.10-fbx/fs/nfsd/vfs.c
--- linux-3.11.10/fs/nfsd/vfs.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/nfsd/vfs.c	2015-09-29 11:15:08.501752705 +0200
@@ -298,41 +298,12 @@
 }
 
 /*
- * Set various file attributes.
- * N.B. After this call fhp needs an fh_put
+ * Go over the attributes and take care of the small differences between
+ * NFS semantics and what Linux expects.
  */
-__be32
-nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
-	     int check_guard, time_t guardtime)
+static void
+nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
 {
-	struct dentry	*dentry;
-	struct inode	*inode;
-	int		accmode = NFSD_MAY_SATTR;
-	umode_t		ftype = 0;
-	__be32		err;
-	int		host_err;
-	int		size_change = 0;
-
-	if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
-		accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE;
-	if (iap->ia_valid & ATTR_SIZE)
-		ftype = S_IFREG;
-
-	/* Get inode */
-	err = fh_verify(rqstp, fhp, ftype, accmode);
-	if (err)
-		goto out;
-
-	dentry = fhp->fh_dentry;
-	inode = dentry->d_inode;
-
-	/* Ignore any mode updates on symlinks */
-	if (S_ISLNK(inode->i_mode))
-		iap->ia_valid &= ~ATTR_MODE;
-
-	if (!iap->ia_valid)
-		goto out;
-
 	/*
 	 * NFSv2 does not differentiate between "set-[ac]time-to-now"
 	 * which only requires access, and "set-[ac]time-to-X" which
@@ -342,8 +313,7 @@
 	 * convert to "set to now" instead of "set to explicit time"
 	 *
 	 * We only call inode_change_ok as the last test as technically
-	 * it is not an interface that we should be using.  It is only
-	 * valid if the filesystem does not define it's own i_op->setattr.
+	 * it is not an interface that we should be using.
 	 */
 #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
 #define	MAX_TOUCH_TIME_ERROR (30*60)
@@ -370,30 +340,6 @@
 		}
 	}
 	    
-	/*
-	 * The size case is special.
-	 * It changes the file as well as the attributes.
-	 */
-	if (iap->ia_valid & ATTR_SIZE) {
-		if (iap->ia_size < inode->i_size) {
-			err = nfsd_permission(rqstp, fhp->fh_export, dentry,
-					NFSD_MAY_TRUNC|NFSD_MAY_OWNER_OVERRIDE);
-			if (err)
-				goto out;
-		}
-
-		host_err = get_write_access(inode);
-		if (host_err)
-			goto out_nfserr;
-
-		size_change = 1;
-		host_err = locks_verify_truncate(inode, NULL, iap->ia_size);
-		if (host_err) {
-			put_write_access(inode);
-			goto out_nfserr;
-		}
-	}
-
 	/* sanitize the mode change */
 	if (iap->ia_valid & ATTR_MODE) {
 		iap->ia_mode &= S_IALLUGO;
@@ -415,32 +361,120 @@
 			iap->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID);
 		}
 	}
+}
+
+static __be32
+nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp,
+		struct iattr *iap)
+{
+	struct inode *inode = fhp->fh_dentry->d_inode;
+	int host_err;
+
+	if (iap->ia_size < inode->i_size) {
+		__be32 err;
+
+		err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
+				NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE);
+		if (err)
+			return err;
+	}
+
+	host_err = get_write_access(inode);
+	if (host_err)
+		goto out_nfserrno;
+
+	host_err = locks_verify_truncate(inode, NULL, iap->ia_size);
+	if (host_err)
+		goto out_put_write_access;
+	return 0;
+
+out_put_write_access:
+	put_write_access(inode);
+out_nfserrno:
+	return nfserrno(host_err);
+}
+
+/*
+ * Set various file attributes.  After this call fhp needs an fh_put.
+ */
+__be32
+nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
+	     int check_guard, time_t guardtime)
+{
+	struct dentry	*dentry;
+	struct inode	*inode;
+	int		accmode = NFSD_MAY_SATTR;
+	umode_t		ftype = 0;
+	__be32		err;
+	int		host_err;
+	bool		get_write_count;
+	int		size_change = 0;
+
+	if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
+		accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE;
+	if (iap->ia_valid & ATTR_SIZE)
+		ftype = S_IFREG;
+
+	/* Callers that do fh_verify should do the fh_want_write: */
+	get_write_count = !fhp->fh_dentry;
+
+	/* Get inode */
+	err = fh_verify(rqstp, fhp, ftype, accmode);
+	if (err)
+		goto out;
+	if (get_write_count) {
+		host_err = fh_want_write(fhp);
+		if (host_err)
+			return nfserrno(host_err);
+	}
+
+	dentry = fhp->fh_dentry;
+	inode = dentry->d_inode;
+
+	/* Ignore any mode updates on symlinks */
+	if (S_ISLNK(inode->i_mode))
+		iap->ia_valid &= ~ATTR_MODE;
+
+	if (!iap->ia_valid)
+		goto out;
 
-	/* Change the attributes. */
+	nfsd_sanitize_attrs(inode, iap);
+
+	/*
+	 * The size case is special, it changes the file in addition to the
+	 * attributes.
+	 */
+	if (iap->ia_valid & ATTR_SIZE) {
+		err = nfsd_get_write_access(rqstp, fhp, iap);
+		if (err)
+			goto out;
+		size_change = 1;
+	}
 
 	iap->ia_valid |= ATTR_CTIME;
 
+	if (check_guard && guardtime != inode->i_ctime.tv_sec) {
 	err = nfserr_notsync;
-	if (!check_guard || guardtime == inode->i_ctime.tv_sec) {
+		goto out_put_write_access;
+	}
+
 		host_err = nfsd_break_lease(inode);
 		if (host_err)
-			goto out_nfserr;
-		fh_lock(fhp);
+		goto out_put_write_access_nfserror;
 
+	fh_lock(fhp);
 		host_err = notify_change(dentry, iap);
-		err = nfserrno(host_err);
 		fh_unlock(fhp);
-	}
+
+out_put_write_access_nfserror:
+	err = nfserrno(host_err);
+out_put_write_access:
 	if (size_change)
 		put_write_access(inode);
 	if (!err)
 		commit_metadata(fhp);
 out:
 	return err;
-
-out_nfserr:
-	err = nfserrno(host_err);
-	goto out;
 }
 
 #if defined(CONFIG_NFSD_V2_ACL) || \
diff -ruw linux-3.11.10/fs/notify/fanotify/fanotify_user.c linux-3.11.10-fbx/fs/notify/fanotify/fanotify_user.c
--- linux-3.11.10/fs/notify/fanotify/fanotify_user.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/notify/fanotify/fanotify_user.c	2015-09-29 11:15:08.525752887 +0200
@@ -888,9 +888,9 @@
 {
 	return sys_fanotify_mark(fanotify_fd, flags,
 #ifdef __BIG_ENDIAN
-				((__u64)mask1 << 32) | mask0,
-#else
 				((__u64)mask0 << 32) | mask1,
+#else
+				((__u64)mask1 << 32) | mask0,
 #endif
 				 dfd, pathname);
 }
diff -ruw linux-3.11.10/fs/open.c linux-3.11.10-fbx/fs/open.c
--- linux-3.11.10/fs/open.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/open.c	2015-09-29 11:15:08.553753096 +0200
@@ -628,23 +628,12 @@
 static inline int __get_file_write_access(struct inode *inode,
 					  struct vfsmount *mnt)
 {
-	int error;
-	error = get_write_access(inode);
+	int error = get_write_access(inode);
 	if (error)
 		return error;
-	/*
-	 * Do not take mount writer counts on
-	 * special files since no writes to
-	 * the mount itself will occur.
-	 */
-	if (!special_file(inode->i_mode)) {
-		/*
-		 * Balanced in __fput()
-		 */
 		error = __mnt_want_write(mnt);
 		if (error)
 			put_write_access(inode);
-	}
 	return error;
 }
 
@@ -677,11 +666,10 @@
 
 	path_get(&f->f_path);
 	inode = f->f_inode = f->f_path.dentry->d_inode;
-	if (f->f_mode & FMODE_WRITE) {
+	if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) {
 		error = __get_file_write_access(inode, f->f_path.mnt);
 		if (error)
 			goto cleanup_file;
-		if (!special_file(inode->i_mode))
 			file_take_write(f);
 	}
 
@@ -723,7 +711,6 @@
 	fops_put(f->f_op);
 	file_sb_list_del(f);
 	if (f->f_mode & FMODE_WRITE) {
-		put_write_access(inode);
 		if (!special_file(inode->i_mode)) {
 			/*
 			 * We don't consider this a real
@@ -731,6 +718,7 @@
 			 * because it all happenend right
 			 * here, so just reset the state.
 			 */
+			put_write_access(inode);
 			file_reset_write(f);
 			__mnt_drop_write(f->f_path.mnt);
 		}
diff -ruw linux-3.11.10/fs/pipe.c linux-3.11.10-fbx/fs/pipe.c
--- linux-3.11.10/fs/pipe.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/pipe.c	2015-09-29 11:15:08.557753125 +0200
@@ -726,11 +726,25 @@
 	return mask;
 }
 
+static void put_pipe_info(struct inode *inode, struct pipe_inode_info *pipe)
+{
+	int kill = 0;
+
+	spin_lock(&inode->i_lock);
+	if (!--pipe->files) {
+		inode->i_pipe = NULL;
+		kill = 1;
+	}
+	spin_unlock(&inode->i_lock);
+
+	if (kill)
+		free_pipe_info(pipe);
+}
+
 static int
 pipe_release(struct inode *inode, struct file *file)
 {
-	struct pipe_inode_info *pipe = inode->i_pipe;
-	int kill = 0;
+	struct pipe_inode_info *pipe = file->private_data;
 
 	__pipe_lock(pipe);
 	if (file->f_mode & FMODE_READ)
@@ -743,17 +757,9 @@
 		kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
 		kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
 	}
-	spin_lock(&inode->i_lock);
-	if (!--pipe->files) {
-		inode->i_pipe = NULL;
-		kill = 1;
-	}
-	spin_unlock(&inode->i_lock);
 	__pipe_unlock(pipe);
 
-	if (kill)
-		free_pipe_info(pipe);
-
+	put_pipe_info(inode, pipe);
 	return 0;
 }
 
@@ -1014,7 +1020,6 @@
 {
 	struct pipe_inode_info *pipe;
 	bool is_pipe = inode->i_sb->s_magic == PIPEFS_MAGIC;
-	int kill = 0;
 	int ret;
 
 	filp->f_version = 0;
@@ -1130,15 +1135,9 @@
 	goto err;
 
 err:
-	spin_lock(&inode->i_lock);
-	if (!--pipe->files) {
-		inode->i_pipe = NULL;
-		kill = 1;
-	}
-	spin_unlock(&inode->i_lock);
 	__pipe_unlock(pipe);
-	if (kill)
-		free_pipe_info(pipe);
+
+	put_pipe_info(inode, pipe);
 	return ret;
 }
 
diff -ruw linux-3.11.10/fs/posix_acl.c linux-3.11.10-fbx/fs/posix_acl.c
--- linux-3.11.10/fs/posix_acl.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/posix_acl.c	2015-09-29 11:15:08.557753125 +0200
@@ -158,6 +158,12 @@
 	umode_t mode = 0;
 	int not_equiv = 0;
 
+	/*
+	 * A null ACL can always be presented as mode bits.
+	 */
+	if (!acl)
+		return 0;
+
 	FOREACH_ACL_ENTRY(pa, acl, pe) {
 		switch (pa->e_tag) {
 			case ACL_USER_OBJ:
diff -ruw linux-3.11.10/fs/proc/array.c linux-3.11.10-fbx/fs/proc/array.c
--- linux-3.11.10/fs/proc/array.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/proc/array.c	2015-09-29 11:15:08.557753125 +0200
@@ -160,6 +160,21 @@
 	return *p;
 }
 
+static const char *const task_exec_mode_array[] = {
+	"0 (Denied)",
+	"1 (Once)",
+	"2 (Unlimited)",
+};
+
+static inline const char *get_task_exec_mode(struct task_struct *tsk)
+{
+	unsigned int exec_mode = tsk->exec_mode;
+
+	if (exec_mode > EXEC_MODE_UNLIMITED)
+		return "? (Invalid)";
+	return task_exec_mode_array[exec_mode];
+}
+
 static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
 				struct pid *pid, struct task_struct *p)
 {
@@ -353,6 +368,12 @@
 			p->nivcsw);
 }
 
+static inline void task_exec_mode(struct seq_file *m,
+				  struct task_struct *p)
+{
+	seq_printf(m, "Exec mode: %s\n", get_task_exec_mode(p));
+}
+
 static void task_cpus_allowed(struct seq_file *m, struct task_struct *task)
 {
 	seq_puts(m, "Cpus_allowed:\t");
@@ -381,6 +402,7 @@
 	task_cpus_allowed(m, task);
 	cpuset_task_status_allowed(m, task);
 	task_context_switch_counts(m, task);
+	task_exec_mode(m, task);
 	return 0;
 }
 
diff -ruw linux-3.11.10/fs/proc/base.c linux-3.11.10-fbx/fs/proc/base.c
--- linux-3.11.10/fs/proc/base.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/proc/base.c	2015-09-29 11:15:08.557753125 +0200
@@ -1813,6 +1813,7 @@
 	if (rc)
 		goto out_mmput;
 
+	rc = -ENOENT;
 	down_read(&mm->mmap_sem);
 	vma = find_exact_vma(mm, vm_start, vm_end);
 	if (vma && vma->vm_file) {
diff -ruw linux-3.11.10/fs/proc/stat.c linux-3.11.10-fbx/fs/proc/stat.c
--- linux-3.11.10/fs/proc/stat.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/proc/stat.c	2015-09-29 11:15:08.561753161 +0200
@@ -184,29 +184,11 @@
 
 static int stat_open(struct inode *inode, struct file *file)
 {
-	size_t size = 1024 + 128 * num_possible_cpus();
-	char *buf;
-	struct seq_file *m;
-	int res;
+	size_t size = 1024 + 128 * num_online_cpus();
 
 	/* minimum size to display an interrupt count : 2 bytes */
 	size += 2 * nr_irqs;
-
-	/* don't ask for more than the kmalloc() max size */
-	if (size > KMALLOC_MAX_SIZE)
-		size = KMALLOC_MAX_SIZE;
-	buf = kmalloc(size, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	res = single_open(file, show_stat, NULL);
-	if (!res) {
-		m = file->private_data;
-		m->buf = buf;
-		m->size = ksize(buf);
-	} else
-		kfree(buf);
-	return res;
+	return single_open_size(file, show_stat, NULL, size);
 }
 
 static const struct file_operations proc_stat_operations = {
diff -ruw linux-3.11.10/fs/pstore/ram.c linux-3.11.10-fbx/fs/pstore/ram.c
--- linux-3.11.10/fs/pstore/ram.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/pstore/ram.c	2015-09-29 11:15:08.561753161 +0200
@@ -400,11 +400,11 @@
 		goto fail_out;
 	}
 
-	if (!is_power_of_2(pdata->record_size))
+	if (pdata->record_size && !is_power_of_2(pdata->record_size))
 		pdata->record_size = rounddown_pow_of_two(pdata->record_size);
-	if (!is_power_of_2(pdata->console_size))
+	if (pdata->console_size && !is_power_of_2(pdata->console_size))
 		pdata->console_size = rounddown_pow_of_two(pdata->console_size);
-	if (!is_power_of_2(pdata->ftrace_size))
+	if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size))
 		pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size);
 
 	cxt->dump_read_cnt = 0;
diff -ruw linux-3.11.10/fs/ramfs/file-mmu.c linux-3.11.10-fbx/fs/ramfs/file-mmu.c
--- linux-3.11.10/fs/ramfs/file-mmu.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ramfs/file-mmu.c	2015-09-29 11:15:08.565753187 +0200
@@ -27,6 +27,7 @@
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/ramfs.h>
+#include <linux/xattr.h>
 
 #include "internal.h"
 
@@ -52,4 +53,11 @@
 const struct inode_operations ramfs_file_inode_operations = {
 	.setattr	= simple_setattr,
 	.getattr	= simple_getattr,
+#ifdef CONFIG_RAMFS_XATTR
+	.setxattr	= generic_setxattr,
+	.getxattr	= generic_getxattr,
+	.listxattr	= generic_listxattr,
+	.removexattr	= generic_removexattr,
+#endif
 };
+
diff -ruw linux-3.11.10/fs/ramfs/inode.c linux-3.11.10-fbx/fs/ramfs/inode.c
--- linux-3.11.10/fs/ramfs/inode.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ramfs/inode.c	2015-09-29 11:15:08.565753187 +0200
@@ -36,12 +36,14 @@
 #include <linux/magic.h>
 #include <linux/slab.h>
 #include <asm/uaccess.h>
+#include <linux/xattr.h>
 #include "internal.h"
 
 #define RAMFS_DEFAULT_MODE	0755
 
 static const struct super_operations ramfs_ops;
 static const struct inode_operations ramfs_dir_inode_operations;
+static struct kmem_cache *ramfs_inode_cache;
 
 static struct backing_dev_info ramfs_backing_dev_info = {
 	.name		= "ramfs",
@@ -51,6 +53,28 @@
 			  BDI_CAP_READ_MAP | BDI_CAP_WRITE_MAP | BDI_CAP_EXEC_MAP,
 };
 
+static struct inode *ramfs_alloc_inode(struct super_block *sb)
+{
+	struct ramfs_inode_info *rii;
+
+	rii = kmem_cache_alloc(ramfs_inode_cache, GFP_KERNEL);
+	if (!rii)
+		return NULL;
+	return &rii->vfs_inode;
+}
+
+static void ramfs_destroy_inode(struct inode *ino)
+{
+	struct ramfs_inode_info *rii;
+
+	rii = RAMFS_I(ino);
+
+#ifdef CONFIG_RAMFS_XATTR
+	ramfs_inode_purge_xattrs(rii);
+#endif
+	kmem_cache_free(ramfs_inode_cache, rii);
+}
+
 struct inode *ramfs_get_inode(struct super_block *sb,
 				const struct inode *dir, umode_t mode, dev_t dev)
 {
@@ -148,9 +172,17 @@
 	.rmdir		= simple_rmdir,
 	.mknod		= ramfs_mknod,
 	.rename		= simple_rename,
+#ifdef CONFIG_RAMFS_XATTR
+	.setxattr	= generic_setxattr,
+	.getxattr	= generic_getxattr,
+	.listxattr	= generic_listxattr,
+	.removexattr	= generic_removexattr,
+#endif
 };
 
 static const struct super_operations ramfs_ops = {
+	.alloc_inode	= ramfs_alloc_inode,
+	.destroy_inode	= ramfs_destroy_inode,
 	.statfs		= simple_statfs,
 	.drop_inode	= generic_delete_inode,
 	.show_options	= generic_show_options,
@@ -229,6 +261,9 @@
 	sb->s_magic		= RAMFS_MAGIC;
 	sb->s_op		= &ramfs_ops;
 	sb->s_time_gran		= 1;
+#ifdef CONFIG_RAMFS_XATTR
+	sb->s_xattr = ramfs_xattr_handlers;
+#endif
 
 	inode = ramfs_get_inode(sb, NULL, S_IFDIR | fsi->mount_opts.mode, 0);
 	sb->s_root = d_make_root(inode);
@@ -268,6 +303,16 @@
 	.kill_sb	= kill_litter_super,
 };
 
+static void ramfs_inode_init_once(void *ptr)
+{
+	struct ramfs_inode_info *p = (struct ramfs_inode_info *)ptr;
+
+	inode_init_once(&p->vfs_inode);
+#ifdef CONFIG_RAMFS_XATTR_USER
+	INIT_LIST_HEAD(&p->xattr_user_list);
+#endif
+}
+
 static int __init init_ramfs_fs(void)
 {
 	return register_filesystem(&ramfs_fs_type);
@@ -282,9 +327,18 @@
 	if (err)
 		return err;
 
-	err = register_filesystem(&rootfs_fs_type);
-	if (err)
+	ramfs_inode_cache = kmem_cache_create("ramfs_inode_cache",
+					      sizeof (struct ramfs_inode_info),
+					      0, 0, ramfs_inode_init_once);
+	if (!ramfs_inode_cache) {
 		bdi_destroy(&ramfs_backing_dev_info);
+		return -ENOMEM;
+	}
 
+	err = register_filesystem(&rootfs_fs_type);
+	if (err) {
+		kmem_cache_destroy(ramfs_inode_cache);
+		bdi_destroy(&ramfs_backing_dev_info);
+	}
 	return err;
 }
diff -ruw linux-3.11.10/fs/ramfs/internal.h linux-3.11.10-fbx/fs/ramfs/internal.h
--- linux-3.11.10/fs/ramfs/internal.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ramfs/internal.h	2015-09-29 11:15:08.565753187 +0200
@@ -9,6 +9,42 @@
  * 2 of the License, or (at your option) any later version.
  */
 
+#ifndef RAMFS_INTERNAL_H
+# define RAMFS_INTERNAL_H
+
+/* need list_head */
+#include <linux/list.h>
 
 extern const struct address_space_operations ramfs_aops;
 extern const struct inode_operations ramfs_file_inode_operations;
+
+
+struct ramfs_xattr
+{
+	char *name;
+	void *data;
+	size_t data_len;
+
+	struct list_head list;
+};
+
+struct ramfs_inode_info
+{
+	struct inode vfs_inode;
+#ifdef CONFIG_RAMFS_XATTR_USER
+	struct list_head xattr_user_list;
+#endif
+};
+
+static inline struct ramfs_inode_info *RAMFS_I(struct inode *inode)
+{
+	return container_of(inode, struct ramfs_inode_info, vfs_inode);
+}
+
+#ifdef CONFIG_RAMFS_XATTR
+void ramfs_inode_purge_xattrs(struct ramfs_inode_info *rii);
+extern const struct xattr_handler *ramfs_xattr_handlers[];
+#endif
+
+
+#endif /* !RAMFS_INTERNAL_H */
diff -ruw linux-3.11.10/fs/ramfs/Makefile linux-3.11.10-fbx/fs/ramfs/Makefile
--- linux-3.11.10/fs/ramfs/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ramfs/Makefile	2015-09-29 11:15:08.565753187 +0200
@@ -6,4 +6,5 @@
 
 file-mmu-y := file-nommu.o
 file-mmu-$(CONFIG_MMU) := file-mmu.o
-ramfs-objs += inode.o $(file-mmu-y)
+ramfs-xattr-$(CONFIG_RAMFS_XATTR) += xattr.o
+ramfs-objs += inode.o $(file-mmu-y) $(ramfs-xattr-y)
diff -ruw linux-3.11.10/fs/read_write.c linux-3.11.10-fbx/fs/read_write.c
--- linux-3.11.10/fs/read_write.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/read_write.c	2015-09-29 11:15:08.565753187 +0200
@@ -980,9 +980,9 @@
 	return ret;
 }
 
-COMPAT_SYSCALL_DEFINE3(readv, unsigned long, fd,
+COMPAT_SYSCALL_DEFINE3(readv, compat_ulong_t, fd,
 		const struct compat_iovec __user *,vec,
-		unsigned long, vlen)
+		compat_ulong_t, vlen)
 {
 	struct fd f = fdget(fd);
 	ssize_t ret;
@@ -1017,9 +1017,9 @@
 	return ret;
 }
 
-COMPAT_SYSCALL_DEFINE5(preadv, unsigned long, fd,
+COMPAT_SYSCALL_DEFINE5(preadv, compat_ulong_t, fd,
 		const struct compat_iovec __user *,vec,
-		unsigned long, vlen, u32, pos_low, u32, pos_high)
+		compat_ulong_t, vlen, u32, pos_low, u32, pos_high)
 {
 	loff_t pos = ((loff_t)pos_high << 32) | pos_low;
 	return compat_sys_preadv64(fd, vec, vlen, pos);
@@ -1047,9 +1047,9 @@
 	return ret;
 }
 
-COMPAT_SYSCALL_DEFINE3(writev, unsigned long, fd,
+COMPAT_SYSCALL_DEFINE3(writev, compat_ulong_t, fd,
 		const struct compat_iovec __user *, vec,
-		unsigned long, vlen)
+		compat_ulong_t, vlen)
 {
 	struct fd f = fdget(fd);
 	ssize_t ret;
@@ -1084,9 +1084,9 @@
 	return ret;
 }
 
-COMPAT_SYSCALL_DEFINE5(pwritev, unsigned long, fd,
+COMPAT_SYSCALL_DEFINE5(pwritev, compat_ulong_t, fd,
 		const struct compat_iovec __user *,vec,
-		unsigned long, vlen, u32, pos_low, u32, pos_high)
+		compat_ulong_t, vlen, u32, pos_low, u32, pos_high)
 {
 	loff_t pos = ((loff_t)pos_high << 32) | pos_low;
 	return compat_sys_pwritev64(fd, vec, vlen, pos);
diff -ruw linux-3.11.10/fs/splice.c linux-3.11.10-fbx/fs/splice.c
--- linux-3.11.10/fs/splice.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/splice.c	2015-09-29 11:15:08.577753278 +0200
@@ -555,6 +555,24 @@
 	.get = generic_pipe_buf_get,
 };
 
+static int generic_pipe_buf_nosteal(struct pipe_inode_info *pipe,
+				    struct pipe_buffer *buf)
+{
+	return 1;
+}
+
+/* Pipe buffer operations for a socket and similar. */
+const struct pipe_buf_operations nosteal_pipe_buf_ops = {
+	.can_merge = 0,
+	.map = generic_pipe_buf_map,
+	.unmap = generic_pipe_buf_unmap,
+	.confirm = generic_pipe_buf_confirm,
+	.release = generic_pipe_buf_release,
+	.steal = generic_pipe_buf_nosteal,
+	.get = generic_pipe_buf_get,
+};
+EXPORT_SYMBOL(nosteal_pipe_buf_ops);
+
 static ssize_t kernel_readv(struct file *file, const struct iovec *vec,
 			    unsigned long vlen, loff_t offset)
 {
diff -ruw linux-3.11.10/fs/squashfs/block.c linux-3.11.10-fbx/fs/squashfs/block.c
--- linux-3.11.10/fs/squashfs/block.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/block.c	2014-07-29 21:29:44.905867048 +0200
@@ -36,6 +36,7 @@
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "page_actor.h"
 
 /*
  * Read the metadata block length, this is stored in the first two
@@ -86,16 +87,16 @@
  * generated a larger block - this does occasionally happen with compression
  * algorithms).
  */
-int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
-			int length, u64 *next_index, int srclength, int pages)
+int squashfs_read_data(struct super_block *sb, u64 index, int length,
+		u64 *next_index, struct squashfs_page_actor *output)
 {
 	struct squashfs_sb_info *msblk = sb->s_fs_info;
 	struct buffer_head **bh;
 	int offset = index & ((1 << msblk->devblksize_log2) - 1);
 	u64 cur_index = index >> msblk->devblksize_log2;
-	int bytes, compressed, b = 0, k = 0, page = 0, avail;
+	int bytes, compressed, b = 0, k = 0, avail, i;
 
-	bh = kcalloc(((srclength + msblk->devblksize - 1)
+	bh = kcalloc(((output->length + msblk->devblksize - 1)
 		>> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
 	if (bh == NULL)
 		return -ENOMEM;
@@ -111,9 +112,9 @@
 			*next_index = index + length;
 
 		TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
-			index, compressed ? "" : "un", length, srclength);
+			index, compressed ? "" : "un", length, output->length);
 
-		if (length < 0 || length > srclength ||
+		if (length < 0 || length > output->length ||
 				(index + length) > msblk->bytes_used)
 			goto read_failure;
 
@@ -145,7 +146,7 @@
 		TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
 				compressed ? "" : "un", length);
 
-		if (length < 0 || length > srclength ||
+		if (length < 0 || length > output->length ||
 					(index + length) > msblk->bytes_used)
 			goto block_release;
 
@@ -158,35 +159,36 @@
 		ll_rw_block(READ, b - 1, bh + 1);
 	}
 
+	for (i = 0; i < b; i++) {
+		wait_on_buffer(bh[i]);
+		if (!buffer_uptodate(bh[i]))
+			goto block_release;
+	}
+
 	if (compressed) {
-		length = squashfs_decompress(msblk, buffer, bh, b, offset,
-			 length, srclength, pages);
+		length = squashfs_decompress(msblk, bh, b, offset, length,
+			output);
 		if (length < 0)
 			goto read_failure;
 	} else {
 		/*
 		 * Block is uncompressed.
 		 */
-		int i, in, pg_offset = 0;
-
-		for (i = 0; i < b; i++) {
-			wait_on_buffer(bh[i]);
-			if (!buffer_uptodate(bh[i]))
-				goto block_release;
-		}
+		int in, pg_offset = 0;
+		void *data = squashfs_first_page(output);
 
 		for (bytes = length; k < b; k++) {
 			in = min(bytes, msblk->devblksize - offset);
 			bytes -= in;
 			while (in) {
 				if (pg_offset == PAGE_CACHE_SIZE) {
-					page++;
+					data = squashfs_next_page(output);
 					pg_offset = 0;
 				}
 				avail = min_t(int, in, PAGE_CACHE_SIZE -
 						pg_offset);
-				memcpy(buffer[page] + pg_offset,
-						bh[k]->b_data + offset, avail);
+				memcpy(data + pg_offset, bh[k]->b_data + offset,
+						avail);
 				in -= avail;
 				pg_offset += avail;
 				offset += avail;
@@ -194,6 +196,7 @@
 			offset = 0;
 			put_bh(bh[k]);
 		}
+		squashfs_finish_page(output);
 	}
 
 	kfree(bh);
diff -ruw linux-3.11.10/fs/squashfs/cache.c linux-3.11.10-fbx/fs/squashfs/cache.c
--- linux-3.11.10/fs/squashfs/cache.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/cache.c	2014-07-29 21:29:44.905867048 +0200
@@ -56,6 +56,7 @@
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
+#include "page_actor.h"
 
 /*
  * Look-up block in cache, and increment usage count.  If not in cache, read
@@ -119,9 +120,8 @@
 			entry->error = 0;
 			spin_unlock(&cache->lock);
 
-			entry->length = squashfs_read_data(sb, entry->data,
-				block, length, &entry->next_index,
-				cache->block_size, cache->pages);
+			entry->length = squashfs_read_data(sb, block, length,
+				&entry->next_index, entry->actor);
 
 			spin_lock(&cache->lock);
 
@@ -220,6 +220,7 @@
 				kfree(cache->entry[i].data[j]);
 			kfree(cache->entry[i].data);
 		}
+		kfree(cache->entry[i].actor);
 	}
 
 	kfree(cache->entry);
@@ -280,6 +281,13 @@
 				goto cleanup;
 			}
 		}
+
+		entry->actor = squashfs_page_actor_init(entry->data,
+						cache->pages, 0);
+		if (entry->actor == NULL) {
+			ERROR("Failed to allocate %s cache entry\n", name);
+			goto cleanup;
+		}
 	}
 
 	return cache;
@@ -410,6 +418,7 @@
 	int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
 	int i, res;
 	void *table, *buffer, **data;
+	struct squashfs_page_actor *actor;
 
 	table = buffer = kmalloc(length, GFP_KERNEL);
 	if (table == NULL)
@@ -421,19 +430,28 @@
 		goto failed;
 	}
 
+	actor = squashfs_page_actor_init(data, pages, length);
+	if (actor == NULL) {
+		res = -ENOMEM;
+		goto failed2;
+	}
+
 	for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)
 		data[i] = buffer;
 
-	res = squashfs_read_data(sb, data, block, length |
-		SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length, pages);
+	res = squashfs_read_data(sb, block, length |
+		SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor);
 
 	kfree(data);
+	kfree(actor);
 
 	if (res < 0)
 		goto failed;
 
 	return table;
 
+failed2:
+	kfree(data);
 failed:
 	kfree(table);
 	return ERR_PTR(res);
diff -ruw linux-3.11.10/fs/squashfs/decompressor.c linux-3.11.10-fbx/fs/squashfs/decompressor.c
--- linux-3.11.10/fs/squashfs/decompressor.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/decompressor.c	2015-09-29 11:15:08.577753278 +0200
@@ -30,6 +30,7 @@
 #include "squashfs_fs_sb.h"
 #include "decompressor.h"
 #include "squashfs.h"
+#include "page_actor.h"
 
 /*
  * This file (and decompressor.h) implements a decompressor framework for
@@ -37,29 +38,29 @@
  */
 
 static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = {
-	NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
+	NULL, NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
 };
 
 #ifndef CONFIG_SQUASHFS_LZO
 static const struct squashfs_decompressor squashfs_lzo_comp_ops = {
-	NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
+	NULL, NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
 };
 #endif
 
 #ifndef CONFIG_SQUASHFS_XZ
 static const struct squashfs_decompressor squashfs_xz_comp_ops = {
-	NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0
+	NULL, NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0
 };
 #endif
 
 #ifndef CONFIG_SQUASHFS_ZLIB
 static const struct squashfs_decompressor squashfs_zlib_comp_ops = {
-	NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0
+	NULL, NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0
 };
 #endif
 
 static const struct squashfs_decompressor squashfs_unknown_comp_ops = {
-	NULL, NULL, NULL, 0, "unknown", 0
+	NULL, NULL, NULL, NULL, 0, "unknown", 0
 };
 
 static const struct squashfs_decompressor *decompressor[] = {
@@ -83,10 +84,11 @@
 }
 
 
-void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
+static void *get_comp_opts(struct super_block *sb, unsigned short flags)
 {
 	struct squashfs_sb_info *msblk = sb->s_fs_info;
-	void *strm, *buffer = NULL;
+	void *buffer = NULL, *comp_opts;
+	struct squashfs_page_actor *actor = NULL;
 	int length = 0;
 
 	/*
@@ -94,23 +96,46 @@
 	 */
 	if (SQUASHFS_COMP_OPTS(flags)) {
 		buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
-		if (buffer == NULL)
-			return ERR_PTR(-ENOMEM);
+		if (buffer == NULL) {
+			comp_opts = ERR_PTR(-ENOMEM);
+			goto out;
+		}
+
+		actor = squashfs_page_actor_init(&buffer, 1, 0);
+		if (actor == NULL) {
+			comp_opts = ERR_PTR(-ENOMEM);
+			goto out;
+		}
 
-		length = squashfs_read_data(sb, &buffer,
-			sizeof(struct squashfs_super_block), 0, NULL,
-			PAGE_CACHE_SIZE, 1);
+		length = squashfs_read_data(sb,
+			sizeof(struct squashfs_super_block), 0, NULL, actor);
 
 		if (length < 0) {
-			strm = ERR_PTR(length);
-			goto finished;
+			comp_opts = ERR_PTR(length);
+			goto out;
 		}
 	}
 
-	strm = msblk->decompressor->init(msblk, buffer, length);
+	comp_opts = squashfs_comp_opts(msblk, buffer, length);
 
-finished:
+out:
+	kfree(actor);
 	kfree(buffer);
+	return comp_opts;
+}
+
+
+void *squashfs_decompressor_setup(struct super_block *sb, unsigned short flags)
+{
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	void *stream, *comp_opts = get_comp_opts(sb, flags);
+
+	if (IS_ERR(comp_opts))
+		return comp_opts;
+
+	stream = squashfs_decompressor_create(msblk, comp_opts);
+	if (IS_ERR(stream))
+		kfree(comp_opts);
 
-	return strm;
+	return stream;
 }
diff -ruw linux-3.11.10/fs/squashfs/decompressor.h linux-3.11.10-fbx/fs/squashfs/decompressor.h
--- linux-3.11.10/fs/squashfs/decompressor.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/decompressor.h	2015-09-29 11:15:08.577753278 +0200
@@ -24,28 +24,22 @@
  */
 
 struct squashfs_decompressor {
-	void	*(*init)(struct squashfs_sb_info *, void *, int);
+	void	*(*init)(struct squashfs_sb_info *, void *);
+	void	*(*comp_opts)(struct squashfs_sb_info *, void *, int);
 	void	(*free)(void *);
-	int	(*decompress)(struct squashfs_sb_info *, void **,
-		struct buffer_head **, int, int, int, int, int);
+	int	(*decompress)(struct squashfs_sb_info *, void *,
+		struct buffer_head **, int, int, int,
+		struct squashfs_page_actor *);
 	int	id;
 	char	*name;
 	int	supported;
 };
 
-static inline void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
-	void *s)
+static inline void *squashfs_comp_opts(struct squashfs_sb_info *msblk,
+							void *buff, int length)
 {
-	if (msblk->decompressor)
-		msblk->decompressor->free(s);
-}
-
-static inline int squashfs_decompress(struct squashfs_sb_info *msblk,
-	void **buffer, struct buffer_head **bh, int b, int offset, int length,
-	int srclength, int pages)
-{
-	return msblk->decompressor->decompress(msblk, buffer, bh, b, offset,
-		length, srclength, pages);
+	return msblk->decompressor->comp_opts ?
+		msblk->decompressor->comp_opts(msblk, buff, length) : NULL;
 }
 
 #ifdef CONFIG_SQUASHFS_XZ
diff -ruw linux-3.11.10/fs/squashfs/dir.c linux-3.11.10-fbx/fs/squashfs/dir.c
--- linux-3.11.10/fs/squashfs/dir.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/dir.c	2014-07-29 21:29:44.905867048 +0200
@@ -54,6 +54,7 @@
 {
 	struct squashfs_sb_info *msblk = sb->s_fs_info;
 	int err, i, index, length = 0;
+	unsigned int size;
 	struct squashfs_dir_index dir_index;
 
 	TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %lld\n",
@@ -81,8 +82,14 @@
 			 */
 			break;
 
+		size = le32_to_cpu(dir_index.size) + 1;
+
+		/* size should never be larger than SQUASHFS_NAME_LEN */
+		if (size > SQUASHFS_NAME_LEN)
+			break;
+
 		err = squashfs_read_metadata(sb, NULL, &index_start,
-				&index_offset, le32_to_cpu(dir_index.size) + 1);
+				&index_offset, size);
 		if (err < 0)
 			break;
 
@@ -105,9 +112,8 @@
 	struct inode *inode = file_inode(file);
 	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
 	u64 block = squashfs_i(inode)->start + msblk->directory_table;
-	int offset = squashfs_i(inode)->offset, length, dir_count, size,
-				type, err;
-	unsigned int inode_number;
+	int offset = squashfs_i(inode)->offset, length, err;
+	unsigned int inode_number, dir_count, size, type;
 	struct squashfs_dir_header dirh;
 	struct squashfs_dir_entry *dire;
 
@@ -200,6 +206,9 @@
 				((short) le16_to_cpu(dire->inode_number));
 			type = le16_to_cpu(dire->type);
 
+			if (type > SQUASHFS_MAX_DIR_TYPE)
+				goto failed_read;
+
 			if (!dir_emit(ctx, dire->name, size,
 					inode_number,
 					squashfs_filetype_table[type]))
diff -ruw linux-3.11.10/fs/squashfs/file.c linux-3.11.10-fbx/fs/squashfs/file.c
--- linux-3.11.10/fs/squashfs/file.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/file.c	2014-07-29 21:29:44.905867048 +0200
@@ -370,77 +370,15 @@
 	return le32_to_cpu(size);
 }
 
-
-static int squashfs_readpage(struct file *file, struct page *page)
+/* Copy data into page cache  */
+void squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer,
+	int bytes, int offset)
 {
 	struct inode *inode = page->mapping->host;
 	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-	int bytes, i, offset = 0, sparse = 0;
-	struct squashfs_cache_entry *buffer = NULL;
 	void *pageaddr;
-
-	int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
-	int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
-	int start_index = page->index & ~mask;
-	int end_index = start_index | mask;
-	int file_end = i_size_read(inode) >> msblk->block_log;
-
-	TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
-				page->index, squashfs_i(inode)->start);
-
-	if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
-					PAGE_CACHE_SHIFT))
-		goto out;
-
-	if (index < file_end || squashfs_i(inode)->fragment_block ==
-					SQUASHFS_INVALID_BLK) {
-		/*
-		 * Reading a datablock from disk.  Need to read block list
-		 * to get location and block size.
-		 */
-		u64 block = 0;
-		int bsize = read_blocklist(inode, index, &block);
-		if (bsize < 0)
-			goto error_out;
-
-		if (bsize == 0) { /* hole */
-			bytes = index == file_end ?
-				(i_size_read(inode) & (msblk->block_size - 1)) :
-				 msblk->block_size;
-			sparse = 1;
-		} else {
-			/*
-			 * Read and decompress datablock.
-			 */
-			buffer = squashfs_get_datablock(inode->i_sb,
-								block, bsize);
-			if (buffer->error) {
-				ERROR("Unable to read page, block %llx, size %x"
-					"\n", block, bsize);
-				squashfs_cache_put(buffer);
-				goto error_out;
-			}
-			bytes = buffer->length;
-		}
-	} else {
-		/*
-		 * Datablock is stored inside a fragment (tail-end packed
-		 * block).
-		 */
-		buffer = squashfs_get_fragment(inode->i_sb,
-				squashfs_i(inode)->fragment_block,
-				squashfs_i(inode)->fragment_size);
-
-		if (buffer->error) {
-			ERROR("Unable to read page, block %llx, size %x\n",
-				squashfs_i(inode)->fragment_block,
-				squashfs_i(inode)->fragment_size);
-			squashfs_cache_put(buffer);
-			goto error_out;
-		}
-		bytes = i_size_read(inode) & (msblk->block_size - 1);
-		offset = squashfs_i(inode)->fragment_offset;
-	}
+	int i, mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
+	int start_index = page->index & ~mask, end_index = start_index | mask;
 
 	/*
 	 * Loop copying datablock into pages.  As the datablock likely covers
@@ -451,7 +389,7 @@
 	for (i = start_index; i <= end_index && bytes > 0; i++,
 			bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
 		struct page *push_page;
-		int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE);
+		int avail = buffer ? min_t(int, bytes, PAGE_CACHE_SIZE) : 0;
 
 		TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail);
 
@@ -475,10 +413,74 @@
 		if (i != page->index)
 			page_cache_release(push_page);
 	}
+}
+
+/* Read datablock stored packed inside a fragment (tail-end packed block) */
+static int squashfs_readpage_fragment(struct page *page)
+{
+	struct inode *inode = page->mapping->host;
+	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+	struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb,
+		squashfs_i(inode)->fragment_block,
+		squashfs_i(inode)->fragment_size);
+	int res = buffer->error;
+
+	if (res)
+		ERROR("Unable to read page, block %llx, size %x\n",
+			squashfs_i(inode)->fragment_block,
+			squashfs_i(inode)->fragment_size);
+	else
+		squashfs_copy_cache(page, buffer, i_size_read(inode) &
+			(msblk->block_size - 1),
+			squashfs_i(inode)->fragment_offset);
 
-	if (!sparse)
 		squashfs_cache_put(buffer);
+	return res;
+}
+
+static int squashfs_readpage_sparse(struct page *page, int index, int file_end)
+{
+	struct inode *inode = page->mapping->host;
+	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+	int bytes = index == file_end ?
+			(i_size_read(inode) & (msblk->block_size - 1)) :
+			 msblk->block_size;
+
+	squashfs_copy_cache(page, NULL, bytes, 0);
+	return 0;
+}
+
+static int squashfs_readpage(struct file *file, struct page *page)
+{
+	struct inode *inode = page->mapping->host;
+	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+	int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
+	int file_end = i_size_read(inode) >> msblk->block_log;
+	int res;
+	void *pageaddr;
+
+	TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
+				page->index, squashfs_i(inode)->start);
+
+	if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+					PAGE_CACHE_SHIFT))
+		goto out;
+
+	if (index < file_end || squashfs_i(inode)->fragment_block ==
+					SQUASHFS_INVALID_BLK) {
+		u64 block = 0;
+		int bsize = read_blocklist(inode, index, &block);
+		if (bsize < 0)
+			goto error_out;
+
+		if (bsize == 0)
+			res = squashfs_readpage_sparse(page, index, file_end);
+		else
+			res = squashfs_readpage_block(page, block, bsize);
+	} else
+		res = squashfs_readpage_fragment(page);
 
+	if (!res)
 	return 0;
 
 error_out:
diff -ruw linux-3.11.10/fs/squashfs/Kconfig linux-3.11.10-fbx/fs/squashfs/Kconfig
--- linux-3.11.10/fs/squashfs/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/Kconfig	2015-09-29 11:15:08.577753278 +0200
@@ -25,6 +25,78 @@
 
 	  If unsure, say N.
 
+choice
+	prompt "File decompression options"
+	depends on SQUASHFS
+	help
+	  Squashfs now supports two options for decompressing file
+	  data.  Traditionally Squashfs has decompressed into an
+	  intermediate buffer and then memcopied it into the page cache.
+	  Squashfs now supports the ability to decompress directly into
+	  the page cache.
+
+	  If unsure, select "Decompress file data into an intermediate buffer"
+
+config SQUASHFS_FILE_CACHE
+	bool "Decompress file data into an intermediate buffer"
+	help
+	  Decompress file data into an intermediate buffer and then
+	  memcopy it into the page cache.
+
+config SQUASHFS_FILE_DIRECT
+	bool "Decompress files directly into the page cache"
+	help
+	  Directly decompress file data into the page cache.
+	  Doing so can significantly improve performance because
+	  it eliminates a memcpy and it also removes the lock contention
+	  on the single buffer.
+
+endchoice
+
+choice
+	prompt "Decompressor parallelisation options"
+	depends on SQUASHFS
+	help
+	  Squashfs now supports three parallelisation options for
+	  decompression.  Each one exhibits various trade-offs between
+	  decompression performance and CPU and memory usage.
+
+	  If in doubt, select "Single threaded compression"
+
+config SQUASHFS_DECOMP_SINGLE
+	bool "Single threaded compression"
+	help
+	  Traditionally Squashfs has used single-threaded decompression.
+	  Only one block (data or metadata) can be decompressed at any
+	  one time.  This limits CPU and memory usage to a minimum.
+
+config SQUASHFS_DECOMP_MULTI
+	bool "Use multiple decompressors for parallel I/O"
+	help
+	  By default Squashfs uses a single decompressor but it gives
+	  poor performance on parallel I/O workloads when using multiple CPU
+	  machines due to waiting on decompressor availability.
+
+	  If you have a parallel I/O workload and your system has enough memory,
+	  using this option may improve overall I/O performance.
+
+	  This decompressor implementation uses up to two parallel
+	  decompressors per core.  It dynamically allocates decompressors
+	  on a demand basis.
+
+config SQUASHFS_DECOMP_MULTI_PERCPU
+	bool "Use percpu multiple decompressors for parallel I/O"
+	help
+	  By default Squashfs uses a single decompressor but it gives
+	  poor performance on parallel I/O workloads when using multiple CPU
+	  machines due to waiting on decompressor availability.
+
+	  This decompressor implementation uses a maximum of one
+	  decompressor per core.  It uses percpu variables to ensure
+	  decompression is load-balanced across the cores.
+
+endchoice
+
 config SQUASHFS_XATTR
 	bool "Squashfs XATTR support"
 	depends on SQUASHFS
diff -ruw linux-3.11.10/fs/squashfs/Makefile linux-3.11.10-fbx/fs/squashfs/Makefile
--- linux-3.11.10/fs/squashfs/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/Makefile	2015-09-29 11:15:08.577753278 +0200
@@ -5,6 +5,11 @@
 obj-$(CONFIG_SQUASHFS) += squashfs.o
 squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
 squashfs-y += namei.o super.o symlink.o decompressor.o
+squashfs-$(CONFIG_SQUASHFS_FILE_CACHE) += file_cache.o
+squashfs-$(CONFIG_SQUASHFS_FILE_DIRECT) += file_direct.o page_actor.o
+squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o
+squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o
+squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o
 squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o
 squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o
 squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o
diff -ruw linux-3.11.10/fs/squashfs/namei.c linux-3.11.10-fbx/fs/squashfs/namei.c
--- linux-3.11.10/fs/squashfs/namei.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/namei.c	2014-07-29 21:29:44.905867048 +0200
@@ -79,7 +79,8 @@
 			int len)
 {
 	struct squashfs_sb_info *msblk = sb->s_fs_info;
-	int i, size, length = 0, err;
+	int i, length = 0, err;
+	unsigned int size;
 	struct squashfs_dir_index *index;
 	char *str;
 
@@ -103,6 +104,8 @@
 
 
 		size = le32_to_cpu(index->size) + 1;
+		if (size > SQUASHFS_NAME_LEN)
+			break;
 
 		err = squashfs_read_metadata(sb, index->name, &index_start,
 					&index_offset, size);
@@ -144,7 +147,8 @@
 	struct squashfs_dir_entry *dire;
 	u64 block = squashfs_i(dir)->start + msblk->directory_table;
 	int offset = squashfs_i(dir)->offset;
-	int err, length, dir_count, size;
+	int err, length;
+	unsigned int dir_count, size;
 
 	TRACE("Entered squashfs_lookup [%llx:%x]\n", block, offset);
 
diff -ruw linux-3.11.10/fs/squashfs/squashfs_fs.h linux-3.11.10-fbx/fs/squashfs/squashfs_fs.h
--- linux-3.11.10/fs/squashfs/squashfs_fs.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/squashfs_fs.h	2015-09-29 11:15:08.577753278 +0200
@@ -87,7 +87,7 @@
 #define SQUASHFS_COMP_OPTS(flags)		SQUASHFS_BIT(flags, \
 						SQUASHFS_COMP_OPT)
 
-/* Max number of types and file types */
+/* Inode types including extended types */
 #define SQUASHFS_DIR_TYPE		1
 #define SQUASHFS_REG_TYPE		2
 #define SQUASHFS_SYMLINK_TYPE		3
@@ -103,6 +103,9 @@
 #define SQUASHFS_LFIFO_TYPE		13
 #define SQUASHFS_LSOCKET_TYPE		14
 
+/* Max type value stored in directory entry */
+#define SQUASHFS_MAX_DIR_TYPE		7
+
 /* Xattr types */
 #define SQUASHFS_XATTR_USER             0
 #define SQUASHFS_XATTR_TRUSTED          1
diff -ruw linux-3.11.10/fs/squashfs/squashfs_fs_sb.h linux-3.11.10-fbx/fs/squashfs/squashfs_fs_sb.h
--- linux-3.11.10/fs/squashfs/squashfs_fs_sb.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/squashfs_fs_sb.h	2014-07-29 21:29:44.905867048 +0200
@@ -50,6 +50,7 @@
 	wait_queue_head_t	wait_queue;
 	struct squashfs_cache	*cache;
 	void			**data;
+	struct squashfs_page_actor	*actor;
 };
 
 struct squashfs_sb_info {
@@ -63,10 +64,9 @@
 	__le64					*id_table;
 	__le64					*fragment_index;
 	__le64					*xattr_id_table;
-	struct mutex				read_data_mutex;
 	struct mutex				meta_index_mutex;
 	struct meta_index			*meta_index;
-	void					*stream;
+	struct squashfs_stream			*stream;
 	__le64					*inode_lookup_table;
 	u64					inode_table;
 	u64					directory_table;
diff -ruw linux-3.11.10/fs/squashfs/squashfs.h linux-3.11.10-fbx/fs/squashfs/squashfs.h
--- linux-3.11.10/fs/squashfs/squashfs.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/squashfs.h	2015-09-29 11:15:08.577753278 +0200
@@ -28,8 +28,8 @@
 #define WARNING(s, args...)	pr_warning("SQUASHFS: "s, ## args)
 
 /* block.c */
-extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *,
-				int, int);
+extern int squashfs_read_data(struct super_block *, u64, int, u64 *,
+				struct squashfs_page_actor *);
 
 /* cache.c */
 extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
@@ -48,7 +48,14 @@
 
 /* decompressor.c */
 extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int);
-extern void *squashfs_decompressor_init(struct super_block *, unsigned short);
+extern void *squashfs_decompressor_setup(struct super_block *, unsigned short);
+
+/* decompressor_xxx.c */
+extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *);
+extern void squashfs_decompressor_destroy(struct squashfs_sb_info *);
+extern int squashfs_decompress(struct squashfs_sb_info *, struct buffer_head **,
+	int, int, int, struct squashfs_page_actor *);
+extern int squashfs_max_decompressors(void);
 
 /* export.c */
 extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64,
@@ -59,6 +66,13 @@
 extern __le64 *squashfs_read_fragment_index_table(struct super_block *,
 				u64, u64, unsigned int);
 
+/* file.c */
+void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int,
+				int);
+
+/* file_xxx.c */
+extern int squashfs_readpage_block(struct page *, u64, int);
+
 /* id.c */
 extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
 extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, u64,
diff -ruw linux-3.11.10/fs/squashfs/super.c linux-3.11.10-fbx/fs/squashfs/super.c
--- linux-3.11.10/fs/squashfs/super.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/super.c	2015-09-29 11:15:08.577753278 +0200
@@ -98,7 +98,6 @@
 	msblk->devblksize = sb_min_blocksize(sb, SQUASHFS_DEVBLK_SIZE);
 	msblk->devblksize_log2 = ffz(~msblk->devblksize);
 
-	mutex_init(&msblk->read_data_mutex);
 	mutex_init(&msblk->meta_index_mutex);
 
 	/*
@@ -206,13 +205,14 @@
 		goto failed_mount;
 
 	/* Allocate read_page block */
-	msblk->read_page = squashfs_cache_init("data", 1, msblk->block_size);
+	msblk->read_page = squashfs_cache_init("data",
+		squashfs_max_decompressors(), msblk->block_size);
 	if (msblk->read_page == NULL) {
 		ERROR("Failed to allocate read_page block\n");
 		goto failed_mount;
 	}
 
-	msblk->stream = squashfs_decompressor_init(sb, flags);
+	msblk->stream = squashfs_decompressor_setup(sb, flags);
 	if (IS_ERR(msblk->stream)) {
 		err = PTR_ERR(msblk->stream);
 		msblk->stream = NULL;
@@ -336,7 +336,7 @@
 	squashfs_cache_delete(msblk->block_cache);
 	squashfs_cache_delete(msblk->fragment_cache);
 	squashfs_cache_delete(msblk->read_page);
-	squashfs_decompressor_free(msblk, msblk->stream);
+	squashfs_decompressor_destroy(msblk);
 	kfree(msblk->inode_lookup_table);
 	kfree(msblk->fragment_index);
 	kfree(msblk->id_table);
@@ -383,7 +383,7 @@
 		squashfs_cache_delete(sbi->block_cache);
 		squashfs_cache_delete(sbi->fragment_cache);
 		squashfs_cache_delete(sbi->read_page);
-		squashfs_decompressor_free(sbi, sbi->stream);
+		squashfs_decompressor_destroy(sbi);
 		kfree(sbi->id_table);
 		kfree(sbi->fragment_index);
 		kfree(sbi->meta_index);
diff -ruw linux-3.11.10/fs/squashfs/zlib_wrapper.c linux-3.11.10-fbx/fs/squashfs/zlib_wrapper.c
--- linux-3.11.10/fs/squashfs/zlib_wrapper.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/squashfs/zlib_wrapper.c	2014-07-29 21:29:44.905867048 +0200
@@ -32,8 +32,9 @@
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "page_actor.h"
 
-static void *zlib_init(struct squashfs_sb_info *dummy, void *buff, int len)
+static void *zlib_init(struct squashfs_sb_info *dummy, void *buff)
 {
 	z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL);
 	if (stream == NULL)
@@ -61,44 +62,37 @@
 }
 
 
-static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
-	struct buffer_head **bh, int b, int offset, int length, int srclength,
-	int pages)
+static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
+	struct buffer_head **bh, int b, int offset, int length,
+	struct squashfs_page_actor *output)
 {
-	int zlib_err, zlib_init = 0;
-	int k = 0, page = 0;
-	z_stream *stream = msblk->stream;
-
-	mutex_lock(&msblk->read_data_mutex);
+	int zlib_err, zlib_init = 0, k = 0;
+	z_stream *stream = strm;
 
-	stream->avail_out = 0;
+	stream->avail_out = PAGE_CACHE_SIZE;
+	stream->next_out = squashfs_first_page(output);
 	stream->avail_in = 0;
 
 	do {
 		if (stream->avail_in == 0 && k < b) {
 			int avail = min(length, msblk->devblksize - offset);
 			length -= avail;
-			wait_on_buffer(bh[k]);
-			if (!buffer_uptodate(bh[k]))
-				goto release_mutex;
-
 			stream->next_in = bh[k]->b_data + offset;
 			stream->avail_in = avail;
 			offset = 0;
 		}
 
-		if (stream->avail_out == 0 && page < pages) {
-			stream->next_out = buffer[page++];
+		if (stream->avail_out == 0) {
+			stream->next_out = squashfs_next_page(output);
+			if (stream->next_out != NULL)
 			stream->avail_out = PAGE_CACHE_SIZE;
 		}
 
 		if (!zlib_init) {
 			zlib_err = zlib_inflateInit(stream);
 			if (zlib_err != Z_OK) {
-				ERROR("zlib_inflateInit returned unexpected "
-					"result 0x%x, srclength %d\n",
-					zlib_err, srclength);
-				goto release_mutex;
+				squashfs_finish_page(output);
+				goto out;
 			}
 			zlib_init = 1;
 		}
@@ -109,29 +103,21 @@
 			put_bh(bh[k++]);
 	} while (zlib_err == Z_OK);
 
-	if (zlib_err != Z_STREAM_END) {
-		ERROR("zlib_inflate error, data probably corrupt\n");
-		goto release_mutex;
-	}
+	squashfs_finish_page(output);
 
-	zlib_err = zlib_inflateEnd(stream);
-	if (zlib_err != Z_OK) {
-		ERROR("zlib_inflate error, data probably corrupt\n");
-		goto release_mutex;
-	}
+	if (zlib_err != Z_STREAM_END)
+		goto out;
 
-	if (k < b) {
-		ERROR("zlib_uncompress error, data remaining\n");
-		goto release_mutex;
-	}
+	zlib_err = zlib_inflateEnd(stream);
+	if (zlib_err != Z_OK)
+		goto out;
 
-	length = stream->total_out;
-	mutex_unlock(&msblk->read_data_mutex);
-	return length;
+	if (k < b)
+		goto out;
 
-release_mutex:
-	mutex_unlock(&msblk->read_data_mutex);
+	return stream->total_out;
 
+out:
 	for (; k < b; k++)
 		put_bh(bh[k]);
 
diff -ruw linux-3.11.10/fs/ubifs/file.c linux-3.11.10-fbx/fs/ubifs/file.c
--- linux-3.11.10/fs/ubifs/file.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ubifs/file.c	2015-09-29 11:15:08.581753307 +0200
@@ -1525,8 +1525,7 @@
 	}
 
 	wait_for_stable_page(page);
-	unlock_page(page);
-	return 0;
+	return VM_FAULT_LOCKED;
 
 out_unlock:
 	unlock_page(page);
diff -ruw linux-3.11.10/fs/ubifs/shrinker.c linux-3.11.10-fbx/fs/ubifs/shrinker.c
--- linux-3.11.10/fs/ubifs/shrinker.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/ubifs/shrinker.c	2015-09-29 11:15:08.585753344 +0200
@@ -128,7 +128,6 @@
 			freed = ubifs_destroy_tnc_subtree(znode);
 			atomic_long_sub(freed, &ubifs_clean_zn_cnt);
 			atomic_long_sub(freed, &c->clean_zn_cnt);
-			ubifs_assert(atomic_long_read(&c->clean_zn_cnt) >= 0);
 			total_freed += freed;
 			znode = zprev;
 		}
diff -ruw linux-3.11.10/fs/xattr_acl.c linux-3.11.10-fbx/fs/xattr_acl.c
--- linux-3.11.10/fs/xattr_acl.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/xattr_acl.c	2015-09-29 11:15:08.593753399 +0200
@@ -146,7 +146,7 @@
 		   void *buffer, size_t size)
 {
 	posix_acl_xattr_header *ext_acl = (posix_acl_xattr_header *)buffer;
-	posix_acl_xattr_entry *ext_entry = ext_acl->a_entries;
+	posix_acl_xattr_entry *ext_entry;
 	int real_size, n;
 
 	real_size = posix_acl_xattr_size(acl->a_count);
@@ -155,6 +155,7 @@
 	if (real_size > size)
 		return -ERANGE;
 	
+	ext_entry = ext_acl->a_entries;
 	ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
 
 	for (n=0; n < acl->a_count; n++, ext_entry++) {
diff -ruw linux-3.11.10/fs/xfs/xfs_buf.c linux-3.11.10-fbx/fs/xfs/xfs_buf.c
--- linux-3.11.10/fs/xfs/xfs_buf.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/xfs/xfs_buf.c	2015-09-29 11:15:08.601753461 +0200
@@ -431,6 +431,37 @@
 	return error;
 }
 
+#ifdef CONFIG_MIPS
+static void cache_flush_buf_page(xfs_buf_t *bp, int page_id)
+{
+	struct page *page;
+	void *vmaddr, *addr;
+
+	if (!xfs_buf_is_vmapped(bp))
+		return;
+
+	page = bp->b_pages[page_id];
+	vmaddr = bp->b_addr - bp->b_offset + (page_id * PAGE_CACHE_SIZE);
+	addr = page_address(page);
+
+	if (pages_do_alias((unsigned long)addr, (unsigned long)vmaddr)) {
+		local_flush_data_cache_page(vmaddr);
+		local_flush_data_cache_page(addr);
+	}
+}
+
+static void cache_flush_buf(xfs_buf_t *bp)
+{
+	unsigned int i;
+
+	for (i = 0; i < bp->b_page_count; i++)
+		cache_flush_buf_page(bp, i);
+}
+#else
+static inline void cache_flush_buf_page(xfs_buf_t *bp, int page_id) { }
+static inline void cache_flush_buf(xfs_buf_t *bp) { }
+#endif
+
 /*
  *	Map buffer into kernel address-space if necessary.
  */
@@ -459,6 +490,7 @@
 		if (!bp->b_addr)
 			return -ENOMEM;
 		bp->b_addr += bp->b_offset;
+		cache_flush_buf(bp);
 	}
 
 	return 0;
@@ -1296,6 +1328,7 @@
 		if (nbytes > size)
 			nbytes = size;
 
+		cache_flush_buf_page(bp, page_index);
 		rbytes = bio_add_page(bio, bp->b_pages[page_index], nbytes,
 				      offset);
 		if (rbytes < nbytes)
diff -ruw linux-3.11.10/fs/xfs/xfs_da_btree.c linux-3.11.10-fbx/fs/xfs/xfs_da_btree.c
--- linux-3.11.10/fs/xfs/xfs_da_btree.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/xfs/xfs_da_btree.c	2015-09-29 11:15:08.601753461 +0200
@@ -1334,7 +1334,7 @@
 		node = blk->bp->b_addr;
 		xfs_da3_node_hdr_from_disk(&nodehdr, node);
 		btree = xfs_da3_node_tree_p(node);
-		if (be32_to_cpu(btree->hashval) == lasthash)
+		if (be32_to_cpu(btree[blk->index].hashval) == lasthash)
 			break;
 		blk->hashval = lasthash;
 		btree[blk->index].hashval = cpu_to_be32(lasthash);
diff -ruw linux-3.11.10/fs/xfs/xfs_dir2_block.c linux-3.11.10-fbx/fs/xfs/xfs_dir2_block.c
--- linux-3.11.10/fs/xfs/xfs_dir2_block.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/xfs/xfs_dir2_block.c	2015-09-29 11:15:08.605753490 +0200
@@ -37,6 +37,7 @@
 #include "xfs_error.h"
 #include "xfs_trace.h"
 #include "xfs_cksum.h"
+#include "xfs_icache.h"
 
 /*
  * Local function prototypes.
@@ -614,6 +615,9 @@
 	 * Each object is a real entry (dep) or an unused one (dup).
 	 */
 	while (ptr < endptr) {
+		struct xfs_inode *inode;
+		unsigned int type;
+
 		dup = (xfs_dir2_data_unused_t *)ptr;
 		/*
 		 * Unused, skip it.
@@ -638,6 +642,14 @@
 		cook = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
 					    (char *)dep - (char *)hdr);
 
+		if (xfs_iget(mp, NULL, be64_to_cpu(dep->inumber), 0, 0,
+			     &inode) == 0) {
+			type = (inode->i_d.di_mode >> 12) & 0xf;
+			xfs_iunlock(inode, 0);
+			IRELE(inode);
+		} else
+			type = DT_UNKNOWN;
+
 		ctx->pos = cook & 0x7fffffff;
 		/*
 		 * If it didn't fit, set the final offset to here & return.
diff -ruw linux-3.11.10/fs/xfs/xfs_dir2_leaf.c linux-3.11.10-fbx/fs/xfs/xfs_dir2_leaf.c
--- linux-3.11.10/fs/xfs/xfs_dir2_leaf.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/xfs/xfs_dir2_leaf.c	2015-09-29 11:15:08.605753490 +0200
@@ -36,6 +36,7 @@
 #include "xfs_trace.h"
 #include "xfs_buf_item.h"
 #include "xfs_cksum.h"
+#include "xfs_icache.h"
 
 /*
  * Local function declarations.
@@ -1358,6 +1359,9 @@
 	 * Get more blocks and readahead as necessary.
 	 */
 	while (curoff < XFS_DIR2_LEAF_OFFSET) {
+		struct xfs_inode *inode;
+		unsigned int type;
+
 		/*
 		 * If we have no buffer, or we're off the end of the
 		 * current buffer, need to get another one.
@@ -1445,6 +1449,14 @@
 		dep = (xfs_dir2_data_entry_t *)ptr;
 		length = xfs_dir2_data_entsize(dep->namelen);
 
+		if (xfs_iget(mp, NULL, be64_to_cpu(dep->inumber), 0, 0,
+			     &inode) == 0) {
+			type = (inode->i_d.di_mode >> 12) & 0xf;
+			xfs_iunlock(inode, 0);
+			IRELE(inode);
+		} else
+			type = DT_UNKNOWN;
+
 		ctx->pos = xfs_dir2_byte_to_dataptr(mp, curoff) & 0x7fffffff;
 		if (!dir_emit(ctx, (char *)dep->name, dep->namelen,
 			    be64_to_cpu(dep->inumber), DT_UNKNOWN))
diff -ruw linux-3.11.10/fs/xfs/xfs_dir2_sf.c linux-3.11.10-fbx/fs/xfs/xfs_dir2_sf.c
--- linux-3.11.10/fs/xfs/xfs_dir2_sf.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/xfs/xfs_dir2_sf.c	2015-09-29 11:15:08.605753490 +0200
@@ -33,6 +33,7 @@
 #include "xfs_dir2_format.h"
 #include "xfs_dir2_priv.h"
 #include "xfs_trace.h"
+#include "xfs_icache.h"
 
 /*
  * Prototypes for internal functions.
@@ -838,6 +839,9 @@
 	 */
 	sfep = xfs_dir2_sf_firstentry(sfp);
 	for (i = 0; i < sfp->count; i++) {
+		struct xfs_inode *inode;
+		unsigned int type;
+
 		off = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
 				xfs_dir2_sf_get_offset(sfep));
 
@@ -847,6 +851,13 @@
 		}
 
 		ino = xfs_dir2_sfe_get_ino(sfp, sfep);
+		if (xfs_iget(mp, NULL, ino, 0, 0, &inode) == 0) {
+			type = (inode->i_d.di_mode >> 12) & 0xf;
+			xfs_iunlock(inode, 0);
+			IRELE(inode);
+		} else
+			type = DT_UNKNOWN;
+
 		ctx->pos = off & 0x7fffffff;
 		if (!dir_emit(ctx, (char *)sfep->name, sfep->namelen,
 			    ino, DT_UNKNOWN))
diff -ruw linux-3.11.10/fs/xfs/xfs_fsops.c linux-3.11.10-fbx/fs/xfs/xfs_fsops.c
--- linux-3.11.10/fs/xfs/xfs_fsops.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/xfs/xfs_fsops.c	2015-09-29 11:15:08.609753527 +0200
@@ -216,6 +216,8 @@
 	 */
 	nfree = 0;
 	for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) {
+		__be32	*agfl_bno;
+
 		/*
 		 * AG freespace header block
 		 */
@@ -275,8 +277,10 @@
 			agfl->agfl_seqno = cpu_to_be32(agno);
 			uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_uuid);
 		}
+
+		agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp);
 		for (bucket = 0; bucket < XFS_AGFL_SIZE(mp); bucket++)
-			agfl->agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
+			agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
 
 		error = xfs_bwrite(bp);
 		xfs_buf_relse(bp);
diff -ruw linux-3.11.10/fs/xfs/xfs_ioctl.c linux-3.11.10-fbx/fs/xfs/xfs_ioctl.c
--- linux-3.11.10/fs/xfs/xfs_ioctl.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/xfs/xfs_ioctl.c	2015-09-29 11:15:08.613753552 +0200
@@ -409,7 +409,8 @@
 		return -XFS_ERROR(EPERM);
 	if (copy_from_user(&al_hreq, arg, sizeof(xfs_fsop_attrlist_handlereq_t)))
 		return -XFS_ERROR(EFAULT);
-	if (al_hreq.buflen > XATTR_LIST_MAX)
+	if (al_hreq.buflen < sizeof(struct attrlist) ||
+	    al_hreq.buflen > XATTR_LIST_MAX)
 		return -XFS_ERROR(EINVAL);
 
 	/*
@@ -1612,6 +1613,12 @@
 	case XFS_IOC_FREE_EOFBLOCKS: {
 		struct xfs_eofblocks eofb;
 
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+
+		if (mp->m_flags & XFS_MOUNT_RDONLY)
+			return -XFS_ERROR(EROFS);
+
 		if (copy_from_user(&eofb, arg, sizeof(eofb)))
 			return -XFS_ERROR(EFAULT);
 
diff -ruw linux-3.11.10/fs/xfs/xfs_mount.c linux-3.11.10-fbx/fs/xfs/xfs_mount.c
--- linux-3.11.10/fs/xfs/xfs_mount.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/fs/xfs/xfs_mount.c	2015-09-29 11:15:08.617753581 +0200
@@ -749,6 +749,11 @@
  * single bit error could clear the feature bit and unused parts of the
  * superblock are supposed to be zero. Hence a non-null crc field indicates that
  * we've potentially lost a feature bit and we should check it anyway.
+ *
+ * However, past bugs (i.e. in growfs) left non-zeroed regions beyond the
+ * last field in V4 secondary superblocks.  So for secondary superblocks,
+ * we are more forgiving, and ignore CRC failures if the primary doesn't
+ * indicate that the fs version is V5.
  */
 static void
 xfs_sb_read_verify(
@@ -769,10 +774,14 @@
 
 		if (!xfs_verify_cksum(bp->b_addr, be16_to_cpu(dsb->sb_sectsize),
 				      offsetof(struct xfs_sb, sb_crc))) {
+			/* Only fail bad secondaries on a known V5 filesystem */
+			if (bp->b_bn != XFS_SB_DADDR &&
+			    xfs_sb_version_hascrc(&mp->m_sb)) {
 			error = EFSCORRUPTED;
 			goto out_error;
 		}
 	}
+	}
 	error = xfs_sb_verify(bp, true);
 
 out_error:
diff -ruw linux-3.11.10/include/asm-generic/pgtable.h linux-3.11.10-fbx/include/asm-generic/pgtable.h
--- linux-3.11.10/include/asm-generic/pgtable.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/asm-generic/pgtable.h	2015-09-29 11:15:08.633753710 +0200
@@ -221,7 +221,7 @@
 #endif
 
 #ifndef pte_accessible
-# define pte_accessible(pte)		((void)(pte),1)
+# define pte_accessible(mm, pte)	((void)(pte), 1)
 #endif
 
 #ifndef flush_tlb_fix_spurious_fault
@@ -673,32 +673,47 @@
 #ifndef pte_mknonnuma
 static inline pte_t pte_mknonnuma(pte_t pte)
 {
-	pte = pte_clear_flags(pte, _PAGE_NUMA);
-	return pte_set_flags(pte, _PAGE_PRESENT|_PAGE_ACCESSED);
+	pteval_t val = pte_val(pte);
+
+	val &= ~_PAGE_NUMA;
+	val |= (_PAGE_PRESENT|_PAGE_ACCESSED);
+	return __pte(val);
 }
 #endif
 
 #ifndef pmd_mknonnuma
 static inline pmd_t pmd_mknonnuma(pmd_t pmd)
 {
-	pmd = pmd_clear_flags(pmd, _PAGE_NUMA);
-	return pmd_set_flags(pmd, _PAGE_PRESENT|_PAGE_ACCESSED);
+	pmdval_t val = pmd_val(pmd);
+
+	val &= ~_PAGE_NUMA;
+	val |= (_PAGE_PRESENT|_PAGE_ACCESSED);
+
+	return __pmd(val);
 }
 #endif
 
 #ifndef pte_mknuma
 static inline pte_t pte_mknuma(pte_t pte)
 {
-	pte = pte_set_flags(pte, _PAGE_NUMA);
-	return pte_clear_flags(pte, _PAGE_PRESENT);
+	pteval_t val = pte_val(pte);
+
+	val &= ~_PAGE_PRESENT;
+	val |= _PAGE_NUMA;
+
+	return __pte(val);
 }
 #endif
 
 #ifndef pmd_mknuma
 static inline pmd_t pmd_mknuma(pmd_t pmd)
 {
-	pmd = pmd_set_flags(pmd, _PAGE_NUMA);
-	return pmd_clear_flags(pmd, _PAGE_PRESENT);
+	pmdval_t val = pmd_val(pmd);
+
+	val &= ~_PAGE_PRESENT;
+	val |= _PAGE_NUMA;
+
+	return __pmd(val);
 }
 #endif
 #else
diff -ruw linux-3.11.10/include/crypto/scatterwalk.h linux-3.11.10-fbx/include/crypto/scatterwalk.h
--- linux-3.11.10/include/crypto/scatterwalk.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/crypto/scatterwalk.h	2015-09-29 11:15:08.633753710 +0200
@@ -36,6 +36,7 @@
 {
 	sg_set_page(&sg1[num - 1], (void *)sg2, 0, 0);
 	sg1[num - 1].page_link &= ~0x02;
+	sg1[num - 1].page_link |= 0x01;
 }
 
 static inline struct scatterlist *scatterwalk_sg_next(struct scatterlist *sg)
@@ -43,7 +44,7 @@
 	if (sg_is_last(sg))
 		return NULL;
 
-	return (++sg)->length ? sg : (void *)sg_page(sg);
+	return (++sg)->length ? sg : sg_chain_ptr(sg);
 }
 
 static inline void scatterwalk_crypto_chain(struct scatterlist *head,
diff -ruw linux-3.11.10/include/linux/audit.h linux-3.11.10-fbx/include/linux/audit.h
--- linux-3.11.10/include/linux/audit.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/audit.h	2015-09-29 11:15:08.645753801 +0200
@@ -135,7 +135,7 @@
 {
 	if (unlikely(current->audit_context)) {
 		int success = is_syscall_success(pt_regs);
-		int return_code = regs_return_value(pt_regs);
+		long return_code = regs_return_value(pt_regs);
 
 		__audit_syscall_exit(success, return_code);
 	}
@@ -178,8 +178,6 @@
 
 static inline void audit_seccomp(unsigned long syscall, long signr, int code)
 {
-	/* Force a record to be reported if a signal was delivered. */
-	if (signr || unlikely(!audit_dummy_context()))
 		__audit_seccomp(syscall, signr, code);
 }
 
diff -ruw linux-3.11.10/include/linux/auxvec.h linux-3.11.10-fbx/include/linux/auxvec.h
--- linux-3.11.10/include/linux/auxvec.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/auxvec.h	2014-07-29 21:29:44.973867385 +0200
@@ -3,6 +3,6 @@
 
 #include <uapi/linux/auxvec.h>
 
-#define AT_VECTOR_SIZE_BASE 19 /* NEW_AUX_ENT entries in auxiliary table */
+#define AT_VECTOR_SIZE_BASE 20 /* NEW_AUX_ENT entries in auxiliary table */
   /* number of "#define AT_.*" above, minus {AT_NULL, AT_IGNORE, AT_NOTELF} */
 #endif /* _LINUX_AUXVEC_H */
diff -ruw linux-3.11.10/include/linux/backing-dev.h linux-3.11.10-fbx/include/linux/backing-dev.h
--- linux-3.11.10/include/linux/backing-dev.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/backing-dev.h	2015-09-29 11:15:08.645753801 +0200
@@ -95,7 +95,7 @@
 	unsigned int max_ratio, max_prop_frac;
 
 	struct bdi_writeback wb;  /* default writeback info for this bdi */
-	spinlock_t wb_lock;	  /* protects work_list */
+	spinlock_t wb_lock;	  /* protects work_list & wb.dwork scheduling */
 
 	struct list_head work_list;
 
diff -ruw linux-3.11.10/include/linux/bitops.h linux-3.11.10-fbx/include/linux/bitops.h
--- linux-3.11.10/include/linux/bitops.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/bitops.h	2015-09-29 11:15:08.645753801 +0200
@@ -185,6 +185,21 @@
 
 #ifdef __KERNEL__
 
+#ifndef set_mask_bits
+#define set_mask_bits(ptr, _mask, _bits)	\
+({								\
+	const typeof(*ptr) mask = (_mask), bits = (_bits);	\
+	typeof(*ptr) old, new;					\
+								\
+	do {							\
+		old = ACCESS_ONCE(*ptr);			\
+		new = (old & ~mask) | bits;			\
+	} while (cmpxchg(ptr, old, new) != old);		\
+								\
+	new;							\
+})
+#endif
+
 #ifndef find_last_bit
 /**
  * find_last_bit - find the last set bit in a memory region
diff -ruw linux-3.11.10/include/linux/capability.h linux-3.11.10-fbx/include/linux/capability.h
--- linux-3.11.10/include/linux/capability.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/capability.h	2015-09-29 11:15:08.649753827 +0200
@@ -211,7 +211,7 @@
 extern bool capable(int cap);
 extern bool ns_capable(struct user_namespace *ns, int cap);
 extern bool nsown_capable(int cap);
-extern bool inode_capable(const struct inode *inode, int cap);
+extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
 extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);
 
 /* audit system wants to get cap info from files as well */
diff -ruw linux-3.11.10/include/linux/compat.h linux-3.11.10-fbx/include/linux/compat.h
--- linux-3.11.10/include/linux/compat.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/compat.h	2015-09-29 11:15:08.653753856 +0200
@@ -326,16 +326,16 @@
 			      u32 arg2, u32 arg3, u32 arg4, u32 arg5);
 asmlinkage long compat_sys_ustat(unsigned dev, struct compat_ustat __user *u32);
 
-asmlinkage ssize_t compat_sys_readv(unsigned long fd,
-		const struct compat_iovec __user *vec, unsigned long vlen);
-asmlinkage ssize_t compat_sys_writev(unsigned long fd,
-		const struct compat_iovec __user *vec, unsigned long vlen);
-asmlinkage ssize_t compat_sys_preadv(unsigned long fd,
+asmlinkage ssize_t compat_sys_readv(compat_ulong_t fd,
+		const struct compat_iovec __user *vec, compat_ulong_t vlen);
+asmlinkage ssize_t compat_sys_writev(compat_ulong_t fd,
+		const struct compat_iovec __user *vec, compat_ulong_t vlen);
+asmlinkage ssize_t compat_sys_preadv(compat_ulong_t fd,
 		const struct compat_iovec __user *vec,
-		unsigned long vlen, u32 pos_low, u32 pos_high);
-asmlinkage ssize_t compat_sys_pwritev(unsigned long fd,
+		compat_ulong_t vlen, u32 pos_low, u32 pos_high);
+asmlinkage ssize_t compat_sys_pwritev(compat_ulong_t fd,
 		const struct compat_iovec __user *vec,
-		unsigned long vlen, u32 pos_low, u32 pos_high);
+		compat_ulong_t vlen, u32 pos_low, u32 pos_high);
 asmlinkage long comat_sys_lseek(unsigned int, compat_off_t, unsigned int);
 
 asmlinkage long compat_sys_execve(const char __user *filename, const compat_uptr_t __user *argv,
@@ -421,7 +421,7 @@
 asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
 				  compat_long_t addr, compat_long_t data);
 
-asmlinkage long compat_sys_lookup_dcookie(u32, u32, char __user *, size_t);
+asmlinkage long compat_sys_lookup_dcookie(u32, u32, char __user *, compat_size_t);
 /*
  * epoll (fs/eventpoll.c) compat bits follow ...
  */
diff -ruw linux-3.11.10/include/linux/compiler-gcc4.h linux-3.11.10-fbx/include/linux/compiler-gcc4.h
--- linux-3.11.10/include/linux/compiler-gcc4.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/compiler-gcc4.h	2015-09-29 11:15:08.653753856 +0200
@@ -75,11 +75,7 @@
  *
  * (asm goto is automatically volatile - the naming reflects this.)
  */
-#if GCC_VERSION <= 40801
 # define asm_volatile_goto(x...)	do { asm goto(x); asm (""); } while (0)
-#else
-# define asm_volatile_goto(x...)	do { asm goto(x); } while (0)
-#endif
 
 #ifdef CONFIG_ARCH_USE_BUILTIN_BSWAP
 #if GCC_VERSION >= 40400
diff -ruw linux-3.11.10/include/linux/efi.h linux-3.11.10-fbx/include/linux/efi.h
--- linux-3.11.10/include/linux/efi.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/efi.h	2015-09-29 11:15:08.657753893 +0200
@@ -782,6 +782,8 @@
 	struct efi_variable var;
 	struct list_head list;
 	struct kobject kobj;
+	bool scanning;
+	bool deleting;
 };
 
 extern struct list_head efivar_sysfs_list;
@@ -840,6 +842,8 @@
 #if defined(CONFIG_EFI_VARS) || defined(CONFIG_EFI_VARS_MODULE)
 int efivars_sysfs_init(void);
 
+#define EFIVARS_DATA_SIZE_MAX 1024
+
 #endif /* CONFIG_EFI_VARS */
 
 #endif /* _LINUX_EFI_H */
diff -ruw linux-3.11.10/include/linux/fsl_devices.h linux-3.11.10-fbx/include/linux/fsl_devices.h
--- linux-3.11.10/include/linux/fsl_devices.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/fsl_devices.h	2015-09-29 11:15:08.665753947 +0200
@@ -26,6 +26,7 @@
 #define FSL_USB_VER_2_4		3
 
 #include <linux/types.h>
+#include <linux/mbus.h>
 
 /*
  * Some conventions on how we handle peripherals on Freescale chips
@@ -104,6 +105,8 @@
 	u32		pm_configured_flag;
 	u32		pm_portsc;
 	u32		pm_usbgenctrl;
+
+	struct mbus_dram_target_info *dram;
 };
 
 /* Flags in fsl_usb2_mph_platform_data */
diff -ruw linux-3.11.10/include/linux/ftrace_event.h linux-3.11.10-fbx/include/linux/ftrace_event.h
--- linux-3.11.10/include/linux/ftrace_event.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/ftrace_event.h	2015-09-29 11:15:08.665753947 +0200
@@ -325,10 +325,6 @@
 	FILTER_TRACE_FN,
 };
 
-#define EVENT_STORAGE_SIZE 128
-extern struct mutex event_storage_mutex;
-extern char event_storage[EVENT_STORAGE_SIZE];
-
 extern int trace_event_raw_init(struct ftrace_event_call *call);
 extern int trace_define_field(struct ftrace_event_call *call, const char *type,
 			      const char *name, int offset, int size,
diff -ruw linux-3.11.10/include/linux/ftrace.h linux-3.11.10-fbx/include/linux/ftrace.h
--- linux-3.11.10/include/linux/ftrace.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/ftrace.h	2015-09-29 11:15:08.665753947 +0200
@@ -524,6 +524,7 @@
 extern int ftrace_arch_read_dyn_info(char *buf, int size);
 
 extern int skip_trace(unsigned long ip);
+extern void ftrace_module_init(struct module *mod);
 
 extern void ftrace_disable_daemon(void);
 extern void ftrace_enable_daemon(void);
@@ -533,6 +534,7 @@
 static inline void ftrace_disable_daemon(void) { }
 static inline void ftrace_enable_daemon(void) { }
 static inline void ftrace_release_mod(struct module *mod) {}
+static inline void ftrace_module_init(struct module *mod) {}
 static inline int register_ftrace_command(struct ftrace_func_command *cmd)
 {
 	return -EINVAL;
diff -ruw linux-3.11.10/include/linux/futex.h linux-3.11.10-fbx/include/linux/futex.h
--- linux-3.11.10/include/linux/futex.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/futex.h	2014-07-29 21:29:45.005867544 +0200
@@ -55,7 +55,11 @@
 #ifdef CONFIG_FUTEX
 extern void exit_robust_list(struct task_struct *curr);
 extern void exit_pi_state_list(struct task_struct *curr);
+#ifdef CONFIG_HAVE_FUTEX_CMPXCHG
+#define futex_cmpxchg_enabled 1
+#else
 extern int futex_cmpxchg_enabled;
+#endif
 #else
 static inline void exit_robust_list(struct task_struct *curr)
 {
diff -ruw linux-3.11.10/include/linux/genhd.h linux-3.11.10-fbx/include/linux/genhd.h
--- linux-3.11.10/include/linux/genhd.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/genhd.h	2015-09-29 11:15:08.665753947 +0200
@@ -85,6 +85,7 @@
 	unsigned long ticks[2];
 	unsigned long io_ticks;
 	unsigned long time_in_queue;
+	unsigned long io_errors[2];
 };
 
 #define PARTITION_META_INFO_VOLNAMELTH	64
diff -ruw linux-3.11.10/include/linux/hugetlb.h linux-3.11.10-fbx/include/linux/hugetlb.h
--- linux-3.11.10/include/linux/hugetlb.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/hugetlb.h	2015-09-29 11:15:08.669753984 +0200
@@ -31,6 +31,7 @@
 void hugepage_put_subpool(struct hugepage_subpool *spool);
 
 int PageHuge(struct page *page);
+int PageHeadHuge(struct page *page_head);
 
 void reset_vma_resv_huge_pages(struct vm_area_struct *vma);
 int hugetlb_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *);
@@ -100,6 +101,11 @@
 {
 	return 0;
 }
+
+static inline int PageHeadHuge(struct page *page_head)
+{
+	return 0;
+}
 
 static inline void reset_vma_resv_huge_pages(struct vm_area_struct *vma)
 {
diff -ruw linux-3.11.10/include/linux/ieee80211.h linux-3.11.10-fbx/include/linux/ieee80211.h
--- linux-3.11.10/include/linux/ieee80211.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/ieee80211.h	2015-09-29 11:15:08.673754009 +0200
@@ -16,6 +16,7 @@
 #define LINUX_IEEE80211_H
 
 #include <linux/types.h>
+#include <linux/if_ether.h>
 #include <asm/byteorder.h>
 
 /*
@@ -209,28 +210,28 @@
 struct ieee80211_hdr {
 	__le16 frame_control;
 	__le16 duration_id;
-	u8 addr1[6];
-	u8 addr2[6];
-	u8 addr3[6];
+	u8 addr1[ETH_ALEN];
+	u8 addr2[ETH_ALEN];
+	u8 addr3[ETH_ALEN];
 	__le16 seq_ctrl;
-	u8 addr4[6];
+	u8 addr4[ETH_ALEN];
 } __packed __aligned(2);
 
 struct ieee80211_hdr_3addr {
 	__le16 frame_control;
 	__le16 duration_id;
-	u8 addr1[6];
-	u8 addr2[6];
-	u8 addr3[6];
+	u8 addr1[ETH_ALEN];
+	u8 addr2[ETH_ALEN];
+	u8 addr3[ETH_ALEN];
 	__le16 seq_ctrl;
 } __packed __aligned(2);
 
 struct ieee80211_qos_hdr {
 	__le16 frame_control;
 	__le16 duration_id;
-	u8 addr1[6];
-	u8 addr2[6];
-	u8 addr3[6];
+	u8 addr1[ETH_ALEN];
+	u8 addr2[ETH_ALEN];
+	u8 addr3[ETH_ALEN];
 	__le16 seq_ctrl;
 	__le16 qos_ctrl;
 } __packed __aligned(2);
@@ -608,8 +609,8 @@
 	u8 flags;
 	u8 ttl;
 	__le32 seqnum;
-	u8 eaddr1[6];
-	u8 eaddr2[6];
+	u8 eaddr1[ETH_ALEN];
+	u8 eaddr2[ETH_ALEN];
 } __packed __aligned(2);
 
 /* Mesh flags */
@@ -696,6 +697,18 @@
 } __packed;
 
 /**
+ * struct ieee80211_mesh_chansw_params_ie - mesh channel switch parameters IE
+ *
+ * This structure represents the "Mesh Channel Switch Paramters element"
+ */
+struct ieee80211_mesh_chansw_params_ie {
+	u8 mesh_ttl;
+	u8 mesh_flags;
+	__le16 mesh_reason;
+	__le16 mesh_pre_value;
+} __packed;
+
+/**
  * struct ieee80211_wide_bw_chansw_ie - wide bandwidth channel switch IE
  */
 struct ieee80211_wide_bw_chansw_ie {
@@ -750,6 +763,14 @@
 };
 
 /**
+ * mesh channel switch parameters element's flag indicator
+ *
+ */
+#define WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT BIT(0)
+#define WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR BIT(1)
+#define WLAN_EID_CHAN_SWITCH_PARAM_REASON BIT(2)
+
+/**
  * struct ieee80211_rann_ie
  *
  * This structure refers to "Root Announcement information element"
@@ -758,7 +779,7 @@
 	u8 rann_flags;
 	u8 rann_hopcount;
 	u8 rann_ttl;
-	u8 rann_addr[6];
+	u8 rann_addr[ETH_ALEN];
 	__le32 rann_seq;
 	__le32 rann_interval;
 	__le32 rann_metric;
@@ -802,9 +823,9 @@
 struct ieee80211_mgmt {
 	__le16 frame_control;
 	__le16 duration;
-	u8 da[6];
-	u8 sa[6];
-	u8 bssid[6];
+	u8 da[ETH_ALEN];
+	u8 sa[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
 	__le16 seq_ctrl;
 	union {
 		struct {
@@ -833,7 +854,7 @@
 		struct {
 			__le16 capab_info;
 			__le16 listen_interval;
-			u8 current_ap[6];
+			u8 current_ap[ETH_ALEN];
 			/* followed by SSID and Supported rates */
 			u8 variable[0];
 		} __packed reassoc_req;
@@ -966,21 +987,21 @@
 struct ieee80211_rts {
 	__le16 frame_control;
 	__le16 duration;
-	u8 ra[6];
-	u8 ta[6];
+	u8 ra[ETH_ALEN];
+	u8 ta[ETH_ALEN];
 } __packed __aligned(2);
 
 struct ieee80211_cts {
 	__le16 frame_control;
 	__le16 duration;
-	u8 ra[6];
+	u8 ra[ETH_ALEN];
 } __packed __aligned(2);
 
 struct ieee80211_pspoll {
 	__le16 frame_control;
 	__le16 aid;
-	u8 bssid[6];
-	u8 ta[6];
+	u8 bssid[ETH_ALEN];
+	u8 ta[ETH_ALEN];
 } __packed __aligned(2);
 
 /* TDLS */
@@ -989,14 +1010,14 @@
 struct ieee80211_tdls_lnkie {
 	u8 ie_type; /* Link Identifier IE */
 	u8 ie_len;
-	u8 bssid[6];
-	u8 init_sta[6];
-	u8 resp_sta[6];
+	u8 bssid[ETH_ALEN];
+	u8 init_sta[ETH_ALEN];
+	u8 resp_sta[ETH_ALEN];
 } __packed;
 
 struct ieee80211_tdls_data {
-	u8 da[6];
-	u8 sa[6];
+	u8 da[ETH_ALEN];
+	u8 sa[ETH_ALEN];
 	__be16 ether_type;
 	u8 payload_type;
 	u8 category;
@@ -1090,8 +1111,8 @@
 struct ieee80211_bar {
 	__le16 frame_control;
 	__le16 duration;
-	__u8 ra[6];
-	__u8 ta[6];
+	__u8 ra[ETH_ALEN];
+	__u8 ta[ETH_ALEN];
 	__le16 control;
 	__le16 start_seq_num;
 } __packed;
@@ -1390,8 +1411,8 @@
 #define IEEE80211_VHT_CAP_RXSTBC_MASK				0x00000700
 #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_DIMENSIONS_MAX		0x00030000
+#define IEEE80211_VHT_CAP_BEAMFORMEE_STS_MAX			0x0000e000
+#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX		0x00070000
 #define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE			0x00080000
 #define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE			0x00100000
 #define IEEE80211_VHT_CAP_VHT_TXOP_PS				0x00200000
@@ -1709,6 +1730,10 @@
 	WLAN_EID_OPMODE_NOTIF = 199,
 	WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194,
 	WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196,
+	WLAN_EID_EXTENDED_BSS_LOAD = 193,
+	WLAN_EID_VHT_TX_POWER_ENVELOPE = 195,
+	WLAN_EID_AID = 197,
+	WLAN_EID_QUIET_CHANNEL = 198,
 
 	/* 802.11ad */
 	WLAN_EID_NON_TX_BSSID_CAP =  83,
@@ -1860,6 +1885,11 @@
 	WLAN_TDLS_DISCOVERY_REQUEST = 10,
 };
 
+/* Interworking capabilities are set in 7th bit of 4th byte of the
+ * @WLAN_EID_EXT_CAPABILITY information element
+ */
+#define WLAN_EXT_CAPA4_INTERWORKING_ENABLED	BIT(7)
+
 /*
  * TDLS capabililites to be enabled in the 5th byte of the
  * @WLAN_EID_EXT_CAPABILITY information element
@@ -2279,4 +2309,8 @@
 	return !!(tim->virtual_map[index] & mask);
 }
 
+/* convert time units */
+#define TU_TO_JIFFIES(x)	(usecs_to_jiffies((x) * 1024))
+#define TU_TO_EXP_TIME(x)	(jiffies + TU_TO_JIFFIES(x))
+
 #endif /* LINUX_IEEE80211_H */
diff -ruw linux-3.11.10/include/linux/if_vlan.h linux-3.11.10-fbx/include/linux/if_vlan.h
--- linux-3.11.10/include/linux/if_vlan.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/if_vlan.h	2015-09-29 11:15:08.673754009 +0200
@@ -87,8 +87,10 @@
 extern struct net_device *__vlan_find_dev_deep(struct net_device *real_dev,
 					       __be16 vlan_proto, u16 vlan_id);
 extern struct net_device *vlan_dev_real_dev(const struct net_device *dev);
+extern struct net_device *vlan_dev_upper_dev(const struct net_device *dev);
 extern u16 vlan_dev_vlan_id(const struct net_device *dev);
 
+
 extern bool vlan_do_receive(struct sk_buff **skb);
 extern struct sk_buff *vlan_untag(struct sk_buff *skb);
 
@@ -113,6 +115,12 @@
 {
 	BUG();
 	return NULL;
+}
+
+static inline struct net_device *vlan_dev_upper_dev(const struct net_device *dev)
+{
+	BUG();
+	return NULL;
 }
 
 static inline u16 vlan_dev_vlan_id(const struct net_device *dev)
diff -ruw linux-3.11.10/include/linux/in.h linux-3.11.10-fbx/include/linux/in.h
--- linux-3.11.10/include/linux/in.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/in.h	2015-06-12 14:02:27.298993709 +0200
@@ -34,6 +34,9 @@
 		return 0;
 	case IPPROTO_AH:	/* SPI */
 		return 4;
+	case IPPROTO_IPV6:
+		/* third byte of ipv6 destination address */
+		return 36;
 	default:
 		return -EINVAL;
 	}
diff -ruw linux-3.11.10/include/linux/init_task.h linux-3.11.10-fbx/include/linux/init_task.h
--- linux-3.11.10/include/linux/init_task.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/init_task.h	2015-09-29 11:15:08.673754009 +0200
@@ -40,6 +40,7 @@
 
 #define INIT_SIGNALS(sig) {						\
 	.nr_threads	= 1,						\
+	.thread_head	= LIST_HEAD_INIT(init_task.thread_node),	\
 	.wait_chldexit	= __WAIT_QUEUE_HEAD_INITIALIZER(sig.wait_chldexit),\
 	.shared_pending	= { 						\
 		.list = LIST_HEAD_INIT(sig.shared_pending.list),	\
@@ -164,6 +165,7 @@
 	.stack		= &init_thread_info,				\
 	.usage		= ATOMIC_INIT(2),				\
 	.flags		= PF_KTHREAD,					\
+	.exec_mode	= EXEC_MODE_UNLIMITED,				\
 	.prio		= MAX_PRIO-20,					\
 	.static_prio	= MAX_PRIO-20,					\
 	.normal_prio	= MAX_PRIO-20,					\
@@ -213,6 +215,7 @@
 		[PIDTYPE_SID]  = INIT_PID_LINK(PIDTYPE_SID),		\
 	},								\
 	.thread_group	= LIST_HEAD_INIT(tsk.thread_group),		\
+	.thread_node	= LIST_HEAD_INIT(init_signals.thread_head),	\
 	INIT_IDS							\
 	INIT_PERF_EVENTS(tsk)						\
 	INIT_TRACE_IRQFLAGS						\
diff -ruw linux-3.11.10/include/linux/interrupt.h linux-3.11.10-fbx/include/linux/interrupt.h
--- linux-3.11.10/include/linux/interrupt.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/interrupt.h	2015-09-29 11:15:08.677754038 +0200
@@ -239,7 +239,40 @@
 
 extern cpumask_var_t irq_default_affinity;
 
-extern int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask);
+/* Internal implementation. Use the helpers below */
+extern int __irq_set_affinity(unsigned int irq, const struct cpumask *cpumask,
+			      bool force);
+
+/**
+ * irq_set_affinity - Set the irq affinity of a given irq
+ * @irq:	Interrupt to set affinity
+ * @mask:	cpumask
+ *
+ * Fails if cpumask does not contain an online CPU
+ */
+static inline int
+irq_set_affinity(unsigned int irq, const struct cpumask *cpumask)
+{
+	return __irq_set_affinity(irq, cpumask, false);
+}
+
+/**
+ * irq_force_affinity - Force the irq affinity of a given irq
+ * @irq:	Interrupt to set affinity
+ * @mask:	cpumask
+ *
+ * Same as irq_set_affinity, but without checking the mask against
+ * online cpus.
+ *
+ * Solely for low level cpu hotplug code, where we need to make per
+ * cpu interrupts affine before the cpu becomes online.
+ */
+static inline int
+irq_force_affinity(unsigned int irq, const struct cpumask *cpumask)
+{
+	return __irq_set_affinity(irq, cpumask, true);
+}
+
 extern int irq_can_set_affinity(unsigned int irq);
 extern int irq_select_affinity(unsigned int irq);
 
@@ -275,6 +308,11 @@
 	return -EINVAL;
 }
 
+static inline int irq_force_affinity(unsigned int irq, const struct cpumask *cpumask)
+{
+	return 0;
+}
+
 static inline int irq_can_set_affinity(unsigned int irq)
 {
 	return 0;
diff -ruw linux-3.11.10/include/linux/ipc_namespace.h linux-3.11.10-fbx/include/linux/ipc_namespace.h
--- linux-3.11.10/include/linux/ipc_namespace.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/ipc_namespace.h	2015-09-29 11:15:08.677754038 +0200
@@ -34,9 +34,9 @@
 	int		sem_ctls[4];
 	int		used_sems;
 
-	int		msg_ctlmax;
-	int		msg_ctlmnb;
-	int		msg_ctlmni;
+	unsigned int	msg_ctlmax;
+	unsigned int	msg_ctlmnb;
+	unsigned int	msg_ctlmni;
 	atomic_t	msg_bytes;
 	atomic_t	msg_hdrs;
 	int		auto_msgmni;
@@ -119,9 +119,7 @@
  *     the new maximum will handle anyone else.  I may have to revisit this
  *     in the future.
  */
-#define MIN_QUEUESMAX			1
 #define DFLT_QUEUESMAX		      256
-#define HARD_QUEUESMAX		     1024
 #define MIN_MSGMAX			1
 #define DFLT_MSG		       10U
 #define DFLT_MSGMAX		       10
diff -ruw linux-3.11.10/include/linux/irqdesc.h linux-3.11.10-fbx/include/linux/irqdesc.h
--- linux-3.11.10/include/linux/irqdesc.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/irqdesc.h	2015-09-29 11:15:08.677754038 +0200
@@ -27,6 +27,8 @@
  * @irq_count:		stats field to detect stalled irqs
  * @last_unhandled:	aging timer for unhandled count
  * @irqs_unhandled:	stats field for spurious unhandled interrupts
+ * @threads_handled:	stats field for deferred spurious detection of threaded handlers
+ * @threads_handled_last: comparator field for deferred spurious detection of theraded handlers
  * @lock:		locking for SMP
  * @affinity_hint:	hint to user space for preferred irq affinity
  * @affinity_notify:	context for notification of affinity changes
@@ -52,6 +54,8 @@
 	unsigned int		irq_count;	/* For detecting broken IRQs */
 	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
 	unsigned int		irqs_unhandled;
+	atomic_t		threads_handled;
+	int			threads_handled_last;
 	raw_spinlock_t		lock;
 	struct cpumask		*percpu_enabled;
 #ifdef CONFIG_SMP
diff -ruw linux-3.11.10/include/linux/irq.h linux-3.11.10-fbx/include/linux/irq.h
--- linux-3.11.10/include/linux/irq.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/irq.h	2015-09-29 11:15:08.677754038 +0200
@@ -380,7 +380,8 @@
 
 extern void irq_cpu_online(void);
 extern void irq_cpu_offline(void);
-extern int __irq_set_affinity_locked(struct irq_data *data,  const struct cpumask *cpumask);
+extern int irq_set_affinity_locked(struct irq_data *data,
+				   const struct cpumask *cpumask, bool force);
 
 #ifdef CONFIG_GENERIC_HARDIRQS
 
diff -ruw linux-3.11.10/include/linux/jiffies.h linux-3.11.10-fbx/include/linux/jiffies.h
--- linux-3.11.10/include/linux/jiffies.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/jiffies.h	2015-09-29 11:15:08.681754075 +0200
@@ -101,13 +101,13 @@
 #define time_after(a,b)		\
 	(typecheck(unsigned long, a) && \
 	 typecheck(unsigned long, b) && \
-	 ((long)(b) - (long)(a) < 0))
+	 ((long)((b) - (a)) < 0))
 #define time_before(a,b)	time_after(b,a)
 
 #define time_after_eq(a,b)	\
 	(typecheck(unsigned long, a) && \
 	 typecheck(unsigned long, b) && \
-	 ((long)(a) - (long)(b) >= 0))
+	 ((long)((a) - (b)) >= 0))
 #define time_before_eq(a,b)	time_after_eq(b,a)
 
 /*
@@ -130,13 +130,13 @@
 #define time_after64(a,b)	\
 	(typecheck(__u64, a) &&	\
 	 typecheck(__u64, b) && \
-	 ((__s64)(b) - (__s64)(a) < 0))
+	 ((__s64)((b) - (a)) < 0))
 #define time_before64(a,b)	time_after64(b,a)
 
 #define time_after_eq64(a,b)	\
 	(typecheck(__u64, a) && \
 	 typecheck(__u64, b) && \
-	 ((__s64)(a) - (__s64)(b) >= 0))
+	 ((__s64)((a) - (b)) >= 0))
 #define time_before_eq64(a,b)	time_after_eq64(b,a)
 
 #define time_in_range64(a, b, c) \
diff -ruw linux-3.11.10/include/linux/kexec.h linux-3.11.10-fbx/include/linux/kexec.h
--- linux-3.11.10/include/linux/kexec.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/kexec.h	2015-09-29 11:15:08.681754075 +0200
@@ -198,6 +198,9 @@
 extern size_t vmcoreinfo_size;
 extern size_t vmcoreinfo_max_size;
 
+/* flag to track if kexec reboot is in progress */
+extern bool kexec_in_progress;
+
 int __init parse_crashkernel(char *cmdline, unsigned long long system_ram,
 		unsigned long long *crash_size, unsigned long long *crash_base);
 int parse_crashkernel_high(char *cmdline, unsigned long long system_ram,
diff -ruw linux-3.11.10/include/linux/libata.h linux-3.11.10-fbx/include/linux/libata.h
--- linux-3.11.10/include/linux/libata.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/libata.h	2015-09-29 11:15:08.685754101 +0200
@@ -400,6 +400,8 @@
 	ATA_HORKAGE_DUMP_ID	= (1 << 16),	/* dump IDENTIFY data */
 	ATA_HORKAGE_MAX_SEC_LBA48 = (1 << 17),	/* Set max sects to 65535 */
 	ATA_HORKAGE_ATAPI_DMADIR = (1 << 18),	/* device requires dmadir */
+	ATA_HORKAGE_NOLPM	= (1 << 20),	/* don't use LPM */
+	ATA_HORKAGE_WD_BROKEN_LPM = (1 << 21),	/* some WDs have broken LPM */
 
 	 /* DMA mask for user DMA control: User visible values; DO NOT
 	    renumber */
@@ -771,6 +773,7 @@
 	unsigned long		qc_allocated;
 	unsigned int		qc_active;
 	int			nr_active_links; /* #links with active qcs */
+	unsigned int		last_tag;	/* track next tag hw expects */
 
 	struct ata_link		link;		/* host default link */
 	struct ata_link		*slave_link;	/* see ata_slave_link_init() */
diff -ruw linux-3.11.10/include/linux/list.h linux-3.11.10-fbx/include/linux/list.h
--- linux-3.11.10/include/linux/list.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/list.h	2015-09-29 11:15:08.685754101 +0200
@@ -373,6 +373,22 @@
 	(!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL)
 
 /**
+ * list_next_entry - get the next element in list
+ * @pos:	the type * to cursor
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_next_entry(pos, member) \
+	list_entry((pos)->member.next, typeof(*(pos)), member)
+
+/**
+ * list_prev_entry - get the prev element in list
+ * @pos:	the type * to cursor
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_prev_entry(pos, member) \
+	list_entry((pos)->member.prev, typeof(*(pos)), member)
+
+/**
  * list_for_each	-	iterate over a list
  * @pos:	the &struct list_head to use as a loop cursor.
  * @head:	the head for your list.
diff -ruw linux-3.11.10/include/linux/migrate.h linux-3.11.10-fbx/include/linux/migrate.h
--- linux-3.11.10/include/linux/migrate.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/migrate.h	2015-09-29 11:15:08.693754167 +0200
@@ -92,10 +92,18 @@
 #endif /* CONFIG_MIGRATION */
 
 #ifdef CONFIG_NUMA_BALANCING
-extern int migrate_misplaced_page(struct page *page, int node);
+extern bool pmd_trans_migrating(pmd_t pmd);
+extern void wait_migrate_huge_page(struct anon_vma *anon_vma, pmd_t *pmd);
 extern int migrate_misplaced_page(struct page *page, int node);
 extern bool migrate_ratelimited(int node);
 #else
+static inline bool pmd_trans_migrating(pmd_t pmd)
+{
+	return false;
+}
+static inline void wait_migrate_huge_page(struct anon_vma *anon_vma, pmd_t *pmd)
+{
+}
 static inline int migrate_misplaced_page(struct page *page, int node)
 {
 	return -EAGAIN; /* can't migrate now */
diff -ruw linux-3.11.10/include/linux/mm.h linux-3.11.10-fbx/include/linux/mm.h
--- linux-3.11.10/include/linux/mm.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/mm.h	2015-09-29 11:15:08.697754192 +0200
@@ -155,7 +155,7 @@
  * Special vmas that are non-mergable, non-mlock()able.
  * Note: mm/huge_memory.c VM_NO_THP depends on this definition.
  */
-#define VM_SPECIAL (VM_IO | VM_DONTEXPAND | VM_PFNMAP)
+#define VM_SPECIAL (VM_IO | VM_DONTEXPAND | VM_PFNMAP | VM_MIXEDMAP)
 
 /*
  * mapping from the currently active vm_flags protection bits (the
@@ -769,11 +769,14 @@
 #endif
 
 #if defined(WANT_PAGE_VIRTUAL)
-#define page_address(page) ((page)->virtual)
-#define set_page_address(page, address)			\
-	do {						\
-		(page)->virtual = (address);		\
-	} while(0)
+static inline void *page_address(const struct page *page)
+{
+	return page->virtual;
+}
+static inline void set_page_address(struct page *page, void *address)
+{
+	page->virtual = address;
+}
 #define page_address_init()  do { } while(0)
 #endif
 
diff -ruw linux-3.11.10/include/linux/mm_types.h linux-3.11.10-fbx/include/linux/mm_types.h
--- linux-3.11.10/include/linux/mm_types.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/mm_types.h	2015-09-29 11:15:08.697754192 +0200
@@ -434,6 +434,14 @@
 	 */
 	int first_nid;
 #endif
+#if defined(CONFIG_NUMA_BALANCING) || defined(CONFIG_COMPACTION)
+	/*
+	 * An operation with batched TLB flushing is going on. Anything that
+	 * can move process memory needs to flush the TLB when moving a
+	 * PROT_NONE or PROT_NUMA mapped page.
+	 */
+	bool tlb_flush_pending;
+#endif
 	struct uprobes_state uprobes_state;
 };
 
@@ -454,4 +462,45 @@
 	return mm->cpu_vm_mask_var;
 }
 
+#if defined(CONFIG_NUMA_BALANCING) || defined(CONFIG_COMPACTION)
+/*
+ * Memory barriers to keep this state in sync are graciously provided by
+ * the page table locks, outside of which no page table modifications happen.
+ * The barriers below prevent the compiler from re-ordering the instructions
+ * around the memory barriers that are already present in the code.
+ */
+static inline bool mm_tlb_flush_pending(struct mm_struct *mm)
+{
+	barrier();
+	return mm->tlb_flush_pending;
+}
+static inline void set_tlb_flush_pending(struct mm_struct *mm)
+{
+	mm->tlb_flush_pending = true;
+
+	/*
+	 * Guarantee that the tlb_flush_pending store does not leak into the
+	 * critical section updating the page tables
+	 */
+	smp_mb__before_spinlock();
+}
+/* Clearing is done after a TLB flush, which also provides a barrier. */
+static inline void clear_tlb_flush_pending(struct mm_struct *mm)
+{
+	barrier();
+	mm->tlb_flush_pending = false;
+}
+#else
+static inline bool mm_tlb_flush_pending(struct mm_struct *mm)
+{
+	return false;
+}
+static inline void set_tlb_flush_pending(struct mm_struct *mm)
+{
+}
+static inline void clear_tlb_flush_pending(struct mm_struct *mm)
+{
+}
+#endif
+
 #endif /* _LINUX_MM_TYPES_H */
diff -ruw linux-3.11.10/include/linux/mmzone.h linux-3.11.10-fbx/include/linux/mmzone.h
--- linux-3.11.10/include/linux/mmzone.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/mmzone.h	2015-09-29 11:15:08.701754221 +0200
@@ -75,9 +75,13 @@
 
 extern int page_group_by_mobility_disabled;
 
+#define NR_MIGRATETYPE_BITS (PB_migrate_end - PB_migrate + 1)
+#define MIGRATETYPE_MASK ((1UL << NR_MIGRATETYPE_BITS) - 1)
+
 static inline int get_pageblock_migratetype(struct page *page)
 {
-	return get_pageblock_flags_group(page, PB_migrate, PB_migrate_end);
+	BUILD_BUG_ON(PB_migrate_end - PB_migrate != 2);
+	return get_pageblock_flags_mask(page, PB_migrate_end, MIGRATETYPE_MASK);
 }
 
 struct free_area {
diff -ruw linux-3.11.10/include/linux/msg.h linux-3.11.10-fbx/include/linux/msg.h
--- linux-3.11.10/include/linux/msg.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/msg.h	2015-09-29 11:15:08.701754221 +0200
@@ -8,7 +8,7 @@
 struct msg_msg {
 	struct list_head m_list; 
 	long  m_type;          
-	int m_ts;           /* message text size */
+	size_t m_ts;		/* message text size */
 	struct msg_msgseg* next;
 	void *security;
 	/* the actual message follows immediately */
diff -ruw linux-3.11.10/include/linux/mtd/map.h linux-3.11.10-fbx/include/linux/mtd/map.h
--- linux-3.11.10/include/linux/mtd/map.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/mtd/map.h	2015-09-29 11:15:08.701754221 +0200
@@ -365,7 +365,7 @@
 			bitpos = (map_bankwidth(map)-1-i)*8;
 #endif
 			orig.x[0] &= ~(0xff << bitpos);
-			orig.x[0] |= buf[i-start] << bitpos;
+			orig.x[0] |= (unsigned long)buf[i-start] << bitpos;
 		}
 	}
 	return orig;
@@ -384,7 +384,7 @@
 
 	if (map_bankwidth(map) < MAP_FF_LIMIT) {
 		int bw = 8 * map_bankwidth(map);
-		r.x[0] = (1 << bw) - 1;
+		r.x[0] = (1UL << bw) - 1;
 	} else {
 		for (i=0; i<map_words(map); i++)
 			r.x[i] = ~0UL;
diff -ruw linux-3.11.10/include/linux/mtd/mtd.h linux-3.11.10-fbx/include/linux/mtd/mtd.h
--- linux-3.11.10/include/linux/mtd/mtd.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/mtd/mtd.h	2015-09-29 11:15:08.701754221 +0200
@@ -166,6 +166,11 @@
 	 */
 	unsigned int bitflip_threshold;
 
+	/* NAND related attributes */
+	const char *nand_type;
+	const char *nand_manufacturer;
+	const char *onfi_version;
+
 	// Kernel-only stuff starts here.
 	const char *name;
 	int index;
diff -ruw linux-3.11.10/include/linux/mtd/nand.h linux-3.11.10-fbx/include/linux/mtd/nand.h
--- linux-3.11.10/include/linux/mtd/nand.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/mtd/nand.h	2015-09-29 11:15:08.701754221 +0200
@@ -191,6 +191,9 @@
  */
 #define NAND_BUSWIDTH_AUTO      0x00080000
 
+/* NAND controller does not want RNDOUT commands, even in NAND_ECC_SOFT */
+#define NAND_NO_RNDOUT		0x00800000
+
 /* Options set by nand scan */
 /* Nand scan has allocated controller struct */
 #define NAND_CONTROLLER_ALLOC	0x80000000
diff -ruw linux-3.11.10/include/linux/mv643xx_eth.h linux-3.11.10-fbx/include/linux/mv643xx_eth.h
--- linux-3.11.10/include/linux/mv643xx_eth.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/mv643xx_eth.h	2015-09-29 11:15:08.701754221 +0200
@@ -6,6 +6,8 @@
 #define __LINUX_MV643XX_ETH_H
 
 #include <linux/mbus.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
 
 #define MV643XX_ETH_SHARED_NAME		"mv643xx_eth"
 #define MV643XX_ETH_NAME		"mv643xx_eth_port"
@@ -45,10 +47,19 @@
 	struct device_node	*phy_node;
 
 	/*
+	 * if PHY_NONE, called at probe/remove time to configure mii bus
+	 */
+	int (*mii_config)(struct net_device *dev, int probe,
+			  int (*mii_read)(struct net_device *dev,
+					  int phy_id, int reg),
+			  void (*mii_write)(struct net_device *dev,
+					    int phy_id, int reg, int val));
+
+	/*
 	 * Use this MAC address if it is valid, overriding the
 	 * address that is already in the hardware.
 	 */
-	u8			mac_addr[6];
+	u8			mac_addr[ETH_ALEN];
 
 	/*
 	 * If speed is 0, autonegotiation is enabled.
diff -ruw linux-3.11.10/include/linux/netdevice.h linux-3.11.10-fbx/include/linux/netdevice.h
--- linux-3.11.10/include/linux/netdevice.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/netdevice.h	2015-09-29 11:15:08.705754258 +0200
@@ -1336,6 +1336,12 @@
 	/* MRP */
 	struct mrp_port __rcu	*mrp_port;
 
+#ifdef CONFIG_FBXBRIDGE
+	struct fbxbridge	*fbx_bridge;
+	struct fbxbridge	*fbx_bridge_port;
+	int			fbx_bridge_maybe_port;
+#endif
+
 	/* class/net/name entry */
 	struct device		dev;
 	/* space for optional device, statistics, and wireless sysfs groups */
@@ -1836,6 +1842,15 @@
 	return dev->header_ops->parse(skb, haddr);
 }
 
+static inline int dev_rebuild_header(struct sk_buff *skb)
+{
+	const struct net_device *dev = skb->dev;
+
+	if (!dev->header_ops || !dev->header_ops->rebuild)
+		return 0;
+	return dev->header_ops->rebuild(skb);
+}
+
 typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len);
 extern int		register_gifconf(unsigned int family, gifconf_func_t * gifconf);
 static inline int unregister_gifconf(unsigned int family)
@@ -2833,7 +2848,12 @@
 void netif_stacked_transfer_operstate(const struct net_device *rootdev,
 					struct net_device *dev);
 
-netdev_features_t netif_skb_features(struct sk_buff *skb);
+netdev_features_t netif_skb_dev_features(struct sk_buff *skb,
+					 const struct net_device *dev);
+static inline netdev_features_t netif_skb_features(struct sk_buff *skb)
+{
+	return netif_skb_dev_features(skb, skb->dev);
+}
 
 static inline bool net_gso_ok(netdev_features_t features, int gso_type)
 {
@@ -2870,6 +2890,19 @@
 	dev->gso_max_size = size;
 }
 
+static inline void skb_gso_error_unwind(struct sk_buff *skb, __be16 protocol,
+					int pulled_hlen, u16 mac_offset,
+					int mac_len)
+{
+	skb->protocol = protocol;
+	skb->encapsulation = 1;
+	skb_push(skb, pulled_hlen);
+	skb_reset_transport_header(skb);
+	skb->mac_header = mac_offset;
+	skb->network_header = skb->mac_header + mac_len;
+	skb->mac_len = mac_len;
+}
+
 static inline bool netif_is_bond_master(struct net_device *dev)
 {
 	return dev->flags & IFF_MASTER && dev->priv_flags & IFF_BONDING;
diff -ruw linux-3.11.10/include/linux/netfilter/nf_conntrack_ftp.h linux-3.11.10-fbx/include/linux/netfilter/nf_conntrack_ftp.h
--- linux-3.11.10/include/linux/netfilter/nf_conntrack_ftp.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/netfilter/nf_conntrack_ftp.h	2015-06-12 14:02:27.338993998 +0200
@@ -17,6 +17,11 @@
 	u_int16_t seq_aft_nl_num[IP_CT_DIR_MAX];
 	/* pickup sequence tracking, useful for conntrackd */
 	u_int16_t flags[IP_CT_DIR_MAX];
+#if defined(CONFIG_FREEBOX_BRIDGE) || defined(CONFIG_FREEBOX_BRIDGE_MODULE)
+	unsigned int is_fbxbridge;
+	unsigned long fbxbridge_remote;
+	unsigned long fbxbridge_wan;
+#endif
 };
 
 struct nf_conntrack_expect;
diff -ruw linux-3.11.10/include/linux/net.h linux-3.11.10-fbx/include/linux/net.h
--- linux-3.11.10/include/linux/net.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/net.h	2015-09-29 11:15:08.701754221 +0200
@@ -163,6 +163,14 @@
 #endif
 	int		(*sendmsg)   (struct kiocb *iocb, struct socket *sock,
 				      struct msghdr *m, size_t total_len);
+	/* Notes for implementing recvmsg:
+	 * ===============================
+	 * msg->msg_namelen should get updated by the recvmsg handlers
+	 * iff msg_name != NULL. It is by default 0 to prevent
+	 * returning uninitialized memory to user space.  The recvfrom
+	 * handlers can assume that msg.msg_name is either NULL or has
+	 * a minimum size of sizeof(struct sockaddr_storage).
+	 */
 	int		(*recvmsg)   (struct kiocb *iocb, struct socket *sock,
 				      struct msghdr *m, size_t total_len,
 				      int flags);
@@ -172,7 +180,7 @@
 				      int offset, size_t size, int flags);
 	ssize_t 	(*splice_read)(struct socket *sock,  loff_t *ppos,
 				       struct pipe_inode_info *pipe, size_t len, unsigned int flags);
-	void		(*set_peek_off)(struct sock *sk, int val);
+	int		(*set_peek_off)(struct sock *sk, int val);
 };
 
 #define DECLARE_SOCKADDR(type, dst, src)	\
diff -ruw linux-3.11.10/include/linux/netlink.h linux-3.11.10-fbx/include/linux/netlink.h
--- linux-3.11.10/include/linux/netlink.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/netlink.h	2015-09-29 11:15:08.705754258 +0200
@@ -19,6 +19,7 @@
 	NETLINK_SKB_MMAPED	= 0x1,		/* Packet data is mmaped */
 	NETLINK_SKB_TX		= 0x2,		/* Packet was sent by userspace */
 	NETLINK_SKB_DELIVERED	= 0x4,		/* Packet was delivered */
+	NETLINK_SKB_DST		= 0x8,	/* Dst set in sendto or sendmsg */
 };
 
 struct netlink_skb_parms {
@@ -171,4 +172,11 @@
 extern int __netlink_remove_tap(struct netlink_tap *nt);
 extern int netlink_remove_tap(struct netlink_tap *nt);
 
+bool __netlink_ns_capable(const struct netlink_skb_parms *nsp,
+			  struct user_namespace *ns, int cap);
+bool netlink_ns_capable(const struct sk_buff *skb,
+			struct user_namespace *ns, int cap);
+bool netlink_capable(const struct sk_buff *skb, int cap);
+bool netlink_net_capable(const struct sk_buff *skb, int cap);
+
 #endif	/* __LINUX_NETLINK_H */
diff -ruw linux-3.11.10/include/linux/nfs4.h linux-3.11.10-fbx/include/linux/nfs4.h
--- linux-3.11.10/include/linux/nfs4.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/nfs4.h	2015-09-29 11:15:08.705754258 +0200
@@ -395,7 +395,7 @@
 #define FATTR4_WORD1_FS_LAYOUT_TYPES    (1UL << 30)
 #define FATTR4_WORD2_LAYOUT_BLKSIZE     (1UL << 1)
 #define FATTR4_WORD2_MDSTHRESHOLD       (1UL << 4)
-#define FATTR4_WORD2_SECURITY_LABEL     (1UL << 17)
+#define FATTR4_WORD2_SECURITY_LABEL     (1UL << 16)
 
 /* MDS threshold bitmap bits */
 #define THRESHOLD_RD                    (1UL << 0)
@@ -408,16 +408,6 @@
 #define NFS4_VERSION 4
 #define NFS4_MINOR_VERSION 0
 
-#if defined(CONFIG_NFS_V4_2)
-#define NFS4_MAX_MINOR_VERSION 2
-#else
-#if defined(CONFIG_NFS_V4_1)
-#define NFS4_MAX_MINOR_VERSION 1
-#else
-#define NFS4_MAX_MINOR_VERSION 0
-#endif /* CONFIG_NFS_V4_1 */
-#endif /* CONFIG_NFS_V4_2 */
-
 #define NFS4_DEBUG 1
 
 /* Index of predefined Linux client operations */
diff -ruw linux-3.11.10/include/linux/nfs_fs.h linux-3.11.10-fbx/include/linux/nfs_fs.h
--- linux-3.11.10/include/linux/nfs_fs.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/nfs_fs.h	2015-09-29 11:15:08.709754284 +0200
@@ -503,24 +503,6 @@
 extern void nfs_release_automount_timer(void);
 
 /*
- * linux/fs/nfs/nfs4proc.c
- */
-#ifdef CONFIG_NFS_V4_SECURITY_LABEL
-extern struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags);
-static inline void nfs4_label_free(struct nfs4_label *label)
-{
-	if (label) {
-		kfree(label->label);
-		kfree(label);
-	}
-	return;
-}
-#else
-static inline struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags) { return NULL; }
-static inline void nfs4_label_free(void *label) {}
-#endif
-
-/*
  * linux/fs/nfs/unlink.c
  */
 extern void nfs_complete_unlink(struct dentry *dentry, struct inode *);
diff -ruw linux-3.11.10/include/linux/pageblock-flags.h linux-3.11.10-fbx/include/linux/pageblock-flags.h
--- linux-3.11.10/include/linux/pageblock-flags.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/pageblock-flags.h	2015-09-29 11:15:08.713754313 +0200
@@ -30,9 +30,12 @@
 	PB_migrate,
 	PB_migrate_end = PB_migrate + 3 - 1,
 			/* 3 bits required for migrate types */
-#ifdef CONFIG_COMPACTION
 	PB_migrate_skip,/* If set the block is skipped by compaction */
-#endif /* CONFIG_COMPACTION */
+
+	/*
+	 * Assume the bits will always align on a word. If this assumption
+	 * changes then get/set pageblock needs updating.
+	 */
 	NR_PAGEBLOCK_BITS
 };
 
@@ -62,11 +65,33 @@
 /* Forward declaration */
 struct page;
 
+unsigned long get_pageblock_flags_mask(struct page *page,
+				unsigned long end_bitidx,
+				unsigned long mask);
+void set_pageblock_flags_mask(struct page *page,
+				unsigned long flags,
+				unsigned long end_bitidx,
+				unsigned long mask);
+
 /* Declarations for getting and setting flags. See mm/page_alloc.c */
-unsigned long get_pageblock_flags_group(struct page *page,
-					int start_bitidx, int end_bitidx);
-void set_pageblock_flags_group(struct page *page, unsigned long flags,
-					int start_bitidx, int end_bitidx);
+static inline unsigned long get_pageblock_flags_group(struct page *page,
+					int start_bitidx, int end_bitidx)
+{
+	unsigned long nr_flag_bits = end_bitidx - start_bitidx + 1;
+	unsigned long mask = (1 << nr_flag_bits) - 1;
+
+	return get_pageblock_flags_mask(page, end_bitidx, mask);
+}
+
+static inline void set_pageblock_flags_group(struct page *page,
+					unsigned long flags,
+					int start_bitidx, int end_bitidx)
+{
+	unsigned long nr_flag_bits = end_bitidx - start_bitidx + 1;
+	unsigned long mask = (1 << nr_flag_bits) - 1;
+
+	set_pageblock_flags_mask(page, flags, end_bitidx, mask);
+}
 
 #ifdef CONFIG_COMPACTION
 #define get_pageblock_skip(page) \
diff -ruw linux-3.11.10/include/linux/page-flags.h linux-3.11.10-fbx/include/linux/page-flags.h
--- linux-3.11.10/include/linux/page-flags.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/page-flags.h	2015-09-29 11:15:08.713754313 +0200
@@ -317,13 +317,23 @@
 extern void cancel_dirty_page(struct page *page, unsigned int account_size);
 
 int test_clear_page_writeback(struct page *page);
-int test_set_page_writeback(struct page *page);
+int __test_set_page_writeback(struct page *page, bool keep_write);
+
+#define test_set_page_writeback(page)			\
+	__test_set_page_writeback(page, false)
+#define test_set_page_writeback_keepwrite(page)	\
+	__test_set_page_writeback(page, true)
 
 static inline void set_page_writeback(struct page *page)
 {
 	test_set_page_writeback(page);
 }
 
+static inline void set_page_writeback_keepwrite(struct page *page)
+{
+	test_set_page_writeback_keepwrite(page);
+}
+
 #ifdef CONFIG_PAGEFLAGS_EXTENDED
 /*
  * System with lots of page flags available. This allows separate
diff -ruw linux-3.11.10/include/linux/pci_ids.h linux-3.11.10-fbx/include/linux/pci_ids.h
--- linux-3.11.10/include/linux/pci_ids.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/pci_ids.h	2015-09-29 11:15:08.713754313 +0200
@@ -2964,3 +2964,6 @@
 #define PCI_DEVICE_ID_XEN_PLATFORM	0x0001
 
 #define PCI_VENDOR_ID_OCZ		0x1b85
+
+#define PCI_VENDOR_ID_PERICOM		0x12d8
+#define PCI_DEVICE_ID_PI7C9X20303SL	0xa303
diff -ruw linux-3.11.10/include/linux/percpu-refcount.h linux-3.11.10-fbx/include/linux/percpu-refcount.h
--- linux-3.11.10/include/linux/percpu-refcount.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/percpu-refcount.h	2015-09-29 11:15:08.713754313 +0200
@@ -110,7 +110,7 @@
 	pcpu_count = ACCESS_ONCE(ref->pcpu_count);
 
 	if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR))
-		__this_cpu_inc(*pcpu_count);
+		this_cpu_inc(*pcpu_count);
 	else
 		atomic_inc(&ref->count);
 
@@ -139,7 +139,7 @@
 	pcpu_count = ACCESS_ONCE(ref->pcpu_count);
 
 	if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR)) {
-		__this_cpu_inc(*pcpu_count);
+		this_cpu_inc(*pcpu_count);
 		ret = true;
 	}
 
@@ -164,7 +164,7 @@
 	pcpu_count = ACCESS_ONCE(ref->pcpu_count);
 
 	if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR))
-		__this_cpu_dec(*pcpu_count);
+		this_cpu_dec(*pcpu_count);
 	else if (unlikely(atomic_dec_and_test(&ref->count)))
 		ref->release(ref);
 
diff -ruw linux-3.11.10/include/linux/phy.h linux-3.11.10-fbx/include/linux/phy.h
--- linux-3.11.10/include/linux/phy.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/phy.h	2015-09-29 11:15:08.717754350 +0200
@@ -145,7 +145,7 @@
 struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
 int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
 int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
-
+struct mii_bus *mdio_find_bus(const char *name);
 
 #define PHY_INTERRUPT_DISABLED	0x0
 #define PHY_INTERRUPT_ENABLED	0x80000000
diff -ruw linux-3.11.10/include/linux/pipe_fs_i.h linux-3.11.10-fbx/include/linux/pipe_fs_i.h
--- linux-3.11.10/include/linux/pipe_fs_i.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/pipe_fs_i.h	2015-09-29 11:15:08.717754350 +0200
@@ -157,6 +157,8 @@
 int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *);
 void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *);
 
+extern const struct pipe_buf_operations nosteal_pipe_buf_ops;
+
 /* for F_SETPIPE_SZ and F_GETPIPE_SZ */
 long pipe_fcntl(struct file *, unsigned int, unsigned long arg);
 struct pipe_inode_info *get_pipe_info(struct file *file);
diff -ruw linux-3.11.10/include/linux/platform_data/mtd-orion_nand.h linux-3.11.10-fbx/include/linux/platform_data/mtd-orion_nand.h
--- linux-3.11.10/include/linux/platform_data/mtd-orion_nand.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/platform_data/mtd-orion_nand.h	2015-09-29 11:15:08.721754375 +0200
@@ -20,6 +20,9 @@
 	u8 cle;		/* address line number connected to CLE */
 	u8 width;	/* buswidth */
 	u8 chip_delay;
+	u8 ecc;
+	u16 bch_ecc_size;
+	u8 bch_ecc_bytes;
 };
 
 
diff -ruw linux-3.11.10/include/linux/ppp_channel.h linux-3.11.10-fbx/include/linux/ppp_channel.h
--- linux-3.11.10/include/linux/ppp_channel.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/ppp_channel.h	2015-06-12 14:02:27.366994202 +0200
@@ -49,6 +49,9 @@
 /* Called by the channel when it can send some more data. */
 extern void ppp_output_wakeup(struct ppp_channel *);
 
+/* Called by the channel when it want to prevent further transmit on it */
+extern void ppp_output_stop(struct ppp_channel *);
+
 /* Called by the channel to process a received PPP packet.
    The packet should have just the 2-byte PPP protocol header. */
 extern void ppp_input(struct ppp_channel *, struct sk_buff *);
diff -ruw linux-3.11.10/include/linux/ptrace.h linux-3.11.10-fbx/include/linux/ptrace.h
--- linux-3.11.10/include/linux/ptrace.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/ptrace.h	2015-09-29 11:15:08.725754404 +0200
@@ -5,6 +5,7 @@
 #include <linux/sched.h>		/* For struct task_struct.  */
 #include <linux/err.h>			/* for IS_ERR_VALUE */
 #include <linux/bug.h>			/* For BUG_ON.  */
+#include <linux/pid_namespace.h>	/* For task_active_pid_ns.  */
 #include <uapi/linux/ptrace.h>
 
 /*
@@ -129,6 +130,37 @@
 }
 
 /**
+ * ptrace_event_pid - possibly stop for a ptrace event notification
+ * @event:	%PTRACE_EVENT_* value to report
+ * @pid:	process identifier for %PTRACE_GETEVENTMSG to return
+ *
+ * Check whether @event is enabled and, if so, report @event and @pid
+ * to the ptrace parent.  @pid is reported as the pid_t seen from the
+ * the ptrace parent's pid namespace.
+ *
+ * Called without locks.
+ */
+static inline void ptrace_event_pid(int event, struct pid *pid)
+{
+	/*
+	 * FIXME: There's a potential race if a ptracer in a different pid
+	 * namespace than parent attaches between computing message below and
+	 * when we acquire tasklist_lock in ptrace_stop().  If this happens,
+	 * the ptracer will get a bogus pid from PTRACE_GETEVENTMSG.
+	 */
+	unsigned long message = 0;
+	struct pid_namespace *ns;
+
+	rcu_read_lock();
+	ns = task_active_pid_ns(rcu_dereference(current->parent));
+	if (ns)
+		message = pid_nr_ns(pid, ns);
+	rcu_read_unlock();
+
+	ptrace_event(event, message);
+}
+
+/**
  * ptrace_init_task - initialize ptrace state for a new child
  * @child:		new child task
  * @ptrace:		true if child should be ptrace'd by parent's tracer
@@ -302,6 +334,9 @@
  * calling arch_ptrace_stop() when it would be superfluous.  For example,
  * if the thread has not been back to user mode since the last stop, the
  * thread state might indicate that nothing needs to be done.
+ *
+ * This is guaranteed to be invoked once before a task stops for ptrace and
+ * may include arch-specific operations necessary prior to a ptrace stop.
  */
 #define arch_ptrace_stop_needed(code, info)	(0)
 #endif
diff -ruw linux-3.11.10/include/linux/random.h linux-3.11.10-fbx/include/linux/random.h
--- linux-3.11.10/include/linux/random.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/random.h	2015-09-29 11:15:08.729754441 +0200
@@ -50,9 +50,9 @@
 {
 	u32 i = (seed >> 32) ^ (seed << 10) ^ seed;
 
-	state->s1 = __seed(i, 1);
-	state->s2 = __seed(i, 7);
-	state->s3 = __seed(i, 15);
+	state->s1 = __seed(i, 2);
+	state->s2 = __seed(i, 8);
+	state->s3 = __seed(i, 16);
 }
 
 #ifdef CONFIG_ARCH_RANDOM
diff -ruw linux-3.11.10/include/linux/reboot.h linux-3.11.10-fbx/include/linux/reboot.h
--- linux-3.11.10/include/linux/reboot.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/reboot.h	2015-09-29 11:15:08.729754441 +0200
@@ -43,6 +43,7 @@
  * Architecture-specific implementations of sys_reboot commands.
  */
 
+extern void migrate_to_reboot_cpu(void);
 extern void machine_restart(char *cmd);
 extern void machine_halt(void);
 extern void machine_power_off(void);
diff -ruw linux-3.11.10/include/linux/ring_buffer.h linux-3.11.10-fbx/include/linux/ring_buffer.h
--- linux-3.11.10/include/linux/ring_buffer.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/ring_buffer.h	2015-09-29 11:15:08.729754441 +0200
@@ -97,7 +97,7 @@
 	__ring_buffer_alloc((size), (flags), &__key);	\
 })
 
-void ring_buffer_wait(struct ring_buffer *buffer, int cpu);
+int ring_buffer_wait(struct ring_buffer *buffer, int cpu);
 int ring_buffer_poll_wait(struct ring_buffer *buffer, int cpu,
 			  struct file *filp, poll_table *poll_table);
 
diff -ruw linux-3.11.10/include/linux/sched.h linux-3.11.10-fbx/include/linux/sched.h
--- linux-3.11.10/include/linux/sched.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/sched.h	2015-09-29 11:15:08.733754466 +0200
@@ -478,6 +478,7 @@
 	atomic_t		sigcnt;
 	atomic_t		live;
 	int			nr_threads;
+	struct list_head	thread_head;
 
 	wait_queue_head_t	wait_chldexit;	/* for wait4() */
 
@@ -1028,6 +1029,12 @@
 	perf_nr_task_contexts,
 };
 
+enum task_exec_mode {
+	EXEC_MODE_DENIED,
+	EXEC_MODE_ONCE,
+	EXEC_MODE_UNLIMITED,
+};
+
 struct task_struct {
 	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
 	void *stack;
@@ -1035,6 +1042,8 @@
 	unsigned int flags;	/* per process flags, defined below */
 	unsigned int ptrace;
 
+	enum task_exec_mode exec_mode;
+
 #ifdef CONFIG_SMP
 	struct llist_node wake_entry;
 	int on_cpu;
@@ -1153,6 +1162,7 @@
 	/* PID/PID hash table linkage. */
 	struct pid_link pids[PIDTYPE_MAX];
 	struct list_head thread_group;
+	struct list_head thread_node;
 
 	struct completion *vfork_done;		/* for vfork() */
 	int __user *set_child_tid;		/* CLONE_CHILD_SETTID */
@@ -1229,6 +1239,7 @@
 	unsigned int sessionid;
 #endif
 	struct seccomp seccomp;
+	struct fbxlsmjail *fbxjail;
 
 /* Thread group tracking */
    	u32 parent_exec_id;
@@ -1500,6 +1511,24 @@
 }
 
 
+static int pid_alive(const struct task_struct *p);
+static inline pid_t task_ppid_nr_ns(const struct task_struct *tsk, struct pid_namespace *ns)
+{
+	pid_t pid = 0;
+
+	rcu_read_lock();
+	if (pid_alive(tsk))
+		pid = task_tgid_nr_ns(rcu_dereference(tsk->real_parent), ns);
+	rcu_read_unlock();
+
+	return pid;
+}
+
+static inline pid_t task_ppid_nr(const struct task_struct *tsk)
+{
+	return task_ppid_nr_ns(tsk, &init_pid_ns);
+}
+
 static inline pid_t task_pgrp_nr_ns(struct task_struct *tsk,
 					struct pid_namespace *ns)
 {
@@ -1539,7 +1568,7 @@
  *
  * Return: 1 if the process is alive. 0 otherwise.
  */
-static inline int pid_alive(struct task_struct *p)
+static inline int pid_alive(const struct task_struct *p)
 {
 	return p->pids[PIDTYPE_PID].pid != NULL;
 }
@@ -2162,6 +2191,16 @@
 #define while_each_thread(g, t) \
 	while ((t = next_thread(t)) != g)
 
+#define __for_each_thread(signal, t)	\
+	list_for_each_entry_rcu(t, &(signal)->thread_head, thread_node)
+
+#define for_each_thread(p, t)		\
+	__for_each_thread((p)->signal, t)
+
+/* Careful: this is a double loop, 'break' won't work as expected. */
+#define for_each_process_thread(p, t)	\
+	for_each_process(p) for_each_thread(p, t)
+
 static inline int get_nr_threads(struct task_struct *tsk)
 {
 	return tsk->signal->nr_threads;
diff -ruw linux-3.11.10/include/linux/seccomp.h linux-3.11.10-fbx/include/linux/seccomp.h
--- linux-3.11.10/include/linux/seccomp.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/seccomp.h	2015-09-29 11:15:08.733754466 +0200
@@ -6,6 +6,7 @@
 #ifdef CONFIG_SECCOMP
 
 #include <linux/thread_info.h>
+#include <linux/filter.h>
 #include <asm/seccomp.h>
 
 struct seccomp_filter;
@@ -47,6 +48,44 @@
 	return s->mode;
 }
 
+/**
+ * struct seccomp_filter - container for seccomp BPF programs
+ *
+ * @usage: reference count to manage the object lifetime.
+ *         get/put helpers should be used when accessing an instance
+ *         outside of a lifetime-guarded section.  In general, this
+ *         is only needed for handling filters shared across tasks.
+ * @prev: points to a previously installed, or inherited, filter
+ * @len: the number of instructions in the program
+ * @insns: the BPF program instructions to evaluate
+ *
+ * seccomp_filter objects are organized in a tree linked via the @prev
+ * pointer.  For any task, it appears to be a singly-linked list starting
+ * with current->seccomp.filter, the most recently attached or inherited filter.
+ * However, multiple filters may share a @prev node, by way of fork(), which
+ * results in a unidirectional tree existing in memory.  This is similar to
+ * how namespaces work.
+ *
+ * seccomp_filter objects should never be modified after being attached
+ * to a task_struct (other than @usage).
+ */
+struct seccomp_filter {
+	atomic_t usage;
+	struct seccomp_filter *prev;
+	unsigned short len;  /* Instruction count */
+	unsigned int (*bpf_func)(const struct sk_buff *skb,
+				 const struct sock_filter *filter);
+	struct sock_filter insns[];
+};
+
+#ifdef CONFIG_SECCOMP_FILTER_JIT
+extern void seccomp_jit_compile(struct seccomp_filter *fp);
+extern void seccomp_jit_free(struct seccomp_filter *fp);
+#else
+static inline void seccomp_jit_compile(struct seccomp_filter *fp) { }
+static inline void seccomp_jit_free(struct seccomp_filter *fp) { }
+#endif
+
 #else /* CONFIG_SECCOMP */
 
 #include <linux/errno.h>
diff -ruw linux-3.11.10/include/linux/skbuff.h linux-3.11.10-fbx/include/linux/skbuff.h
--- linux-3.11.10/include/linux/skbuff.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/skbuff.h	2015-09-29 11:15:08.737754495 +0200
@@ -333,10 +333,12 @@
 typedef unsigned char *sk_buff_data_t;
 #endif
 
-#if defined(CONFIG_NF_DEFRAG_IPV4) || defined(CONFIG_NF_DEFRAG_IPV4_MODULE) || \
-    defined(CONFIG_NF_DEFRAG_IPV6) || defined(CONFIG_NF_DEFRAG_IPV6_MODULE)
-#define NET_SKBUFF_NF_DEFRAG_NEEDED 1
-#endif
+enum {
+	FFN_STATE_INIT = 0,
+	FFN_STATE_FORWARDABLE,
+	FFN_STATE_FAST_FORWARDED,
+	FFN_STATE_INCOMPATIBLE,
+};
 
 /** 
  *	struct sk_buff - socket buffer
@@ -370,7 +372,6 @@
  *	@protocol: Packet protocol from driver
  *	@destructor: Destruct function
  *	@nfct: Associated connection, if any
- *	@nfct_reasm: netfilter conntrack re-assembly pointer
  *	@nf_bridge: Saved data about a bridged frame - see br_netfilter.c
  *	@skb_iif: ifindex of device we arrived on
  *	@tc_index: Traffic control index
@@ -459,13 +460,18 @@
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 	struct nf_conntrack	*nfct;
 #endif
-#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
-	struct sk_buff		*nfct_reasm;
+#ifdef CONFIG_IP_FFN
+	int			ffn_state;
+	int			ffn_orig_tos;
 #endif
 #ifdef CONFIG_BRIDGE_NETFILTER
 	struct nf_bridge_info	*nf_bridge;
 #endif
 
+#ifdef CONFIG_FBXBRIDGE
+	int			fbxbridge_state;
+#endif
+
 	int			skb_iif;
 
 	__u32			rxhash;
@@ -480,6 +486,10 @@
 #endif
 #endif
 
+#ifdef CONFIG_NETRXTHREAD
+	int			rxthread_prio;
+#endif
+
 	__u16			queue_mapping;
 	kmemcheck_bitfield_begin(flags2);
 #ifdef CONFIG_IPV6_NDISC_NODETYPE
@@ -1652,6 +1662,11 @@
 	skb->mac_header += offset;
 }
 
+static inline void skb_pop_mac_header(struct sk_buff *skb)
+{
+	skb->mac_header = skb->network_header;
+}
+
 static inline void skb_probe_transport_header(struct sk_buff *skb,
 					      const int offset_hint)
 {
@@ -1754,6 +1769,10 @@
  * get_rps_cpus() for example only access one 64 bytes aligned block :
  * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8)
  */
+#ifdef CONFIG_NETSKBPAD
+#define NET_SKB_PAD	CONFIG_NETSKBPAD
+#endif
+
 #ifndef NET_SKB_PAD
 #define NET_SKB_PAD	max(32, L1_CACHE_BYTES)
 #endif
@@ -2395,6 +2414,8 @@
 extern struct sk_buff *skb_segment(struct sk_buff *skb,
 				   netdev_features_t features);
 
+unsigned int skb_gso_transport_seglen(const struct sk_buff *skb);
+
 static inline void *skb_header_pointer(const struct sk_buff *skb, int offset,
 				       int len, void *buffer)
 {
@@ -2603,18 +2624,6 @@
 		atomic_inc(&nfct->use);
 }
 #endif
-#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
-static inline void nf_conntrack_get_reasm(struct sk_buff *skb)
-{
-	if (skb)
-		atomic_inc(&skb->users);
-}
-static inline void nf_conntrack_put_reasm(struct sk_buff *skb)
-{
-	if (skb)
-		kfree_skb(skb);
-}
-#endif
 #ifdef CONFIG_BRIDGE_NETFILTER
 static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
 {
@@ -2633,10 +2642,6 @@
 	nf_conntrack_put(skb->nfct);
 	skb->nfct = NULL;
 #endif
-#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
-	nf_conntrack_put_reasm(skb->nfct_reasm);
-	skb->nfct_reasm = NULL;
-#endif
 #ifdef CONFIG_BRIDGE_NETFILTER
 	nf_bridge_put(skb->nf_bridge);
 	skb->nf_bridge = NULL;
@@ -2658,10 +2663,6 @@
 	nf_conntrack_get(src->nfct);
 	dst->nfctinfo = src->nfctinfo;
 #endif
-#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
-	dst->nfct_reasm = src->nfct_reasm;
-	nf_conntrack_get_reasm(src->nfct_reasm);
-#endif
 #ifdef CONFIG_BRIDGE_NETFILTER
 	dst->nf_bridge  = src->nf_bridge;
 	nf_bridge_get(src->nf_bridge);
@@ -2673,9 +2674,6 @@
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 	nf_conntrack_put(dst->nfct);
 #endif
-#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
-	nf_conntrack_put_reasm(dst->nfct_reasm);
-#endif
 #ifdef CONFIG_BRIDGE_NETFILTER
 	nf_bridge_put(dst->nf_bridge);
 #endif
@@ -2841,5 +2839,22 @@
 {
 	return !skb->head_frag || skb_cloned(skb);
 }
+
+/**
+ * skb_gso_network_seglen - Return length of individual segments of a gso packet
+ *
+ * @skb: GSO skb
+ *
+ * skb_gso_network_seglen is used to determine the real size of the
+ * individual segments, including Layer3 (IP, IPv6) and L4 headers (TCP/UDP).
+ *
+ * The MAC/L2 header is not accounted for.
+ */
+static inline unsigned int skb_gso_network_seglen(const struct sk_buff *skb)
+{
+	unsigned int hdr_len = skb_transport_header(skb) -
+			       skb_network_header(skb);
+	return hdr_len + skb_gso_transport_seglen(skb);
+}
 #endif	/* __KERNEL__ */
 #endif	/* _LINUX_SKBUFF_H */
diff -ruw linux-3.11.10/include/linux/sock_diag.h linux-3.11.10-fbx/include/linux/sock_diag.h
--- linux-3.11.10/include/linux/sock_diag.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/sock_diag.h	2015-09-29 11:15:08.737754495 +0200
@@ -23,7 +23,7 @@
 void sock_diag_save_cookie(void *sk, __u32 *cookie);
 
 int sock_diag_put_meminfo(struct sock *sk, struct sk_buff *skb, int attr);
-int sock_diag_put_filterinfo(struct user_namespace *user_ns, struct sock *sk,
+int sock_diag_put_filterinfo(bool may_report_filterinfo, struct sock *sk,
 			     struct sk_buff *skb, int attrtype);
 
 #endif
diff -ruw linux-3.11.10/include/linux/sunrpc/svcsock.h linux-3.11.10-fbx/include/linux/sunrpc/svcsock.h
--- linux-3.11.10/include/linux/sunrpc/svcsock.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/sunrpc/svcsock.h	2015-09-29 11:15:08.741754532 +0200
@@ -56,6 +56,7 @@
 int		svc_send(struct svc_rqst *);
 void		svc_drop(struct svc_rqst *);
 void		svc_sock_update_bufs(struct svc_serv *serv);
+bool		svc_alien_sock(struct net *net, int fd);
 int		svc_addsock(struct svc_serv *serv, const int fd,
 					char *name_return, const size_t len);
 void		svc_init_xprt_sock(void);
diff -ruw linux-3.11.10/include/linux/tracepoint.h linux-3.11.10-fbx/include/linux/tracepoint.h
--- linux-3.11.10/include/linux/tracepoint.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/tracepoint.h	2015-09-29 11:15:08.749754587 +0200
@@ -60,6 +60,12 @@
 	unsigned int num_tracepoints;
 	struct tracepoint * const *tracepoints_ptrs;
 };
+bool trace_module_has_bad_taint(struct module *mod);
+#else
+static inline bool trace_module_has_bad_taint(struct module *mod)
+{
+	return false;
+}
 #endif /* CONFIG_MODULES */
 
 struct tracepoint_iter {
diff -ruw linux-3.11.10/include/linux/usb.h linux-3.11.10-fbx/include/linux/usb.h
--- linux-3.11.10/include/linux/usb.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/usb.h	2015-09-29 11:15:08.749754587 +0200
@@ -474,7 +474,8 @@
  * @lpm_capable: device supports LPM
  * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM
  * @usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM
- * @usb2_hw_lpm_enabled: USB2 hardware LPM enabled
+ * @usb2_hw_lpm_enabled: USB2 hardware LPM is enabled
+ * @usb2_hw_lpm_allowed: Userspace allows USB 2.0 LPM to be enabled
  * @usb3_lpm_enabled: USB3 hardware LPM enabled
  * @string_langid: language ID for strings
  * @product: iProduct string, if present (static)
@@ -547,6 +548,7 @@
 	unsigned usb2_hw_lpm_capable:1;
 	unsigned usb2_hw_lpm_besl_capable:1;
 	unsigned usb2_hw_lpm_enabled:1;
+	unsigned usb2_hw_lpm_allowed:1;
 	unsigned usb3_lpm_enabled:1;
 	int string_langid;
 
diff -ruw linux-3.11.10/include/linux/vm_event_item.h linux-3.11.10-fbx/include/linux/vm_event_item.h
--- linux-3.11.10/include/linux/vm_event_item.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/vm_event_item.h	2015-09-29 11:15:08.753754624 +0200
@@ -39,6 +39,7 @@
 		PAGEOUTRUN, ALLOCSTALL, PGROTATED,
 #ifdef CONFIG_NUMA_BALANCING
 		NUMA_PTE_UPDATES,
+		NUMA_HUGE_PTE_UPDATES,
 		NUMA_HINT_FAULTS,
 		NUMA_HINT_FAULTS_LOCAL,
 		NUMA_PAGE_MIGRATE,
diff -ruw linux-3.11.10/include/net/cfg80211.h linux-3.11.10-fbx/include/net/cfg80211.h
--- linux-3.11.10/include/net/cfg80211.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/net/cfg80211.h	2015-09-29 11:15:08.765754715 +0200
@@ -91,9 +91,8 @@
  * Channel flags set by the regulatory control code.
  *
  * @IEEE80211_CHAN_DISABLED: This channel is disabled.
- * @IEEE80211_CHAN_PASSIVE_SCAN: Only passive scanning is permitted
- *	on this channel.
- * @IEEE80211_CHAN_NO_IBSS: IBSS is not allowed on this channel.
+ * @IEEE80211_CHAN_NO_IR: do not initiate radiation, this includes
+ * 	sending probe requests or beaconing.
  * @IEEE80211_CHAN_RADAR: Radar detection is required on this channel.
  * @IEEE80211_CHAN_NO_HT40PLUS: extension channel above this channel
  * 	is not permitted.
@@ -113,8 +112,8 @@
  */
 enum ieee80211_channel_flags {
 	IEEE80211_CHAN_DISABLED		= 1<<0,
-	IEEE80211_CHAN_PASSIVE_SCAN	= 1<<1,
-	IEEE80211_CHAN_NO_IBSS		= 1<<2,
+	IEEE80211_CHAN_NO_IR		= 1<<1,
+	/* hole at 1<<2 */
 	IEEE80211_CHAN_RADAR		= 1<<3,
 	IEEE80211_CHAN_NO_HT40PLUS	= 1<<4,
 	IEEE80211_CHAN_NO_HT40MINUS	= 1<<5,
@@ -126,9 +125,12 @@
 #define IEEE80211_CHAN_NO_HT40 \
 	(IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
 
-#define IEEE80211_DFS_MIN_CAC_TIME_MS		60000
+#define IEEE80211_DFS_MIN_CAC_TIME_MS		(60 * 1000)
 #define IEEE80211_DFS_MIN_NOP_TIME_MS		(30 * 60 * 1000)
 
+/* ETSI EN 301 893 V1.7.0 - Table D.1 */
+#define IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS	(10 * 60 * 1000)
+
 /**
  * struct ieee80211_channel - channel definition
  *
@@ -437,6 +439,15 @@
 			     u32 prohibited_flags);
 
 /**
+ * cfg80211_chandef_dfs_required - checks if radar detection is required
+ * @wiphy: the wiphy to validate against
+ * @chandef: the channel definition to check
+ * Return: 1 if radar detection is required, 0 if it is not, < 0 on error
+ */
+int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
+				  const struct cfg80211_chan_def *chandef);
+
+/**
  * ieee80211_chandef_rate_flags - returns rate flags for a channel
  *
  * In some channel types, not all rates may be used - for example CCK
@@ -461,6 +472,33 @@
 }
 
 /**
+ * ieee80211_chandef_max_power - maximum transmission power for the chandef
+ *
+ * In some regulations, the transmit power may depend on the configured channel
+ * bandwidth which may be defined as dBm/MHz. This function returns the actual
+ * max_power for non-standard (20 MHz) channels.
+ *
+ * @chandef: channel definition for the channel
+ *
+ * Returns: maximum allowed transmission power in dBm for the chandef
+ */
+static inline int
+ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef)
+{
+	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_5:
+		return min(chandef->chan->max_reg_power - 6,
+			   chandef->chan->max_power);
+	case NL80211_CHAN_WIDTH_10:
+		return min(chandef->chan->max_reg_power - 3,
+			   chandef->chan->max_power);
+	default:
+		break;
+	}
+	return chandef->chan->max_power;
+}
+
+/**
  * enum survey_info_flags - survey information flags
  *
  * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in
@@ -639,6 +677,31 @@
 };
 
 /**
+ * struct cfg80211_csa_settings - channel switch settings
+ *
+ * Used for channel switch
+ *
+ * @chandef: defines the channel to use after the switch
+ * @beacon_csa: beacon data while performing the switch
+ * @counter_offset_beacon: offset for the counter within the beacon (tail)
+ * @counter_offset_presp: offset for the counter within the probe response
+ * @beacon_after: beacon data to be used on the new channel
+ * @radar_required: whether radar detection is required on the new channel
+ * @block_tx: whether transmissions should be blocked while changing
+ * @count: number of beacons until switch
+ */
+struct cfg80211_csa_settings {
+	struct net_device *dev;
+	struct cfg80211_chan_def chandef;
+	struct cfg80211_beacon_data beacon_csa;
+	u16 counter_offset_beacon, counter_offset_presp;
+	struct cfg80211_beacon_data beacon_after;
+	bool radar_required;
+	bool block_tx;
+	u8 count;
+};
+
+/**
  * enum station_parameters_apply_mask - station parameter values to apply
  * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp)
  * @STATION_PARAM_APPLY_CAPABILITY: apply new capability
@@ -684,6 +747,12 @@
  * @capability: station capability
  * @ext_capab: extended capabilities of the station
  * @ext_capab_len: number of extended capabilities
+ * @supported_channels: supported channels in IEEE 802.11 format
+ * @supported_channels_len: number of supported channels
+ * @supported_oper_classes: supported oper classes in IEEE 802.11 format
+ * @supported_oper_classes_len: number of supported operating classes
+ * @opmode_notif: operating mode field from Operating Mode Notification
+ * @opmode_notif_used: information if operating mode field is used
  */
 struct station_parameters {
 	const u8 *supported_rates;
@@ -703,6 +772,12 @@
 	u16 capability;
 	const u8 *ext_capab;
 	u8 ext_capab_len;
+	const u8 *supported_channels;
+	u8 supported_channels_len;
+	const u8 *supported_oper_classes;
+	u8 supported_oper_classes_len;
+	u8 opmode_notif;
+	bool opmode_notif_used;
 };
 
 /**
@@ -1285,6 +1360,7 @@
  * @n_ssids: number of SSIDs
  * @channels: channels to scan on.
  * @n_channels: total number of channels to scan
+ * @scan_width: channel width for scanning
  * @ie: optional information element(s) to add into Probe Request or %NULL
  * @ie_len: length of ie in octets
  * @flags: bit field of flags controlling operation
@@ -1300,6 +1376,7 @@
 	struct cfg80211_ssid *ssids;
 	int n_ssids;
 	u32 n_channels;
+	enum nl80211_bss_scan_width scan_width;
 	const u8 *ie;
 	size_t ie_len;
 	u32 flags;
@@ -1333,6 +1410,7 @@
  * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans)
  * @n_ssids: number of SSIDs
  * @n_channels: total number of channels to scan
+ * @scan_width: channel width for scanning
  * @interval: interval between each scheduled scan cycle
  * @ie: optional information element(s) to add into Probe Request or %NULL
  * @ie_len: length of ie in octets
@@ -1352,6 +1430,7 @@
 	struct cfg80211_ssid *ssids;
 	int n_ssids;
 	u32 n_channels;
+	enum nl80211_bss_scan_width scan_width;
 	u32 interval;
 	const u8 *ie;
 	size_t ie_len;
@@ -1403,6 +1482,7 @@
  * for use in scan results and similar.
  *
  * @channel: channel this BSS is on
+ * @scan_width: width of the control channel
  * @bssid: BSSID of the BSS
  * @beacon_interval: the beacon interval as from the frame
  * @capability: the capability field in host byte order
@@ -1424,6 +1504,7 @@
  */
 struct cfg80211_bss {
 	struct ieee80211_channel *channel;
+	enum nl80211_bss_scan_width scan_width;
 
 	const struct cfg80211_bss_ies __rcu *ies;
 	const struct cfg80211_bss_ies __rcu *beacon_ies;
@@ -1590,8 +1671,14 @@
  *	sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is
  *	required to assume that the port is unauthorized until authorized by
  *	user space. Otherwise, port is marked authorized by default.
+ * @userspace_handles_dfs: whether user space controls DFS operation, i.e.
+ *	changes the channel when a radar is detected. This is required
+ *	to operate on DFS channels.
  * @basic_rates: bitmap of basic rates to use when creating the IBSS
  * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
+ * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
+ *	will be used in ht_capa.  Un-supported values will be ignored.
+ * @ht_capa_mask:  The bits of ht_capa which are to be used.
  */
 struct cfg80211_ibss_params {
 	u8 *ssid;
@@ -1604,7 +1691,10 @@
 	bool channel_fixed;
 	bool privacy;
 	bool control_port;
+	bool userspace_handles_dfs;
 	int mcast_rate[IEEE80211_NUM_BANDS];
+	struct ieee80211_ht_cap ht_capa;
+	struct ieee80211_ht_cap ht_capa_mask;
 };
 
 /**
@@ -1680,7 +1770,9 @@
 struct cfg80211_bitrate_mask {
 	struct {
 		u32 legacy;
-		u8 mcs[IEEE80211_HT_MCS_MASK_LEN];
+		u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
+		u16 vht_mcs[NL80211_VHT_NSS_MAX];
+		enum nl80211_txrate_gi gi;
 	} control[IEEE80211_NUM_BANDS];
 };
 /**
@@ -1698,7 +1790,7 @@
 };
 
 /**
- * struct cfg80211_wowlan_trig_pkt_pattern - packet pattern
+ * struct cfg80211_pkt_pattern - packet pattern
  * @mask: bitmask where to match pattern and where to ignore bytes,
  *	one bit per byte, in same format as nl80211
  * @pattern: bytes to match where bitmask is 1
@@ -1708,7 +1800,7 @@
  * Internal note: @mask and @pattern are allocated in one chunk of
  * memory, free @mask only!
  */
-struct cfg80211_wowlan_trig_pkt_pattern {
+struct cfg80211_pkt_pattern {
 	u8 *mask, *pattern;
 	int pattern_len;
 	int pkt_offset;
@@ -1770,12 +1862,41 @@
 	bool any, disconnect, magic_pkt, gtk_rekey_failure,
 	     eap_identity_req, four_way_handshake,
 	     rfkill_release;
-	struct cfg80211_wowlan_trig_pkt_pattern *patterns;
+	struct cfg80211_pkt_pattern *patterns;
 	struct cfg80211_wowlan_tcp *tcp;
 	int n_patterns;
 };
 
 /**
+ * struct cfg80211_coalesce_rules - Coalesce rule parameters
+ *
+ * This structure defines coalesce rule for the device.
+ * @delay: maximum coalescing delay in msecs.
+ * @condition: condition for packet coalescence.
+ *	see &enum nl80211_coalesce_condition.
+ * @patterns: array of packet patterns
+ * @n_patterns: number of patterns
+ */
+struct cfg80211_coalesce_rules {
+	int delay;
+	enum nl80211_coalesce_condition condition;
+	struct cfg80211_pkt_pattern *patterns;
+	int n_patterns;
+};
+
+/**
+ * struct cfg80211_coalesce - Packet coalescing settings
+ *
+ * This structure defines coalescing settings.
+ * @rules: array of coalesce rules
+ * @n_rules: number of rules
+ */
+struct cfg80211_coalesce {
+	struct cfg80211_coalesce_rules *rules;
+	int n_rules;
+};
+
+/**
  * struct cfg80211_wowlan_wakeup - wakeup report
  * @disconnect: woke up by getting disconnected
  * @magic_pkt: woke up by receiving magic packet
@@ -1990,7 +2111,7 @@
  * @mgmt_tx_cancel_wait: Cancel the wait time from transmitting a management
  *	frame on another channel
  *
- * @testmode_cmd: run a test mode command
+ * @testmode_cmd: run a test mode command; @wdev may be %NULL
  * @testmode_dump: Implement a test mode dump. The cb->args[2] and up may be
  *	used by the function, but 0 and 1 must not be touched. Additionally,
  *	return error codes other than -ENOBUFS and -ENOENT will terminate the
@@ -2071,6 +2192,10 @@
  *	driver can take the most appropriate actions.
  * @crit_proto_stop: Indicates critical protocol no longer needs increased link
  *	reliability. This operation can not fail.
+ * @set_coalesce: Set coalesce parameters.
+ *
+ * @channel_switch: initiate channel-switch procedure (with CSA)
+ *	num_params is always >= 1.
  */
 struct cfg80211_ops {
 	int	(*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -2196,7 +2321,8 @@
 	void	(*rfkill_poll)(struct wiphy *wiphy);
 
 #ifdef CONFIG_NL80211_TESTMODE
-	int	(*testmode_cmd)(struct wiphy *wiphy, void *data, int len);
+	int	(*testmode_cmd)(struct wiphy *wiphy, struct wireless_dev *wdev,
+				void *data, int len);
 	int	(*testmode_dump)(struct wiphy *wiphy, struct sk_buff *skb,
 				 struct netlink_callback *cb,
 				 void *data, int len);
@@ -2297,7 +2423,8 @@
 
 	int	(*start_radar_detection)(struct wiphy *wiphy,
 					 struct net_device *dev,
-					 struct cfg80211_chan_def *chandef);
+					 struct cfg80211_chan_def *chandef,
+					 u32 cac_time_ms);
 	int	(*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
 				 struct cfg80211_update_ft_ies_params *ftie);
 	int	(*crit_proto_start)(struct wiphy *wiphy,
@@ -2306,6 +2433,12 @@
 				    u16 duration);
 	void	(*crit_proto_stop)(struct wiphy *wiphy,
 				   struct wireless_dev *wdev);
+	int	(*set_coalesce)(struct wiphy *wiphy,
+				struct cfg80211_coalesce *coalesce);
+
+	int	(*channel_switch)(struct wiphy *wiphy,
+				  struct cfg80211_csa_settings *params,
+				  int num_params);
 };
 
 /*
@@ -2371,6 +2504,11 @@
  * @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX.
  * @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call.
  * @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
+ * @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
+ *	beaconing mode (AP, IBSS, Mesh, ...).
+ * @WIPHY_FLAG_HAS_MULTI_IF_CHSWITCH: Device supports multi-interface channel
+ *	switching in beaconing mode (AP, IBSS, Mesh). If this is set it is
+ *	expected that @WIPHY_FLAG_HAS_CHANNEL_SWITCH is set as well.
  */
 enum wiphy_flags {
 	WIPHY_FLAG_CUSTOM_REGULATORY		= BIT(0),
@@ -2395,6 +2533,8 @@
 	WIPHY_FLAG_OFFCHAN_TX			= BIT(20),
 	WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL	= BIT(21),
 	WIPHY_FLAG_SUPPORTS_5_10_MHZ		= BIT(22),
+	WIPHY_FLAG_HAS_CHANNEL_SWITCH		= BIT(23),
+	WIPHY_FLAG_HAS_MULTI_IF_CHSWITCH	= BIT(24),
 };
 
 /**
@@ -2532,6 +2672,25 @@
 };
 
 /**
+ * struct wiphy_coalesce_support - coalesce support data
+ * @n_rules: maximum number of coalesce rules
+ * @max_delay: maximum supported coalescing delay in msecs
+ * @n_patterns: number of supported patterns in a rule
+ *	(see nl80211.h for the pattern definition)
+ * @pattern_max_len: maximum length of each pattern
+ * @pattern_min_len: minimum length of each pattern
+ * @max_pkt_offset: maximum Rx packet offset
+ */
+struct wiphy_coalesce_support {
+	int n_rules;
+	int max_delay;
+	int n_patterns;
+	int pattern_max_len;
+	int pattern_min_len;
+	int max_pkt_offset;
+};
+
+/**
  * struct wiphy - wireless hardware description
  * @reg_notifier: the driver's regulatory notification callback,
  *	note that if your driver uses wiphy_apply_custom_regulatory()
@@ -2641,6 +2800,7 @@
  *	802.11-2012 8.4.2.29 for the defined fields.
  * @extended_capabilities_mask: mask of the valid values
  * @extended_capabilities_len: length of the extended capabilities
+ * @coalesce: packet coalescing support information
  */
 struct wiphy {
 	/* assign these fields before you register the wiphy */
@@ -2750,6 +2910,8 @@
 	const struct iw_handler_def *wext;
 #endif
 
+	const struct wiphy_coalesce_support *coalesce;
+
 	char priv[0] __aligned(NETDEV_ALIGN);
 };
 
@@ -2841,7 +3003,7 @@
  *
  * Return: A non-negative wiphy index or a negative error code.
  */
-extern int wiphy_register(struct wiphy *wiphy);
+int wiphy_register(struct wiphy *wiphy);
 
 /**
  * wiphy_unregister - deregister a wiphy from cfg80211
@@ -2852,14 +3014,14 @@
  * pointer, but the call may sleep to wait for an outstanding
  * request that is being handled.
  */
-extern void wiphy_unregister(struct wiphy *wiphy);
+void wiphy_unregister(struct wiphy *wiphy);
 
 /**
  * wiphy_free - free wiphy
  *
  * @wiphy: The wiphy to free
  */
-extern void wiphy_free(struct wiphy *wiphy);
+void wiphy_free(struct wiphy *wiphy);
 
 /* internal structs */
 struct cfg80211_conn;
@@ -2911,6 +3073,7 @@
  * @p2p_started: true if this is a P2P Device that has been started
  * @cac_started: true if DFS channel availability check has been started
  * @cac_start_time: timestamp (jiffies) when the dfs state was entered.
+ * @cac_time_ms: CAC time in ms
  * @ps: powersave mode is enabled
  * @ps_timeout: dynamic powersave timeout
  * @ap_unexpected_nlportid: (private) netlink port ID of application
@@ -2918,6 +3081,7 @@
  * @conn: (private) cfg80211 software SME connection state machine data
  * @connect_keys: (private) keys to set after connection is established
  * @ibss_fixed: (private) IBSS is using fixed BSSID
+ * @ibss_dfs_possible: (private) IBSS may change to a DFS channel
  * @event_list: (private) list for internal event processing
  * @event_lock: (private) lock for event list
  */
@@ -2956,6 +3120,7 @@
 	struct ieee80211_channel *channel;
 
 	bool ibss_fixed;
+	bool ibss_dfs_possible;
 
 	bool ps;
 	int ps_timeout;
@@ -2966,6 +3131,7 @@
 
 	bool cac_started;
 	unsigned long cac_start_time;
+	unsigned int cac_time_ms;
 
 #ifdef CONFIG_CFG80211_WEXT
 	/* wext data */
@@ -3014,14 +3180,14 @@
  * @band: band, necessary due to channel number overlap
  * Return: The corresponding frequency (in MHz), or 0 if the conversion failed.
  */
-extern int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band);
+int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band);
 
 /**
  * ieee80211_frequency_to_channel - convert frequency to channel number
  * @freq: center frequency
  * Return: The corresponding channel, or 0 if the conversion failed.
  */
-extern int ieee80211_frequency_to_channel(int freq);
+int ieee80211_frequency_to_channel(int freq);
 
 /*
  * Name indirection necessary because the ieee80211 code also has
@@ -3030,7 +3196,7 @@
  * to include both header files you'll (rightfully!) get a symbol
  * clash.
  */
-extern struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy,
+struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy,
 							 int freq);
 /**
  * ieee80211_get_channel - get channel struct from wiphy for specified frequency
@@ -3063,11 +3229,13 @@
 /**
  * ieee80211_mandatory_rates - get mandatory rates for a given band
  * @sband: the band to look for rates in
+ * @scan_width: width of the control channel
  *
  * This function returns a bitmap of the mandatory rates for the given
  * band, bits are set according to the rate position in the bitrates array.
  */
-u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband);
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
+			      enum nl80211_bss_scan_width scan_width);
 
 /*
  * Radiotap parsing functions -- for controlled injection support
@@ -3141,13 +3309,14 @@
 	int _reset_on_ext;
 };
 
-extern int ieee80211_radiotap_iterator_init(
-	struct ieee80211_radiotap_iterator *iterator,
+int
+ieee80211_radiotap_iterator_init(struct ieee80211_radiotap_iterator *iterator,
 	struct ieee80211_radiotap_header *radiotap_header,
-	int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns);
+				 int max_length,
+				 const struct ieee80211_radiotap_vendor_namespaces *vns);
 
-extern int ieee80211_radiotap_iterator_next(
-	struct ieee80211_radiotap_iterator *iterator);
+int
+ieee80211_radiotap_iterator_next(struct ieee80211_radiotap_iterator *iterator);
 
 
 extern const unsigned char rfc1042_header[6];
@@ -3307,7 +3476,7 @@
  *
  * Return: 0 on success. -ENOMEM.
  */
-extern int regulatory_hint(struct wiphy *wiphy, const char *alpha2);
+int regulatory_hint(struct wiphy *wiphy, const char *alpha2);
 
 /**
  * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain
@@ -3321,8 +3490,7 @@
  * default channel settings will be disregarded. If no rule is found for a
  * channel on the regulatory domain the channel will be disabled.
  */
-extern void wiphy_apply_custom_regulatory(
-	struct wiphy *wiphy,
+void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
 	const struct ieee80211_regdomain *regd);
 
 /**
@@ -3346,6 +3514,15 @@
 const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
 					       u32 center_freq);
 
+/**
+ * reg_initiator_name - map regulatory request initiator enum to name
+ * @initiator: the regulatory request initiator
+ *
+ * You can use this to map the regulatory request initiator enum to a
+ * proper string representation.
+ */
+const char *reg_initiator_name(enum nl80211_reg_initiator initiator);
+
 /*
  * callbacks for asynchronous cfg80211 methods, notification
  * functions and BSS handling helpers
@@ -3379,10 +3556,11 @@
 void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
 
 /**
- * cfg80211_inform_bss_frame - inform cfg80211 of a received BSS frame
+ * cfg80211_inform_bss_width_frame - inform cfg80211 of a received BSS frame
  *
  * @wiphy: the wiphy reporting the BSS
  * @channel: The channel the frame was received on
+ * @scan_width: width of the control channel
  * @mgmt: the management frame (probe response or beacon)
  * @len: length of the management frame
  * @signal: the signal strength, type depends on the wiphy's signal_type
@@ -3395,16 +3573,29 @@
  * Or %NULL on error.
  */
 struct cfg80211_bss * __must_check
-cfg80211_inform_bss_frame(struct wiphy *wiphy,
+cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
 			  struct ieee80211_channel *channel,
+				enum nl80211_bss_scan_width scan_width,
 			  struct ieee80211_mgmt *mgmt, size_t len,
 			  s32 signal, gfp_t gfp);
 
+static inline struct cfg80211_bss * __must_check
+cfg80211_inform_bss_frame(struct wiphy *wiphy,
+			  struct ieee80211_channel *channel,
+			  struct ieee80211_mgmt *mgmt, size_t len,
+			  s32 signal, gfp_t gfp)
+{
+	return cfg80211_inform_bss_width_frame(wiphy, channel,
+					       NL80211_BSS_CHAN_WIDTH_20,
+					       mgmt, len, signal, gfp);
+}
+
 /**
  * cfg80211_inform_bss - inform cfg80211 of a new BSS
  *
  * @wiphy: the wiphy reporting the BSS
  * @channel: The channel the frame was received on
+ * @scan_width: width of the control channel
  * @bssid: the BSSID of the BSS
  * @tsf: the TSF sent by the peer in the beacon/probe response (or 0)
  * @capability: the capability field sent by the peer
@@ -3421,12 +3612,27 @@
  * Or %NULL on error.
  */
 struct cfg80211_bss * __must_check
-cfg80211_inform_bss(struct wiphy *wiphy,
+cfg80211_inform_bss_width(struct wiphy *wiphy,
 		    struct ieee80211_channel *channel,
+			  enum nl80211_bss_scan_width scan_width,
 		    const u8 *bssid, u64 tsf, u16 capability,
 		    u16 beacon_interval, const u8 *ie, size_t ielen,
 		    s32 signal, gfp_t gfp);
 
+static inline struct cfg80211_bss * __must_check
+cfg80211_inform_bss(struct wiphy *wiphy,
+		    struct ieee80211_channel *channel,
+		    const u8 *bssid, u64 tsf, u16 capability,
+		    u16 beacon_interval, const u8 *ie, size_t ielen,
+		    s32 signal, gfp_t gfp)
+{
+	return cfg80211_inform_bss_width(wiphy, channel,
+					 NL80211_BSS_CHAN_WIDTH_20,
+					 bssid, tsf, capability,
+					 beacon_interval, ie, ielen, signal,
+					 gfp);
+}
+
 struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
 				      struct ieee80211_channel *channel,
 				      const u8 *bssid,
@@ -3471,6 +3677,19 @@
  */
 void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
 
+static inline enum nl80211_bss_scan_width
+cfg80211_chandef_to_scan_width(const struct cfg80211_chan_def *chandef)
+{
+	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_5:
+		return NL80211_BSS_CHAN_WIDTH_5;
+	case NL80211_CHAN_WIDTH_10:
+		return NL80211_BSS_CHAN_WIDTH_10;
+	default:
+		return NL80211_BSS_CHAN_WIDTH_20;
+	}
+}
+
 /**
  * cfg80211_rx_mlme_mgmt - notification of processed MLME management frame
  * @dev: network device
@@ -3886,6 +4105,7 @@
  * @sig_dbm: signal strength in mBm, or 0 if unknown
  * @buf: Management frame (header + body)
  * @len: length of the frame data
+ * @flags: flags, as defined in enum nl80211_rxmgmt_flags
  * @gfp: context flags
  *
  * This function is called whenever an Action frame is received for a station
@@ -3897,7 +4117,7 @@
  * driver is responsible for rejecting the frame.
  */
 bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_dbm,
-		      const u8 *buf, size_t len, gfp_t gfp);
+		      const u8 *buf, size_t len, u32 flags, gfp_t gfp);
 
 /**
  * cfg80211_mgmt_tx_status - notification of TX status for management frame
@@ -3943,6 +4163,7 @@
 /**
  * cfg80211_cac_event - Channel availability check (CAC) event
  * @netdev: network device
+ * @chandef: chandef for the current channel
  * @event: type of event
  * @gfp: context flags
  *
@@ -3951,6 +4172,7 @@
  * also by full-MAC drivers.
  */
 void cfg80211_cac_event(struct net_device *netdev,
+			struct cfg80211_chan_def *chandef,
 			enum nl80211_radar_event event, gfp_t gfp);
 
 
@@ -4076,7 +4298,8 @@
  * @dev: the device which switched channels
  * @chandef: the new channel definition
  *
- * Acquires wdev_lock, so must only be called from sleepable driver context!
+ * Caller must acquire wdev_lock, therefore must only be called from sleepable
+ * driver context!
  */
 void cfg80211_ch_switch_notify(struct net_device *dev,
 			       struct cfg80211_chan_def *chandef);
diff -ruw linux-3.11.10/include/net/ieee80211_radiotap.h linux-3.11.10-fbx/include/net/ieee80211_radiotap.h
--- linux-3.11.10/include/net/ieee80211_radiotap.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/net/ieee80211_radiotap.h	2015-09-29 11:15:08.769754741 +0200
@@ -230,6 +230,10 @@
 #define	IEEE80211_CHAN_PASSIVE	0x0200	/* Only passive scan allowed */
 #define	IEEE80211_CHAN_DYN	0x0400	/* Dynamic CCK-OFDM channel */
 #define	IEEE80211_CHAN_GFSK	0x0800	/* GFSK channel (FHSS PHY) */
+#define	IEEE80211_CHAN_GSM	0x1000	/* GSM (900 MHz) */
+#define	IEEE80211_CHAN_STURBO	0x2000	/* Static Turbo */
+#define	IEEE80211_CHAN_HALF	0x4000	/* Half channel (10 MHz wide) */
+#define	IEEE80211_CHAN_QUARTER	0x8000	/* Quarter channel (5 MHz wide) */
 
 /* For IEEE80211_RADIOTAP_FLAGS */
 #define	IEEE80211_RADIOTAP_F_CFP	0x01	/* sent/received
diff -ruw linux-3.11.10/include/net/inetpeer.h linux-3.11.10-fbx/include/net/inetpeer.h
--- linux-3.11.10/include/net/inetpeer.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/net/inetpeer.h	2015-09-29 11:15:08.769754741 +0200
@@ -178,16 +178,9 @@
 /* can be called with or without local BH being disabled */
 static inline int inet_getid(struct inet_peer *p, int more)
 {
-	int old, new;
 	more++;
 	inet_peer_refcheck(p);
-	do {
-		old = atomic_read(&p->ip_id_count);
-		new = old + more;
-		if (!new)
-			new = 1;
-	} while (atomic_cmpxchg(&p->ip_id_count, old, new) != old);
-	return new;
+	return atomic_add_return(more, &p->ip_id_count) - more;
 }
 
 #endif /* _NET_INETPEER_H */
diff -ruw linux-3.11.10/include/net/ip6_route.h linux-3.11.10-fbx/include/net/ip6_route.h
--- linux-3.11.10/include/net/ip6_route.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/net/ip6_route.h	2015-09-29 11:15:08.769754741 +0200
@@ -32,6 +32,11 @@
 #define RT6_LOOKUP_F_SRCPREF_PUBLIC	0x00000010
 #define RT6_LOOKUP_F_SRCPREF_COA	0x00000020
 
+/* We do not (yet ?) support IPv6 jumbograms (RFC 2675)
+ * Unlike IPv4, hdr->seg_len doesn't include the IPv6 header
+ */
+#define IP6_MAX_MTU (0xFFFF + sizeof(struct ipv6hdr))
+
 /*
  * rt6_srcprefs2flags() and rt6_flags2srcprefs() translate
  * between IPV6_ADDR_PREFERENCES socket option values
diff -ruw linux-3.11.10/include/net/ip.h linux-3.11.10-fbx/include/net/ip.h
--- linux-3.11.10/include/net/ip.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/net/ip.h	2015-09-29 11:15:08.769754741 +0200
@@ -415,6 +415,20 @@
 int ip_frag_nqueues(struct net *net);
 
 /*
+ *     Functions provided by ip_ffn.c
+ */
+
+enum {
+	IP_FFN_FINISH_OUT,
+	IP_FFN_LOCAL_IN,
+};
+
+extern void ip_ffn_init(void);
+extern int ip_ffn_process(struct sk_buff *skb);
+extern void ip_ffn_add(struct sk_buff *skb, int when);
+extern void ip_ffn_flush_all(void);
+
+/*
  *	Functions provided by ip_forward.c
  */
  
@@ -454,7 +468,7 @@
 			int optname, char __user *optval, int __user *optlen);
 extern int	ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct sock *));
 
-extern int 	ip_recv_error(struct sock *sk, struct msghdr *msg, int len);
+extern int 	ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len);
 extern void	ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, 
 			      __be16 port, u32 info, u8 *payload);
 extern void	ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 dport,
diff -ruw linux-3.11.10/include/net/ipv6.h linux-3.11.10-fbx/include/net/ipv6.h
--- linux-3.11.10/include/net/ipv6.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/net/ipv6.h	2015-09-29 11:15:08.773754770 +0200
@@ -799,8 +799,10 @@
 extern int			ip6_datagram_connect(struct sock *sk, 
 						     struct sockaddr *addr, int addr_len);
 
-extern int 			ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len);
-extern int 			ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len);
+extern int 			ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len,
+						int *addr_len);
+extern int 			ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len,
+						 int *addr_len);
 extern void			ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port,
 						u32 info, u8 *payload);
 extern void			ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info);
diff -ruw linux-3.11.10/include/net/mac80211.h linux-3.11.10-fbx/include/net/mac80211.h
--- linux-3.11.10/include/net/mac80211.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/net/mac80211.h	2015-09-29 11:15:08.777754807 +0200
@@ -152,11 +152,16 @@
  * @IEEE80211_CHANCTX_CHANGE_WIDTH: The channel width changed
  * @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed
  * @IEEE80211_CHANCTX_CHANGE_RADAR: radar detection flag changed
+ * @IEEE80211_CHANCTX_CHANGE_CHANNEL: switched to another operating channel,
+ *	this is used only with channel switching with CSA
+ * @IEEE80211_CHANCTX_CHANGE_MIN_WIDTH: The min required channel width changed
  */
 enum ieee80211_chanctx_change {
 	IEEE80211_CHANCTX_CHANGE_WIDTH		= BIT(0),
 	IEEE80211_CHANCTX_CHANGE_RX_CHAINS	= BIT(1),
 	IEEE80211_CHANCTX_CHANGE_RADAR		= BIT(2),
+	IEEE80211_CHANCTX_CHANGE_CHANNEL	= BIT(3),
+	IEEE80211_CHANCTX_CHANGE_MIN_WIDTH	= BIT(4),
 };
 
 /**
@@ -166,6 +171,7 @@
  * that contains it is visible in mac80211 only.
  *
  * @def: the channel definition
+ * @min_def: the minimum channel definition currently required.
  * @rx_chains_static: The number of RX chains that must always be
  *	active on the channel to receive MIMO transmissions
  * @rx_chains_dynamic: The number of RX chains that must be enabled
@@ -177,6 +183,7 @@
  */
 struct ieee80211_chanctx_conf {
 	struct cfg80211_chan_def def;
+	struct cfg80211_chan_def min_def;
 
 	u8 rx_chains_static, rx_chains_dynamic;
 
@@ -372,7 +379,7 @@
 };
 
 /**
- * enum mac80211_tx_control_flags - flags to describe transmission information/status
+ * enum mac80211_tx_info_flags - flags to describe transmission information/status
  *
  * These flags are used with the @flags member of &ieee80211_tx_info.
  *
@@ -468,7 +475,7 @@
  * Note: If you have to add new flags to the enumeration, then don't
  *	 forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary.
  */
-enum mac80211_tx_control_flags {
+enum mac80211_tx_info_flags {
 	IEEE80211_TX_CTL_REQ_TX_STATUS		= BIT(0),
 	IEEE80211_TX_CTL_ASSIGN_SEQ		= BIT(1),
 	IEEE80211_TX_CTL_NO_ACK			= BIT(2),
@@ -504,6 +511,18 @@
 
 #define IEEE80211_TX_CTL_STBC_SHIFT		23
 
+/**
+ * enum mac80211_tx_control_flags - flags to describe transmit control
+ *
+ * @IEEE80211_TX_CTRL_PORT_CTRL_PROTO: this frame is a port control
+ *	protocol frame (e.g. EAP)
+ *
+ * These flags are used in tx_info->control.flags.
+ */
+enum mac80211_tx_control_flags {
+	IEEE80211_TX_CTRL_PORT_CTRL_PROTO	= BIT(0),
+};
+
 /*
  * This definition is used as a mask to clear all temporary flags, which are
  * set by the tx handlers for each transmission attempt by the mac80211 stack.
@@ -677,7 +696,8 @@
 			/* NB: vif can be NULL for injected frames */
 			struct ieee80211_vif *vif;
 			struct ieee80211_key_conf *hw_key;
-			/* 8 bytes free */
+			u32 flags;
+			/* 4 bytes free */
 		} control;
 		struct {
 			struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
@@ -811,6 +831,17 @@
  * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
  *	is stored in the @ampdu_delimiter_crc field)
  * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
+ * @RX_FLAG_10MHZ: 10 MHz (half channel) was used
+ * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
+ * @RX_FLAG_AMSDU_MORE: Some drivers may prefer to report separate A-MSDU
+ *	subframes instead of a one huge frame for performance reasons.
+ *	All, but the last MSDU from an A-MSDU should have this flag set. E.g.
+ *	if an A-MSDU has 3 frames, the first 2 must have the flag set, while
+ *	the 3rd (last) one must not have this flag set. The flag is used to
+ *	deal with retransmission/duplication recovery properly since A-MSDU
+ *	subframes share the same sequence number. Reported subframes can be
+ *	either regular MSDU or singly A-MSDUs. Subframes must not be
+ *	interleaved with other frames.
  */
 enum mac80211_rx_flags {
 	RX_FLAG_MMIC_ERROR		= BIT(0),
@@ -839,6 +870,9 @@
 	RX_FLAG_80P80MHZ		= BIT(24),
 	RX_FLAG_160MHZ			= BIT(25),
 	RX_FLAG_STBC_MASK		= BIT(26) | BIT(27),
+	RX_FLAG_10MHZ			= BIT(28),
+	RX_FLAG_5MHZ			= BIT(29),
+	RX_FLAG_AMSDU_MORE		= BIT(30),
 };
 
 #define RX_FLAG_STBC_SHIFT		26
@@ -1080,6 +1114,7 @@
  * @addr: address of this interface
  * @p2p: indicates whether this AP or STA interface is a p2p
  *	interface, i.e. a GO or p2p-sta respectively
+ * @csa_active: marks whether a channel switch is going on
  * @driver_flags: flags/capabilities the driver has for this interface,
  *	these need to be set (or cleared) when the interface is added
  *	or, if supported by the driver, the interface type is changed
@@ -1102,6 +1137,7 @@
 	struct ieee80211_bss_conf bss_conf;
 	u8 addr[ETH_ALEN];
 	bool p2p;
+	bool csa_active;
 
 	u8 cab_queue;
 	u8 hw_queue[IEEE80211_NUM_ACS];
@@ -1197,6 +1233,36 @@
 };
 
 /**
+ * struct ieee80211_cipher_scheme - cipher scheme
+ *
+ * This structure contains a cipher scheme information defining
+ * the secure packet crypto handling.
+ *
+ * @cipher: a cipher suite selector
+ * @iftype: a cipher iftype bit mask indicating an allowed cipher usage
+ * @hdr_len: a length of a security header used the cipher
+ * @pn_len: a length of a packet number in the security header
+ * @pn_off: an offset of pn from the beginning of the security header
+ * @key_idx_off: an offset of key index byte in the security header
+ * @key_idx_mask: a bit mask of key_idx bits
+ * @key_idx_shift: a bit shift needed to get key_idx
+ *     key_idx value calculation:
+ *      (sec_header_base[key_idx_off] & key_idx_mask) >> key_idx_shift
+ * @mic_len: a mic length in bytes
+ */
+struct ieee80211_cipher_scheme {
+	u32 cipher;
+	u16 iftype;
+	u8 hdr_len;
+	u8 pn_len;
+	u8 pn_off;
+	u8 key_idx_off;
+	u8 key_idx_mask;
+	u8 key_idx_shift;
+	u8 mic_len;
+};
+
+/**
  * enum set_key_cmd - key command
  *
  * Used with the set_key() callback in &struct ieee80211_ops, this
@@ -1470,6 +1536,11 @@
  *
  * @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames
  *	only, to allow getting TBTT of a DTIM beacon.
+ *
+ * @IEEE80211_HW_CHANCTX_STA_CSA: Support 802.11h based channel-switch (CSA)
+ *	for a single active channel while using channel contexts. When support
+ *	is not enabled the default action is to disconnect when getting the
+ *	CSA frame.
  */
 enum ieee80211_hw_flags {
 	IEEE80211_HW_HAS_RATE_CONTROL			= 1<<0,
@@ -1500,6 +1571,7 @@
 	IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF		= 1<<25,
 	IEEE80211_HW_TIMING_BEACON_ONLY			= 1<<26,
 	IEEE80211_HW_SUPPORTS_HT_CCK_RATES		= 1<<27,
+	IEEE80211_HW_CHANCTX_STA_CSA			= 1<<28,
 };
 
 /**
@@ -1594,6 +1666,10 @@
  * @uapsd_max_sp_len: maximum number of total buffered frames the WMM AP may
  *	deliver to a WMM STA during any Service Period triggered by the WMM STA.
  *	Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct values.
+ *
+ * @n_cipher_schemes: a size of an array of cipher schemes definitions.
+ * @cipher_schemes: a pointer to an array of cipher scheme definitions
+ *	supported by HW.
  */
 struct ieee80211_hw {
 	struct ieee80211_conf conf;
@@ -1621,6 +1697,8 @@
 	netdev_features_t netdev_features;
 	u8 uapsd_queues;
 	u8 uapsd_max_sp_len;
+	u8 n_cipher_schemes;
+	const struct ieee80211_cipher_scheme *cipher_schemes;
 };
 
 /**
@@ -2495,8 +2573,8 @@
  *	in IEEE 802.11-2007 section 17.3.8.6 and modify ACK timeout
  *	accordingly. This callback is not required and may sleep.
  *
- * @testmode_cmd: Implement a cfg80211 test mode command.
- *	The callback can sleep.
+ * @testmode_cmd: Implement a cfg80211 test mode command. The passed @vif may
+ *	be %NULL. The callback can sleep.
  * @testmode_dump: Implement a cfg80211 test mode dump. The callback can sleep.
  *
  * @flush: Flush all pending frames from the hardware queue, making sure
@@ -2634,6 +2712,22 @@
  * @ipv6_addr_change: IPv6 address assignment on the given interface changed.
  *	Currently, this is only called for managed or P2P client interfaces.
  *	This callback is optional; it must not sleep.
+ *
+ * @channel_switch_beacon: Starts a channel switch to a new channel.
+ *	Beacons are modified to include CSA or ECSA IEs before calling this
+ *	function. The corresponding count fields in these IEs must be
+ *	decremented, and when they reach 1 the driver must call
+ *	ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get()
+ *	get the csa counter decremented by mac80211, but must check if it is
+ *	1 using ieee80211_csa_is_complete() after the beacon has been
+ *	transmitted and then call ieee80211_csa_finish().
+ *	If the CSA count starts as zero or 1, this function will not be called,
+ *	since there won't be any time to beacon before the switch anyway.
+ *
+ * @join_ibss: Join an IBSS (on an IBSS interface); this is called after all
+ *	information in bss_conf is set up and the beacon can be retrieved. A
+ *	channel context is bound before this is called.
+ * @leave_ibss: Leave the IBSS again.
  */
 struct ieee80211_ops {
 	void (*tx)(struct ieee80211_hw *hw,
@@ -2747,7 +2841,8 @@
 	void (*rfkill_poll)(struct ieee80211_hw *hw);
 	void (*set_coverage_class)(struct ieee80211_hw *hw, u8 coverage_class);
 #ifdef CONFIG_NL80211_TESTMODE
-	int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len);
+	int (*testmode_cmd)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+			    void *data, int len);
 	int (*testmode_dump)(struct ieee80211_hw *hw, struct sk_buff *skb,
 			     struct netlink_callback *cb,
 			     void *data, int len);
@@ -2821,6 +2916,12 @@
 				 struct ieee80211_vif *vif,
 				 struct inet6_dev *idev);
 #endif
+	void (*channel_switch_beacon)(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif,
+				      struct cfg80211_chan_def *chandef);
+
+	int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+	void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
 };
 
 /**
@@ -2878,12 +2979,12 @@
 };
 
 #ifdef CONFIG_MAC80211_LEDS
-extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw);
-extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw);
-extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw);
-extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw);
-extern char *__ieee80211_create_tpt_led_trigger(
-				struct ieee80211_hw *hw, unsigned int flags,
+char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw);
+char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw);
+char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw);
+char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw);
+char *__ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
+					 unsigned int flags,
 				const struct ieee80211_tpt_blink *blink_table,
 				unsigned int blink_table_len);
 #endif
@@ -3316,6 +3417,25 @@
 }
 
 /**
+ * ieee80211_csa_finish - notify mac80211 about channel switch
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * After a channel switch announcement was scheduled and the counter in this
+ * announcement hits 1, this function must be called by the driver to
+ * notify mac80211 that the channel can be changed.
+ */
+void ieee80211_csa_finish(struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_csa_is_complete - find out if counters reached 1
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * This function returns whether the channel switch counters reached zero.
+ */
+bool ieee80211_csa_is_complete(struct ieee80211_vif *vif);
+
+
+/**
  * ieee80211_proberesp_get - retrieve a Probe Response template
  * @hw: pointer obtained from ieee80211_alloc_hw().
  * @vif: &struct ieee80211_vif pointer from the add_interface callback.
@@ -3634,6 +3754,89 @@
 			      int tid, struct ieee80211_key_seq *seq);
 
 /**
+ * ieee80211_set_key_tx_seq - set key TX sequence counter
+ *
+ * @keyconf: the parameter passed with the set key
+ * @seq: new sequence data
+ *
+ * This function allows a driver to set the current TX IV/PNs for the
+ * given key. This is useful when resuming from WoWLAN sleep and the
+ * device may have transmitted frames using the PTK, e.g. replies to
+ * ARP requests.
+ *
+ * Note that this function may only be called when no TX processing
+ * can be done concurrently.
+ */
+void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf,
+			      struct ieee80211_key_seq *seq);
+
+/**
+ * ieee80211_set_key_rx_seq - set key RX sequence counter
+ *
+ * @keyconf: the parameter passed with the set key
+ * @tid: The TID, or -1 for the management frame value (CCMP only);
+ *	the value on TID 0 is also used for non-QoS frames. For
+ *	CMAC, only TID 0 is valid.
+ * @seq: new sequence data
+ *
+ * This function allows a driver to set the current RX IV/PNs for the
+ * given key. This is useful when resuming from WoWLAN sleep and GTK
+ * rekey may have been done while suspended. It should not be called
+ * if IV checking is done by the device and not by mac80211.
+ *
+ * Note that this function may only be called when no RX processing
+ * can be done concurrently.
+ */
+void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf,
+			      int tid, struct ieee80211_key_seq *seq);
+
+/**
+ * ieee80211_remove_key - remove the given key
+ * @keyconf: the parameter passed with the set key
+ *
+ * Remove the given key. If the key was uploaded to the hardware at the
+ * time this function is called, it is not deleted in the hardware but
+ * instead assumed to have been removed already.
+ *
+ * Note that due to locking considerations this function can (currently)
+ * only be called during key iteration (ieee80211_iter_keys().)
+ */
+void ieee80211_remove_key(struct ieee80211_key_conf *keyconf);
+
+/**
+ * ieee80211_gtk_rekey_add - add a GTK key from rekeying during WoWLAN
+ * @vif: the virtual interface to add the key on
+ * @keyconf: new key data
+ *
+ * When GTK rekeying was done while the system was suspended, (a) new
+ * key(s) will be available. These will be needed by mac80211 for proper
+ * RX processing, so this function allows setting them.
+ *
+ * The function returns the newly allocated key structure, which will
+ * have similar contents to the passed key configuration but point to
+ * mac80211-owned memory. In case of errors, the function returns an
+ * ERR_PTR(), use IS_ERR() etc.
+ *
+ * Note that this function assumes the key isn't added to hardware
+ * acceleration, so no TX will be done with the key. Since it's a GTK
+ * on managed (station) networks, this is true anyway. If the driver
+ * calls this function from the resume callback and subsequently uses
+ * the return code 1 to reconfigure the device, this key will be part
+ * of the reconfiguration.
+ *
+ * Note that the driver should also call ieee80211_set_key_rx_seq()
+ * for the new key for each TID to set up sequence counters properly.
+ *
+ * IMPORTANT: If this replaces a key that is present in the hardware,
+ * then it will attempt to remove it during this call. In many cases
+ * this isn't what you want, so call ieee80211_remove_key() first for
+ * the key that's being replaced.
+ */
+struct ieee80211_key_conf *
+ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
+			struct ieee80211_key_conf *keyconf);
+
+/**
  * ieee80211_gtk_rekey_notify - notify userspace supplicant of rekeying
  * @vif: virtual interface the rekeying was done on
  * @bssid: The BSSID of the AP, for checking association
@@ -3782,6 +3985,25 @@
 						void *data);
 
 /**
+ * ieee80211_iterate_active_interfaces_rtnl - iterate active interfaces
+ *
+ * This function iterates over the interfaces associated with a given
+ * hardware that are currently active and calls the callback for them.
+ * This version can only be used while holding the RTNL.
+ *
+ * @hw: the hardware struct of which the interfaces should be iterated over
+ * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
+ * @iterator: the iterator function to call, cannot sleep
+ * @data: first argument of the iterator function
+ */
+void ieee80211_iterate_active_interfaces_rtnl(struct ieee80211_hw *hw,
+					      u32 iter_flags,
+					      void (*iterator)(void *data,
+						u8 *mac,
+						struct ieee80211_vif *vif),
+					      void *data);
+
+/**
  * ieee80211_queue_work - add work onto the mac80211 workqueue
  *
  * Drivers and mac80211 use this to add work onto the mac80211 workqueue.
@@ -4205,8 +4427,10 @@
 
 	void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp);
 	void (*rate_init)(void *priv, struct ieee80211_supported_band *sband,
+			  struct cfg80211_chan_def *chandef,
 			  struct ieee80211_sta *sta, void *priv_sta);
 	void (*rate_update)(void *priv, struct ieee80211_supported_band *sband,
+			    struct cfg80211_chan_def *chandef,
 			    struct ieee80211_sta *sta, void *priv_sta,
 			    u32 changed);
 	void (*free_sta)(void *priv, struct ieee80211_sta *sta,
@@ -4385,4 +4609,18 @@
 				    struct cfg80211_wowlan_wakeup *wakeup,
 				    gfp_t gfp);
 
+/**
+ * ieee80211_tx_prepare_skb - prepare an 802.11 skb for transmission
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @vif: virtual interface
+ * @skb: frame to be sent from within the driver
+ * @band: the band to transmit on
+ * @sta: optional pointer to get the station to send the frame to
+ *
+ * Note: must be called under RCU lock
+ */
+bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif, struct sk_buff *skb,
+			      int band, struct ieee80211_sta **sta);
+
 #endif /* MAC80211_H */
diff -ruw linux-3.11.10/include/net/netfilter/ipv6/nf_defrag_ipv6.h linux-3.11.10-fbx/include/net/netfilter/ipv6/nf_defrag_ipv6.h
--- linux-3.11.10/include/net/netfilter/ipv6/nf_defrag_ipv6.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/net/netfilter/ipv6/nf_defrag_ipv6.h	2015-09-29 11:15:08.777754807 +0200
@@ -6,10 +6,7 @@
 extern int nf_ct_frag6_init(void);
 extern void nf_ct_frag6_cleanup(void);
 extern struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user);
-extern void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
-			       struct net_device *in,
-			       struct net_device *out,
-			       int (*okfn)(struct sk_buff *));
+extern void nf_ct_frag6_consume_orig(struct sk_buff *skb);
 
 struct inet_frags_ctl;
 
diff -ruw linux-3.11.10/include/net/netfilter/nf_conntrack_extend.h linux-3.11.10-fbx/include/net/netfilter/nf_conntrack_extend.h
--- linux-3.11.10/include/net/netfilter/nf_conntrack_extend.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/net/netfilter/nf_conntrack_extend.h	2015-09-29 11:15:08.777754807 +0200
@@ -41,8 +41,8 @@
 /* Extensions: optional stuff which isn't permanently in struct. */
 struct nf_ct_ext {
 	struct rcu_head rcu;
-	u8 offset[NF_CT_EXT_NUM];
-	u8 len;
+	u16 offset[NF_CT_EXT_NUM];
+	u16 len;
 	char data[0];
 };
 
@@ -80,7 +80,7 @@
 static inline void nf_ct_ext_free(struct nf_conn *ct)
 {
 	if (ct->ext)
-		kfree(ct->ext);
+		kfree_rcu(ct->ext, rcu);
 }
 
 /* Add this type, returns pointer to data or NULL. */
diff -ruw linux-3.11.10/include/net/ping.h linux-3.11.10-fbx/include/net/ping.h
--- linux-3.11.10/include/net/ping.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/net/ping.h	2015-09-29 11:15:08.781754832 +0200
@@ -31,7 +31,8 @@
 
 /* Compatibility glue so we can support IPv6 when it's compiled as a module */
 struct pingv6_ops {
-	int (*ipv6_recv_error)(struct sock *sk, struct msghdr *msg, int len);
+	int (*ipv6_recv_error)(struct sock *sk, struct msghdr *msg, int len,
+			       int *addr_len);
 	int (*ip6_datagram_recv_ctl)(struct sock *sk, struct msghdr *msg,
 				     struct sk_buff *skb);
 	int (*icmpv6_err_convert)(u8 type, u8 code, int *err);
diff -ruw linux-3.11.10/include/net/regulatory.h linux-3.11.10-fbx/include/net/regulatory.h
--- linux-3.11.10/include/net/regulatory.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/net/regulatory.h	2015-09-29 11:15:08.781754832 +0200
@@ -79,7 +79,7 @@
 	enum nl80211_reg_initiator initiator;
 	enum nl80211_user_reg_hint_type user_reg_hint_type;
 	char alpha2[2];
-	u8 dfs_region;
+	enum nl80211_dfs_regions dfs_region;
 	bool intersect;
 	bool processed;
 	enum environment_cap country_ie_env;
@@ -107,7 +107,7 @@
 	struct rcu_head rcu_head;
 	u32 n_reg_rules;
 	char alpha2[2];
-	u8 dfs_region;
+	enum nl80211_dfs_regions dfs_region;
 	struct ieee80211_reg_rule reg_rules[];
 };
 
diff -ruw linux-3.11.10/include/net/sock.h linux-3.11.10-fbx/include/net/sock.h
--- linux-3.11.10/include/net/sock.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/net/sock.h	2015-09-29 11:15:08.785754862 +0200
@@ -182,6 +182,7 @@
 	volatile unsigned char	skc_state;
 	unsigned char		skc_reuse:4;
 	unsigned char		skc_reuseport:4;
+	unsigned char		skc_reuse_conflict;
 	int			skc_bound_dev_if;
 	union {
 		struct hlist_node	skc_bind_node;
@@ -303,6 +304,7 @@
 #define sk_state		__sk_common.skc_state
 #define sk_reuse		__sk_common.skc_reuse
 #define sk_reuseport		__sk_common.skc_reuseport
+#define sk_reuse_conflict	__sk_common.skc_reuse_conflict
 #define sk_bound_dev_if		__sk_common.skc_bound_dev_if
 #define sk_bind_node		__sk_common.skc_bind_node
 #define sk_prot			__sk_common.skc_prot
@@ -676,6 +678,7 @@
 		     */
 	SOCK_FILTER_LOCKED, /* Filter cannot be changed anymore */
 	SOCK_SELECT_ERR_QUEUE, /* Wake select on error queue */
+	SOCK_UDP_DUP_UNICAST,
 };
 
 static inline void sock_copy_flags(struct sock *nsk, struct sock *osk)
@@ -1443,6 +1446,11 @@
  */
 #define sock_owned_by_user(sk)	((sk)->sk_lock.owned)
 
+static inline void sock_release_ownership(struct sock *sk)
+{
+	sk->sk_lock.owned = 0;
+}
+
 /*
  * Macro so as to not evaluate some arguments when
  * lockdep is not enabled.
@@ -2252,6 +2260,11 @@
 extern int sock_get_timestamp(struct sock *, struct timeval __user *);
 extern int sock_get_timestampns(struct sock *, struct timespec __user *);
 
+bool sk_ns_capable(const struct sock *sk,
+		   struct user_namespace *user_ns, int cap);
+bool sk_capable(const struct sock *sk, int cap);
+bool sk_net_capable(const struct sock *sk, int cap);
+
 /*
  *	Enable debug/info messages
  */
diff -ruw linux-3.11.10/include/net/tcp.h linux-3.11.10-fbx/include/net/tcp.h
--- linux-3.11.10/include/net/tcp.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/net/tcp.h	2015-09-29 11:15:08.785754862 +0200
@@ -1308,7 +1308,8 @@
 	/* Fast Open cookie. Size 0 means a cookie request */
 	struct tcp_fastopen_cookie	cookie;
 	struct msghdr			*data;  /* data in MSG_FASTOPEN */
-	u16				copied;	/* queued in tcp_connect() */
+	size_t				size;
+	int				copied;	/* queued in tcp_connect() */
 };
 void tcp_free_fastopen_req(struct tcp_sock *tp);
 
diff -ruw linux-3.11.10/include/scsi/scsi_device.h linux-3.11.10-fbx/include/scsi/scsi_device.h
--- linux-3.11.10/include/scsi/scsi_device.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/scsi/scsi_device.h	2015-09-29 11:15:08.793754929 +0200
@@ -249,7 +249,7 @@
 	struct list_head	siblings;
 	struct list_head	devices;
 	struct device		dev;
-	unsigned int		reap_ref; /* protected by the host lock */
+	struct kref		reap_ref; /* last put renders target invisible */
 	unsigned int		channel;
 	unsigned int		id; /* target id ... replace
 				     * scsi_device.id eventually */
@@ -273,7 +273,6 @@
 #define SCSI_DEFAULT_TARGET_BLOCKED	3
 
 	char			scsi_level;
-	struct execute_work	ew;
 	enum scsi_target_state	state;
 	void 			*hostdata; /* available to low-level driver */
 	unsigned long		starget_data[0]; /* for the transport */
diff -ruw linux-3.11.10/include/scsi/scsi_host.h linux-3.11.10-fbx/include/scsi/scsi_host.h
--- linux-3.11.10/include/scsi/scsi_host.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/scsi/scsi_host.h	2015-09-29 11:15:08.793754929 +0200
@@ -475,6 +475,9 @@
 	 */
 	unsigned ordered_tag:1;
 
+	/* True if the controller does not support WRITE SAME */
+	unsigned no_write_same:1;
+
 	/*
 	 * Countdown for host blocking with no commands outstanding.
 	 */
@@ -674,6 +677,9 @@
 	/* Don't resume host in EH */
 	unsigned eh_noresume:1;
 
+	/* The controller does not support WRITE SAME */
+	unsigned no_write_same:1;
+
 	/*
 	 * Optional work queue to be utilized by the transport
 	 */
diff -ruw linux-3.11.10/include/sound/compress_driver.h linux-3.11.10-fbx/include/sound/compress_driver.h
--- linux-3.11.10/include/sound/compress_driver.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/sound/compress_driver.h	2015-09-29 11:15:08.793754929 +0200
@@ -171,4 +171,13 @@
 	wake_up(&stream->runtime->sleep);
 }
 
+static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
+{
+	if (snd_BUG_ON(!stream))
+		return;
+
+	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
+	wake_up(&stream->runtime->sleep);
+}
+
 #endif
diff -ruw linux-3.11.10/include/sound/core.h linux-3.11.10-fbx/include/sound/core.h
--- linux-3.11.10/include/sound/core.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/sound/core.h	2015-09-29 11:15:08.797754952 +0200
@@ -120,6 +120,8 @@
 	int user_ctl_count;		/* count of all user controls */
 	struct list_head controls;	/* all controls for this card */
 	struct list_head ctl_files;	/* active control files */
+	struct mutex user_ctl_lock;	/* protects user controls against
+					   concurrent access */
 
 	struct snd_info_entry *proc_root;	/* root for soundcard specific files */
 	struct snd_info_entry *proc_id;	/* the card id */
diff -ruw linux-3.11.10/include/sound/memalloc.h linux-3.11.10-fbx/include/sound/memalloc.h
--- linux-3.11.10/include/sound/memalloc.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/sound/memalloc.h	2015-09-29 11:15:08.797754952 +0200
@@ -103,7 +103,7 @@
 {
 	struct snd_sg_buf *sgbuf = dmab->private_data;
 	dma_addr_t addr = sgbuf->table[offset >> PAGE_SHIFT].addr;
-	addr &= PAGE_MASK;
+	addr &= ~((dma_addr_t)PAGE_SIZE - 1);
 	return addr + offset % PAGE_SIZE;
 }
 
diff -ruw linux-3.11.10/include/sound/soc-dapm.h linux-3.11.10-fbx/include/sound/soc-dapm.h
--- linux-3.11.10/include/sound/soc-dapm.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/sound/soc-dapm.h	2015-09-29 11:15:08.797754952 +0200
@@ -404,14 +404,22 @@
 /* dapm audio pin control and status */
 int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm,
 			    const char *pin);
+int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
+				     const char *pin);
 int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm,
 			     const char *pin);
+int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm,
+				      const char *pin);
 int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin);
+int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm,
+				 const char *pin);
 int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm,
 				const char *pin);
 int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm);
 int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,
 				  const char *pin);
+int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
+					   const char *pin);
 int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
 				const char *pin);
 void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec);
diff -ruw linux-3.11.10/include/trace/events/block.h linux-3.11.10-fbx/include/trace/events/block.h
--- linux-3.11.10/include/trace/events/block.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/trace/events/block.h	2015-09-29 11:15:08.801754984 +0200
@@ -132,6 +132,7 @@
  * block_rq_complete - block IO operation completed by device driver
  * @q: queue containing the block operation request
  * @rq: block operations request
+ * @nr_bytes: number of completed bytes
  *
  * The block_rq_complete tracepoint event indicates that some portion
  * of operation request has been completed by the device driver.  If
@@ -139,11 +140,37 @@
  * do for the request. If @rq->bio is non-NULL then there is
  * additional work required to complete the request.
  */
-DEFINE_EVENT(block_rq_with_error, block_rq_complete,
+TRACE_EVENT(block_rq_complete,
 
-	TP_PROTO(struct request_queue *q, struct request *rq),
+	TP_PROTO(struct request_queue *q, struct request *rq,
+		 unsigned int nr_bytes),
 
-	TP_ARGS(q, rq)
+	TP_ARGS(q, rq, nr_bytes),
+
+	TP_STRUCT__entry(
+		__field(  dev_t,	dev			)
+		__field(  sector_t,	sector			)
+		__field(  unsigned int,	nr_sector		)
+		__field(  int,		errors			)
+		__array(  char,		rwbs,	RWBS_LEN	)
+		__dynamic_array( char,	cmd,	blk_cmd_buf_len(rq)	)
+	),
+
+	TP_fast_assign(
+		__entry->dev	   = rq->rq_disk ? disk_devt(rq->rq_disk) : 0;
+		__entry->sector    = blk_rq_pos(rq);
+		__entry->nr_sector = nr_bytes >> 9;
+		__entry->errors    = rq->errors;
+
+		blk_fill_rwbs(__entry->rwbs, rq->cmd_flags, nr_bytes);
+		blk_dump_cmd(__get_str(cmd), rq);
+	),
+
+	TP_printk("%d,%d %s (%s) %llu + %u [%d]",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->rwbs, __get_str(cmd),
+		  (unsigned long long)__entry->sector,
+		  __entry->nr_sector, __entry->errors)
 );
 
 DECLARE_EVENT_CLASS(block_rq,
diff -ruw linux-3.11.10/include/trace/events/module.h linux-3.11.10-fbx/include/trace/events/module.h
--- linux-3.11.10/include/trace/events/module.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/trace/events/module.h	2015-09-29 11:15:08.801754984 +0200
@@ -78,7 +78,7 @@
 
 	TP_fast_assign(
 		__entry->ip	= ip;
-		__entry->refcnt	= __this_cpu_read(mod->refptr->incs) + __this_cpu_read(mod->refptr->decs);
+		__entry->refcnt	= __this_cpu_read(mod->refptr->incs) - __this_cpu_read(mod->refptr->decs);
 		__assign_str(name, mod->name);
 	),
 
diff -ruw linux-3.11.10/include/trace/syscall.h linux-3.11.10-fbx/include/trace/syscall.h
--- linux-3.11.10/include/trace/syscall.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/trace/syscall.h	2015-09-29 11:15:08.805755021 +0200
@@ -4,6 +4,7 @@
 #include <linux/tracepoint.h>
 #include <linux/unistd.h>
 #include <linux/ftrace_event.h>
+#include <linux/thread_info.h>
 
 #include <asm/ptrace.h>
 
@@ -32,4 +33,18 @@
 	struct ftrace_event_call *exit_event;
 };
 
+#if defined(CONFIG_TRACEPOINTS) && defined(CONFIG_HAVE_SYSCALL_TRACEPOINTS)
+static inline void syscall_tracepoint_update(struct task_struct *p)
+{
+	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
+		set_tsk_thread_flag(p, TIF_SYSCALL_TRACEPOINT);
+	else
+		clear_tsk_thread_flag(p, TIF_SYSCALL_TRACEPOINT);
+}
+#else
+static inline void syscall_tracepoint_update(struct task_struct *p)
+{
+}
+#endif
+
 #endif /* _TRACE_SYSCALL_H */
diff -ruw linux-3.11.10/include/uapi/asm-generic/socket.h linux-3.11.10-fbx/include/uapi/asm-generic/socket.h
--- linux-3.11.10/include/uapi/asm-generic/socket.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/asm-generic/socket.h	2015-09-29 11:15:08.805755021 +0200
@@ -78,4 +78,6 @@
 
 #define SO_BUSY_POLL		46
 
+#define SO_UDP_DUP_UNICAST	47
+
 #endif /* __ASM_GENERIC_SOCKET_H */
diff -ruw linux-3.11.10/include/uapi/drm/tegra_drm.h linux-3.11.10-fbx/include/uapi/drm/tegra_drm.h
--- linux-3.11.10/include/uapi/drm/tegra_drm.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/drm/tegra_drm.h	2015-09-29 11:15:08.809755046 +0200
@@ -105,7 +105,6 @@
 	__u32 num_waitchks;
 	__u32 waitchk_mask;
 	__u32 timeout;
-	__u32 pad;
 	__u64 syncpts;
 	__u64 cmdbufs;
 	__u64 relocs;
diff -ruw linux-3.11.10/include/uapi/linux/Kbuild linux-3.11.10-fbx/include/uapi/linux/Kbuild
--- linux-3.11.10/include/uapi/linux/Kbuild	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/Kbuild	2015-09-29 17:19:37.785978371 +0200
@@ -110,11 +110,16 @@
 header-y += errqueue.h
 header-y += ethtool.h
 header-y += eventpoll.h
+header-y += exfat_user.h
 header-y += fadvise.h
 header-y += falloc.h
 header-y += fanotify.h
 header-y += fb.h
 header-y += fcntl.h
+header-y += fbxatm.h
+header-y += fbxjtag.h
+header-y += fbxbridge.h
+header-y += fbxmtd_map_ioctl.h
 header-y += fd.h
 header-y += fdreg.h
 header-y += fib_rules.h
@@ -422,3 +427,5 @@
 header-y += xattr.h
 header-y += xfrm.h
 header-y += hw_breakpoint.h
+header-y += remoti/
+header-y += hdmi-cec/
diff -ruw linux-3.11.10/include/uapi/linux/major.h linux-3.11.10-fbx/include/uapi/linux/major.h
--- linux-3.11.10/include/uapi/linux/major.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/major.h	2015-09-29 11:15:08.817755111 +0200
@@ -171,7 +171,16 @@
 
 #define VIOTAPE_MAJOR		230
 
-#define BLOCK_EXT_MAJOR		259
+/*
+ * Until userland gets decent support for large majors number (if the
+ * day ever comes), use 242 for BLOCK_EXT_MAJOR. According to
+ * devices.txt area 240 to 254 is assigned for "LOCAL/EXPERIMENTAL
+ * USE". This kind of hack would fall in this category.
+ */
+/* #define BLOCK_EXT_MAJOR		259 */
+#define BLOCK_EXT_MAJOR		242
+
+
 #define SCSI_OSD_MAJOR		260	/* open-osd's OSD scsi device */
 
 #endif
diff -ruw linux-3.11.10/include/uapi/linux/netlink.h linux-3.11.10-fbx/include/uapi/linux/netlink.h
--- linux-3.11.10/include/uapi/linux/netlink.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/netlink.h	2015-09-29 11:15:08.821755136 +0200
@@ -1,7 +1,7 @@
 #ifndef _UAPI__LINUX_NETLINK_H
 #define _UAPI__LINUX_NETLINK_H
 
-#include <linux/kernel.h>
+//#include <linux/kernel.h>
 #include <linux/socket.h> /* for __kernel_sa_family_t */
 #include <linux/types.h>
 
diff -ruw linux-3.11.10/include/uapi/linux/nl80211.h linux-3.11.10-fbx/include/uapi/linux/nl80211.h
--- linux-3.11.10/include/uapi/linux/nl80211.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/nl80211.h	2015-09-29 11:15:08.821755136 +0200
@@ -126,6 +126,31 @@
  */
 
 /**
+ * DOC: packet coalesce support
+ *
+ * In most cases, host that receives IPv4 and IPv6 multicast/broadcast
+ * packets does not do anything with these packets. Therefore the
+ * reception of these unwanted packets causes unnecessary processing
+ * and power consumption.
+ *
+ * Packet coalesce feature helps to reduce number of received interrupts
+ * to host by buffering these packets in firmware/hardware for some
+ * predefined time. Received interrupt will be generated when one of the
+ * following events occur.
+ * a) Expiration of hardware timer whose expiration time is set to maximum
+ * coalescing delay of matching coalesce rule.
+ * b) Coalescing buffer in hardware reaches it's limit.
+ * c) Packet doesn't match any of the configured coalesce rules.
+ *
+ * User needs to configure following parameters for creating a coalesce
+ * rule.
+ * a) Maximum coalescing delay
+ * b) List of packet patterns which needs to be matched
+ * c) Condition for coalescence. pattern 'match' or 'no match'
+ * Multiple such rules can be created.
+ */
+
+/**
  * enum nl80211_commands - supported nl80211 commands
  *
  * @NL80211_CMD_UNSPEC: unspecified command to catch errors
@@ -393,8 +418,18 @@
  *	%NL80211_ATTR_SSID attribute, and can optionally specify the association
  *	IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
  *	%NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
- *	%NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
- *	%NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+ *	%NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ *	%NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and
+ *	%NL80211_ATTR_WIPHY_FREQ_HINT.
+ *	If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are
+ *	restrictions on BSS selection, i.e., they effectively prevent roaming
+ *	within the ESS. %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT
+ *	can be included to provide a recommendation of the initial BSS while
+ *	allowing the driver to roam to other BSSes within the ESS and also to
+ *	ignore this recommendation if the indicated BSS is not ideal. Only one
+ *	set of BSSID,frequency parameters is used (i.e., either the enforcing
+ *	%NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict
+ *	%NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT).
  *	Background scan period can optionally be
  *	specified in %NL80211_ATTR_BG_SCAN_PERIOD,
  *	if not specified default background scan configuration
@@ -556,7 +591,14 @@
  *	operation, %NL80211_ATTR_MAC contains the peer MAC address, and
  *	%NL80211_ATTR_REASON_CODE the reason code to be used (only with
  *	%NL80211_TDLS_TEARDOWN).
- * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame.
+ * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. The
+ *	%NL80211_ATTR_TDLS_ACTION attribute determines the type of frame to be
+ *	sent. Public Action codes (802.11-2012 8.1.5.1) will be sent as
+ *	802.11 management frames, while TDLS action codes (802.11-2012
+ *	8.5.13.1) will be encapsulated and sent as data frames. The currently
+ *	supported Public Action code is %WLAN_PUB_ACTION_TDLS_DISCOVER_RES
+ *	and the currently supported TDLS actions codes are given in
+ *	&enum ieee80211_tdls_actioncode.
  *
  * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP
  *	(or GO) interface (i.e. hostapd) to ask for unexpected frames to
@@ -648,6 +690,34 @@
  * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can
  *	return back to normal.
  *
+ * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
+ * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
+ *
+ * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the
+ *	the new channel information (Channel Switch Announcement - CSA)
+ *	in the beacon for some time (as defined in the
+ *	%NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the
+ *	new channel. Userspace provides the new channel information (using
+ *	%NL80211_ATTR_WIPHY_FREQ and the attributes determining channel
+ *	width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform
+ *	other station that transmission must be blocked until the channel
+ *	switch is complete.
+ *
+ * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified
+ *	by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in
+ *	%NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in
+ *	%NL80211_ATTR_VENDOR_DATA.
+ *	For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is
+ *	used in the wiphy data as a nested attribute containing descriptions
+ *	(&struct nl80211_vendor_cmd_info) of the supported vendor commands.
+ *	This may also be sent as an event with the same attributes.
+ *
+ * @NL80211_CMD_SET_QOS_MAP: Set Interworking QoS mapping for IP DSCP values.
+ *	The QoS mapping information is included in %NL80211_ATTR_QOS_MAP. If
+ *	that attribute is not included, QoS mapping is disabled. Since this
+ *	QoS mapping is relevant for IP packets, it is only valid during an
+ *	association. This is cleared on disassociation and AP restart.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -810,6 +880,15 @@
 	NL80211_CMD_CRIT_PROTOCOL_START,
 	NL80211_CMD_CRIT_PROTOCOL_STOP,
 
+	NL80211_CMD_GET_COALESCE,
+	NL80211_CMD_SET_COALESCE,
+
+	NL80211_CMD_CHANNEL_SWITCH,
+
+	NL80211_CMD_VENDOR,
+
+	NL80211_CMD_SET_QOS_MAP,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -945,7 +1024,7 @@
  * 	to query the CRDA to retrieve one regulatory domain. This attribute can
  * 	also be used by userspace to query the kernel for the currently set
  * 	regulatory domain. We chose an alpha2 as that is also used by the
- * 	IEEE-802.11d country information element to identify a country.
+ * 	IEEE-802.11 country information element to identify a country.
  * 	Users can also simply ask the wireless core to set regulatory domain
  * 	to a specific alpha2.
  * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
@@ -1436,6 +1515,70 @@
  *	allowed to be used with the first @NL80211_CMD_SET_STATION command to
  *	update a TDLS peer STA entry.
  *
+ * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
+ *
+ * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
+ *	until the channel switch event.
+ * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
+ *	must be blocked on the current channel (before the channel switch
+ *	operation).
+ * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
+ *	for the time while performing a channel switch.
+ * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
+ *	field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
+ *	field in the probe response (%NL80211_ATTR_PROBE_RESP).
+ *
+ * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
+ *	As specified in the &enum nl80211_rxmgmt_flags.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
+ *      supported operating classes.
+ *
+ * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space
+ *	controls DFS operation in IBSS mode. If the flag is included in
+ *	%NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS
+ *	channels and reports radar events to userspace. Userspace is required
+ *	to react to radar events, e.g. initiate a channel switch or leave the
+ *	IBSS network.
+ *
+ * @NL80211_ATTR_SUPPORT_5_MHZ: A flag indicating that the device supports
+ *	5 MHz channel bandwidth.
+ * @NL80211_ATTR_SUPPORT_10_MHZ: A flag indicating that the device supports
+ *	10 MHz channel bandwidth.
+ *
+ * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode
+ *	Notification Element based on association request when used with
+ *	%NL80211_CMD_NEW_STATION; u8 attribute.
+ *
+ * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if
+ *	%NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet)
+ * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command
+ * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this
+ *	attribute is also used for vendor command feature advertisement
+ * @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy
+ *	info, containing a nested array of possible events
+ *
+ * @NL80211_ATTR_QOS_MAP: IP DSCP mapping for Interworking QoS mapping. This
+ *	data is in the format defined for the payload of the QoS Map Set element
+ *	in IEEE Std 802.11-2012, 8.4.2.97.
+ *
+ * @NL80211_ATTR_MAC_HINT: MAC address recommendation as initial BSS
+ * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS
+ *
+ * @NL80211_ATTR_MAX_AP_ASSOC_STA: Device attribute that indicates how many
+ *	associated stations are supported in AP mode (including P2P GO); u32.
+ *	Since drivers may not have a fixed limit on the maximum number (e.g.,
+ *	other concurrent operations may affect this), drivers are allowed to
+ *	advertise values that cannot always be met. In such cases, an attempt
+ *	to add a new station entry with @NL80211_CMD_NEW_STATION may fail.
+ *
+ * @NL80211_ATTR_CH_SWITCH_IFACES: Nested attribute with channel switch
+ *	settings in each entry (ifindex, frequency, beacon IEs). Also used as a
+ *	device capability flag in nl80211_send_wiphy().
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1736,6 +1879,41 @@
 
 	NL80211_ATTR_PEER_AID,
 
+	NL80211_ATTR_COALESCE_RULE,
+
+	NL80211_ATTR_CH_SWITCH_COUNT,
+	NL80211_ATTR_CH_SWITCH_BLOCK_TX,
+	NL80211_ATTR_CSA_IES,
+	NL80211_ATTR_CSA_C_OFF_BEACON,
+	NL80211_ATTR_CSA_C_OFF_PRESP,
+
+	NL80211_ATTR_RXMGMT_FLAGS,
+
+	NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+
+	NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+
+	NL80211_ATTR_HANDLE_DFS,
+
+	NL80211_ATTR_SUPPORT_5_MHZ,
+	NL80211_ATTR_SUPPORT_10_MHZ,
+
+	NL80211_ATTR_OPMODE_NOTIF,
+
+	NL80211_ATTR_VENDOR_ID,
+	NL80211_ATTR_VENDOR_SUBCMD,
+	NL80211_ATTR_VENDOR_DATA,
+	NL80211_ATTR_VENDOR_EVENTS,
+
+	NL80211_ATTR_QOS_MAP,
+
+	NL80211_ATTR_MAC_HINT,
+	NL80211_ATTR_WIPHY_FREQ_HINT,
+
+	NL80211_ATTR_MAX_AP_ASSOC_STA,
+
+	NL80211_ATTR_CH_SWITCH_IFACES,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -2136,10 +2314,9 @@
  * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz
  * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current
  *	regulatory domain.
- * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is
- *	permitted on this channel in current regulatory domain.
- * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted
- *	on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation
+ * 	are permitted on this channel, this includes sending probe
+ * 	requests, or modes of operation that require beaconing.
  * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory
  *	on this channel in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
@@ -2166,8 +2343,8 @@
 	__NL80211_FREQUENCY_ATTR_INVALID,
 	NL80211_FREQUENCY_ATTR_FREQ,
 	NL80211_FREQUENCY_ATTR_DISABLED,
-	NL80211_FREQUENCY_ATTR_PASSIVE_SCAN,
-	NL80211_FREQUENCY_ATTR_NO_IBSS,
+	NL80211_FREQUENCY_ATTR_NO_IR,
+	__NL80211_FREQUENCY_ATTR_NO_IBSS,
 	NL80211_FREQUENCY_ATTR_RADAR,
 	NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
 	NL80211_FREQUENCY_ATTR_DFS_STATE,
@@ -2183,6 +2360,9 @@
 };
 
 #define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER
+#define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN	NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_NO_IBSS		NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_NO_IR		NL80211_FREQUENCY_ATTR_NO_IR
 
 /**
  * enum nl80211_bitrate_attr - bitrate attributes
@@ -2325,8 +2505,9 @@
  * @NL80211_RRF_DFS: DFS support is required to be used
  * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links
  * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links
- * @NL80211_RRF_PASSIVE_SCAN: passive scan is required
- * @NL80211_RRF_NO_IBSS: no IBSS is allowed
+ * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed,
+ * 	this includes probe requests or modes of operation that require
+ * 	beaconing.
  */
 enum nl80211_reg_rule_flags {
 	NL80211_RRF_NO_OFDM		= 1<<0,
@@ -2336,10 +2517,17 @@
 	NL80211_RRF_DFS			= 1<<4,
 	NL80211_RRF_PTP_ONLY		= 1<<5,
 	NL80211_RRF_PTMP_ONLY		= 1<<6,
-	NL80211_RRF_PASSIVE_SCAN	= 1<<7,
-	NL80211_RRF_NO_IBSS		= 1<<8,
+	NL80211_RRF_NO_IR		= 1<<7,
+	__NL80211_RRF_NO_IBSS		= 1<<8,
 };
 
+#define NL80211_RRF_PASSIVE_SCAN	NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_IBSS		NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_IR		NL80211_RRF_NO_IR
+
+/* For backport compatibility with older userspace */
+#define NL80211_RRF_NO_IR_ALL		(NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
+
 /**
  * enum nl80211_dfs_regions - regulatory DFS regions
  *
@@ -2773,6 +2961,21 @@
 };
 
 /**
+ * enum nl80211_bss_scan_width - control channel width for a BSS
+ *
+ * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute.
+ *
+ * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible
+ * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide
+ */
+enum nl80211_bss_scan_width {
+	NL80211_BSS_CHAN_WIDTH_20,
+	NL80211_BSS_CHAN_WIDTH_10,
+	NL80211_BSS_CHAN_WIDTH_5,
+};
+
+/**
  * enum nl80211_bss - netlink attributes for a BSS
  *
  * @__NL80211_BSS_INVALID: invalid
@@ -2796,6 +2999,8 @@
  * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information
  *	elements from a Beacon frame (bin); not present if no Beacon frame has
  *	yet been received
+ * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel
+ *	(u32, enum nl80211_bss_scan_width)
  * @__NL80211_BSS_AFTER_LAST: internal
  * @NL80211_BSS_MAX: highest BSS attribute
  */
@@ -2812,6 +3017,7 @@
 	NL80211_BSS_STATUS,
 	NL80211_BSS_SEEN_MS_AGO,
 	NL80211_BSS_BEACON_IES,
+	NL80211_BSS_CHAN_WIDTH,
 
 	/* keep last */
 	__NL80211_BSS_AFTER_LAST,
@@ -2952,21 +3158,43 @@
  *	in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with
  *	1 = 500 kbps) but without the IE length restriction (at most
  *	%NL80211_MAX_SUPP_RATES in a single array).
- * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection
+ * @NL80211_TXRATE_HT: HT (MCS) rates allowed for TX rate selection
  *	in an array of MCS numbers.
+ * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection,
+ *	see &struct nl80211_txrate_vht
+ * @NL80211_TXRATE_GI: configure GI, see &enum nl80211_txrate_gi
  * @__NL80211_TXRATE_AFTER_LAST: internal
  * @NL80211_TXRATE_MAX: highest TX rate attribute
  */
 enum nl80211_tx_rate_attributes {
 	__NL80211_TXRATE_INVALID,
 	NL80211_TXRATE_LEGACY,
-	NL80211_TXRATE_MCS,
+	NL80211_TXRATE_HT,
+	NL80211_TXRATE_VHT,
+	NL80211_TXRATE_GI,
 
 	/* keep last */
 	__NL80211_TXRATE_AFTER_LAST,
 	NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1
 };
 
+#define NL80211_TXRATE_MCS NL80211_TXRATE_HT
+#define NL80211_VHT_NSS_MAX		8
+
+/**
+ * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap
+ * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.)
+ */
+struct nl80211_txrate_vht {
+	__u16 mcs[NL80211_VHT_NSS_MAX];
+};
+
+enum nl80211_txrate_gi {
+	NL80211_TXRATE_DEFAULT_GI,
+	NL80211_TXRATE_FORCE_SGI,
+	NL80211_TXRATE_FORCE_LGI,
+};
+
 /**
  * enum nl80211_band - Frequency band
  * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
@@ -3060,11 +3288,11 @@
 };
 
 /**
- * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute
- * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute
- * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has
+ * enum nl80211_packet_pattern_attr - packet pattern attribute
+ * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute
+ * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has
  *	a zero bit are ignored
- * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have
+ * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have
  *	a bit for each byte in the pattern. The lowest-order bit corresponds
  *	to the first byte of the pattern, but the bytes of the pattern are
  *	in a little-endian-like format, i.e. the 9th byte of the pattern
@@ -3075,39 +3303,50 @@
  *	Note that the pattern matching is done as though frames were not
  *	802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
  *	first (including SNAP header unpacking) and then matched.
- * @NL80211_WOWLAN_PKTPAT_OFFSET: packet offset, pattern is matched after
+ * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after
  *	these fixed number of bytes of received packet
- * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes
- * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number
+ * @NUM_NL80211_PKTPAT: number of attributes
+ * @MAX_NL80211_PKTPAT: max attribute number
  */
-enum nl80211_wowlan_packet_pattern_attr {
-	__NL80211_WOWLAN_PKTPAT_INVALID,
-	NL80211_WOWLAN_PKTPAT_MASK,
-	NL80211_WOWLAN_PKTPAT_PATTERN,
-	NL80211_WOWLAN_PKTPAT_OFFSET,
+enum nl80211_packet_pattern_attr {
+	__NL80211_PKTPAT_INVALID,
+	NL80211_PKTPAT_MASK,
+	NL80211_PKTPAT_PATTERN,
+	NL80211_PKTPAT_OFFSET,
 
-	NUM_NL80211_WOWLAN_PKTPAT,
-	MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1,
+	NUM_NL80211_PKTPAT,
+	MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1,
 };
 
 /**
- * struct nl80211_wowlan_pattern_support - pattern support information
+ * struct nl80211_pattern_support - packet pattern support information
  * @max_patterns: maximum number of patterns supported
  * @min_pattern_len: minimum length of each pattern
  * @max_pattern_len: maximum length of each pattern
  * @max_pkt_offset: maximum Rx packet offset
  *
  * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
- * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the
- * capability information given by the kernel to userspace.
+ * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in
+ * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of
+ * %NL80211_ATTR_COALESCE_RULE in the capability information given
+ * by the kernel to userspace.
  */
-struct nl80211_wowlan_pattern_support {
+struct nl80211_pattern_support {
 	__u32 max_patterns;
 	__u32 min_pattern_len;
 	__u32 max_pattern_len;
 	__u32 max_pkt_offset;
 } __attribute__((packed));
 
+/* only for backward compatibility */
+#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID
+#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK
+#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN
+#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET
+#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT
+#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT
+#define nl80211_wowlan_pattern_support nl80211_pattern_support
+
 /**
  * enum nl80211_wowlan_triggers - WoWLAN trigger definitions
  * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes
@@ -3127,7 +3366,7 @@
  *	pattern matching is done after the packet is converted to the MSDU.
  *
  *	In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
- *	carrying a &struct nl80211_wowlan_pattern_support.
+ *	carrying a &struct nl80211_pattern_support.
  *
  *	When reporting wakeup. it is a u32 attribute containing the 0-based
  *	index of the pattern that caused the wakeup, in the patterns passed
@@ -3284,7 +3523,7 @@
  * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a
  *	u32 attribute holding the maximum length
  * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for
- *	feature advertising. The mask works like @NL80211_WOWLAN_PKTPAT_MASK
+ *	feature advertising. The mask works like @NL80211_PKTPAT_MASK
  *	but on the TCP payload only.
  * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes
  * @MAX_NL80211_WOWLAN_TCP: highest attribute number
@@ -3309,6 +3548,55 @@
 };
 
 /**
+ * struct nl80211_coalesce_rule_support - coalesce rule support information
+ * @max_rules: maximum number of rules supported
+ * @pat: packet pattern support information
+ * @max_delay: maximum supported coalescing delay in msecs
+ *
+ * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the
+ * capability information given by the kernel to userspace.
+ */
+struct nl80211_coalesce_rule_support {
+	__u32 max_rules;
+	struct nl80211_pattern_support pat;
+	__u32 max_delay;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_attr_coalesce_rule - coalesce rule attribute
+ * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute
+ * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing
+ * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence,
+ *	see &enum nl80211_coalesce_condition.
+ * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched
+ *	after these fixed number of bytes of received packet
+ * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes
+ * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number
+ */
+enum nl80211_attr_coalesce_rule {
+	__NL80211_COALESCE_RULE_INVALID,
+	NL80211_ATTR_COALESCE_RULE_DELAY,
+	NL80211_ATTR_COALESCE_RULE_CONDITION,
+	NL80211_ATTR_COALESCE_RULE_PKT_PATTERN,
+
+	/* keep last */
+	NUM_NL80211_ATTR_COALESCE_RULE,
+	NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1
+};
+
+/**
+ * enum nl80211_coalesce_condition - coalesce rule conditions
+ * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
+ *	in a rule are matched.
+ * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
+ *	in a rule are not matched.
+ */
+enum nl80211_coalesce_condition {
+	NL80211_COALESCE_CONDITION_MATCH,
+	NL80211_COALESCE_CONDITION_NO_MATCH
+};
+
+/**
  * enum nl80211_iface_limit_attrs - limit attributes
  * @NL80211_IFACE_LIMIT_UNSPEC: (reserved)
  * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that
@@ -3712,13 +4000,12 @@
  *
  * Channel states used by the DFS code.
  *
- * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
+ * @NL80211_DFS_USABLE: The channel can be used, but channel availability
  *	check (CAC) must be performed before using it for AP or IBSS.
- * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
  *	is therefore marked as not available.
- * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
  */
-
 enum nl80211_dfs_state {
 	NL80211_DFS_USABLE,
 	NL80211_DFS_UNAVAILABLE,
@@ -3758,4 +4045,35 @@
 /* maximum duration for critical protocol measures */
 #define NL80211_CRIT_PROTO_MAX_DURATION		5000 /* msec */
 
+/**
+ * enum nl80211_rxmgmt_flags - flags for received management frame.
+ *
+ * Used by cfg80211_rx_mgmt()
+ *
+ * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver.
+ */
+enum nl80211_rxmgmt_flags {
+	NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0,
+};
+
+/*
+ * If this flag is unset, the lower 24 bits are an OUI, if set
+ * a Linux nl80211 vendor ID is used (no such IDs are allocated
+ * yet, so that's not valid so far)
+ */
+#define NL80211_VENDOR_ID_IS_LINUX	0x80000000
+
+/**
+ * struct nl80211_vendor_cmd_info - vendor command data
+ * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the
+ *	value is a 24-bit OUI; if it is set then a separately allocated ID
+ *	may be used, but no such IDs are allocated yet. New IDs should be
+ *	added to this file when needed.
+ * @subcmd: sub-command ID for the command
+ */
+struct nl80211_vendor_cmd_info {
+	__u32 vendor_id;
+	__u32 subcmd;
+};
+
 #endif /* __LINUX_NL80211_H */
diff -ruw linux-3.11.10/include/uapi/linux/prctl.h linux-3.11.10-fbx/include/uapi/linux/prctl.h
--- linux-3.11.10/include/uapi/linux/prctl.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/prctl.h	2015-09-29 17:19:37.785978371 +0200
@@ -149,4 +149,25 @@
 
 #define PR_GET_TID_ADDRESS	40
 
+/*
+ * Freebox addition
+ */
+#define PR_SET_JAIL		50
+#define PR_GET_JAIL		51
+#define PR_JAIL_EXEC_ALL	0x0
+#define PR_JAIL_EXEC_ONCE	0x1
+#define PR_JAIL_EXEC_NO		0x2
+
+#define PR_SET_JAIL_ENTRIES	52
+
+#define PR_ADD_JAIL_FILES	53
+#define PR_JAIL_FILE_READ	0x1
+#define PR_JAIL_FILE_WRITE	0x2
+
+/*
+ * Freebox addition: set/get exec mode.
+ */
+#define PR_SET_EXEC_MODE	54
+#define PR_GET_EXEC_MODE	55
+
 #endif /* _LINUX_PRCTL_H */
diff -ruw linux-3.11.10/include/uapi/linux/serial_reg.h linux-3.11.10-fbx/include/uapi/linux/serial_reg.h
--- linux-3.11.10/include/uapi/linux/serial_reg.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/serial_reg.h	2015-09-29 11:15:08.825755164 +0200
@@ -18,9 +18,18 @@
  * DLAB=0
  */
 #define UART_RX		0	/* In:  Receive buffer */
+#ifdef CONFIG_TANGO2
+#define UART_TX		1	/* Out: Transmit buffer */
+#else
 #define UART_TX		0	/* Out: Transmit buffer */
+#endif
 
+#ifdef CONFIG_TANGO2
+#define UART_IER	2	/* Out: Interrupt Enable Register */
+#else
 #define UART_IER	1	/* Out: Interrupt Enable Register */
+#endif
+
 #define UART_IER_MSI		0x08 /* Enable Modem status interrupt */
 #define UART_IER_RLSI		0x04 /* Enable receiver line status interrupt */
 #define UART_IER_THRI		0x02 /* Enable Transmitter holding register int. */
@@ -30,7 +39,12 @@
  */
 #define UART_IERX_SLEEP		0x10 /* Enable sleep mode */
 
+#ifdef CONFIG_TANGO2
+#define UART_IIR	3	/* In:  Interrupt ID Register */
+#else
 #define UART_IIR	2	/* In:  Interrupt ID Register */
+#endif
+
 #define UART_IIR_NO_INT		0x01 /* No interrupts pending */
 #define UART_IIR_ID		0x06 /* Mask for the interrupt ID */
 #define UART_IIR_MSI		0x00 /* Modem status interrupt */
@@ -44,7 +58,12 @@
 #define UART_IIR_XOFF		0x10 /* OMAP XOFF/Special Character */
 #define UART_IIR_CTS_RTS_DSR	0x20 /* OMAP CTS/RTS/DSR Change */
 
+#ifdef CONFIG_TANGO2
+#define UART_FCR	4	/* Out: FIFO Control Register */
+#else
 #define UART_FCR	2	/* Out: FIFO Control Register */
+#endif
+
 #define UART_FCR_ENABLE_FIFO	0x01 /* Enable the FIFO */
 #define UART_FCR_CLEAR_RCVR	0x02 /* Clear the RCVR FIFO */
 #define UART_FCR_CLEAR_XMIT	0x04 /* Clear the XMIT FIFO */
@@ -88,7 +107,12 @@
 #define UART_FCR6_T_TRIGGER_30	0x30 /* Mask for transmit trigger set at 30 */
 #define UART_FCR7_64BYTE	0x20 /* Go into 64 byte mode (TI16C750) */
 
+#ifdef CONFIG_TANGO2
+#define UART_LCR	5	/* Out: Line Control Register */
+#else
 #define UART_LCR	3	/* Out: Line Control Register */
+#endif
+
 /*
  * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting 
  * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits.
@@ -111,7 +135,11 @@
 #define UART_LCR_CONF_MODE_A	UART_LCR_DLAB	/* Configutation mode A */
 #define UART_LCR_CONF_MODE_B	0xBF		/* Configutation mode B */
 
+#ifdef CONFIG_TANGO2
+#define UART_MCR	6	/* Out: Modem Control Register */
+#else
 #define UART_MCR	4	/* Out: Modem Control Register */
+#endif
 #define UART_MCR_CLKSEL		0x80 /* Divide clock by 4 (TI16C752, EFR[4]=1) */
 #define UART_MCR_TCRTLR		0x40 /* Access TCR/TLR (TI16C752, EFR[4]=1) */
 #define UART_MCR_XONANY		0x20 /* Enable Xon Any (TI16C752, EFR[4]=1) */
@@ -122,8 +150,14 @@
 #define UART_MCR_RTS		0x02 /* RTS complement */
 #define UART_MCR_DTR		0x01 /* DTR complement */
 
+#ifdef CONFIG_TANGO2
+#define UART_LSR	7	/* In:  Line Status Register */
+#else
 #define UART_LSR	5	/* In:  Line Status Register */
+#endif
+
 #define UART_LSR_FIFOE		0x80 /* Fifo error */
+
 #define UART_LSR_TEMT		0x40 /* Transmitter empty */
 #define UART_LSR_THRE		0x20 /* Transmit-hold-register empty */
 #define UART_LSR_BI		0x10 /* Break interrupt indicator */
@@ -133,7 +167,11 @@
 #define UART_LSR_DR		0x01 /* Receiver data ready */
 #define UART_LSR_BRK_ERROR_BITS	0x1E /* BI, FE, PE, OE bits */
 
+#ifdef CONFIG_TANGO2
+#define UART_MSR	8	/* In:  Modem Status Register */
+#else
 #define UART_MSR	6	/* In:  Modem Status Register */
+#endif
 #define UART_MSR_DCD		0x80 /* Data Carrier Detect */
 #define UART_MSR_RI		0x40 /* Ring Indicator */
 #define UART_MSR_DSR		0x20 /* Data Set Ready */
@@ -144,18 +182,37 @@
 #define UART_MSR_DCTS		0x01 /* Delta CTS */
 #define UART_MSR_ANY_DELTA	0x0F /* Any of the delta bits! */
 
+#ifdef CONFIG_TANGO2
+#define UART_SCR	9	/* I/O: Scratch Register */
+#else
 #define UART_SCR	7	/* I/O: Scratch Register */
+#endif
 
 /*
  * DLAB=1
  */
+#ifdef CONFIG_TANGO2
+/*
+ * smp863x has DLM and DLM in one register
+ */
+#define UART_DL		10
+#define UART_CLKSEL     11      /* Clock selection */
+#else
 #define UART_DLL	0	/* Out: Divisor Latch Low */
 #define UART_DLM	1	/* Out: Divisor Latch High */
+#endif
 
 /*
  * LCR=0xBF (or DLAB=1 for 16C660)
  */
+#ifdef CONFIG_TANGO2
+/* EFR does not exist on TANGO2, we use a magic to catch accesses and
+ * make them nop */
+#define UART_EFR	42
+#else
 #define UART_EFR	2	/* I/O: Extended Features Register */
+#endif
+
 #define UART_XR_EFR	9	/* I/O: Extended Features Register (XR17D15x) */
 #define UART_EFR_CTS		0x80 /* CTS flow control */
 #define UART_EFR_RTS		0x40 /* RTS flow control */
diff -ruw linux-3.11.10/include/uapi/linux/sockios.h linux-3.11.10-fbx/include/uapi/linux/sockios.h
--- linux-3.11.10/include/uapi/linux/sockios.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/sockios.h	2015-09-29 11:15:08.829755198 +0200
@@ -127,6 +127,14 @@
 /* hardware time stamping: parameters in linux/net_tstamp.h */
 #define SIOCSHWTSTAMP   0x89b0
 
+/* fbxdiverter call */
+#define SIOCGFBXDIVERT  0x89c0		/* fbxdiverter support          */
+#define SIOCSFBXDIVERT  0x89c1		/* Set fbxdiverter options      */
+
+/* fbxbridge call */
+#define SIOCGFBXBRIDGE	0x89b2		/* fbxbridge support          */
+#define SIOCSFBXBRIDGE	0x89b3		/* Set fbxbridge options      */
+
 /* Device private ioctl calls */
 
 /*
diff -ruw linux-3.11.10/include/uapi/linux/tty.h linux-3.11.10-fbx/include/uapi/linux/tty.h
--- linux-3.11.10/include/uapi/linux/tty.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/tty.h	2015-09-29 11:15:08.829755198 +0200
@@ -34,5 +34,6 @@
 #define N_TI_WL		22	/* for TI's WL BT, FM, GPS combo chips */
 #define N_TRACESINK	23	/* Trace data routing for MIPI P1149.7 */
 #define N_TRACEROUTER	24	/* Trace data routing for MIPI P1149.7 */
+#define N_REMOTI	25	/* RemoTI over UART */
 
 #endif /* _UAPI_LINUX_TTY_H */
diff -ruw linux-3.11.10/include/uapi/linux/usb/cdc-wdm.h linux-3.11.10-fbx/include/uapi/linux/usb/cdc-wdm.h
--- linux-3.11.10/include/uapi/linux/usb/cdc-wdm.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/usb/cdc-wdm.h	2014-07-29 21:29:45.257868793 +0200
@@ -9,6 +9,8 @@
 #ifndef _UAPI__LINUX_USB_CDC_WDM_H
 #define _UAPI__LINUX_USB_CDC_WDM_H
 
+#include <linux/types.h>
+
 /*
  * This IOCTL is used to retrieve the wMaxCommand for the device,
  * defining the message limit for both reading and writing.
diff -ruw linux-3.11.10/include/uapi/linux/usb/Kbuild linux-3.11.10-fbx/include/uapi/linux/usb/Kbuild
--- linux-3.11.10/include/uapi/linux/usb/Kbuild	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/usb/Kbuild	2014-07-29 21:29:45.257868793 +0200
@@ -1,6 +1,7 @@
 # UAPI Header export list
 header-y += audio.h
 header-y += cdc.h
+header-y += cdc-wdm.h
 header-y += ch11.h
 header-y += ch9.h
 header-y += functionfs.h
diff -ruw linux-3.11.10/include/uapi/sound/compress_offload.h linux-3.11.10-fbx/include/uapi/sound/compress_offload.h
--- linux-3.11.10/include/uapi/sound/compress_offload.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/sound/compress_offload.h	2015-09-29 11:15:08.833755235 +0200
@@ -30,7 +30,7 @@
 #include <sound/compress_params.h>
 
 
-#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 1)
+#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 2)
 /**
  * struct snd_compressed_buffer: compressed buffer
  * @fragment_size: size of buffer fragment in bytes
@@ -67,8 +67,8 @@
 struct snd_compr_tstamp {
 	__u32 byte_offset;
 	__u32 copied_total;
-	snd_pcm_uframes_t pcm_frames;
-	snd_pcm_uframes_t pcm_io_frames;
+	__u32 pcm_frames;
+	__u32 pcm_io_frames;
 	__u32 sampling_rate;
 };
 
@@ -80,7 +80,7 @@
 struct snd_compr_avail {
 	__u64 avail;
 	struct snd_compr_tstamp tstamp;
-};
+} __attribute__((packed));
 
 enum snd_compr_direction {
 	SND_COMPRESS_PLAYBACK = 0,
diff -ruw linux-3.11.10/include/uapi/xen/Kbuild linux-3.11.10-fbx/include/uapi/xen/Kbuild
--- linux-3.11.10/include/uapi/xen/Kbuild	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/xen/Kbuild	2014-07-29 21:29:45.269868853 +0200
@@ -1,3 +1,5 @@
 # UAPI Header export list
 header-y += evtchn.h
+header-y += gntalloc.h
+header-y += gntdev.h
 header-y += privcmd.h
diff -ruw linux-3.11.10/init/initramfs.c linux-3.11.10-fbx/init/initramfs.c
--- linux-3.11.10/init/initramfs.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/init/initramfs.c	2015-09-29 11:15:08.841755289 +0200
@@ -602,6 +602,16 @@
 		fd = sys_open("/initrd.image",
 			      O_WRONLY|O_CREAT, 0700);
 		if (fd >= 0) {
+#ifdef CONFIG_FBX_DECRYPT_INITRD
+			int err;
+			extern int fbx_decrypt_initrd(char *start,
+						      u32 size);
+
+			err = fbx_decrypt_initrd((char*)initrd_start,
+						 initrd_end - initrd_start);
+			if (err)
+				printk(KERN_ERR "Decrypt failed: %i\n", err);
+#endif
 			sys_write(fd, (char *)initrd_start,
 					initrd_end - initrd_start);
 			sys_close(fd);
diff -ruw linux-3.11.10/init/Kconfig linux-3.11.10-fbx/init/Kconfig
--- linux-3.11.10/init/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/init/Kconfig	2015-09-29 11:15:08.841755289 +0200
@@ -789,6 +789,15 @@
 		     13 =>  8 KB
 		     12 =>  4 KB
 
+config FBX_DECRYPT_INITRD
+	bool "Decrypt initrd at boot"
+	depends on BLK_DEV_RAM
+	default n
+
+config FBX_DECRYPT_INITRD_KEY
+	string "Decryption key"
+	depends on FBX_DECRYPT_INITRD
+
 #
 # Architectures with an unreliable sched_clock() should select this:
 #
@@ -1291,7 +1300,6 @@
 menuconfig EXPERT
 	bool "Configure standard kernel features (expert users)"
 	# Unhide debug options, to make the on-by-default options visible
-	select DEBUG_KERNEL
 	help
 	  This option allows certain base kernel options and settings
           to be disabled or tweaked. This is for specialized
@@ -1402,6 +1410,13 @@
 	  support for "fast userspace mutexes".  The resulting kernel may not
 	  run glibc-based applications correctly.
 
+config HAVE_FUTEX_CMPXCHG
+	bool
+	help
+	  Architectures should select this if futex_atomic_cmpxchg_inatomic()
+	  is implemented and always working. This removes a couple of runtime
+	  checks.
+
 config EPOLL
 	bool "Enable eventpoll support" if EXPERT
 	default y
diff -ruw linux-3.11.10/init/Makefile linux-3.11.10-fbx/init/Makefile
--- linux-3.11.10/init/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/init/Makefile	2015-06-12 14:02:27.518995293 +0200
@@ -13,6 +13,7 @@
 ifneq ($(CONFIG_ARCH_INIT_TASK),y)
 obj-y                          += init_task.o
 endif
+obj-$(CONFIG_FBX_DECRYPT_INITRD)+= fbx_decrypt_initrd.o rc4.o
 
 mounts-y			:= do_mounts.o
 mounts-$(CONFIG_BLK_DEV_RAM)	+= do_mounts_rd.o
diff -ruw linux-3.11.10/ipc/ipc_sysctl.c linux-3.11.10-fbx/ipc/ipc_sysctl.c
--- linux-3.11.10/ipc/ipc_sysctl.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/ipc/ipc_sysctl.c	2015-09-29 11:15:08.841755289 +0200
@@ -62,7 +62,7 @@
 	return err;
 }
 
-static int proc_ipc_callback_dointvec(ctl_table *table, int write,
+static int proc_ipc_callback_dointvec_minmax(ctl_table *table, int write,
 	void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct ctl_table ipc_table;
@@ -72,7 +72,7 @@
 	memcpy(&ipc_table, table, sizeof(ipc_table));
 	ipc_table.data = get_ipc(table);
 
-	rc = proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
+	rc = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
 
 	if (write && !rc && lenp_bef == *lenp)
 		/*
@@ -152,15 +152,13 @@
 #define proc_ipc_dointvec	   NULL
 #define proc_ipc_dointvec_minmax   NULL
 #define proc_ipc_dointvec_minmax_orphans   NULL
-#define proc_ipc_callback_dointvec NULL
+#define proc_ipc_callback_dointvec_minmax  NULL
 #define proc_ipcauto_dointvec_minmax NULL
 #endif
 
 static int zero;
 static int one = 1;
-#ifdef CONFIG_CHECKPOINT_RESTORE
 static int int_max = INT_MAX;
-#endif
 
 static struct ctl_table ipc_kern_table[] = {
 	{
@@ -198,21 +196,27 @@
 		.data		= &init_ipc_ns.msg_ctlmax,
 		.maxlen		= sizeof (init_ipc_ns.msg_ctlmax),
 		.mode		= 0644,
-		.proc_handler	= proc_ipc_dointvec,
+		.proc_handler	= proc_ipc_dointvec_minmax,
+		.extra1		= &zero,
+		.extra2		= &int_max,
 	},
 	{
 		.procname	= "msgmni",
 		.data		= &init_ipc_ns.msg_ctlmni,
 		.maxlen		= sizeof (init_ipc_ns.msg_ctlmni),
 		.mode		= 0644,
-		.proc_handler	= proc_ipc_callback_dointvec,
+		.proc_handler	= proc_ipc_callback_dointvec_minmax,
+		.extra1		= &zero,
+		.extra2		= &int_max,
 	},
 	{
 		.procname	=  "msgmnb",
 		.data		= &init_ipc_ns.msg_ctlmnb,
 		.maxlen		= sizeof (init_ipc_ns.msg_ctlmnb),
 		.mode		= 0644,
-		.proc_handler	= proc_ipc_dointvec,
+		.proc_handler	= proc_ipc_dointvec_minmax,
+		.extra1		= &zero,
+		.extra2		= &int_max,
 	},
 	{
 		.procname	= "sem",
diff -ruw linux-3.11.10/ipc/msg.c linux-3.11.10-fbx/ipc/msg.c
--- linux-3.11.10/ipc/msg.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/ipc/msg.c	2015-09-29 11:15:08.841755289 +0200
@@ -885,6 +885,8 @@
 		return -EINVAL;
 
 	if (msgflg & MSG_COPY) {
+		if ((msgflg & MSG_EXCEPT) || !(msgflg & IPC_NOWAIT))
+			return -EINVAL;
 		copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax));
 		if (IS_ERR(copy))
 			return PTR_ERR(copy);
diff -ruw linux-3.11.10/ipc/msgutil.c linux-3.11.10-fbx/ipc/msgutil.c
--- linux-3.11.10/ipc/msgutil.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/ipc/msgutil.c	2015-09-29 11:15:08.841755289 +0200
@@ -41,15 +41,15 @@
 	/* the next part of the message follows immediately */
 };
 
-#define DATALEN_MSG	(int)(PAGE_SIZE-sizeof(struct msg_msg))
-#define DATALEN_SEG	(int)(PAGE_SIZE-sizeof(struct msg_msgseg))
+#define DATALEN_MSG	((size_t)PAGE_SIZE-sizeof(struct msg_msg))
+#define DATALEN_SEG	((size_t)PAGE_SIZE-sizeof(struct msg_msgseg))
 
 
-static struct msg_msg *alloc_msg(int len)
+static struct msg_msg *alloc_msg(size_t len)
 {
 	struct msg_msg *msg;
 	struct msg_msgseg **pseg;
-	int alen;
+	size_t alen;
 
 	alen = min(len, DATALEN_MSG);
 	msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL);
@@ -80,12 +80,12 @@
 	return NULL;
 }
 
-struct msg_msg *load_msg(const void __user *src, int len)
+struct msg_msg *load_msg(const void __user *src, size_t len)
 {
 	struct msg_msg *msg;
 	struct msg_msgseg *seg;
 	int err = -EFAULT;
-	int alen;
+	size_t alen;
 
 	msg = alloc_msg(len);
 	if (msg == NULL)
@@ -117,8 +117,8 @@
 struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
 {
 	struct msg_msgseg *dst_pseg, *src_pseg;
-	int len = src->m_ts;
-	int alen;
+	size_t len = src->m_ts;
+	size_t alen;
 
 	BUG_ON(dst == NULL);
 	if (src->m_ts > dst->m_ts)
@@ -147,9 +147,9 @@
 	return ERR_PTR(-ENOSYS);
 }
 #endif
-int store_msg(void __user *dest, struct msg_msg *msg, int len)
+int store_msg(void __user *dest, struct msg_msg *msg, size_t len)
 {
-	int alen;
+	size_t alen;
 	struct msg_msgseg *seg;
 
 	alen = min(len, DATALEN_MSG);
diff -ruw linux-3.11.10/ipc/sem.c linux-3.11.10-fbx/ipc/sem.c
--- linux-3.11.10/ipc/sem.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/ipc/sem.c	2015-09-29 11:15:08.841755289 +0200
@@ -1282,6 +1282,12 @@
 
 	sem_lock(sma, NULL, -1);
 
+	if (sma->sem_perm.deleted) {
+		sem_unlock(sma, -1);
+		rcu_read_unlock();
+		return -EIDRM;
+	}
+
 	curr = &sma->sem_base[semnum];
 
 	ipc_assert_locked_object(&sma->sem_perm);
@@ -1336,12 +1342,14 @@
 		int i;
 
 		sem_lock(sma, NULL, -1);
+		if (sma->sem_perm.deleted) {
+			err = -EIDRM;
+			goto out_unlock;
+		}
 		if(nsems > SEMMSL_FAST) {
 			if (!ipc_rcu_getref(sma)) {
-				sem_unlock(sma, -1);
-				rcu_read_unlock();
 				err = -EIDRM;
-				goto out_free;
+				goto out_unlock;
 			}
 			sem_unlock(sma, -1);
 			rcu_read_unlock();
@@ -1354,10 +1362,8 @@
 			rcu_read_lock();
 			sem_lock_and_putref(sma);
 			if (sma->sem_perm.deleted) {
-				sem_unlock(sma, -1);
-				rcu_read_unlock();
 				err = -EIDRM;
-				goto out_free;
+				goto out_unlock;
 			}
 		}
 		for (i = 0; i < sma->sem_nsems; i++)
@@ -1375,8 +1381,8 @@
 		struct sem_undo *un;
 
 		if (!ipc_rcu_getref(sma)) {
-			rcu_read_unlock();
-			return -EIDRM;
+			err = -EIDRM;
+			goto out_rcu_wakeup;
 		}
 		rcu_read_unlock();
 
@@ -1404,10 +1410,8 @@
 		rcu_read_lock();
 		sem_lock_and_putref(sma);
 		if (sma->sem_perm.deleted) {
-			sem_unlock(sma, -1);
-			rcu_read_unlock();
 			err = -EIDRM;
-			goto out_free;
+			goto out_unlock;
 		}
 
 		for (i = 0; i < nsems; i++)
@@ -1431,6 +1435,10 @@
 		goto out_rcu_wakeup;
 
 	sem_lock(sma, NULL, -1);
+	if (sma->sem_perm.deleted) {
+		err = -EIDRM;
+		goto out_unlock;
+	}
 	curr = &sma->sem_base[semnum];
 
 	switch (cmd) {
@@ -1836,6 +1844,10 @@
 	if (error)
 		goto out_rcu_wakeup;
 
+	error = -EIDRM;
+	locknum = sem_lock(sma, sops, nsops);
+	if (sma->sem_perm.deleted)
+		goto out_unlock_free;
 	/*
 	 * semid identifiers are not unique - find_alloc_undo may have
 	 * allocated an undo structure, it was invalidated by an RMID
@@ -1843,8 +1855,6 @@
 	 * This case can be detected checking un->semid. The existence of
 	 * "un" itself is guaranteed by rcu.
 	 */
-	error = -EIDRM;
-	locknum = sem_lock(sma, sops, nsops);
 	if (un && un->semid == -1)
 		goto out_unlock_free;
 
@@ -2057,6 +2067,12 @@
 		}
 
 		sem_lock(sma, NULL, -1);
+		/* exit_sem raced with IPC_RMID, nothing to do */
+		if (sma->sem_perm.deleted) {
+			sem_unlock(sma, -1);
+			rcu_read_unlock();
+			continue;
+		}
 		un = __lookup_undo(ulp, semid);
 		if (un == NULL) {
 			/* exit_sem raced with IPC_RMID+semget() that created
diff -ruw linux-3.11.10/ipc/util.c linux-3.11.10-fbx/ipc/util.c
--- linux-3.11.10/ipc/util.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/ipc/util.c	2015-09-29 11:15:08.845755320 +0200
@@ -17,12 +17,27 @@
  *            Pavel Emelianov <xemul@openvz.org>
  *
  * General sysv ipc locking scheme:
- *  when doing ipc id lookups, take the ids->rwsem
  *      rcu_read_lock()
- *          obtain the ipc object (kern_ipc_perm)
- *          perform security, capabilities, auditing and permission checks, etc.
- *          acquire the ipc lock (kern_ipc_perm.lock) throught ipc_lock_object()
- *             perform data updates (ie: SET, RMID, LOCK/UNLOCK commands)
+ *          obtain the ipc object (kern_ipc_perm) by looking up the id in an idr
+ *	    tree.
+ *	    - perform initial checks (capabilities, auditing and permission,
+ *	      etc).
+ *	    - perform read-only operations, such as STAT, INFO commands.
+ *	      acquire the ipc lock (kern_ipc_perm.lock) through
+ *	      ipc_lock_object()
+ *		- perform data updates, such as SET, RMID commands and
+ *		  mechanism-specific operations (semop/semtimedop,
+ *		  msgsnd/msgrcv, shmat/shmdt).
+ *	    drop the ipc lock, through ipc_unlock_object().
+ *	rcu_read_unlock()
+ *
+ *  The ids->rwsem must be taken when:
+ *	- creating, removing and iterating the existing entries in ipc
+ *	  identifier sets.
+ *	- iterating through files under /proc/sysvipc/
+ *
+ *  Note that sems have a special fast path that avoids kern_ipc_perm.lock -
+ *  see sem_lock().
  */
 
 #include <linux/mm.h>
diff -ruw linux-3.11.10/ipc/util.h linux-3.11.10-fbx/ipc/util.h
--- linux-3.11.10/ipc/util.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/ipc/util.h	2015-09-29 11:15:08.845755320 +0200
@@ -148,9 +148,9 @@
 #endif
 
 extern void free_msg(struct msg_msg *msg);
-extern struct msg_msg *load_msg(const void __user *src, int len);
+extern struct msg_msg *load_msg(const void __user *src, size_t len);
 extern struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst);
-extern int store_msg(void __user *dest, struct msg_msg *msg, int len);
+extern int store_msg(void __user *dest, struct msg_msg *msg, size_t len);
 
 extern void recompute_msgmni(struct ipc_namespace *);
 
diff -ruw linux-3.11.10/kernel/audit.c linux-3.11.10-fbx/kernel/audit.c
--- linux-3.11.10/kernel/audit.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/audit.c	2015-09-29 11:15:08.845755320 +0200
@@ -103,7 +103,8 @@
 
 /* Number of outstanding audit_buffers allowed. */
 static int	audit_backlog_limit = 64;
-static int	audit_backlog_wait_time = 60 * HZ;
+#define AUDIT_BACKLOG_WAIT_TIME (60 * HZ)
+static int	audit_backlog_wait_time = AUDIT_BACKLOG_WAIT_TIME;
 static int	audit_backlog_wait_overflow = 0;
 
 /* The identity of the user shutting down the audit system. */
@@ -592,13 +593,13 @@
 	case AUDIT_TTY_SET:
 	case AUDIT_TRIM:
 	case AUDIT_MAKE_EQUIV:
-		if (!capable(CAP_AUDIT_CONTROL))
+		if (!netlink_capable(skb, CAP_AUDIT_CONTROL))
 			err = -EPERM;
 		break;
 	case AUDIT_USER:
 	case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG:
 	case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2:
-		if (!capable(CAP_AUDIT_WRITE))
+		if (!netlink_capable(skb, CAP_AUDIT_WRITE))
 			err = -EPERM;
 		break;
 	default:  /* bad msg */
@@ -613,7 +614,7 @@
 	int rc = 0;
 	uid_t uid = from_kuid(&init_user_ns, current_uid());
 
-	if (!audit_enabled) {
+	if (!audit_enabled && msg_type != AUDIT_USER_AVC) {
 		*ab = NULL;
 		return rc;
 	}
@@ -659,6 +660,7 @@
 
 	switch (msg_type) {
 	case AUDIT_GET:
+		status_set.mask		 = 0;
 		status_set.enabled	 = audit_enabled;
 		status_set.failure	 = audit_failure;
 		status_set.pid		 = audit_pid;
@@ -670,7 +672,7 @@
 				 &status_set, sizeof(status_set));
 		break;
 	case AUDIT_SET:
-		if (nlh->nlmsg_len < sizeof(struct audit_status))
+		if (nlmsg_len(nlh) < sizeof(struct audit_status))
 			return -EINVAL;
 		status_get   = (struct audit_status *)data;
 		if (status_get->mask & AUDIT_STATUS_ENABLED) {
@@ -832,7 +834,7 @@
 
 		memset(&s, 0, sizeof(s));
 		/* guard against past and future API changes */
-		memcpy(&s, data, min(sizeof(s), (size_t)nlh->nlmsg_len));
+		memcpy(&s, data, min_t(size_t, sizeof(s), nlmsg_len(nlh)));
 		if ((s.enabled != 0 && s.enabled != 1) ||
 		    (s.log_passwd != 0 && s.log_passwd != 1))
 			return -EINVAL;
@@ -1134,6 +1136,8 @@
 		return NULL;
 	}
 
+	audit_backlog_wait_time = AUDIT_BACKLOG_WAIT_TIME;
+
 	ab = audit_buffer_alloc(ctx, gfp_mask, type);
 	if (!ab) {
 		audit_log_lost("out of memory in audit_log_start");
@@ -1536,6 +1540,26 @@
 		}
 	}
 
+	/* log the audit_names record type */
+	audit_log_format(ab, " nametype=");
+	switch(n->type) {
+	case AUDIT_TYPE_NORMAL:
+		audit_log_format(ab, "NORMAL");
+		break;
+	case AUDIT_TYPE_PARENT:
+		audit_log_format(ab, "PARENT");
+		break;
+	case AUDIT_TYPE_CHILD_DELETE:
+		audit_log_format(ab, "DELETE");
+		break;
+	case AUDIT_TYPE_CHILD_CREATE:
+		audit_log_format(ab, "CREATE");
+		break;
+	default:
+		audit_log_format(ab, "UNKNOWN");
+		break;
+	}
+
 	audit_log_fcaps(ab, n);
 	audit_log_end(ab);
 }
@@ -1589,10 +1613,10 @@
 	spin_unlock_irq(&tsk->sighand->siglock);
 
 	audit_log_format(ab,
-			 " ppid=%ld pid=%d auid=%u uid=%u gid=%u"
+			 " ppid=%d pid=%d auid=%u uid=%u gid=%u"
 			 " euid=%u suid=%u fsuid=%u"
-			 " egid=%u sgid=%u fsgid=%u ses=%u tty=%s",
-			 sys_getppid(),
+			 " egid=%u sgid=%u fsgid=%u tty=%s ses=%u",
+			 task_ppid_nr(tsk),
 			 tsk->pid,
 			 from_kuid(&init_user_ns, audit_get_loginuid(tsk)),
 			 from_kuid(&init_user_ns, cred->uid),
@@ -1603,7 +1627,7 @@
 			 from_kgid(&init_user_ns, cred->egid),
 			 from_kgid(&init_user_ns, cred->sgid),
 			 from_kgid(&init_user_ns, cred->fsgid),
-			 audit_get_sessionid(tsk), tty);
+			 tty, audit_get_sessionid(tsk));
 
 	get_task_comm(name, tsk);
 	audit_log_format(ab, " comm=");
diff -ruw linux-3.11.10/kernel/auditsc.c linux-3.11.10-fbx/kernel/auditsc.c
--- linux-3.11.10/kernel/auditsc.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/auditsc.c	2015-09-29 11:15:08.845755320 +0200
@@ -472,7 +472,7 @@
 		case AUDIT_PPID:
 			if (ctx) {
 				if (!ctx->ppid)
-					ctx->ppid = sys_getppid();
+					ctx->ppid = task_ppid_nr(tsk);
 				result = audit_comparator(ctx->ppid, f->op, f->val);
 			}
 			break;
@@ -733,6 +733,22 @@
 	return AUDIT_BUILD_CONTEXT;
 }
 
+static int audit_in_mask(const struct audit_krule *rule, unsigned long val)
+{
+	int word, bit;
+
+	if (val > 0xffffffff)
+		return false;
+
+	word = AUDIT_WORD(val);
+	if (word >= AUDIT_BITMASK_SIZE)
+		return false;
+
+	bit = AUDIT_BIT(val);
+
+	return rule->mask[word] & bit;
+}
+
 /* At syscall entry and exit time, this filter is called if the
  * audit_state is not low enough that auditing cannot take place, but is
  * also not high enough that we already know we have to write an audit
@@ -750,11 +766,8 @@
 
 	rcu_read_lock();
 	if (!list_empty(list)) {
-		int word = AUDIT_WORD(ctx->major);
-		int bit  = AUDIT_BIT(ctx->major);
-
 		list_for_each_entry_rcu(e, list, list) {
-			if ((e->rule.mask[word] & bit) == bit &&
+			if (audit_in_mask(&e->rule, ctx->major) &&
 			    audit_filter_rules(tsk, &e->rule, ctx, NULL,
 					       &state, false)) {
 				rcu_read_unlock();
@@ -774,20 +787,16 @@
 static int audit_filter_inode_name(struct task_struct *tsk,
 				   struct audit_names *n,
 				   struct audit_context *ctx) {
-	int word, bit;
 	int h = audit_hash_ino((u32)n->ino);
 	struct list_head *list = &audit_inode_hash[h];
 	struct audit_entry *e;
 	enum audit_state state;
 
-	word = AUDIT_WORD(ctx->major);
-	bit  = AUDIT_BIT(ctx->major);
-
 	if (list_empty(list))
 		return 0;
 
 	list_for_each_entry_rcu(e, list, list) {
-		if ((e->rule.mask[word] & bit) == bit &&
+		if (audit_in_mask(&e->rule, ctx->major) &&
 		    audit_filter_rules(tsk, &e->rule, ctx, n, &state, false)) {
 			ctx->current_state = state;
 			return 1;
diff -ruw linux-3.11.10/kernel/capability.c linux-3.11.10-fbx/kernel/capability.c
--- linux-3.11.10/kernel/capability.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/capability.c	2015-09-29 11:15:08.845755320 +0200
@@ -445,22 +445,18 @@
 }
 
 /**
- * inode_capable - Check superior capability over inode
+ * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
  * @inode: The inode in question
  * @cap: The capability in question
  *
- * Return true if the current task has the given superior capability
- * targeted at it's own user namespace and that the given inode is owned
- * by the current user namespace or a child namespace.
- *
- * Currently we check to see if an inode is owned by the current
- * user namespace by seeing if the inode's owner maps into the
- * current user namespace.
- *
+ * Return true if the current task has the given capability targeted at
+ * its own user namespace and that the given inode's uid and gid are
+ * mapped into the current user namespace.
  */
-bool inode_capable(const struct inode *inode, int cap)
+bool capable_wrt_inode_uidgid(const struct inode *inode, int cap)
 {
 	struct user_namespace *ns = current_user_ns();
 
-	return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid);
+	return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid) &&
+		kgid_has_mapping(ns, inode->i_gid);
 }
diff -ruw linux-3.11.10/kernel/cgroup.c linux-3.11.10-fbx/kernel/cgroup.c
--- linux-3.11.10/kernel/cgroup.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/cgroup.c	2015-09-29 11:15:08.849755356 +0200
@@ -3014,9 +3014,14 @@
 		 * We should check if the process is exiting, otherwise
 		 * it will race with cgroup_exit() in that the list
 		 * entry won't be deleted though the process has exited.
+		 * Do it while holding siglock so that we don't end up
+		 * racing against cgroup_exit().
 		 */
+		spin_lock_irq(&p->sighand->siglock);
 		if (!(p->flags & PF_EXITING) && list_empty(&p->cg_list))
 			list_add(&p->cg_list, &task_css_set(p)->tasks);
+		spin_unlock_irq(&p->sighand->siglock);
+
 		task_unlock(p);
 	} while_each_thread(g, p);
 	read_unlock(&tasklist_lock);
diff -ruw linux-3.11.10/kernel/exit.c linux-3.11.10-fbx/kernel/exit.c
--- linux-3.11.10/kernel/exit.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/exit.c	2015-09-29 11:15:08.853755379 +0200
@@ -74,6 +74,7 @@
 		__this_cpu_dec(process_counts);
 	}
 	list_del_rcu(&p->thread_group);
+	list_del_rcu(&p->thread_node);
 }
 
 /*
@@ -559,9 +560,6 @@
 				struct list_head *dead)
 {
 	list_move_tail(&p->sibling, &p->real_parent->children);
-
-	if (p->exit_state == EXIT_DEAD)
-		return;
 	/*
 	 * If this is a threaded reparent there is no need to
 	 * notify anyone anything has happened.
@@ -569,9 +567,19 @@
 	if (same_thread_group(p->real_parent, father))
 		return;
 
-	/* We don't want people slaying init.  */
+	/*
+	 * We don't want people slaying init.
+	 *
+	 * Note: we do this even if it is EXIT_DEAD, wait_task_zombie()
+	 * can change ->exit_state to EXIT_ZOMBIE. If this is the final
+	 * state, do_notify_parent() was already called and ->exit_signal
+	 * doesn't matter.
+	 */
 	p->exit_signal = SIGCHLD;
 
+	if (p->exit_state == EXIT_DEAD)
+		return;
+
 	/* If it has exited notify the new parent about this child's death. */
 	if (!p->ptrace &&
 	    p->exit_state == EXIT_ZOMBIE && thread_group_empty(p)) {
@@ -783,6 +791,8 @@
 	exit_shm(tsk);
 	exit_files(tsk);
 	exit_fs(tsk);
+	if (group_dead)
+		disassociate_ctty(1);
 	exit_task_namespaces(tsk);
 	exit_task_work(tsk);
 	check_stack_usage();
@@ -798,13 +808,9 @@
 
 	cgroup_exit(tsk, 1);
 
-	if (group_dead)
-		disassociate_ctty(1);
-
 	module_put(task_thread_info(tsk)->exec_domain->module);
 
 	proc_exit_connector(tsk);
-
 	/*
 	 * FIXME: do that only when needed, using sched_exit tracepoint
 	 */
diff -ruw linux-3.11.10/kernel/fork.c linux-3.11.10-fbx/kernel/fork.c
--- linux-3.11.10/kernel/fork.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/fork.c	2015-09-29 11:15:08.857755411 +0200
@@ -214,6 +214,8 @@
 	ftrace_graph_exit_task(tsk);
 	put_seccomp_filter(tsk);
 	arch_release_task_struct(tsk);
+	if (tsk->fbxjail)
+		kfree(tsk->fbxjail);
 	free_task_struct(tsk);
 }
 EXPORT_SYMBOL(free_task);
@@ -335,6 +337,11 @@
 
 	account_kernel_stack(ti, 1);
 
+	/*
+	 * inherit parent exec_mode.
+	 */
+	tsk->exec_mode = orig->exec_mode;
+
 	return tsk;
 
 free_ti:
@@ -540,6 +547,7 @@
 	spin_lock_init(&mm->page_table_lock);
 	mm_init_aio(mm);
 	mm_init_owner(mm, p);
+	clear_tlb_flush_pending(mm);
 
 	if (likely(!mm_alloc_pgd(mm))) {
 		mm->def_flags = 0;
@@ -1040,6 +1048,11 @@
 	sig->nr_threads = 1;
 	atomic_set(&sig->live, 1);
 	atomic_set(&sig->sigcnt, 1);
+
+	/* list_add(thread_node, thread_head) without INIT_LIST_HEAD() */
+	sig->thread_head = (struct list_head)LIST_HEAD_INIT(tsk->thread_node);
+	tsk->thread_node = (struct list_head)LIST_HEAD_INIT(sig->thread_head);
+
 	init_waitqueue_head(&sig->wait_chldexit);
 	sig->curr_target = tsk;
 	init_sigpending(&sig->shared_pending);
@@ -1476,6 +1489,8 @@
 			atomic_inc(&current->signal->sigcnt);
 			list_add_tail_rcu(&p->thread_group,
 					  &p->group_leader->thread_group);
+			list_add_tail_rcu(&p->thread_node,
+					  &p->signal->thread_head);
 		}
 		attach_pid(p, PIDTYPE_PID);
 		nr_threads++;
@@ -1483,7 +1498,9 @@
 
 	total_forks++;
 	spin_unlock(&current->sighand->siglock);
+	syscall_tracepoint_update(p);
 	write_unlock_irq(&tasklist_lock);
+
 	proc_fork_connector(p);
 	cgroup_post_fork(p);
 	if (clone_flags & CLONE_THREAD)
@@ -1611,10 +1628,12 @@
 	 */
 	if (!IS_ERR(p)) {
 		struct completion vfork;
+		struct pid *pid;
 
 		trace_sched_process_fork(current, p);
 
-		nr = task_pid_vnr(p);
+		pid = get_task_pid(p, PIDTYPE_PID);
+		nr = pid_vnr(pid);
 
 		if (clone_flags & CLONE_PARENT_SETTID)
 			put_user(nr, parent_tidptr);
@@ -1629,12 +1648,14 @@
 
 		/* forking complete and child started to run, tell ptracer */
 		if (unlikely(trace))
-			ptrace_event(trace, nr);
+			ptrace_event_pid(trace, pid);
 
 		if (clone_flags & CLONE_VFORK) {
 			if (!wait_for_vfork_done(p, &vfork))
-				ptrace_event(PTRACE_EVENT_VFORK_DONE, nr);
+				ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
 		}
+
+		put_pid(pid);
 	} else {
 		nr = PTR_ERR(p);
 	}
diff -ruw linux-3.11.10/kernel/futex.c linux-3.11.10-fbx/kernel/futex.c
--- linux-3.11.10/kernel/futex.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/futex.c	2015-09-29 11:15:08.857755411 +0200
@@ -68,7 +68,9 @@
 
 #include "rtmutex_common.h"
 
+#ifndef CONFIG_HAVE_FUTEX_CMPXCHG
 int __read_mostly futex_cmpxchg_enabled;
+#endif
 
 #define FUTEX_HASHBITS (CONFIG_BASE_SMALL ? 4 : 8)
 
@@ -288,7 +290,7 @@
 		put_page(page);
 		/* serialize against __split_huge_page_splitting() */
 		local_irq_disable();
-		if (likely(__get_user_pages_fast(address, 1, 1, &page) == 1)) {
+		if (likely(__get_user_pages_fast(address, 1, !ro, &page) == 1)) {
 			page_head = compound_head(page);
 			/*
 			 * page_head is valid pointer but we must pin
@@ -591,6 +593,55 @@
 	raw_spin_unlock_irq(&curr->pi_lock);
 }
 
+/*
+ * We need to check the following states:
+ *
+ *      Waiter | pi_state | pi->owner | uTID      | uODIED | ?
+ *
+ * [1]  NULL   | ---      | ---       | 0         | 0/1    | Valid
+ * [2]  NULL   | ---      | ---       | >0        | 0/1    | Valid
+ *
+ * [3]  Found  | NULL     | --        | Any       | 0/1    | Invalid
+ *
+ * [4]  Found  | Found    | NULL      | 0         | 1      | Valid
+ * [5]  Found  | Found    | NULL      | >0        | 1      | Invalid
+ *
+ * [6]  Found  | Found    | task      | 0         | 1      | Valid
+ *
+ * [7]  Found  | Found    | NULL      | Any       | 0      | Invalid
+ *
+ * [8]  Found  | Found    | task      | ==taskTID | 0/1    | Valid
+ * [9]  Found  | Found    | task      | 0         | 0      | Invalid
+ * [10] Found  | Found    | task      | !=taskTID | 0/1    | Invalid
+ *
+ * [1]	Indicates that the kernel can acquire the futex atomically. We
+ *	came came here due to a stale FUTEX_WAITERS/FUTEX_OWNER_DIED bit.
+ *
+ * [2]	Valid, if TID does not belong to a kernel thread. If no matching
+ *      thread is found then it indicates that the owner TID has died.
+ *
+ * [3]	Invalid. The waiter is queued on a non PI futex
+ *
+ * [4]	Valid state after exit_robust_list(), which sets the user space
+ *	value to FUTEX_WAITERS | FUTEX_OWNER_DIED.
+ *
+ * [5]	The user space value got manipulated between exit_robust_list()
+ *	and exit_pi_state_list()
+ *
+ * [6]	Valid state after exit_pi_state_list() which sets the new owner in
+ *	the pi_state but cannot access the user space value.
+ *
+ * [7]	pi_state->owner can only be NULL when the OWNER_DIED bit is set.
+ *
+ * [8]	Owner and user space value match
+ *
+ * [9]	There is no transient state which sets the user space TID to 0
+ *	except exit_robust_list(), but this is indicated by the
+ *	FUTEX_OWNER_DIED bit. See [4]
+ *
+ * [10] There is no transient state which leaves owner and user space
+ *	TID out of sync.
+ */
 static int
 lookup_pi_state(u32 uval, struct futex_hash_bucket *hb,
 		union futex_key *key, struct futex_pi_state **ps)
@@ -606,12 +657,13 @@
 	plist_for_each_entry_safe(this, next, head, list) {
 		if (match_futex(&this->key, key)) {
 			/*
-			 * Another waiter already exists - bump up
-			 * the refcount and return its pi_state:
+			 * Sanity check the waiter before increasing
+			 * the refcount and attaching to it.
 			 */
 			pi_state = this->pi_state;
 			/*
-			 * Userspace might have messed up non-PI and PI futexes
+			 * Userspace might have messed up non-PI and
+			 * PI futexes [3]
 			 */
 			if (unlikely(!pi_state))
 				return -EINVAL;
@@ -619,34 +671,70 @@
 			WARN_ON(!atomic_read(&pi_state->refcount));
 
 			/*
-			 * When pi_state->owner is NULL then the owner died
-			 * and another waiter is on the fly. pi_state->owner
-			 * is fixed up by the task which acquires
-			 * pi_state->rt_mutex.
-			 *
-			 * We do not check for pid == 0 which can happen when
-			 * the owner died and robust_list_exit() cleared the
-			 * TID.
+			 * Handle the owner died case:
+			 */
+			if (uval & FUTEX_OWNER_DIED) {
+				/*
+				 * exit_pi_state_list sets owner to NULL and
+				 * wakes the topmost waiter. The task which
+				 * acquires the pi_state->rt_mutex will fixup
+				 * owner.
+				 */
+				if (!pi_state->owner) {
+					/*
+					 * No pi state owner, but the user
+					 * space TID is not 0. Inconsistent
+					 * state. [5]
+					 */
+					if (pid)
+						return -EINVAL;
+					/*
+					 * Take a ref on the state and
+					 * return. [4]
+					 */
+					goto out_state;
+				}
+
+				/*
+				 * If TID is 0, then either the dying owner
+				 * has not yet executed exit_pi_state_list()
+				 * or some waiter acquired the rtmutex in the
+				 * pi state, but did not yet fixup the TID in
+				 * user space.
+				 *
+				 * Take a ref on the state and return. [6]
 			 */
-			if (pid && pi_state->owner) {
+				if (!pid)
+					goto out_state;
+			} else {
+				/*
+				 * If the owner died bit is not set,
+				 * then the pi_state must have an
+				 * owner. [7]
+				 */
+				if (!pi_state->owner)
+					return -EINVAL;
+			}
+
 				/*
 				 * Bail out if user space manipulated the
-				 * futex value.
+			 * futex value. If pi state exists then the
+			 * owner TID must be the same as the user
+			 * space TID. [9/10]
 				 */
 				if (pid != task_pid_vnr(pi_state->owner))
 					return -EINVAL;
-			}
 
+		out_state:
 			atomic_inc(&pi_state->refcount);
 			*ps = pi_state;
-
 			return 0;
 		}
 	}
 
 	/*
 	 * We are the first waiter - try to look up the real owner and attach
-	 * the new pi_state to it, but bail out when TID = 0
+	 * the new pi_state to it, but bail out when TID = 0 [1]
 	 */
 	if (!pid)
 		return -ESRCH;
@@ -654,6 +742,11 @@
 	if (!p)
 		return -ESRCH;
 
+	if (!p->mm) {
+		put_task_struct(p);
+		return -EPERM;
+	}
+
 	/*
 	 * We need to look at the task state flags to figure out,
 	 * whether the task is exiting. To protect against the do_exit
@@ -674,6 +767,9 @@
 		return ret;
 	}
 
+	/*
+	 * No existing pi state. First waiter. [2]
+	 */
 	pi_state = alloc_pi_state();
 
 	/*
@@ -745,10 +841,18 @@
 		return -EDEADLK;
 
 	/*
-	 * Surprise - we got the lock. Just return to userspace:
+	 * Surprise - we got the lock, but we do not trust user space at all.
 	 */
-	if (unlikely(!curval))
-		return 1;
+	if (unlikely(!curval)) {
+		/*
+		 * We verify whether there is kernel state for this
+		 * futex. If not, we can safely assume, that the 0 ->
+		 * TID transition is correct. If state exists, we do
+		 * not bother to fixup the user space state as it was
+		 * corrupted already.
+		 */
+		return futex_top_waiter(hb, key) ? -EINVAL : 1;
+	}
 
 	uval = curval;
 
@@ -878,6 +982,7 @@
 	struct task_struct *new_owner;
 	struct futex_pi_state *pi_state = this->pi_state;
 	u32 uninitialized_var(curval), newval;
+	int ret = 0;
 
 	if (!pi_state)
 		return -EINVAL;
@@ -901,13 +1006,10 @@
 		new_owner = this->task;
 
 	/*
-	 * We pass it to the next owner. (The WAITERS bit is always
-	 * kept enabled while there is PI state around. We must also
-	 * preserve the owner died bit.)
+	 * We pass it to the next owner. The WAITERS bit is always
+	 * kept enabled while there is PI state around. We cleanup the
+	 * owner died bit, because we are the owner.
 	 */
-	if (!(uval & FUTEX_OWNER_DIED)) {
-		int ret = 0;
-
 		newval = FUTEX_WAITERS | task_pid_vnr(new_owner);
 
 		if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))
@@ -918,7 +1020,6 @@
 			raw_spin_unlock(&pi_state->pi_mutex.wait_lock);
 			return ret;
 		}
-	}
 
 	raw_spin_lock_irq(&pi_state->owner->pi_lock);
 	WARN_ON(list_empty(&pi_state->list));
@@ -1196,7 +1297,7 @@
  *
  * Return:
  *  0 - failed to acquire the lock atomically;
- *  1 - acquired the lock;
+ * >0 - acquired the lock, return value is vpid of the top_waiter
  * <0 - error
  */
 static int futex_proxy_trylock_atomic(u32 __user *pifutex,
@@ -1207,7 +1308,7 @@
 {
 	struct futex_q *top_waiter = NULL;
 	u32 curval;
-	int ret;
+	int ret, vpid;
 
 	if (get_futex_value_locked(&curval, pifutex))
 		return -EFAULT;
@@ -1235,11 +1336,13 @@
 	 * the contended case or if set_waiters is 1.  The pi_state is returned
 	 * in ps in contended cases.
 	 */
+	vpid = task_pid_vnr(top_waiter->task);
 	ret = futex_lock_pi_atomic(pifutex, hb2, key2, ps, top_waiter->task,
 				   set_waiters);
-	if (ret == 1)
+	if (ret == 1) {
 		requeue_pi_wake_futex(top_waiter, key2, hb2);
-
+		return vpid;
+	}
 	return ret;
 }
 
@@ -1271,10 +1374,16 @@
 	struct futex_hash_bucket *hb1, *hb2;
 	struct plist_head *head1;
 	struct futex_q *this, *next;
-	u32 curval2;
 
 	if (requeue_pi) {
 		/*
+		 * Requeue PI only works on two distinct uaddrs. This
+		 * check is only valid for private futexes. See below.
+		 */
+		if (uaddr1 == uaddr2)
+			return -EINVAL;
+
+		/*
 		 * requeue_pi requires a pi_state, try to allocate it now
 		 * without any locks in case it fails.
 		 */
@@ -1312,6 +1421,15 @@
 	if (unlikely(ret != 0))
 		goto out_put_key1;
 
+	/*
+	 * The check above which compares uaddrs is not sufficient for
+	 * shared futexes. We need to compare the keys:
+	 */
+	if (requeue_pi && match_futex(&key1, &key2)) {
+		ret = -EINVAL;
+		goto out_put_keys;
+	}
+
 	hb1 = hash_futex(&key1);
 	hb2 = hash_futex(&key2);
 
@@ -1357,16 +1475,25 @@
 		 * At this point the top_waiter has either taken uaddr2 or is
 		 * waiting on it.  If the former, then the pi_state will not
 		 * exist yet, look it up one more time to ensure we have a
-		 * reference to it.
+		 * reference to it. If the lock was taken, ret contains the
+		 * vpid of the top waiter task.
 		 */
-		if (ret == 1) {
+		if (ret > 0) {
 			WARN_ON(pi_state);
 			drop_count++;
 			task_count++;
-			ret = get_futex_value_locked(&curval2, uaddr2);
-			if (!ret)
-				ret = lookup_pi_state(curval2, hb2, &key2,
-						      &pi_state);
+			/*
+			 * If we acquired the lock, then the user
+			 * space value of uaddr2 should be vpid. It
+			 * cannot be changed by the top waiter as it
+			 * is blocked on hb2 lock if it tries to do
+			 * so. If something fiddled with it behind our
+			 * back the pi state lookup might unearth
+			 * it. So we rather use the known value than
+			 * rereading and handing potential crap to
+			 * lookup_pi_state.
+			 */
+			ret = lookup_pi_state(ret, hb2, &key2, &pi_state);
 		}
 
 		switch (ret) {
@@ -2136,9 +2263,10 @@
 	/*
 	 * To avoid races, try to do the TID -> 0 atomic transition
 	 * again. If it succeeds then we can return without waking
-	 * anyone else up:
+	 * anyone else up. We only try this if neither the waiters nor
+	 * the owner died bit are set.
 	 */
-	if (!(uval & FUTEX_OWNER_DIED) &&
+	if (!(uval & ~FUTEX_TID_MASK) &&
 	    cmpxchg_futex_value_locked(&uval, uaddr, vpid, 0))
 		goto pi_faulted;
 	/*
@@ -2170,11 +2298,9 @@
 	/*
 	 * No waiters - kernel unlocks the futex:
 	 */
-	if (!(uval & FUTEX_OWNER_DIED)) {
 		ret = unlock_futex_pi(uaddr, uval);
 		if (ret == -EFAULT)
 			goto pi_faulted;
-	}
 
 out_unlock:
 	spin_unlock(&hb->lock);
@@ -2333,6 +2459,15 @@
 	if (ret)
 		goto out_key2;
 
+	/*
+	 * The check above which compares uaddrs is not sufficient for
+	 * shared futexes. We need to compare the keys:
+	 */
+	if (match_futex(&q.key, &key2)) {
+		ret = -EINVAL;
+		goto out_put_keys;
+	}
+
 	/* Queue the futex_q, drop the hb lock, wait for wakeup. */
 	futex_wait_queue_me(hb, &q, to);
 
@@ -2730,10 +2865,10 @@
 	return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
 }
 
-static int __init futex_init(void)
+static void __init futex_detect_cmpxchg(void)
 {
+#ifndef CONFIG_HAVE_FUTEX_CMPXCHG
 	u32 curval;
-	int i;
 
 	/*
 	 * This will fail and we want it. Some arch implementations do
@@ -2747,6 +2882,14 @@
 	 */
 	if (cmpxchg_futex_value_locked(&curval, NULL, 0, 0) == -EFAULT)
 		futex_cmpxchg_enabled = 1;
+#endif
+}
+
+static int __init futex_init(void)
+{
+	int i;
+
+	futex_detect_cmpxchg();
 
 	for (i = 0; i < ARRAY_SIZE(futex_queues); i++) {
 		plist_head_init(&futex_queues[i].chain);
diff -ruw linux-3.11.10/kernel/hrtimer.c linux-3.11.10-fbx/kernel/hrtimer.c
--- linux-3.11.10/kernel/hrtimer.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/hrtimer.c	2015-09-29 11:15:08.857755411 +0200
@@ -246,6 +246,11 @@
 			goto again;
 		}
 		timer->base = new_base;
+	} else {
+		if (cpu != this_cpu && hrtimer_check_target(timer, new_base)) {
+			cpu = this_cpu;
+			goto again;
+		}
 	}
 	return new_base;
 }
@@ -581,6 +586,23 @@
 
 	cpu_base->expires_next.tv64 = expires_next.tv64;
 
+	/*
+	 * If a hang was detected in the last timer interrupt then we
+	 * leave the hang delay active in the hardware. We want the
+	 * system to make progress. That also prevents the following
+	 * scenario:
+	 * T1 expires 50ms from now
+	 * T2 expires 5s from now
+	 *
+	 * T1 is removed, so this code is called and would reprogram
+	 * the hardware to 5s from now. Any hrtimer_start after that
+	 * will not reprogram the hardware due to hang_detected being
+	 * set. So we'd effectivly block all timers until the T2 event
+	 * fires.
+	 */
+	if (cpu_base->hang_detected)
+		return;
+
 	if (cpu_base->expires_next.tv64 != KTIME_MAX)
 		tick_program_event(cpu_base->expires_next, 1);
 }
@@ -980,11 +1002,8 @@
 	/* Remove an active timer from the queue: */
 	ret = remove_hrtimer(timer, base);
 
-	/* Switch the timer base, if necessary: */
-	new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED);
-
 	if (mode & HRTIMER_MODE_REL) {
-		tim = ktime_add_safe(tim, new_base->get_time());
+		tim = ktime_add_safe(tim, base->get_time());
 		/*
 		 * CONFIG_TIME_LOW_RES is a temporary way for architectures
 		 * to signal that they simply return xtime in
@@ -999,6 +1018,9 @@
 
 	hrtimer_set_expires_range_ns(timer, tim, delta_ns);
 
+	/* Switch the timer base, if necessary: */
+	new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED);
+
 	timer_stats_hrtimer_set_start_info(timer);
 
 	leftmost = enqueue_hrtimer(timer, new_base);
diff -ruw linux-3.11.10/kernel/irq/irqdesc.c linux-3.11.10-fbx/kernel/irq/irqdesc.c
--- linux-3.11.10/kernel/irq/irqdesc.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/irq/irqdesc.c	2015-09-29 11:15:08.857755411 +0200
@@ -274,6 +274,7 @@
 {
 	return (irq < NR_IRQS) ? irq_desc + irq : NULL;
 }
+EXPORT_SYMBOL(irq_to_desc);
 
 static void free_desc(unsigned int irq)
 {
diff -ruw linux-3.11.10/kernel/irq/Kconfig linux-3.11.10-fbx/kernel/irq/Kconfig
--- linux-3.11.10/kernel/irq/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/irq/Kconfig	2015-09-29 11:15:08.857755411 +0200
@@ -51,6 +51,7 @@
 # Generic configurable interrupt chip implementation
 config GENERIC_IRQ_CHIP
        bool
+       select IRQ_DOMAIN
 
 # Generic irq_domain hw <--> linux irq number translation
 config IRQ_DOMAIN
diff -ruw linux-3.11.10/kernel/irq/manage.c linux-3.11.10-fbx/kernel/irq/manage.c
--- linux-3.11.10/kernel/irq/manage.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/irq/manage.c	2015-09-29 11:15:08.857755411 +0200
@@ -150,7 +150,7 @@
 	struct irq_chip *chip = irq_data_get_irq_chip(data);
 	int ret;
 
-	ret = chip->irq_set_affinity(data, mask, false);
+	ret = chip->irq_set_affinity(data, mask, force);
 	switch (ret) {
 	case IRQ_SET_MASK_OK:
 		cpumask_copy(data->affinity, mask);
@@ -162,7 +162,8 @@
 	return ret;
 }
 
-int __irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask)
+int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask,
+			    bool force)
 {
 	struct irq_chip *chip = irq_data_get_irq_chip(data);
 	struct irq_desc *desc = irq_data_to_desc(data);
@@ -172,7 +173,7 @@
 		return -EINVAL;
 
 	if (irq_can_move_pcntxt(data)) {
-		ret = irq_do_set_affinity(data, mask, false);
+		ret = irq_do_set_affinity(data, mask, force);
 	} else {
 		irqd_set_move_pending(data);
 		irq_copy_pending(desc, mask);
@@ -187,13 +188,7 @@
 	return ret;
 }
 
-/**
- *	irq_set_affinity - Set the irq affinity of a given irq
- *	@irq:		Interrupt to set affinity
- *	@mask:		cpumask
- *
- */
-int irq_set_affinity(unsigned int irq, const struct cpumask *mask)
+int __irq_set_affinity(unsigned int irq, const struct cpumask *mask, bool force)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 	unsigned long flags;
@@ -203,7 +198,7 @@
 		return -EINVAL;
 
 	raw_spin_lock_irqsave(&desc->lock, flags);
-	ret =  __irq_set_affinity_locked(irq_desc_get_irq_data(desc), mask);
+	ret = irq_set_affinity_locked(irq_desc_get_irq_data(desc), mask, force);
 	raw_spin_unlock_irqrestore(&desc->lock, flags);
 	return ret;
 }
@@ -802,8 +797,7 @@
 
 static void wake_threads_waitq(struct irq_desc *desc)
 {
-	if (atomic_dec_and_test(&desc->threads_active) &&
-	    waitqueue_active(&desc->wait_for_threads))
+	if (atomic_dec_and_test(&desc->threads_active))
 		wake_up(&desc->wait_for_threads);
 }
 
@@ -862,8 +856,8 @@
 		irq_thread_check_affinity(desc, action);
 
 		action_ret = handler_fn(desc, action);
-		if (!noirqdebug)
-			note_interrupt(action->irq, desc, action_ret);
+		if (action_ret == IRQ_HANDLED)
+			atomic_inc(&desc->threads_handled);
 
 		wake_threads_waitq(desc);
 	}
@@ -956,7 +950,7 @@
 			goto out_mput;
 		}
 
-		sched_setscheduler(t, SCHED_FIFO, &param);
+		sched_setscheduler_nocheck(t, SCHED_FIFO, &param);
 
 		/*
 		 * We keep the reference to the task struct even if
diff -ruw linux-3.11.10/kernel/irq/spurious.c linux-3.11.10-fbx/kernel/irq/spurious.c
--- linux-3.11.10/kernel/irq/spurious.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/irq/spurious.c	2015-09-29 11:15:08.861755447 +0200
@@ -265,21 +265,119 @@
 	return action && (action->flags & IRQF_IRQPOLL);
 }
 
+#define SPURIOUS_DEFERRED	0x80000000
+
 void note_interrupt(unsigned int irq, struct irq_desc *desc,
 		    irqreturn_t action_ret)
 {
 	if (desc->istate & IRQS_POLL_INPROGRESS)
 		return;
 
-	/* we get here again via the threaded handler */
-	if (action_ret == IRQ_WAKE_THREAD)
-		return;
-
 	if (bad_action_ret(action_ret)) {
 		report_bad_irq(irq, desc, action_ret);
 		return;
 	}
 
+	/*
+	 * We cannot call note_interrupt from the threaded handler
+	 * because we need to look at the compound of all handlers
+	 * (primary and threaded). Aside of that in the threaded
+	 * shared case we have no serialization against an incoming
+	 * hardware interrupt while we are dealing with a threaded
+	 * result.
+	 *
+	 * So in case a thread is woken, we just note the fact and
+	 * defer the analysis to the next hardware interrupt.
+	 *
+	 * The threaded handlers store whether they sucessfully
+	 * handled an interrupt and we check whether that number
+	 * changed versus the last invocation.
+	 *
+	 * We could handle all interrupts with the delayed by one
+	 * mechanism, but for the non forced threaded case we'd just
+	 * add pointless overhead to the straight hardirq interrupts
+	 * for the sake of a few lines less code.
+	 */
+	if (action_ret & IRQ_WAKE_THREAD) {
+		/*
+		 * There is a thread woken. Check whether one of the
+		 * shared primary handlers returned IRQ_HANDLED. If
+		 * not we defer the spurious detection to the next
+		 * interrupt.
+		 */
+		if (action_ret == IRQ_WAKE_THREAD) {
+			int handled;
+			/*
+			 * We use bit 31 of thread_handled_last to
+			 * denote the deferred spurious detection
+			 * active. No locking necessary as
+			 * thread_handled_last is only accessed here
+			 * and we have the guarantee that hard
+			 * interrupts are not reentrant.
+			 */
+			if (!(desc->threads_handled_last & SPURIOUS_DEFERRED)) {
+				desc->threads_handled_last |= SPURIOUS_DEFERRED;
+				return;
+			}
+			/*
+			 * Check whether one of the threaded handlers
+			 * returned IRQ_HANDLED since the last
+			 * interrupt happened.
+			 *
+			 * For simplicity we just set bit 31, as it is
+			 * set in threads_handled_last as well. So we
+			 * avoid extra masking. And we really do not
+			 * care about the high bits of the handled
+			 * count. We just care about the count being
+			 * different than the one we saw before.
+			 */
+			handled = atomic_read(&desc->threads_handled);
+			handled |= SPURIOUS_DEFERRED;
+			if (handled != desc->threads_handled_last) {
+				action_ret = IRQ_HANDLED;
+				/*
+				 * Note: We keep the SPURIOUS_DEFERRED
+				 * bit set. We are handling the
+				 * previous invocation right now.
+				 * Keep it for the current one, so the
+				 * next hardware interrupt will
+				 * account for it.
+				 */
+				desc->threads_handled_last = handled;
+			} else {
+				/*
+				 * None of the threaded handlers felt
+				 * responsible for the last interrupt
+				 *
+				 * We keep the SPURIOUS_DEFERRED bit
+				 * set in threads_handled_last as we
+				 * need to account for the current
+				 * interrupt as well.
+				 */
+				action_ret = IRQ_NONE;
+			}
+		} else {
+			/*
+			 * One of the primary handlers returned
+			 * IRQ_HANDLED. So we don't care about the
+			 * threaded handlers on the same line. Clear
+			 * the deferred detection bit.
+			 *
+			 * In theory we could/should check whether the
+			 * deferred bit is set and take the result of
+			 * the previous run into account here as
+			 * well. But it's really not worth the
+			 * trouble. If every other interrupt is
+			 * handled we never trigger the spurious
+			 * detector. And if this is just the one out
+			 * of 100k unhandled ones which is handled
+			 * then we merily delay the spurious detection
+			 * by one hard interrupt. Not a real problem.
+			 */
+			desc->threads_handled_last &= ~SPURIOUS_DEFERRED;
+		}
+	}
+
 	if (unlikely(action_ret == IRQ_NONE)) {
 		/*
 		 * If we are seeing only the odd spurious IRQ caused by
diff -ruw linux-3.11.10/kernel/module.c linux-3.11.10-fbx/kernel/module.c
--- linux-3.11.10/kernel/module.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/module.c	2015-09-29 11:15:08.865755472 +0200
@@ -3288,6 +3288,9 @@
 
 	dynamic_debug_setup(info->debug, info->num_debug);
 
+	/* Ftrace init must be called in the MODULE_STATE_UNFORMED state */
+	ftrace_module_init(mod);
+
 	/* Finally it's fully formed, ready to start executing. */
 	err = complete_formation(mod, info);
 	if (err)
diff -ruw linux-3.11.10/kernel/pid.c linux-3.11.10-fbx/kernel/pid.c
--- linux-3.11.10/kernel/pid.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/pid.c	2015-09-29 11:15:08.865755472 +0200
@@ -81,7 +81,7 @@
 	.user_ns = &init_user_ns,
 	.proc_inum = PROC_PID_INIT_INO,
 };
-EXPORT_SYMBOL_GPL(init_pid_ns);
+EXPORT_SYMBOL(init_pid_ns);
 
 /*
  * Note: disable interrupts while the pidmap_lock is held as an
diff -ruw linux-3.11.10/kernel/printk/printk.c linux-3.11.10-fbx/kernel/printk/printk.c
--- linux-3.11.10/kernel/printk/printk.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/printk/printk.c	2015-09-29 11:15:08.869755501 +0200
@@ -54,6 +54,10 @@
 #include "console_cmdline.h"
 #include "braille.h"
 
+#ifdef CONFIG_DEBUG_LL
+extern void printascii(char *);
+#endif
+
 /* printk's without a loglevel use this.. */
 #define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL
 
@@ -1080,7 +1084,6 @@
 		next_seq = log_next_seq;
 
 		len = 0;
-		prev = 0;
 		while (len >= 0 && seq < next_seq) {
 			struct printk_log *msg = log_from_idx(idx);
 			int textlen;
@@ -1547,6 +1550,10 @@
 	 */
 	text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
 
+#ifdef CONFIG_DEBUG_LL
+	printascii(text);
+#endif
+
 	/* mark and strip a trailing newline */
 	if (text_len && text[text_len-1] == '\n') {
 		text_len--;
@@ -2783,7 +2790,6 @@
 	next_idx = idx;
 
 	l = 0;
-	prev = 0;
 	while (seq < dumper->next_seq) {
 		struct printk_log *msg = log_from_idx(idx);
 
diff -ruw linux-3.11.10/kernel/reboot.c linux-3.11.10-fbx/kernel/reboot.c
--- linux-3.11.10/kernel/reboot.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/reboot.c	2015-09-29 11:15:08.873755537 +0200
@@ -104,7 +104,7 @@
 }
 EXPORT_SYMBOL(unregister_reboot_notifier);
 
-static void migrate_to_reboot_cpu(void)
+void migrate_to_reboot_cpu(void)
 {
 	/* The boot cpu is always logical cpu 0 */
 	int cpu = reboot_cpu;
diff -ruw linux-3.11.10/kernel/resource.c linux-3.11.10-fbx/kernel/resource.c
--- linux-3.11.10/kernel/resource.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/resource.c	2015-09-29 11:15:08.873755537 +0200
@@ -342,15 +342,14 @@
 	read_lock(&resource_lock);
 	for (p = iomem_resource.child; p ; p = p->sibling) {
 		/* system ram is just marked as IORESOURCE_MEM */
-		if (p->flags != res->flags)
-			continue;
-		if (name && strcmp(p->name, name))
-			continue;
 		if (p->start > end) {
 			p = NULL;
 			break;
 		}
-		if ((p->end >= start) && (p->start < end))
+		if (p->flags != res->flags)
+			continue;
+		if ((p->end >= start) && (p->start < end) &&
+		    (name == NULL || !strcmp(p->name, name)))
 			break;
 	}
 	read_unlock(&resource_lock);
diff -ruw linux-3.11.10/kernel/rtmutex.c linux-3.11.10-fbx/kernel/rtmutex.c
--- linux-3.11.10/kernel/rtmutex.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/rtmutex.c	2015-09-29 11:15:08.873755537 +0200
@@ -82,6 +82,47 @@
 		owner = *p;
 	} while (cmpxchg(p, owner, owner | RT_MUTEX_HAS_WAITERS) != owner);
 }
+
+/*
+ * Safe fastpath aware unlock:
+ * 1) Clear the waiters bit
+ * 2) Drop lock->wait_lock
+ * 3) Try to unlock the lock with cmpxchg
+ */
+static inline bool unlock_rt_mutex_safe(struct rt_mutex *lock)
+	__releases(lock->wait_lock)
+{
+	struct task_struct *owner = rt_mutex_owner(lock);
+
+	clear_rt_mutex_waiters(lock);
+	raw_spin_unlock(&lock->wait_lock);
+	/*
+	 * If a new waiter comes in between the unlock and the cmpxchg
+	 * we have two situations:
+	 *
+	 * unlock(wait_lock);
+	 *					lock(wait_lock);
+	 * cmpxchg(p, owner, 0) == owner
+	 *					mark_rt_mutex_waiters(lock);
+	 *					acquire(lock);
+	 * or:
+	 *
+	 * unlock(wait_lock);
+	 *					lock(wait_lock);
+	 *					mark_rt_mutex_waiters(lock);
+	 *
+	 * cmpxchg(p, owner, 0) != owner
+	 *					enqueue_waiter();
+	 *					unlock(wait_lock);
+	 * lock(wait_lock);
+	 * wake waiter();
+	 * unlock(wait_lock);
+	 *					lock(wait_lock);
+	 *					acquire(lock);
+	 */
+	return rt_mutex_cmpxchg(lock, owner, NULL);
+}
+
 #else
 # define rt_mutex_cmpxchg(l,c,n)	(0)
 static inline void mark_rt_mutex_waiters(struct rt_mutex *lock)
@@ -89,6 +130,17 @@
 	lock->owner = (struct task_struct *)
 			((unsigned long)lock->owner | RT_MUTEX_HAS_WAITERS);
 }
+
+/*
+ * Simple slow path only version: lock->owner is protected by lock->wait_lock.
+ */
+static inline bool unlock_rt_mutex_safe(struct rt_mutex *lock)
+	__releases(lock->wait_lock)
+{
+	lock->owner = NULL;
+	raw_spin_unlock(&lock->wait_lock);
+	return true;
+}
 #endif
 
 /*
@@ -142,16 +194,24 @@
  */
 int max_lock_depth = 1024;
 
+static inline struct rt_mutex *task_blocked_on_lock(struct task_struct *p)
+{
+	return p->pi_blocked_on ? p->pi_blocked_on->lock : NULL;
+}
+
 /*
  * Adjust the priority chain. Also used for deadlock detection.
  * Decreases task's usage by one - may thus free the task.
  *
- * @task: the task owning the mutex (owner) for which a chain walk is probably
- *	  needed
+ * @task:	the task owning the mutex (owner) for which a chain walk is
+ *		probably needed
  * @deadlock_detect: do we have to carry out deadlock detection?
  * @orig_lock: the mutex (can be NULL if we are walking the chain to recheck
  * 	       things for a task that has just got its priority adjusted, and
  *	       is waiting on a mutex)
+ * @next_lock:	the mutex on which the owner of @orig_lock was blocked before
+ *		we dropped its pi_lock. Is never dereferenced, only used for
+ *		comparison to detect lock chain changes.
  * @orig_waiter: rt_mutex_waiter struct for the task that has just donated
  *		 its priority to the mutex owner (can be NULL in the case
  *		 depicted above or if the top waiter is gone away and we are
@@ -163,6 +223,7 @@
 static int rt_mutex_adjust_prio_chain(struct task_struct *task,
 				      int deadlock_detect,
 				      struct rt_mutex *orig_lock,
+				      struct rt_mutex *next_lock,
 				      struct rt_mutex_waiter *orig_waiter,
 				      struct task_struct *top_task)
 {
@@ -196,7 +257,7 @@
 		}
 		put_task_struct(task);
 
-		return deadlock_detect ? -EDEADLK : 0;
+		return -EDEADLK;
 	}
  retry:
 	/*
@@ -221,13 +282,32 @@
 		goto out_unlock_pi;
 
 	/*
+	 * We dropped all locks after taking a refcount on @task, so
+	 * the task might have moved on in the lock chain or even left
+	 * the chain completely and blocks now on an unrelated lock or
+	 * on @orig_lock.
+	 *
+	 * We stored the lock on which @task was blocked in @next_lock,
+	 * so we can detect the chain change.
+	 */
+	if (next_lock != waiter->lock)
+		goto out_unlock_pi;
+
+	/*
 	 * Drop out, when the task has no waiters. Note,
 	 * top_waiter can be NULL, when we are in the deboosting
 	 * mode!
 	 */
-	if (top_waiter && (!task_has_pi_waiters(task) ||
-			   top_waiter != task_top_pi_waiter(task)))
+	if (top_waiter) {
+		if (!task_has_pi_waiters(task))
+			goto out_unlock_pi;
+		/*
+		 * If deadlock detection is off, we stop here if we
+		 * are not the top pi waiter of the task.
+		 */
+		if (!detect_deadlock && top_waiter != task_top_pi_waiter(task))
 		goto out_unlock_pi;
+	}
 
 	/*
 	 * When deadlock detection is off then we check, if further
@@ -243,11 +323,16 @@
 		goto retry;
 	}
 
-	/* Deadlock detection */
+	/*
+	 * Deadlock detection. If the lock is the same as the original
+	 * lock which caused us to walk the lock chain or if the
+	 * current lock is owned by the task which initiated the chain
+	 * walk, we detected a deadlock.
+	 */
 	if (lock == orig_lock || rt_mutex_owner(lock) == top_task) {
 		debug_rt_mutex_deadlock(deadlock_detect, orig_waiter, lock);
 		raw_spin_unlock(&lock->wait_lock);
-		ret = deadlock_detect ? -EDEADLK : 0;
+		ret = -EDEADLK;
 		goto out_unlock_pi;
 	}
 
@@ -294,11 +379,26 @@
 		__rt_mutex_adjust_prio(task);
 	}
 
+	/*
+	 * Check whether the task which owns the current lock is pi
+	 * blocked itself. If yes we store a pointer to the lock for
+	 * the lock chain change detection above. After we dropped
+	 * task->pi_lock next_lock cannot be dereferenced anymore.
+	 */
+	next_lock = task_blocked_on_lock(task);
+
 	raw_spin_unlock_irqrestore(&task->pi_lock, flags);
 
 	top_waiter = rt_mutex_top_waiter(lock);
 	raw_spin_unlock(&lock->wait_lock);
 
+	/*
+	 * We reached the end of the lock chain. Stop right here. No
+	 * point to go back just to figure that out.
+	 */
+	if (!next_lock)
+		goto out_put_task;
+
 	if (!detect_deadlock && waiter != top_waiter)
 		goto out_put_task;
 
@@ -409,8 +509,21 @@
 {
 	struct task_struct *owner = rt_mutex_owner(lock);
 	struct rt_mutex_waiter *top_waiter = waiter;
-	unsigned long flags;
+	struct rt_mutex *next_lock;
 	int chain_walk = 0, res;
+	unsigned long flags;
+
+	/*
+	 * Early deadlock detection. We really don't want the task to
+	 * enqueue on itself just to untangle the mess later. It's not
+	 * only an optimization. We drop the locks, so another waiter
+	 * can come in before the chain walk detects the deadlock. So
+	 * the other will detect the deadlock and return -EDEADLOCK,
+	 * which is wrong, as the other waiter is not in a deadlock
+	 * situation.
+	 */
+	if (owner == task)
+		return -EDEADLK;
 
 	raw_spin_lock_irqsave(&task->pi_lock, flags);
 	__rt_mutex_adjust_prio(task);
@@ -431,20 +544,28 @@
 	if (!owner)
 		return 0;
 
-	if (waiter == rt_mutex_top_waiter(lock)) {
 		raw_spin_lock_irqsave(&owner->pi_lock, flags);
+	if (waiter == rt_mutex_top_waiter(lock)) {
 		plist_del(&top_waiter->pi_list_entry, &owner->pi_waiters);
 		plist_add(&waiter->pi_list_entry, &owner->pi_waiters);
 
 		__rt_mutex_adjust_prio(owner);
 		if (owner->pi_blocked_on)
 			chain_walk = 1;
-		raw_spin_unlock_irqrestore(&owner->pi_lock, flags);
-	}
-	else if (debug_rt_mutex_detect_deadlock(waiter, detect_deadlock))
+	} else if (debug_rt_mutex_detect_deadlock(waiter, detect_deadlock)) {
 		chain_walk = 1;
+	}
 
-	if (!chain_walk)
+	/* Store the lock on which owner is blocked or NULL */
+	next_lock = task_blocked_on_lock(owner);
+
+	raw_spin_unlock_irqrestore(&owner->pi_lock, flags);
+	/*
+	 * Even if full deadlock detection is on, if the owner is not
+	 * blocked itself, we can avoid finding this out in the chain
+	 * walk.
+	 */
+	if (!chain_walk || !next_lock)
 		return 0;
 
 	/*
@@ -456,8 +577,8 @@
 
 	raw_spin_unlock(&lock->wait_lock);
 
-	res = rt_mutex_adjust_prio_chain(owner, detect_deadlock, lock, waiter,
-					 task);
+	res = rt_mutex_adjust_prio_chain(owner, detect_deadlock, lock,
+					 next_lock, waiter, task);
 
 	raw_spin_lock(&lock->wait_lock);
 
@@ -467,7 +588,8 @@
 /*
  * Wake up the next waiter on the lock.
  *
- * Remove the top waiter from the current tasks waiter list and wake it up.
+ * Remove the top waiter from the current tasks pi waiter list and
+ * wake it up.
  *
  * Called with lock->wait_lock held.
  */
@@ -488,10 +610,23 @@
 	 */
 	plist_del(&waiter->pi_list_entry, &current->pi_waiters);
 
-	rt_mutex_set_owner(lock, NULL);
+	/*
+	 * As we are waking up the top waiter, and the waiter stays
+	 * queued on the lock until it gets the lock, this lock
+	 * obviously has waiters. Just set the bit here and this has
+	 * the added benefit of forcing all new tasks into the
+	 * slow path making sure no task of lower priority than
+	 * the top waiter can steal this lock.
+	 */
+	lock->owner = (void *) RT_MUTEX_HAS_WAITERS;
 
 	raw_spin_unlock_irqrestore(&current->pi_lock, flags);
 
+	/*
+	 * It's safe to dereference waiter as it cannot go away as
+	 * long as we hold lock->wait_lock. The waiter task needs to
+	 * acquire it in order to dequeue the waiter.
+	 */
 	wake_up_process(waiter->task);
 }
 
@@ -506,8 +641,8 @@
 {
 	int first = (waiter == rt_mutex_top_waiter(lock));
 	struct task_struct *owner = rt_mutex_owner(lock);
+	struct rt_mutex *next_lock = NULL;
 	unsigned long flags;
-	int chain_walk = 0;
 
 	raw_spin_lock_irqsave(&current->pi_lock, flags);
 	plist_del(&waiter->list_entry, &lock->wait_list);
@@ -531,15 +666,15 @@
 		}
 		__rt_mutex_adjust_prio(owner);
 
-		if (owner->pi_blocked_on)
-			chain_walk = 1;
+		/* Store the lock on which owner is blocked or NULL */
+		next_lock = task_blocked_on_lock(owner);
 
 		raw_spin_unlock_irqrestore(&owner->pi_lock, flags);
 	}
 
 	WARN_ON(!plist_node_empty(&waiter->pi_list_entry));
 
-	if (!chain_walk)
+	if (!next_lock)
 		return;
 
 	/* gets dropped in rt_mutex_adjust_prio_chain()! */
@@ -547,7 +682,7 @@
 
 	raw_spin_unlock(&lock->wait_lock);
 
-	rt_mutex_adjust_prio_chain(owner, 0, lock, NULL, current);
+	rt_mutex_adjust_prio_chain(owner, 0, lock, next_lock, NULL, current);
 
 	raw_spin_lock(&lock->wait_lock);
 }
@@ -560,6 +695,7 @@
 void rt_mutex_adjust_pi(struct task_struct *task)
 {
 	struct rt_mutex_waiter *waiter;
+	struct rt_mutex *next_lock;
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&task->pi_lock, flags);
@@ -569,12 +705,13 @@
 		raw_spin_unlock_irqrestore(&task->pi_lock, flags);
 		return;
 	}
-
+	next_lock = waiter->lock;
 	raw_spin_unlock_irqrestore(&task->pi_lock, flags);
 
 	/* gets dropped in rt_mutex_adjust_prio_chain()! */
 	get_task_struct(task);
-	rt_mutex_adjust_prio_chain(task, 0, NULL, NULL, task);
+
+	rt_mutex_adjust_prio_chain(task, 0, NULL, next_lock, NULL, task);
 }
 
 /**
@@ -626,6 +763,26 @@
 	return ret;
 }
 
+static void rt_mutex_handle_deadlock(int res, int detect_deadlock,
+				     struct rt_mutex_waiter *w)
+{
+	/*
+	 * If the result is not -EDEADLOCK or the caller requested
+	 * deadlock detection, nothing to do here.
+	 */
+	if (res != -EDEADLOCK || detect_deadlock)
+		return;
+
+	/*
+	 * Yell lowdly and stop the task right here.
+	 */
+	rt_mutex_print_deadlock(w);
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule();
+	}
+}
+
 /*
  * Slow path lock function:
  */
@@ -663,8 +820,10 @@
 
 	set_current_state(TASK_RUNNING);
 
-	if (unlikely(ret))
+	if (unlikely(ret)) {
 		remove_waiter(lock, &waiter);
+		rt_mutex_handle_deadlock(ret, detect_deadlock, &waiter);
+	}
 
 	/*
 	 * try_to_take_rt_mutex() sets the waiter bit
@@ -720,12 +879,49 @@
 
 	rt_mutex_deadlock_account_unlock(current);
 
-	if (!rt_mutex_has_waiters(lock)) {
-		lock->owner = NULL;
-		raw_spin_unlock(&lock->wait_lock);
+	/*
+	 * We must be careful here if the fast path is enabled. If we
+	 * have no waiters queued we cannot set owner to NULL here
+	 * because of:
+	 *
+	 * foo->lock->owner = NULL;
+	 *			rtmutex_lock(foo->lock);   <- fast path
+	 *			free = atomic_dec_and_test(foo->refcnt);
+	 *			rtmutex_unlock(foo->lock); <- fast path
+	 *			if (free)
+	 *				kfree(foo);
+	 * raw_spin_unlock(foo->lock->wait_lock);
+	 *
+	 * So for the fastpath enabled kernel:
+	 *
+	 * Nothing can set the waiters bit as long as we hold
+	 * lock->wait_lock. So we do the following sequence:
+	 *
+	 *	owner = rt_mutex_owner(lock);
+	 *	clear_rt_mutex_waiters(lock);
+	 *	raw_spin_unlock(&lock->wait_lock);
+	 *	if (cmpxchg(&lock->owner, owner, 0) == owner)
+	 *		return;
+	 *	goto retry;
+	 *
+	 * The fastpath disabled variant is simple as all access to
+	 * lock->owner is serialized by lock->wait_lock:
+	 *
+	 *	lock->owner = NULL;
+	 *	raw_spin_unlock(&lock->wait_lock);
+	 */
+	while (!rt_mutex_has_waiters(lock)) {
+		/* Drops lock->wait_lock ! */
+		if (unlock_rt_mutex_safe(lock) == true)
 		return;
+		/* Relock the rtmutex and try again */
+		raw_spin_lock(&lock->wait_lock);
 	}
 
+	/*
+	 * The wakeup next waiter path does not suffer from the above
+	 * race. See the comments there.
+	 */
 	wakeup_next_waiter(lock);
 
 	raw_spin_unlock(&lock->wait_lock);
@@ -972,7 +1168,8 @@
 		return 1;
 	}
 
-	ret = task_blocks_on_rt_mutex(lock, waiter, task, detect_deadlock);
+	/* We enforce deadlock detection for futexes */
+	ret = task_blocks_on_rt_mutex(lock, waiter, task, 1);
 
 	if (ret && !rt_mutex_owner(lock)) {
 		/*
diff -ruw linux-3.11.10/kernel/rtmutex-debug.h linux-3.11.10-fbx/kernel/rtmutex-debug.h
--- linux-3.11.10/kernel/rtmutex-debug.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/rtmutex-debug.h	2015-09-29 11:15:08.873755537 +0200
@@ -31,3 +31,8 @@
 {
 	return (waiter != NULL);
 }
+
+static inline void rt_mutex_print_deadlock(struct rt_mutex_waiter *w)
+{
+	debug_rt_mutex_print_deadlock(w);
+}
diff -ruw linux-3.11.10/kernel/sched/core.c linux-3.11.10-fbx/kernel/sched/core.c
--- linux-3.11.10/kernel/sched/core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/sched/core.c	2015-09-29 11:15:08.877755562 +0200
@@ -7283,7 +7283,12 @@
 
 	runtime_enabled = quota != RUNTIME_INF;
 	runtime_was_enabled = cfs_b->quota != RUNTIME_INF;
-	account_cfs_bandwidth_used(runtime_enabled, runtime_was_enabled);
+	/*
+	 * If we need to toggle cfs_bandwidth_used, off->on must occur
+	 * before making related changes, and on->off must occur afterwards
+	 */
+	if (runtime_enabled && !runtime_was_enabled)
+		cfs_bandwidth_usage_inc();
 	raw_spin_lock_irq(&cfs_b->lock);
 	cfs_b->period = ns_to_ktime(period);
 	cfs_b->quota = quota;
@@ -7309,6 +7314,8 @@
 			unthrottle_cfs_rq(cfs_rq);
 		raw_spin_unlock_irq(&rq->lock);
 	}
+	if (runtime_was_enabled && !runtime_enabled)
+		cfs_bandwidth_usage_dec();
 out_unlock:
 	mutex_unlock(&cfs_constraints_mutex);
 
diff -ruw linux-3.11.10/kernel/sched/cputime.c linux-3.11.10-fbx/kernel/sched/cputime.c
--- linux-3.11.10/kernel/sched/cputime.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/sched/cputime.c	2015-09-29 11:15:08.877755562 +0200
@@ -326,50 +326,50 @@
  * softirq as those do not count in task exec_runtime any more.
  */
 static void irqtime_account_process_tick(struct task_struct *p, int user_tick,
-						struct rq *rq)
+					 struct rq *rq, int ticks)
 {
-	cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy);
+	cputime_t scaled = cputime_to_scaled(cputime_one_jiffy);
+	u64 cputime = (__force u64) cputime_one_jiffy;
 	u64 *cpustat = kcpustat_this_cpu->cpustat;
 
 	if (steal_account_process_tick())
 		return;
 
+	cputime *= ticks;
+	scaled *= ticks;
+
 	if (irqtime_account_hi_update()) {
-		cpustat[CPUTIME_IRQ] += (__force u64) cputime_one_jiffy;
+		cpustat[CPUTIME_IRQ] += cputime;
 	} else if (irqtime_account_si_update()) {
-		cpustat[CPUTIME_SOFTIRQ] += (__force u64) cputime_one_jiffy;
+		cpustat[CPUTIME_SOFTIRQ] += cputime;
 	} else if (this_cpu_ksoftirqd() == p) {
 		/*
 		 * ksoftirqd time do not get accounted in cpu_softirq_time.
 		 * So, we have to handle it separately here.
 		 * Also, p->stime needs to be updated for ksoftirqd.
 		 */
-		__account_system_time(p, cputime_one_jiffy, one_jiffy_scaled,
-					CPUTIME_SOFTIRQ);
+		__account_system_time(p, cputime, scaled, CPUTIME_SOFTIRQ);
 	} else if (user_tick) {
-		account_user_time(p, cputime_one_jiffy, one_jiffy_scaled);
+		account_user_time(p, cputime, scaled);
 	} else if (p == rq->idle) {
-		account_idle_time(cputime_one_jiffy);
+		account_idle_time(cputime);
 	} else if (p->flags & PF_VCPU) { /* System time or guest time */
-		account_guest_time(p, cputime_one_jiffy, one_jiffy_scaled);
+		account_guest_time(p, cputime, scaled);
 	} else {
-		__account_system_time(p, cputime_one_jiffy, one_jiffy_scaled,
-					CPUTIME_SYSTEM);
+		__account_system_time(p, cputime, scaled,	CPUTIME_SYSTEM);
 	}
 }
 
 static void irqtime_account_idle_ticks(int ticks)
 {
-	int i;
 	struct rq *rq = this_rq();
 
-	for (i = 0; i < ticks; i++)
-		irqtime_account_process_tick(current, 0, rq);
+	irqtime_account_process_tick(current, 0, rq, ticks);
 }
 #else /* CONFIG_IRQ_TIME_ACCOUNTING */
 static inline void irqtime_account_idle_ticks(int ticks) {}
 static inline void irqtime_account_process_tick(struct task_struct *p, int user_tick,
-						struct rq *rq) {}
+						struct rq *rq, int nr_ticks) {}
 #endif /* CONFIG_IRQ_TIME_ACCOUNTING */
 
 /*
@@ -464,7 +464,7 @@
 		return;
 
 	if (sched_clock_irqtime) {
-		irqtime_account_process_tick(p, user_tick, rq);
+		irqtime_account_process_tick(p, user_tick, rq, 1);
 		return;
 	}
 
diff -ruw linux-3.11.10/kernel/sched/fair.c linux-3.11.10-fbx/kernel/sched/fair.c
--- linux-3.11.10/kernel/sched/fair.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/sched/fair.c	2015-09-29 11:15:08.881755591 +0200
@@ -974,6 +974,13 @@
 		if (vma->vm_end - vma->vm_start < HPAGE_SIZE)
 			continue;
 
+		/*
+		 * Skip inaccessible VMAs to avoid any confusion between
+		 * PROT_NONE and NUMA hinting ptes
+		 */
+		if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
+			continue;
+
 		do {
 			start = max(start, vma->vm_start);
 			end = ALIGN(start + (pages << PAGE_SHIFT), HPAGE_SIZE);
@@ -2070,12 +2077,13 @@
 	return static_key_false(&__cfs_bandwidth_used);
 }
 
-void account_cfs_bandwidth_used(int enabled, int was_enabled)
+void cfs_bandwidth_usage_inc(void)
 {
-	/* only need to count groups transitioning between enabled/!enabled */
-	if (enabled && !was_enabled)
 		static_key_slow_inc(&__cfs_bandwidth_used);
-	else if (!enabled && was_enabled)
+}
+
+void cfs_bandwidth_usage_dec(void)
+{
 		static_key_slow_dec(&__cfs_bandwidth_used);
 }
 #else /* HAVE_JUMP_LABEL */
@@ -2084,7 +2092,8 @@
 	return true;
 }
 
-void account_cfs_bandwidth_used(int enabled, int was_enabled) {}
+void cfs_bandwidth_usage_inc(void) {}
+void cfs_bandwidth_usage_dec(void) {}
 #endif /* HAVE_JUMP_LABEL */
 
 /*
@@ -2335,6 +2344,8 @@
 	cfs_rq->throttled_clock = rq_clock(rq);
 	raw_spin_lock(&cfs_b->lock);
 	list_add_tail_rcu(&cfs_rq->throttled_list, &cfs_b->throttled_cfs_rq);
+	if (!cfs_b->timer_active)
+		__start_cfs_bandwidth(cfs_b);
 	raw_spin_unlock(&cfs_b->lock);
 }
 
@@ -2448,6 +2459,13 @@
 	if (idle)
 		goto out_unlock;
 
+	/*
+	 * if we have relooped after returning idle once, we need to update our
+	 * status as actually running, so that other cpus doing
+	 * __start_cfs_bandwidth will stop trying to cancel us.
+	 */
+	cfs_b->timer_active = 1;
+
 	__refill_cfs_bandwidth_runtime(cfs_b);
 
 	if (!throttled) {
@@ -2508,7 +2526,13 @@
 /* how long we wait to gather additional slack before distributing */
 static const u64 cfs_bandwidth_slack_period = 5 * NSEC_PER_MSEC;
 
-/* are we near the end of the current quota period? */
+/*
+ * Are we near the end of the current quota period?
+ *
+ * Requires cfs_b->lock for hrtimer_expires_remaining to be safe against the
+ * hrtimer base being cleared by __hrtimer_start_range_ns. In the case of
+ * migrate_hrtimers, base is never cleared, so we are fine.
+ */
 static int runtime_refresh_within(struct cfs_bandwidth *cfs_b, u64 min_expire)
 {
 	struct hrtimer *refresh_timer = &cfs_b->period_timer;
@@ -2584,10 +2608,12 @@
 	u64 expires;
 
 	/* confirm we're still not at a refresh boundary */
-	if (runtime_refresh_within(cfs_b, min_bandwidth_expiration))
+	raw_spin_lock(&cfs_b->lock);
+	if (runtime_refresh_within(cfs_b, min_bandwidth_expiration)) {
+		raw_spin_unlock(&cfs_b->lock);
 		return;
+	}
 
-	raw_spin_lock(&cfs_b->lock);
 	if (cfs_b->quota != RUNTIME_INF && cfs_b->runtime > slice) {
 		runtime = cfs_b->runtime;
 		cfs_b->runtime = 0;
@@ -2708,11 +2734,11 @@
 	 * (timer_active==0 becomes visible before the hrtimer call-back
 	 * terminates).  In either case we ensure that it's re-programmed
 	 */
-	while (unlikely(hrtimer_active(&cfs_b->period_timer))) {
+	while (unlikely(hrtimer_active(&cfs_b->period_timer)) &&
+	       hrtimer_try_to_cancel(&cfs_b->period_timer) < 0) {
+		/* bounce the lock to allow do_sched_cfs_period_timer to run */
 		raw_spin_unlock(&cfs_b->lock);
-		/* ensure cfs_b->lock is available while we wait */
-		hrtimer_cancel(&cfs_b->period_timer);
-
+		cpu_relax();
 		raw_spin_lock(&cfs_b->lock);
 		/* if someone else restarted the timer then we're done */
 		if (cfs_b->timer_active)
@@ -5876,15 +5902,15 @@
 	struct cfs_rq *cfs_rq = cfs_rq_of(se);
 
 	/*
-	 * Ensure the task's vruntime is normalized, so that when its
+	 * Ensure the task's vruntime is normalized, so that when it's
 	 * switched back to the fair class the enqueue_entity(.flags=0) will
 	 * do the right thing.
 	 *
-	 * If it was on_rq, then the dequeue_entity(.flags=0) will already
-	 * have normalized the vruntime, if it was !on_rq, then only when
+	 * If it's on_rq, then the dequeue_entity(.flags=0) will already
+	 * have normalized the vruntime, if it's !on_rq, then only when
 	 * the task is sleeping will it still have non-normalized vruntime.
 	 */
-	if (!se->on_rq && p->state != TASK_RUNNING) {
+	if (!p->on_rq && p->state != TASK_RUNNING) {
 		/*
 		 * Fix up our vruntime so that the current sleep doesn't
 		 * cause 'unlimited' sleep bonus.
@@ -6105,7 +6131,8 @@
 		se->cfs_rq = parent->my_q;
 
 	se->my_q = cfs_rq;
-	update_load_set(&se->load, 0);
+	/* guarantee group entities always have weight */
+	update_load_set(&se->load, NICE_0_LOAD);
 	se->parent = parent;
 }
 
diff -ruw linux-3.11.10/kernel/sched/rt.c linux-3.11.10-fbx/kernel/sched/rt.c
--- linux-3.11.10/kernel/sched/rt.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/sched/rt.c	2015-09-29 11:15:08.881755591 +0200
@@ -899,6 +899,13 @@
 {
 	struct rq *rq = rq_of_rt_rq(rt_rq);
 
+#ifdef CONFIG_RT_GROUP_SCHED
+	/*
+	 * Change rq's cpupri only if rt_rq is the top queue.
+	 */
+	if (&rq->rt != rt_rq)
+		return;
+#endif
 	if (rq->online && prio < prev_prio)
 		cpupri_set(&rq->rd->cpupri, rq->cpu, prio);
 }
@@ -908,6 +915,13 @@
 {
 	struct rq *rq = rq_of_rt_rq(rt_rq);
 
+#ifdef CONFIG_RT_GROUP_SCHED
+	/*
+	 * Change rq's cpupri only if rt_rq is the top queue.
+	 */
+	if (&rq->rt != rt_rq)
+		return;
+#endif
 	if (rq->online && rt_rq->highest_prio.curr != prev_prio)
 		cpupri_set(&rq->rd->cpupri, rq->cpu, rt_rq->highest_prio.curr);
 }
diff -ruw linux-3.11.10/kernel/sched/sched.h linux-3.11.10-fbx/kernel/sched/sched.h
--- linux-3.11.10/kernel/sched/sched.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/sched/sched.h	2015-09-29 11:15:08.881755591 +0200
@@ -1305,7 +1305,8 @@
 extern void init_cfs_rq(struct cfs_rq *cfs_rq);
 extern void init_rt_rq(struct rt_rq *rt_rq, struct rq *rq);
 
-extern void account_cfs_bandwidth_used(int enabled, int was_enabled);
+extern void cfs_bandwidth_usage_inc(void);
+extern void cfs_bandwidth_usage_dec(void);
 
 #ifdef CONFIG_NO_HZ_COMMON
 enum rq_nohz_flag_bits {
diff -ruw linux-3.11.10/kernel/seccomp.c linux-3.11.10-fbx/kernel/seccomp.c
--- linux-3.11.10/kernel/seccomp.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/seccomp.c	2015-09-29 11:15:08.881755591 +0200
@@ -30,34 +30,6 @@
 #include <linux/tracehook.h>
 #include <linux/uaccess.h>
 
-/**
- * struct seccomp_filter - container for seccomp BPF programs
- *
- * @usage: reference count to manage the object lifetime.
- *         get/put helpers should be used when accessing an instance
- *         outside of a lifetime-guarded section.  In general, this
- *         is only needed for handling filters shared across tasks.
- * @prev: points to a previously installed, or inherited, filter
- * @len: the number of instructions in the program
- * @insns: the BPF program instructions to evaluate
- *
- * seccomp_filter objects are organized in a tree linked via the @prev
- * pointer.  For any task, it appears to be a singly-linked list starting
- * with current->seccomp.filter, the most recently attached or inherited filter.
- * However, multiple filters may share a @prev node, by way of fork(), which
- * results in a unidirectional tree existing in memory.  This is similar to
- * how namespaces work.
- *
- * seccomp_filter objects should never be modified after being attached
- * to a task_struct (other than @usage).
- */
-struct seccomp_filter {
-	atomic_t usage;
-	struct seccomp_filter *prev;
-	unsigned short len;  /* Instruction count */
-	struct sock_filter insns[];
-};
-
 /* Limit any path through the tree to 256KB worth of instructions. */
 #define MAX_INSNS_PER_PATH ((1 << 18) / sizeof(struct sock_filter))
 
@@ -213,7 +185,7 @@
 	 * value always takes priority (ignoring the DATA).
 	 */
 	for (f = current->seccomp.filter; f; f = f->prev) {
-		u32 cur_ret = sk_run_filter(NULL, f->insns);
+		u32 cur_ret = f->bpf_func(NULL, f->insns);
 		if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION))
 			ret = cur_ret;
 	}
@@ -275,6 +247,9 @@
 	if (ret)
 		goto fail;
 
+	filter->bpf_func = sk_run_filter;
+	seccomp_jit_compile(filter);
+
 	/*
 	 * If there is an existing filter, make it the prev and don't drop its
 	 * task reference.
@@ -332,6 +307,7 @@
 	while (orig && atomic_dec_and_test(&orig->usage)) {
 		struct seccomp_filter *freeme = orig;
 		orig = orig->prev;
+		seccomp_jit_free(freeme);
 		kfree(freeme);
 	}
 }
diff -ruw linux-3.11.10/kernel/sys.c linux-3.11.10-fbx/kernel/sys.c
--- linux-3.11.10/kernel/sys.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/sys.c	2015-09-29 17:19:37.789978400 +0200
@@ -1963,6 +1963,10 @@
 			else
 				return -EINVAL;
 			break;
+		case PR_GET_NO_NEW_PRIVS:
+			if (arg2 || arg3 || arg4 || arg5)
+				return -EINVAL;
+			return current->no_new_privs ? 1 : 0;
 		default:
 			return -EINVAL;
 		}
@@ -1999,6 +2003,18 @@
 		if (arg2 || arg3 || arg4 || arg5)
 			return -EINVAL;
 		return current->no_new_privs ? 1 : 0;
+	case PR_SET_EXEC_MODE:
+		if (arg2 != EXEC_MODE_UNLIMITED &&
+		    arg2 != EXEC_MODE_ONCE &&
+		    arg2 != EXEC_MODE_DENIED)
+			return -EINVAL;
+
+		if (arg2 > current->exec_mode)
+			return -EPERM;
+		current->exec_mode = arg2;
+		return 0;
+	case PR_GET_EXEC_MODE:
+		return current->exec_mode;
 	default:
 		error = -EINVAL;
 		break;
diff -ruw linux-3.11.10/kernel/sysctl.c linux-3.11.10-fbx/kernel/sysctl.c
--- linux-3.11.10/kernel/sysctl.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/sysctl.c	2015-09-29 11:15:08.885755628 +0200
@@ -138,11 +138,15 @@
 /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
 static int maxolduid = 65535;
 static int minolduid;
-static int min_percpu_pagelist_fract = 8;
 
 static int ngroups_max = NGROUPS_MAX;
 static const int cap_last_cap = CAP_LAST_CAP;
 
+/*this is needed for proc_doulongvec_minmax of sysctl_hung_task_timeout_secs */
+#ifdef CONFIG_DETECT_HUNG_TASK
+static unsigned long hung_task_timeout_max = (LONG_MAX/HZ);
+#endif
+
 #ifdef CONFIG_INOTIFY_USER
 #include <linux/inotify.h>
 #endif
@@ -972,6 +976,7 @@
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_dohung_task_timeout_secs,
+		.extra2		= &hung_task_timeout_max,
 	},
 	{
 		.procname	= "hung_task_warnings",
@@ -1049,6 +1054,7 @@
 		.maxlen		= sizeof(sysctl_perf_event_sample_rate),
 		.mode		= 0644,
 		.proc_handler	= perf_proc_update_handler,
+		.extra1		= &one,
 	},
 	{
 		.procname	= "perf_cpu_time_max_percent",
@@ -1286,7 +1292,7 @@
 		.maxlen		= sizeof(percpu_pagelist_fraction),
 		.mode		= 0644,
 		.proc_handler	= percpu_pagelist_fraction_sysctl_handler,
-		.extra1		= &min_percpu_pagelist_fract,
+		.extra1		= &zero,
 	},
 #ifdef CONFIG_MMU
 	{
diff -ruw linux-3.11.10/kernel/time/alarmtimer.c linux-3.11.10-fbx/kernel/time/alarmtimer.c
--- linux-3.11.10/kernel/time/alarmtimer.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/time/alarmtimer.c	2015-09-29 11:15:08.885755628 +0200
@@ -490,7 +490,7 @@
 	clockid_t baseid = alarm_bases[clock2alarm(which_clock)].base_clockid;
 
 	if (!alarmtimer_get_rtcdev())
-		return -ENOTSUPP;
+		return -EINVAL;
 
 	return hrtimer_get_res(baseid, tp);
 }
@@ -507,7 +507,7 @@
 	struct alarm_base *base = &alarm_bases[clock2alarm(which_clock)];
 
 	if (!alarmtimer_get_rtcdev())
-		return -ENOTSUPP;
+		return -EINVAL;
 
 	*tp = ktime_to_timespec(base->gettime());
 	return 0;
diff -ruw linux-3.11.10/kernel/time/jiffies.c linux-3.11.10-fbx/kernel/time/jiffies.c
--- linux-3.11.10/kernel/time/jiffies.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/time/jiffies.c	2015-09-29 11:15:08.885755628 +0200
@@ -51,7 +51,13 @@
  * HZ shrinks, so values greater than 8 overflow 32bits when
  * HZ=100.
  */
+#if HZ < 34
+#define JIFFIES_SHIFT	6
+#elif HZ < 67
+#define JIFFIES_SHIFT	7
+#else
 #define JIFFIES_SHIFT	8
+#endif
 
 static cycle_t jiffies_read(struct clocksource *cs)
 {
diff -ruw linux-3.11.10/kernel/time/ntp.c linux-3.11.10-fbx/kernel/time/ntp.c
--- linux-3.11.10/kernel/time/ntp.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/time/ntp.c	2015-09-29 11:15:08.885755628 +0200
@@ -475,6 +475,7 @@
 	 * called as close as possible to 500 ms before the new second starts.
 	 * This code is run on a timer.  If the clock is set, that timer
 	 * may not expire at the correct time.  Thus, we adjust...
+	 * We want the clock to be within a couple of ticks from the target.
 	 */
 	if (!ntp_synced()) {
 		/*
@@ -485,7 +486,7 @@
 	}
 
 	getnstimeofday(&now);
-	if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2) {
+	if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec * 5) {
 		struct timespec adjust = now;
 
 		fail = -ENODEV;
diff -ruw linux-3.11.10/kernel/time/tick-common.c linux-3.11.10-fbx/kernel/time/tick-common.c
--- linux-3.11.10/kernel/time/tick-common.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/time/tick-common.c	2015-09-29 11:15:08.889755651 +0200
@@ -260,7 +260,7 @@
 bool tick_check_replacement(struct clock_event_device *curdev,
 			    struct clock_event_device *newdev)
 {
-	if (tick_check_percpu(curdev, newdev, smp_processor_id()))
+	if (!tick_check_percpu(curdev, newdev, smp_processor_id()))
 		return false;
 
 	return tick_check_preferred(curdev, newdev);
diff -ruw linux-3.11.10/kernel/time/tick-sched.c linux-3.11.10-fbx/kernel/time/tick-sched.c
--- linux-3.11.10/kernel/time/tick-sched.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/time/tick-sched.c	2015-09-29 11:15:08.889755651 +0200
@@ -714,8 +714,10 @@
 		return false;
 	}
 
-	if (unlikely(ts->nohz_mode == NOHZ_MODE_INACTIVE))
+	if (unlikely(ts->nohz_mode == NOHZ_MODE_INACTIVE)) {
+		ts->sleep_length = (ktime_t) { .tv64 = NSEC_PER_SEC/HZ };
 		return false;
+	}
 
 	if (need_resched())
 		return false;
diff -ruw linux-3.11.10/kernel/time/timekeeping.c linux-3.11.10-fbx/kernel/time/timekeeping.c
--- linux-3.11.10/kernel/time/timekeeping.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/time/timekeeping.c	2015-09-29 11:15:08.889755651 +0200
@@ -77,7 +77,7 @@
 	tk->wall_to_monotonic = wtm;
 	set_normalized_timespec(&tmp, -wtm.tv_sec, -wtm.tv_nsec);
 	tk->offs_real = timespec_to_ktime(tmp);
-	tk->offs_tai = ktime_sub(tk->offs_real, ktime_set(tk->tai_offset, 0));
+	tk->offs_tai = ktime_add(tk->offs_real, ktime_set(tk->tai_offset, 0));
 }
 
 static void tk_set_sleep_time(struct timekeeper *tk, struct timespec t)
@@ -595,7 +595,7 @@
 static void __timekeeping_set_tai_offset(struct timekeeper *tk, s32 tai_offset)
 {
 	tk->tai_offset = tai_offset;
-	tk->offs_tai = ktime_sub(tk->offs_real, ktime_set(tai_offset, 0));
+	tk->offs_tai = ktime_add(tk->offs_real, ktime_set(tai_offset, 0));
 }
 
 /**
@@ -610,6 +610,7 @@
 	raw_spin_lock_irqsave(&timekeeper_lock, flags);
 	write_seqcount_begin(&timekeeper_seq);
 	__timekeeping_set_tai_offset(tk, tai_offset);
+	timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET);
 	write_seqcount_end(&timekeeper_seq);
 	raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
 	clock_was_set();
@@ -1023,6 +1024,8 @@
 		timekeeping_suspend_time =
 			timespec_add(timekeeping_suspend_time, delta_delta);
 	}
+
+	timekeeping_update(tk, TK_MIRROR);
 	write_seqcount_end(&timekeeper_seq);
 	raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
 
@@ -1255,7 +1258,7 @@
 static inline unsigned int accumulate_nsecs_to_secs(struct timekeeper *tk)
 {
 	u64 nsecps = (u64)NSEC_PER_SEC << tk->shift;
-	unsigned int action = 0;
+	unsigned int clock_set = 0;
 
 	while (tk->xtime_nsec >= nsecps) {
 		int leap;
@@ -1277,11 +1280,10 @@
 
 			__timekeeping_set_tai_offset(tk, tk->tai_offset - leap);
 
-			clock_was_set_delayed();
-			action = TK_CLOCK_WAS_SET;
+			clock_set = TK_CLOCK_WAS_SET;
 		}
 	}
-	return action;
+	return clock_set;
 }
 
 /**
@@ -1294,7 +1296,8 @@
  * Returns the unconsumed cycles.
  */
 static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset,
-						u32 shift)
+						u32 shift,
+						unsigned int *clock_set)
 {
 	cycle_t interval = tk->cycle_interval << shift;
 	u64 raw_nsecs;
@@ -1308,7 +1311,7 @@
 	tk->cycle_last += interval;
 
 	tk->xtime_nsec += tk->xtime_interval << shift;
-	accumulate_nsecs_to_secs(tk);
+	*clock_set |= accumulate_nsecs_to_secs(tk);
 
 	/* Accumulate raw time */
 	raw_nsecs = (u64)tk->raw_interval << shift;
@@ -1347,7 +1350,7 @@
 	tk->xtime_nsec -= remainder;
 	tk->xtime_nsec += 1ULL << tk->shift;
 	tk->ntp_error += remainder << tk->ntp_error_shift;
-
+	tk->ntp_error -= (1ULL << tk->shift) << tk->ntp_error_shift;
 }
 #else
 #define old_vsyscall_fixup(tk)
@@ -1366,7 +1369,7 @@
 	struct timekeeper *tk = &shadow_timekeeper;
 	cycle_t offset;
 	int shift = 0, maxshift;
-	unsigned int action;
+	unsigned int clock_set = 0;
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&timekeeper_lock, flags);
@@ -1401,7 +1404,8 @@
 	maxshift = (64 - (ilog2(ntp_tick_length())+1)) - 1;
 	shift = min(shift, maxshift);
 	while (offset >= tk->cycle_interval) {
-		offset = logarithmic_accumulation(tk, offset, shift);
+		offset = logarithmic_accumulation(tk, offset, shift,
+							&clock_set);
 		if (offset < tk->cycle_interval<<shift)
 			shift--;
 	}
@@ -1419,7 +1423,7 @@
 	 * Finally, make sure that after the rounding
 	 * xtime_nsec isn't larger than NSEC_PER_SEC
 	 */
-	action = accumulate_nsecs_to_secs(tk);
+	clock_set |= accumulate_nsecs_to_secs(tk);
 
 	write_seqcount_begin(&timekeeper_seq);
 	/* Update clock->cycle_last with the new value */
@@ -1435,10 +1439,23 @@
 	 * updating.
 	 */
 	memcpy(real_tk, tk, sizeof(*tk));
-	timekeeping_update(real_tk, action);
+	timekeeping_update(real_tk, clock_set);
 	write_seqcount_end(&timekeeper_seq);
 out:
 	raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
+	if (clock_set) {
+		/*
+		 * XXX -  I'd rather we just call clock_was_set(), but
+		 * since we're currently holding the jiffies lock, calling
+		 * clock_was_set would trigger an ipi which would then grab
+		 * the jiffies lock and we'd deadlock. :(
+		 * The right solution should probably be droping
+		 * the jiffies lock before calling update_wall_time
+		 * but that requires some rework of the tick sched
+		 * code.
+		 */
+		clock_was_set_delayed();
+	}
 }
 
 /**
@@ -1697,12 +1714,14 @@
 
 	if (tai != orig_tai) {
 		__timekeeping_set_tai_offset(tk, tai);
-		update_pvclock_gtod(tk, true);
-		clock_was_set_delayed();
+		timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET);
 	}
 	write_seqcount_end(&timekeeper_seq);
 	raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
 
+	if (tai != orig_tai)
+		clock_was_set();
+
 	ntp_notify_cmos_timer();
 
 	return ret;
diff -ruw linux-3.11.10/kernel/timer.c linux-3.11.10-fbx/kernel/timer.c
--- linux-3.11.10/kernel/timer.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/timer.c	2015-09-29 11:15:08.889755651 +0200
@@ -822,7 +822,7 @@
 
 	bit = find_last_bit(&mask, BITS_PER_LONG);
 
-	mask = (1 << bit) - 1;
+	mask = (1UL << bit) - 1;
 
 	expires_limit = expires_limit & ~(mask);
 
diff -ruw linux-3.11.10/kernel/workqueue.c linux-3.11.10-fbx/kernel/workqueue.c
--- linux-3.11.10/kernel/workqueue.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/kernel/workqueue.c	2015-09-29 11:15:08.901755747 +0200
@@ -304,6 +304,9 @@
 /* I: attributes used when instantiating standard unbound pools on demand */
 static struct workqueue_attrs *unbound_std_wq_attrs[NR_STD_WORKER_POOLS];
 
+/* I: attributes used when instantiating ordered pools on demand */
+static struct workqueue_attrs *ordered_wq_attrs[NR_STD_WORKER_POOLS];
+
 struct workqueue_struct *system_wq __read_mostly;
 EXPORT_SYMBOL(system_wq);
 struct workqueue_struct *system_highpri_wq __read_mostly;
@@ -1833,6 +1836,12 @@
 	if (worker->flags & WORKER_IDLE)
 		pool->nr_idle--;
 
+	/*
+	 * Once WORKER_DIE is set, the kworker may destroy itself at any
+	 * point.  Pin to ensure the task stays until we're done with it.
+	 */
+	get_task_struct(worker->task);
+
 	list_del_init(&worker->entry);
 	worker->flags |= WORKER_DIE;
 
@@ -1841,6 +1850,7 @@
 	spin_unlock_irq(&pool->lock);
 
 	kthread_stop(worker->task);
+	put_task_struct(worker->task);
 	kfree(worker);
 
 	spin_lock_irq(&pool->lock);
@@ -1884,6 +1894,12 @@
 
 	/* mayday mayday mayday */
 	if (list_empty(&pwq->mayday_node)) {
+		/*
+		 * If @pwq is for an unbound wq, its base ref may be put at
+		 * any time due to an attribute change.  Pin @pwq until the
+		 * rescuer is done with it.
+		 */
+		get_pwq(pwq);
 		list_add_tail(&pwq->mayday_node, &wq->maydays);
 		wake_up_process(wq->rescuer->task);
 	}
@@ -2359,6 +2375,7 @@
 	struct worker *rescuer = __rescuer;
 	struct workqueue_struct *wq = rescuer->rescue_wq;
 	struct list_head *scheduled = &rescuer->scheduled;
+	bool should_stop;
 
 	set_user_nice(current, RESCUER_NICE_LEVEL);
 
@@ -2370,11 +2387,15 @@
 repeat:
 	set_current_state(TASK_INTERRUPTIBLE);
 
-	if (kthread_should_stop()) {
-		__set_current_state(TASK_RUNNING);
-		rescuer->task->flags &= ~PF_WQ_WORKER;
-		return 0;
-	}
+	/*
+	 * By the time the rescuer is requested to stop, the workqueue
+	 * shouldn't have any work pending, but @wq->maydays may still have
+	 * pwq(s) queued.  This can happen by non-rescuer workers consuming
+	 * all the work items before the rescuer got to them.  Go through
+	 * @wq->maydays processing before acting on should_stop so that the
+	 * list is always empty on exit.
+	 */
+	should_stop = kthread_should_stop();
 
 	/* see whether any pwq is asking for help */
 	spin_lock_irq(&wq_mayday_lock);
@@ -2406,6 +2427,12 @@
 		process_scheduled_works(rescuer);
 
 		/*
+		 * Put the reference grabbed by send_mayday().  @pool won't
+		 * go away while we're holding its lock.
+		 */
+		put_pwq(pwq);
+
+		/*
 		 * Leave this pool.  If keep_working() is %true, notify a
 		 * regular worker; otherwise, we end up with 0 concurrency
 		 * and stalling the execution.
@@ -2420,6 +2447,12 @@
 
 	spin_unlock_irq(&wq_mayday_lock);
 
+	if (should_stop) {
+		__set_current_state(TASK_RUNNING);
+		rescuer->task->flags &= ~PF_WQ_WORKER;
+		return 0;
+	}
+
 	/* rescuers should never participate in concurrency management */
 	WARN_ON_ONCE(!(rescuer->flags & WORKER_NOT_RUNNING));
 	schedule();
@@ -3358,6 +3391,7 @@
 		}
 	}
 
+	dev_set_uevent_suppress(&wq_dev->dev, false);
 	kobject_uevent(&wq_dev->dev.kobj, KOBJ_ADD);
 	return 0;
 }
@@ -4051,7 +4085,8 @@
 	if (!pwq) {
 		pr_warning("workqueue: allocation failed while updating NUMA affinity of \"%s\"\n",
 			   wq->name);
-		goto out_unlock;
+		mutex_lock(&wq->mutex);
+		goto use_dfl_pwq;
 	}
 
 	/*
@@ -4077,7 +4112,7 @@
 static int alloc_and_link_pwqs(struct workqueue_struct *wq)
 {
 	bool highpri = wq->flags & WQ_HIGHPRI;
-	int cpu;
+	int cpu, ret;
 
 	if (!(wq->flags & WQ_UNBOUND)) {
 		wq->cpu_pwqs = alloc_percpu(struct pool_workqueue);
@@ -4097,6 +4132,13 @@
 			mutex_unlock(&wq->mutex);
 		}
 		return 0;
+	} else if (wq->flags & __WQ_ORDERED) {
+		ret = apply_workqueue_attrs(wq, ordered_wq_attrs[highpri]);
+		/* there should only be single pwq for ordering guarantee */
+		WARN(!ret && (wq->pwqs.next != &wq->dfl_pwq->pwqs_node ||
+			      wq->pwqs.prev != &wq->dfl_pwq->pwqs_node),
+		     "ordering guarantee broken for workqueue %s\n", wq->name);
+		return ret;
 	} else {
 		return apply_workqueue_attrs(wq, unbound_std_wq_attrs[highpri]);
 	}
@@ -4955,7 +4997,7 @@
 	BUG_ON(!tbl);
 
 	for_each_node(node)
-		BUG_ON(!alloc_cpumask_var_node(&tbl[node], GFP_KERNEL,
+		BUG_ON(!zalloc_cpumask_var_node(&tbl[node], GFP_KERNEL,
 				node_online(node) ? node : NUMA_NO_NODE));
 
 	for_each_possible_cpu(cpu) {
@@ -5019,13 +5061,23 @@
 		}
 	}
 
-	/* create default unbound wq attrs */
+	/* create default unbound and ordered wq attrs */
 	for (i = 0; i < NR_STD_WORKER_POOLS; i++) {
 		struct workqueue_attrs *attrs;
 
 		BUG_ON(!(attrs = alloc_workqueue_attrs(GFP_KERNEL)));
 		attrs->nice = std_nice[i];
 		unbound_std_wq_attrs[i] = attrs;
+
+		/*
+		 * An ordered wq should have only one pwq as ordering is
+		 * guaranteed by max_active which is enforced by pwqs.
+		 * Turn off NUMA so that dfl_pwq is used for all nodes.
+		 */
+		BUG_ON(!(attrs = alloc_workqueue_attrs(GFP_KERNEL)));
+		attrs->nice = std_nice[i];
+		attrs->no_numa = true;
+		ordered_wq_attrs[i] = attrs;
 	}
 
 	system_wq = alloc_workqueue("events", 0, 0);
diff -ruw linux-3.11.10/lib/decompress_inflate.c linux-3.11.10-fbx/lib/decompress_inflate.c
--- linux-3.11.10/lib/decompress_inflate.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/lib/decompress_inflate.c	2015-09-29 11:15:08.905755777 +0200
@@ -48,7 +48,7 @@
 		out_len = 0x8000; /* 32 K */
 		out_buf = malloc(out_len);
 	} else {
-		out_len = 0x7fffffff; /* no limit */
+		out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */
 	}
 	if (!out_buf) {
 		error("Out of memory while allocating output buffer");
diff -ruw linux-3.11.10/lib/idr.c linux-3.11.10-fbx/lib/idr.c
--- linux-3.11.10/lib/idr.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/lib/idr.c	2015-09-29 11:15:08.909755812 +0200
@@ -250,7 +250,7 @@
 			id = (id | ((1 << (IDR_BITS * l)) - 1)) + 1;
 
 			/* if already at the top layer, we need to grow */
-			if (id >= 1 << (idp->layers * IDR_BITS)) {
+			if (id > idr_max(idp->layers)) {
 				*starting_id = id;
 				return -EAGAIN;
 			}
@@ -827,12 +827,10 @@
 	if (!p)
 		return ERR_PTR(-EINVAL);
 
-	n = (p->layer+1) * IDR_BITS;
-
-	if (id >= (1 << n))
+	if (id > idr_max(p->layer + 1))
 		return ERR_PTR(-EINVAL);
 
-	n -= IDR_BITS;
+	n = p->layer * IDR_BITS;
 	while ((n > 0) && p) {
 		p = p->ary[(id >> n) & IDR_MASK];
 		n -= IDR_BITS;
diff -ruw linux-3.11.10/lib/Kconfig linux-3.11.10-fbx/lib/Kconfig
--- linux-3.11.10/lib/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/lib/Kconfig	2015-09-29 11:15:08.901755747 +0200
@@ -425,4 +425,8 @@
 
 source "lib/fonts/Kconfig"
 
+config FBXSERIAL
+	bool
+	select CRC32
+
 endmenu
diff -ruw linux-3.11.10/lib/lzo/lzo1x_decompress_safe.c linux-3.11.10-fbx/lib/lzo/lzo1x_decompress_safe.c
--- linux-3.11.10/lib/lzo/lzo1x_decompress_safe.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/lib/lzo/lzo1x_decompress_safe.c	2015-09-29 11:15:08.909755812 +0200
@@ -19,11 +19,31 @@
 #include <linux/lzo.h>
 #include "lzodefs.h"
 
-#define HAVE_IP(x)      ((size_t)(ip_end - ip) >= (size_t)(x))
-#define HAVE_OP(x)      ((size_t)(op_end - op) >= (size_t)(x))
-#define NEED_IP(x)      if (!HAVE_IP(x)) goto input_overrun
-#define NEED_OP(x)      if (!HAVE_OP(x)) goto output_overrun
-#define TEST_LB(m_pos)  if ((m_pos) < out) goto lookbehind_overrun
+#define HAVE_IP(t, x)					\
+	(((size_t)(ip_end - ip) >= (size_t)(t + x)) &&	\
+	 (((t + x) >= t) && ((t + x) >= x)))
+
+#define HAVE_OP(t, x)					\
+	(((size_t)(op_end - op) >= (size_t)(t + x)) &&	\
+	 (((t + x) >= t) && ((t + x) >= x)))
+
+#define NEED_IP(t, x)					\
+	do {						\
+		if (!HAVE_IP(t, x))			\
+			goto input_overrun;		\
+	} while (0)
+
+#define NEED_OP(t, x)					\
+	do {						\
+		if (!HAVE_OP(t, x))			\
+			goto output_overrun;		\
+	} while (0)
+
+#define TEST_LB(m_pos)					\
+	do {						\
+		if ((m_pos) < out)			\
+			goto lookbehind_overrun;	\
+	} while (0)
 
 int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
 			  unsigned char *out, size_t *out_len)
@@ -58,14 +78,14 @@
 					while (unlikely(*ip == 0)) {
 						t += 255;
 						ip++;
-						NEED_IP(1);
+						NEED_IP(1, 0);
 					}
 					t += 15 + *ip++;
 				}
 				t += 3;
 copy_literal_run:
 #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
-				if (likely(HAVE_IP(t + 15) && HAVE_OP(t + 15))) {
+				if (likely(HAVE_IP(t, 15) && HAVE_OP(t, 15))) {
 					const unsigned char *ie = ip + t;
 					unsigned char *oe = op + t;
 					do {
@@ -81,8 +101,8 @@
 				} else
 #endif
 				{
-					NEED_OP(t);
-					NEED_IP(t + 3);
+					NEED_OP(t, 0);
+					NEED_IP(t, 3);
 					do {
 						*op++ = *ip++;
 					} while (--t > 0);
@@ -95,7 +115,7 @@
 				m_pos -= t >> 2;
 				m_pos -= *ip++ << 2;
 				TEST_LB(m_pos);
-				NEED_OP(2);
+				NEED_OP(2, 0);
 				op[0] = m_pos[0];
 				op[1] = m_pos[1];
 				op += 2;
@@ -119,10 +139,10 @@
 				while (unlikely(*ip == 0)) {
 					t += 255;
 					ip++;
-					NEED_IP(1);
+					NEED_IP(1, 0);
 				}
 				t += 31 + *ip++;
-				NEED_IP(2);
+				NEED_IP(2, 0);
 			}
 			m_pos = op - 1;
 			next = get_unaligned_le16(ip);
@@ -137,10 +157,10 @@
 				while (unlikely(*ip == 0)) {
 					t += 255;
 					ip++;
-					NEED_IP(1);
+					NEED_IP(1, 0);
 				}
 				t += 7 + *ip++;
-				NEED_IP(2);
+				NEED_IP(2, 0);
 			}
 			next = get_unaligned_le16(ip);
 			ip += 2;
@@ -154,7 +174,7 @@
 #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
 		if (op - m_pos >= 8) {
 			unsigned char *oe = op + t;
-			if (likely(HAVE_OP(t + 15))) {
+			if (likely(HAVE_OP(t, 15))) {
 				do {
 					COPY8(op, m_pos);
 					op += 8;
@@ -164,7 +184,7 @@
 					m_pos += 8;
 				} while (op < oe);
 				op = oe;
-				if (HAVE_IP(6)) {
+				if (HAVE_IP(6, 0)) {
 					state = next;
 					COPY4(op, ip);
 					op += next;
@@ -172,7 +192,7 @@
 					continue;
 				}
 			} else {
-				NEED_OP(t);
+				NEED_OP(t, 0);
 				do {
 					*op++ = *m_pos++;
 				} while (op < oe);
@@ -181,7 +201,7 @@
 #endif
 		{
 			unsigned char *oe = op + t;
-			NEED_OP(t);
+			NEED_OP(t, 0);
 			op[0] = m_pos[0];
 			op[1] = m_pos[1];
 			op += 2;
@@ -194,15 +214,15 @@
 		state = next;
 		t = next;
 #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
-		if (likely(HAVE_IP(6) && HAVE_OP(4))) {
+		if (likely(HAVE_IP(6, 0) && HAVE_OP(4, 0))) {
 			COPY4(op, ip);
 			op += t;
 			ip += t;
 		} else
 #endif
 		{
-			NEED_IP(t + 3);
-			NEED_OP(t);
+			NEED_IP(t, 3);
+			NEED_OP(t, 0);
 			while (t > 0) {
 				*op++ = *ip++;
 				t--;
diff -ruw linux-3.11.10/lib/Makefile linux-3.11.10-fbx/lib/Makefile
--- linux-3.11.10/lib/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/lib/Makefile	2015-09-29 11:15:08.901755747 +0200
@@ -45,6 +45,7 @@
 lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
 lib-$(CONFIG_PERCPU_RWSEM) += percpu-rwsem.o
 
+GCOV_PROFILE_hweight.o := n
 CFLAGS_hweight.o = $(subst $(quote),,$(CONFIG_ARCH_HWEIGHT_CFLAGS))
 obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
 
@@ -95,6 +96,8 @@
 obj-$(CONFIG_SMP) += percpu_counter.o
 obj-$(CONFIG_AUDIT_GENERIC) += audit.o
 
+obj-$(CONFIG_FBXSERIAL) += fbxserial.o
+
 obj-$(CONFIG_SWIOTLB) += swiotlb.o
 obj-$(CONFIG_IOMMU_HELPER) += iommu-helper.o
 obj-$(CONFIG_FAULT_INJECTION) += fault-inject.o
diff -ruw linux-3.11.10/lib/nlattr.c linux-3.11.10-fbx/lib/nlattr.c
--- linux-3.11.10/lib/nlattr.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/lib/nlattr.c	2015-09-29 11:15:08.913755834 +0200
@@ -201,8 +201,8 @@
 	}
 
 	if (unlikely(rem > 0))
-		printk(KERN_WARNING "netlink: %d bytes leftover after parsing "
-		       "attributes.\n", rem);
+		pr_warn_ratelimited("netlink: %d bytes leftover after parsing attributes in process `%s'.\n",
+				    rem, current->comm);
 
 	err = 0;
 errout:
@@ -303,9 +303,15 @@
  */
 int nla_strcmp(const struct nlattr *nla, const char *str)
 {
-	int len = strlen(str) + 1;
-	int d = nla_len(nla) - len;
+	int len = strlen(str);
+	char *buf = nla_data(nla);
+	int attrlen = nla_len(nla);
+	int d;
 
+	if (attrlen > 0 && buf[attrlen - 1] == '\0')
+		attrlen--;
+
+	d = attrlen - len;
 	if (d == 0)
 		d = memcmp(nla_data(nla), str, len);
 
diff -ruw linux-3.11.10/lib/random32.c linux-3.11.10-fbx/lib/random32.c
--- linux-3.11.10/lib/random32.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/lib/random32.c	2015-09-29 11:15:08.913755834 +0200
@@ -141,7 +141,7 @@
 	 */
 	for_each_possible_cpu (i) {
 		struct rnd_state *state = &per_cpu(net_rand_state, i);
-		state->s1 = __seed(state->s1 ^ entropy, 1);
+		state->s1 = __seed(state->s1 ^ entropy, 2);
 	}
 }
 EXPORT_SYMBOL(prandom_seed);
@@ -158,9 +158,9 @@
 		struct rnd_state *state = &per_cpu(net_rand_state,i);
 
 #define LCG(x)	((x) * 69069)	/* super-duper LCG */
-		state->s1 = __seed(LCG(i + jiffies), 1);
-		state->s2 = __seed(LCG(state->s1), 7);
-		state->s3 = __seed(LCG(state->s2), 15);
+		state->s1 = __seed(LCG(i + jiffies), 2);
+		state->s2 = __seed(LCG(state->s1), 8);
+		state->s3 = __seed(LCG(state->s2), 16);
 
 		/* "warm it up" */
 		prandom_u32_state(state);
@@ -187,9 +187,9 @@
 		u32 seeds[3];
 
 		get_random_bytes(&seeds, sizeof(seeds));
-		state->s1 = __seed(seeds[0], 1);
-		state->s2 = __seed(seeds[1], 7);
-		state->s3 = __seed(seeds[2], 15);
+		state->s1 = __seed(seeds[0], 2);
+		state->s2 = __seed(seeds[1], 8);
+		state->s3 = __seed(seeds[2], 16);
 
 		/* mix it in */
 		prandom_u32_state(state);
diff -ruw linux-3.11.10/lib/vsprintf.c linux-3.11.10-fbx/lib/vsprintf.c
--- linux-3.11.10/lib/vsprintf.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/lib/vsprintf.c	2015-09-29 11:15:08.917755866 +0200
@@ -26,6 +26,7 @@
 #include <linux/math64.h>
 #include <linux/uaccess.h>
 #include <linux/ioport.h>
+#include <linux/cred.h>
 #include <net/addrconf.h>
 
 #include <asm/page.h>		/* for PAGE_SIZE */
@@ -1236,11 +1237,37 @@
 				spec.field_width = default_width;
 			return string(buf, end, "pK-error", spec);
 		}
-		if (!((kptr_restrict == 0) ||
-		      (kptr_restrict == 1 &&
-		       has_capability_noaudit(current, CAP_SYSLOG))))
+
+		switch (kptr_restrict) {
+		case 0:
+			/* Always print %pK values */
+			break;
+		case 1: {
+			/*
+			 * Only print the real pointer value if the current
+			 * process has CAP_SYSLOG and is running with the
+			 * same credentials it started with. This is because
+			 * access to files is checked at open() time, but %pK
+			 * checks permission at read() time. We don't want to
+			 * leak pointer values if a binary opens a file using
+			 * %pK and then elevates privileges before reading it.
+			 */
+			const struct cred *cred = current_cred();
+
+			if (!has_capability_noaudit(current, CAP_SYSLOG) ||
+			    !uid_eq(cred->euid, cred->uid) ||
+			    !gid_eq(cred->egid, cred->gid))
+				ptr = NULL;
+			break;
+		}
+		case 2:
+		default:
+			/* Always print 0's for %pK */
 			ptr = NULL;
 		break;
+		}
+		break;
+
 	case 'N':
 		switch (fmt[1]) {
 		case 'F':
diff -ruw linux-3.11.10/Makefile linux-3.11.10-fbx/Makefile
--- linux-3.11.10/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/Makefile	2015-09-29 17:24:52.816409339 +0200
@@ -1,7 +1,7 @@
 VERSION = 3
 PATCHLEVEL = 11
 SUBLEVEL = 10
-EXTRAVERSION =
+EXTRAVERSION = .14
 NAME = Linux for Workgroups
 
 # *DOCUMENTATION*
diff -ruw linux-3.11.10/mm/backing-dev.c linux-3.11.10-fbx/mm/backing-dev.c
--- linux-3.11.10/mm/backing-dev.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/backing-dev.c	2015-09-29 11:15:08.917755866 +0200
@@ -285,13 +285,19 @@
  * Note, we wouldn't bother setting up the timer, but this function is on the
  * fast-path (used by '__mark_inode_dirty()'), so we save few context switches
  * by delaying the wake-up.
+ *
+ * We have to be careful not to postpone flush work if it is scheduled for
+ * earlier. Thus we use queue_delayed_work().
  */
 void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi)
 {
 	unsigned long timeout;
 
 	timeout = msecs_to_jiffies(dirty_writeback_interval * 10);
-	mod_delayed_work(bdi_wq, &bdi->wb.dwork, timeout);
+	spin_lock_bh(&bdi->wb_lock);
+	if (test_bit(BDI_registered, &bdi->state))
+		queue_delayed_work(bdi_wq, &bdi->wb.dwork, timeout);
+	spin_unlock_bh(&bdi->wb_lock);
 }
 
 /*
@@ -304,9 +310,6 @@
 	spin_unlock_bh(&bdi_lock);
 
 	synchronize_rcu_expedited();
-
-	/* bdi_list is now unused, clear it to mark @bdi dying */
-	INIT_LIST_HEAD(&bdi->bdi_list);
 }
 
 int bdi_register(struct backing_dev_info *bdi, struct device *parent,
@@ -357,6 +360,11 @@
 	 */
 	bdi_remove_from_list(bdi);
 
+	/* Make sure nobody queues further work */
+	spin_lock_bh(&bdi->wb_lock);
+	clear_bit(BDI_registered, &bdi->state);
+	spin_unlock_bh(&bdi->wb_lock);
+
 	/*
 	 * Drain work list and shutdown the delayed_work.  At this point,
 	 * @bdi->bdi_list is empty telling bdi_Writeback_workfn() that @bdi
diff -ruw linux-3.11.10/mm/compaction.c linux-3.11.10-fbx/mm/compaction.c
--- linux-3.11.10/mm/compaction.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/compaction.c	2015-09-29 11:15:08.921755902 +0200
@@ -134,6 +134,10 @@
 			bool migrate_scanner)
 {
 	struct zone *zone = cc->zone;
+
+	if (cc->ignore_skip_hint)
+		return;
+
 	if (!page)
 		return;
 
@@ -248,7 +252,6 @@
 {
 	int nr_scanned = 0, total_isolated = 0;
 	struct page *cursor, *valid_page = NULL;
-	unsigned long nr_strict_required = end_pfn - blockpfn;
 	unsigned long flags;
 	bool locked = false;
 
@@ -261,11 +264,12 @@
 
 		nr_scanned++;
 		if (!pfn_valid_within(blockpfn))
-			continue;
+			goto isolate_fail;
+
 		if (!valid_page)
 			valid_page = page;
 		if (!PageBuddy(page))
-			continue;
+			goto isolate_fail;
 
 		/*
 		 * The zone lock must be held to isolate freepages.
@@ -286,12 +290,10 @@
 
 		/* Recheck this is a buddy page under lock */
 		if (!PageBuddy(page))
-			continue;
+			goto isolate_fail;
 
 		/* Found a free page, break it into order-0 pages */
 		isolated = split_free_page(page);
-		if (!isolated && strict)
-			break;
 		total_isolated += isolated;
 		for (i = 0; i < isolated; i++) {
 			list_add(&page->lru, freelist);
@@ -302,7 +304,15 @@
 		if (isolated) {
 			blockpfn += isolated - 1;
 			cursor += isolated - 1;
+			continue;
 		}
+
+isolate_fail:
+		if (strict)
+			break;
+		else
+			continue;
+
 	}
 
 	trace_mm_compaction_isolate_freepages(nr_scanned, total_isolated);
@@ -312,7 +322,7 @@
 	 * pages requested were isolated. If there were any failures, 0 is
 	 * returned and CMA will fail.
 	 */
-	if (strict && nr_strict_required > total_isolated)
+	if (strict && blockpfn < end_pfn)
 		total_isolated = 0;
 
 	if (locked)
@@ -647,17 +657,21 @@
 				struct compact_control *cc)
 {
 	struct page *page;
-	unsigned long high_pfn, low_pfn, pfn, z_end_pfn, end_pfn;
+	unsigned long high_pfn, low_pfn, pfn, z_end_pfn;
 	int nr_freepages = cc->nr_freepages;
 	struct list_head *freelist = &cc->freepages;
 
 	/*
 	 * Initialise the free scanner. The starting point is where we last
-	 * scanned from (or the end of the zone if starting). The low point
-	 * is the end of the pageblock the migration scanner is using.
+	 * successfully isolated from, zone-cached value, or the end of the
+	 * zone when isolating for the first time. We need this aligned to
+	 * the pageblock boundary, because we do pfn -= pageblock_nr_pages
+	 * in the for loop.
+	 * The low boundary is the end of the pageblock the migration scanner
+	 * is using.
 	 */
-	pfn = cc->free_pfn;
-	low_pfn = cc->migrate_pfn + pageblock_nr_pages;
+	pfn = cc->free_pfn & ~(pageblock_nr_pages-1);
+	low_pfn = ALIGN(cc->migrate_pfn + 1, pageblock_nr_pages);
 
 	/*
 	 * Take care that if the migration scanner is at the end of the zone
@@ -673,9 +687,10 @@
 	 * pages on cc->migratepages. We stop searching if the migrate
 	 * and free page scanners meet or enough free pages are isolated.
 	 */
-	for (; pfn > low_pfn && cc->nr_migratepages > nr_freepages;
+	for (; pfn >= low_pfn && cc->nr_migratepages > nr_freepages;
 					pfn -= pageblock_nr_pages) {
 		unsigned long isolated;
+		unsigned long end_pfn;
 
 		if (!pfn_valid(pfn))
 			continue;
@@ -703,13 +718,10 @@
 		isolated = 0;
 
 		/*
-		 * As pfn may not start aligned, pfn+pageblock_nr_page
-		 * may cross a MAX_ORDER_NR_PAGES boundary and miss
-		 * a pfn_valid check. Ensure isolate_freepages_block()
-		 * only scans within a pageblock
+		 * Take care when isolating in last pageblock of a zone which
+		 * ends in the middle of a pageblock.
 		 */
-		end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
-		end_pfn = min(end_pfn, z_end_pfn);
+		end_pfn = min(pfn + pageblock_nr_pages, z_end_pfn);
 		isolated = isolate_freepages_block(cc, pfn, end_pfn,
 						   freelist, false);
 		nr_freepages += isolated;
@@ -728,6 +740,13 @@
 	/* split_free_page does not map the pages */
 	map_pages(freelist);
 
+	/*
+	 * If we crossed the migrate scanner, we want to keep it that way
+	 * so that compact_finished() may detect this
+	 */
+	if (pfn < low_pfn)
+		cc->free_pfn = max(pfn, zone->zone_start_pfn);
+	else
 	cc->free_pfn = high_pfn;
 	cc->nr_freepages = nr_freepages;
 }
@@ -937,6 +956,14 @@
 	}
 
 	/*
+	 * Clear pageblock skip if there were failures recently and compaction
+	 * is about to be retried after being deferred. kswapd does not do
+	 * this reset as it'll reset the cached information when going to sleep.
+	 */
+	if (compaction_restarting(zone, cc->order) && !current_is_kswapd())
+		__reset_isolation_suitable(zone);
+
+	/*
 	 * Setup to move all movable pages to the end of the zone. Used cached
 	 * information on where the scanners should start but check that it
 	 * is initialised by ensuring the values are within zone boundaries.
@@ -952,14 +979,6 @@
 		zone->compact_cached_migrate_pfn = cc->migrate_pfn;
 	}
 
-	/*
-	 * Clear pageblock skip if there were failures recently and compaction
-	 * is about to be retried after being deferred. kswapd does not do
-	 * this reset as it'll reset the cached information when going to sleep.
-	 */
-	if (compaction_restarting(zone, cc->order) && !current_is_kswapd())
-		__reset_isolation_suitable(zone);
-
 	migrate_prep_local();
 
 	while ((ret = compact_finished(zone, cc)) == COMPACT_CONTINUE) {
@@ -993,7 +1012,11 @@
 		if (err) {
 			putback_movable_pages(&cc->migratepages);
 			cc->nr_migratepages = 0;
-			if (err == -ENOMEM) {
+			/*
+			 * migrate_pages() may return -ENOMEM when scanners meet
+			 * and we want compact_finished() to detect it
+			 */
+			if (err == -ENOMEM && cc->free_pfn > cc->migrate_pfn) {
 				ret = COMPACT_PARTIAL;
 				goto out;
 			}
diff -ruw linux-3.11.10/mm/fremap.c linux-3.11.10-fbx/mm/fremap.c
--- linux-3.11.10/mm/fremap.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/fremap.c	2015-09-29 11:15:08.921755902 +0200
@@ -208,9 +208,10 @@
 		if (mapping_cap_account_dirty(mapping)) {
 			unsigned long addr;
 			struct file *file = get_file(vma->vm_file);
+			/* mmap_region may free vma; grab the info now */
+			vm_flags = vma->vm_flags;
 
-			addr = mmap_region(file, start, size,
-					vma->vm_flags, pgoff);
+			addr = mmap_region(file, start, size, vm_flags, pgoff);
 			fput(file);
 			if (IS_ERR_VALUE(addr)) {
 				err = addr;
@@ -218,7 +219,7 @@
 				BUG_ON(addr != start);
 				err = 0;
 			}
-			goto out;
+			goto out_freed;
 		}
 		mutex_lock(&mapping->i_mmap_mutex);
 		flush_dcache_mmap_lock(mapping);
@@ -253,6 +254,7 @@
 out:
 	if (vma)
 		vm_flags = vma->vm_flags;
+out_freed:
 	if (likely(!has_write_lock))
 		up_read(&mm->mmap_sem);
 	else
diff -ruw linux-3.11.10/mm/memory.c linux-3.11.10-fbx/mm/memory.c
--- linux-3.11.10/mm/memory.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/memory.c	2015-09-29 11:15:08.929755957 +0200
@@ -1940,12 +1940,17 @@
 		     unsigned long address, unsigned int fault_flags)
 {
 	struct vm_area_struct *vma;
+	vm_flags_t vm_flags;
 	int ret;
 
 	vma = find_extend_vma(mm, address);
 	if (!vma || address < vma->vm_start)
 		return -EFAULT;
 
+	vm_flags = (fault_flags & FAULT_FLAG_WRITE) ? VM_WRITE : VM_READ;
+	if (!(vm_flags & vma->vm_flags))
+		return -EFAULT;
+
 	ret = handle_mm_fault(mm, vma, address, fault_flags);
 	if (ret & VM_FAULT_ERROR) {
 		if (ret & VM_FAULT_OOM)
diff -ruw linux-3.11.10/mm/mlock.c linux-3.11.10-fbx/mm/mlock.c
--- linux-3.11.10/mm/mlock.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/mlock.c	2015-09-29 11:15:08.929755957 +0200
@@ -76,6 +76,7 @@
  */
 void mlock_vma_page(struct page *page)
 {
+	/* Serialize with page migration */
 	BUG_ON(!PageLocked(page));
 
 	if (!TestSetPageMlocked(page)) {
@@ -106,6 +107,7 @@
 {
 	unsigned int page_mask = 0;
 
+	/* For try_to_munlock() and to serialize with page migration */
 	BUG_ON(!PageLocked(page));
 
 	if (TestClearPageMlocked(page)) {
diff -ruw linux-3.11.10/mm/mmap.c linux-3.11.10-fbx/mm/mmap.c
--- linux-3.11.10/mm/mmap.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/mmap.c	2015-09-29 11:15:08.933755994 +0200
@@ -1855,7 +1855,7 @@
 	struct vm_area_struct *vma;
 	struct vm_unmapped_area_info info;
 
-	if (len > TASK_SIZE)
+	if (len > TASK_SIZE - mmap_min_addr)
 		return -ENOMEM;
 
 	if (flags & MAP_FIXED)
@@ -1864,7 +1864,7 @@
 	if (addr) {
 		addr = PAGE_ALIGN(addr);
 		vma = find_vma(mm, addr);
-		if (TASK_SIZE - len >= addr &&
+		if (TASK_SIZE - len >= addr && addr >= mmap_min_addr &&
 		    (!vma || addr + len <= vma->vm_start))
 			return addr;
 	}
@@ -1894,7 +1894,7 @@
 	struct vm_unmapped_area_info info;
 
 	/* requested length too big for entire address space */
-	if (len > TASK_SIZE)
+	if (len > TASK_SIZE - mmap_min_addr)
 		return -ENOMEM;
 
 	if (flags & MAP_FIXED)
@@ -1904,14 +1904,14 @@
 	if (addr) {
 		addr = PAGE_ALIGN(addr);
 		vma = find_vma(mm, addr);
-		if (TASK_SIZE - len >= addr &&
+		if (TASK_SIZE - len >= addr && addr >= mmap_min_addr &&
 				(!vma || addr + len <= vma->vm_start))
 			return addr;
 	}
 
 	info.flags = VM_UNMAPPED_AREA_TOPDOWN;
 	info.length = len;
-	info.low_limit = PAGE_SIZE;
+	info.low_limit = max(PAGE_SIZE, mmap_min_addr);
 	info.high_limit = mm->mmap_base;
 	info.align_mask = 0;
 	addr = vm_unmapped_area(&info);
diff -ruw linux-3.11.10/mm/mprotect.c linux-3.11.10-fbx/mm/mprotect.c
--- linux-3.11.10/mm/mprotect.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/mprotect.c	2015-09-29 11:15:08.933755994 +0200
@@ -54,13 +54,16 @@
 			pte_t ptent;
 			bool updated = false;
 
-			ptent = ptep_modify_prot_start(mm, addr, pte);
 			if (!prot_numa) {
+				ptent = ptep_modify_prot_start(mm, addr, pte);
+				if (pte_numa(ptent))
+					ptent = pte_mknonnuma(ptent);
 				ptent = pte_modify(ptent, newprot);
 				updated = true;
 			} else {
 				struct page *page;
 
+				ptent = *pte;
 				page = vm_normal_page(vma, addr, oldpte);
 				if (page) {
 					int this_nid = page_to_nid(page);
@@ -73,6 +76,7 @@
 					if (!pte_numa(oldpte) &&
 					    page_mapcount(page) == 1) {
 						ptent = pte_mknuma(ptent);
+						set_pte_at(mm, addr, pte, ptent);
 						updated = true;
 					}
 				}
@@ -89,6 +93,9 @@
 
 			if (updated)
 				pages++;
+
+			/* Only !prot_numa always clears the pte */
+			if (!prot_numa)
 			ptep_modify_prot_commit(mm, addr, pte, ptent);
 		} else if (IS_ENABLED(CONFIG_MIGRATION) && !pte_file(oldpte)) {
 			swp_entry_t entry = pte_to_swp_entry(oldpte);
@@ -138,6 +145,7 @@
 	pmd_t *pmd;
 	unsigned long next;
 	unsigned long pages = 0;
+	unsigned long nr_huge_updates = 0;
 	bool all_same_node;
 
 	pmd = pmd_offset(pud, addr);
@@ -148,7 +156,8 @@
 				split_huge_page_pmd(vma, addr, pmd);
 			else if (change_huge_pmd(vma, pmd, addr, newprot,
 						 prot_numa)) {
-				pages++;
+				pages += HPAGE_PMD_NR;
+				nr_huge_updates++;
 				continue;
 			}
 			/* fall through */
@@ -168,6 +177,9 @@
 			change_pmd_protnuma(vma->vm_mm, addr, pmd);
 	} while (pmd++, addr = next, addr != end);
 
+	if (nr_huge_updates)
+		count_vm_numa_events(NUMA_HUGE_PTE_UPDATES, nr_huge_updates);
+
 	return pages;
 }
 
@@ -204,6 +216,7 @@
 	BUG_ON(addr >= end);
 	pgd = pgd_offset(mm, addr);
 	flush_cache_range(vma, addr, end);
+	set_tlb_flush_pending(mm);
 	do {
 		next = pgd_addr_end(addr, end);
 		if (pgd_none_or_clear_bad(pgd))
@@ -215,6 +228,7 @@
 	/* Only flush the TLB if we actually modified any entries: */
 	if (pages)
 		flush_tlb_range(vma, start, end);
+	clear_tlb_flush_pending(mm);
 
 	return pages;
 }
diff -ruw linux-3.11.10/mm/mremap.c linux-3.11.10-fbx/mm/mremap.c
--- linux-3.11.10/mm/mremap.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/mremap.c	2015-09-29 11:15:08.933755994 +0200
@@ -194,10 +194,17 @@
 			break;
 		if (pmd_trans_huge(*old_pmd)) {
 			int err = 0;
-			if (extent == HPAGE_PMD_SIZE)
+			if (extent == HPAGE_PMD_SIZE) {
+				VM_BUG_ON(vma->vm_file || !vma->anon_vma);
+				/* See comment in move_ptes() */
+				if (need_rmap_locks)
+					anon_vma_lock_write(vma->anon_vma);
 				err = move_huge_pmd(vma, new_vma, old_addr,
 						    new_addr, old_end,
 						    old_pmd, new_pmd);
+				if (need_rmap_locks)
+					anon_vma_unlock_write(vma->anon_vma);
+			}
 			if (err > 0) {
 				need_flush = true;
 				continue;
diff -ruw linux-3.11.10/mm/oom_kill.c linux-3.11.10-fbx/mm/oom_kill.c
--- linux-3.11.10/mm/oom_kill.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/oom_kill.c	2015-09-29 11:15:08.933755994 +0200
@@ -170,7 +170,7 @@
 	 * implementation used by LSMs.
 	 */
 	if (has_capability_noaudit(p, CAP_SYS_ADMIN))
-		adj -= 30;
+		points -= (points * 3) / 100;
 
 	/* Normalize to oom_score_adj units */
 	adj *= totalpages / 1000;
diff -ruw linux-3.11.10/mm/page_alloc.c linux-3.11.10-fbx/mm/page_alloc.c
--- linux-3.11.10/mm/page_alloc.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/page_alloc.c	2015-09-29 11:15:08.937756019 +0200
@@ -68,6 +68,7 @@
 
 /* prevent >1 _updater_ of zone percpu pageset ->high and ->batch fields */
 static DEFINE_MUTEX(pcp_batch_high_lock);
+#define MIN_PERCPU_PAGELIST_FRACTION	(8)
 
 #ifdef CONFIG_USE_PERCPU_NUMA_NODE_ID
 DEFINE_PER_CPU(int, numa_node);
@@ -779,9 +780,21 @@
 		set_page_count(p, 0);
 	} while (++p, --i);
 
-	set_page_refcounted(page);
 	set_pageblock_migratetype(page, MIGRATE_CMA);
+
+	if (pageblock_order >= MAX_ORDER) {
+		i = pageblock_nr_pages;
+		p = page;
+		do {
+			set_page_refcounted(p);
+			__free_pages(p, MAX_ORDER - 1);
+			p += MAX_ORDER_NR_PAGES;
+		} while (i -= MAX_ORDER_NR_PAGES);
+	} else {
+		set_page_refcounted(page);
 	__free_pages(page, pageblock_order);
+	}
+
 	adjust_managed_page_count(page, pageblock_nr_pages);
 }
 #endif
@@ -3988,7 +4001,7 @@
 	memmap_init_zone((size), (nid), (zone), (start_pfn), MEMMAP_EARLY)
 #endif
 
-static int __meminit zone_batchsize(struct zone *zone)
+static int zone_batchsize(struct zone *zone)
 {
 #ifdef CONFIG_MMU
 	int batch;
@@ -4104,7 +4117,7 @@
 	pageset_update(&p->pcp, high, batch);
 }
 
-static void __meminit pageset_set_high_and_batch(struct zone *zone,
+static void pageset_set_high_and_batch(struct zone *zone,
 		struct per_cpu_pageset *pcp)
 {
 	if (percpu_pagelist_fraction)
@@ -5689,23 +5702,38 @@
 	void __user *buffer, size_t *length, loff_t *ppos)
 {
 	struct zone *zone;
-	unsigned int cpu;
+	int old_percpu_pagelist_fraction;
 	int ret;
 
+	mutex_lock(&pcp_batch_high_lock);
+	old_percpu_pagelist_fraction = percpu_pagelist_fraction;
+
 	ret = proc_dointvec_minmax(table, write, buffer, length, ppos);
-	if (!write || (ret < 0))
-		return ret;
+	if (!write || ret < 0)
+		goto out;
+
+	/* Sanity checking to avoid pcp imbalance */
+	if (percpu_pagelist_fraction &&
+	    percpu_pagelist_fraction < MIN_PERCPU_PAGELIST_FRACTION) {
+		percpu_pagelist_fraction = old_percpu_pagelist_fraction;
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* No change? */
+	if (percpu_pagelist_fraction == old_percpu_pagelist_fraction)
+		goto out;
 
-	mutex_lock(&pcp_batch_high_lock);
 	for_each_populated_zone(zone) {
-		unsigned long  high;
-		high = zone->managed_pages / percpu_pagelist_fraction;
+		unsigned int cpu;
+
 		for_each_possible_cpu(cpu)
-			pageset_set_high(per_cpu_ptr(zone->pageset, cpu),
-					 high);
+			pageset_set_high_and_batch(zone,
+					per_cpu_ptr(zone->pageset, cpu));
 	}
+out:
 	mutex_unlock(&pcp_batch_high_lock);
-	return 0;
+	return ret;
 }
 
 int hashdist = HASHDIST_DEFAULT;
@@ -5847,53 +5875,65 @@
  * @end_bitidx: The last bit of interest
  * returns pageblock_bits flags
  */
-unsigned long get_pageblock_flags_group(struct page *page,
-					int start_bitidx, int end_bitidx)
+unsigned long get_pageblock_flags_mask(struct page *page,
+					unsigned long end_bitidx,
+					unsigned long mask)
 {
 	struct zone *zone;
 	unsigned long *bitmap;
-	unsigned long pfn, bitidx;
-	unsigned long flags = 0;
-	unsigned long value = 1;
+	unsigned long pfn, bitidx, word_bitidx;
+	unsigned long word;
 
 	zone = page_zone(page);
 	pfn = page_to_pfn(page);
 	bitmap = get_pageblock_bitmap(zone, pfn);
 	bitidx = pfn_to_bitidx(zone, pfn);
+	word_bitidx = bitidx / BITS_PER_LONG;
+	bitidx &= (BITS_PER_LONG-1);
 
-	for (; start_bitidx <= end_bitidx; start_bitidx++, value <<= 1)
-		if (test_bit(bitidx + start_bitidx, bitmap))
-			flags |= value;
-
-	return flags;
+	word = bitmap[word_bitidx];
+	bitidx += end_bitidx;
+	return (word >> (BITS_PER_LONG - bitidx - 1)) & mask;
 }
 
 /**
- * set_pageblock_flags_group - Set the requested group of flags for a pageblock_nr_pages block of pages
+ * set_pageblock_flags_mask - Set the requested group of flags for a pageblock_nr_pages block of pages
  * @page: The page within the block of interest
  * @start_bitidx: The first bit of interest
  * @end_bitidx: The last bit of interest
  * @flags: The flags to set
  */
-void set_pageblock_flags_group(struct page *page, unsigned long flags,
-					int start_bitidx, int end_bitidx)
+void set_pageblock_flags_mask(struct page *page, unsigned long flags,
+					unsigned long end_bitidx,
+					unsigned long mask)
 {
 	struct zone *zone;
 	unsigned long *bitmap;
-	unsigned long pfn, bitidx;
-	unsigned long value = 1;
+	unsigned long pfn, bitidx, word_bitidx;
+	unsigned long old_word, word;
+
+	BUILD_BUG_ON(NR_PAGEBLOCK_BITS != 4);
 
 	zone = page_zone(page);
 	pfn = page_to_pfn(page);
 	bitmap = get_pageblock_bitmap(zone, pfn);
 	bitidx = pfn_to_bitidx(zone, pfn);
+	word_bitidx = bitidx / BITS_PER_LONG;
+	bitidx &= (BITS_PER_LONG-1);
+
 	VM_BUG_ON(!zone_spans_pfn(zone, pfn));
 
-	for (; start_bitidx <= end_bitidx; start_bitidx++, value <<= 1)
-		if (flags & value)
-			__set_bit(bitidx + start_bitidx, bitmap);
-		else
-			__clear_bit(bitidx + start_bitidx, bitmap);
+	bitidx += end_bitidx;
+	mask <<= (BITS_PER_LONG - bitidx - 1);
+	flags <<= (BITS_PER_LONG - bitidx - 1);
+
+	word = ACCESS_ONCE(bitmap[word_bitidx]);
+	for (;;) {
+		old_word = cmpxchg(&bitmap[word_bitidx], word, (word & ~mask) | flags);
+		if (word == old_word)
+			break;
+		word = old_word;
+	}
 }
 
 /*
diff -ruw linux-3.11.10/mm/page-writeback.c linux-3.11.10-fbx/mm/page-writeback.c
--- linux-3.11.10/mm/page-writeback.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/page-writeback.c	2015-09-29 11:15:08.933755994 +0200
@@ -188,6 +188,25 @@
  * global dirtyable memory first.
  */
 
+/**
+ * zone_dirtyable_memory - number of dirtyable pages in a zone
+ * @zone: the zone
+ *
+ * Returns the zone's number of pages potentially available for dirty
+ * page cache.  This is the base value for the per-zone dirty limits.
+ */
+static unsigned long zone_dirtyable_memory(struct zone *zone)
+{
+	unsigned long nr_pages;
+
+	nr_pages = zone_page_state(zone, NR_FREE_PAGES);
+	nr_pages -= min(nr_pages, zone->dirty_balance_reserve);
+
+	nr_pages += zone_reclaimable_pages(zone);
+
+	return nr_pages;
+}
+
 static unsigned long highmem_dirtyable_memory(unsigned long total)
 {
 #ifdef CONFIG_HIGHMEM
@@ -195,11 +214,9 @@
 	unsigned long x = 0;
 
 	for_each_node_state(node, N_HIGH_MEMORY) {
-		struct zone *z =
-			&NODE_DATA(node)->node_zones[ZONE_HIGHMEM];
+		struct zone *z = &NODE_DATA(node)->node_zones[ZONE_HIGHMEM];
 
-		x += zone_page_state(z, NR_FREE_PAGES) +
-		     zone_reclaimable_pages(z) - z->dirty_balance_reserve;
+		x += zone_dirtyable_memory(z);
 	}
 	/*
 	 * Unreclaimable memory (kernel memory or anonymous memory
@@ -235,9 +252,11 @@
 {
 	unsigned long x;
 
-	x = global_page_state(NR_FREE_PAGES) + global_reclaimable_pages();
+	x = global_page_state(NR_FREE_PAGES);
 	x -= min(x, dirty_balance_reserve);
 
+	x += global_reclaimable_pages();
+
 	if (!vm_highmem_is_dirtyable)
 		x -= highmem_dirtyable_memory(x);
 
@@ -289,32 +308,6 @@
 }
 
 /**
- * zone_dirtyable_memory - number of dirtyable pages in a zone
- * @zone: the zone
- *
- * Returns the zone's number of pages potentially available for dirty
- * page cache.  This is the base value for the per-zone dirty limits.
- */
-static unsigned long zone_dirtyable_memory(struct zone *zone)
-{
-	/*
-	 * The effective global number of dirtyable pages may exclude
-	 * highmem as a big-picture measure to keep the ratio between
-	 * dirty memory and lowmem reasonable.
-	 *
-	 * But this function is purely about the individual zone and a
-	 * highmem zone can hold its share of dirty pages, so we don't
-	 * care about vm_highmem_is_dirtyable here.
-	 */
-	unsigned long nr_pages = zone_page_state(zone, NR_FREE_PAGES) +
-		zone_reclaimable_pages(zone);
-
-	/* don't allow this to underflow */
-	nr_pages -= min(nr_pages, zone->dirty_balance_reserve);
-	return nr_pages;
-}
-
-/**
  * zone_dirty_limit - maximum number of dirty pages allowed in a zone
  * @zone: the zone
  *
@@ -696,7 +689,7 @@
 	 *     => fast response on large errors; small oscillation near setpoint
 	 */
 	setpoint = (freerun + limit) / 2;
-	x = div_s64(((s64)setpoint - (s64)dirty) << RATELIMIT_CALC_SHIFT,
+	x = div64_s64(((s64)setpoint - (s64)dirty) << RATELIMIT_CALC_SHIFT,
 		    limit - setpoint + 1);
 	pos_ratio = x;
 	pos_ratio = pos_ratio * x >> RATELIMIT_CALC_SHIFT;
@@ -762,7 +755,7 @@
 	x_intercept = bdi_setpoint + span;
 
 	if (bdi_dirty < x_intercept - span / 4) {
-		pos_ratio = div_u64(pos_ratio * (x_intercept - bdi_dirty),
+		pos_ratio = div64_u64(pos_ratio * (x_intercept - bdi_dirty),
 				    x_intercept - bdi_setpoint + 1);
 	} else
 		pos_ratio /= 4;
@@ -2031,11 +2024,12 @@
 	if (!TestSetPageDirty(page)) {
 		struct address_space *mapping = page_mapping(page);
 		struct address_space *mapping2;
+		unsigned long flags;
 
 		if (!mapping)
 			return 1;
 
-		spin_lock_irq(&mapping->tree_lock);
+		spin_lock_irqsave(&mapping->tree_lock, flags);
 		mapping2 = page_mapping(page);
 		if (mapping2) { /* Race with truncate? */
 			BUG_ON(mapping2 != mapping);
@@ -2044,7 +2038,7 @@
 			radix_tree_tag_set(&mapping->page_tree,
 				page_index(page), PAGECACHE_TAG_DIRTY);
 		}
-		spin_unlock_irq(&mapping->tree_lock);
+		spin_unlock_irqrestore(&mapping->tree_lock, flags);
 		if (mapping->host) {
 			/* !PageAnon && !swapper_space */
 			__mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
@@ -2250,7 +2244,7 @@
 	return ret;
 }
 
-int test_set_page_writeback(struct page *page)
+int __test_set_page_writeback(struct page *page, bool keep_write)
 {
 	struct address_space *mapping = page_mapping(page);
 	int ret;
@@ -2272,6 +2266,7 @@
 			radix_tree_tag_clear(&mapping->page_tree,
 						page_index(page),
 						PAGECACHE_TAG_DIRTY);
+		if (!keep_write)
 		radix_tree_tag_clear(&mapping->page_tree,
 				     page_index(page),
 				     PAGECACHE_TAG_TOWRITE);
@@ -2284,7 +2279,7 @@
 	return ret;
 
 }
-EXPORT_SYMBOL(test_set_page_writeback);
+EXPORT_SYMBOL(__test_set_page_writeback);
 
 /*
  * Return true if any of the pages in the mapping are marked with the
diff -ruw linux-3.11.10/mm/percpu.c linux-3.11.10-fbx/mm/percpu.c
--- linux-3.11.10/mm/percpu.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/percpu.c	2015-09-29 11:15:08.937756019 +0200
@@ -612,7 +612,7 @@
 	chunk->map = pcpu_mem_zalloc(PCPU_DFL_MAP_ALLOC *
 						sizeof(chunk->map[0]));
 	if (!chunk->map) {
-		kfree(chunk);
+		pcpu_mem_free(chunk, pcpu_chunk_struct_size);
 		return NULL;
 	}
 
diff -ruw linux-3.11.10/mm/pgtable-generic.c linux-3.11.10-fbx/mm/pgtable-generic.c
--- linux-3.11.10/mm/pgtable-generic.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/pgtable-generic.c	2015-09-29 11:15:08.937756019 +0200
@@ -86,9 +86,10 @@
 pte_t ptep_clear_flush(struct vm_area_struct *vma, unsigned long address,
 		       pte_t *ptep)
 {
+	struct mm_struct *mm = (vma)->vm_mm;
 	pte_t pte;
-	pte = ptep_get_and_clear((vma)->vm_mm, address, ptep);
-	if (pte_accessible(pte))
+	pte = ptep_get_and_clear(mm, address, ptep);
+	if (pte_accessible(mm, pte))
 		flush_tlb_page(vma, address);
 	return pte;
 }
@@ -167,6 +168,9 @@
 void pmdp_invalidate(struct vm_area_struct *vma, unsigned long address,
 		     pmd_t *pmdp)
 {
+	pmd_t entry = *pmdp;
+	if (pmd_numa(entry))
+		entry = pmd_mknonnuma(entry);
 	set_pmd_at(vma->vm_mm, address, pmdp, pmd_mknotpresent(*pmdp));
 	flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
 }
diff -ruw linux-3.11.10/mm/rmap.c linux-3.11.10-fbx/mm/rmap.c
--- linux-3.11.10/mm/rmap.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/rmap.c	2015-09-29 11:15:08.937756019 +0200
@@ -103,6 +103,7 @@
 	 * LOCK should suffice since the actual taking of the lock must
 	 * happen _before_ what follows.
 	 */
+	might_sleep();
 	if (rwsem_is_locked(&anon_vma->root->rwsem)) {
 		anon_vma_lock_write(anon_vma);
 		anon_vma_unlock_write(anon_vma);
@@ -426,8 +427,9 @@
 	 * above cannot corrupt).
 	 */
 	if (!page_mapped(page)) {
+		rcu_read_unlock();
 		put_anon_vma(anon_vma);
-		anon_vma = NULL;
+		return NULL;
 	}
 out:
 	rcu_read_unlock();
@@ -477,9 +479,9 @@
 	}
 
 	if (!page_mapped(page)) {
+		rcu_read_unlock();
 		put_anon_vma(anon_vma);
-		anon_vma = NULL;
-		goto out;
+		return NULL;
 	}
 
 	/* we pinned the anon_vma, its safe to sleep */
@@ -600,7 +602,11 @@
 	spinlock_t *ptl;
 
 	if (unlikely(PageHuge(page))) {
+		/* when pud is not present, pte will be NULL */
 		pte = huge_pte_offset(mm, address);
+		if (!pte)
+			return NULL;
+
 		ptl = &mm->page_table_lock;
 		goto check;
 	}
@@ -1391,9 +1397,19 @@
 		BUG_ON(!page || PageAnon(page));
 
 		if (locked_vma) {
-			mlock_vma_page(page);   /* no-op if already mlocked */
-			if (page == check_page)
+			if (page == check_page) {
+				/* we know we have check_page locked */
+				mlock_vma_page(page);
 				ret = SWAP_MLOCK;
+			} else if (trylock_page(page)) {
+				/*
+				 * If we can lock the page, perform mlock.
+				 * Otherwise leave the page alone, it will be
+				 * eventually encountered again later.
+				 */
+				mlock_vma_page(page);
+				unlock_page(page);
+			}
 			continue;	/* don't unmap */
 		}
 
@@ -1670,10 +1686,9 @@
 {
 	struct anon_vma *root = anon_vma->root;
 
+	anon_vma_free(anon_vma);
 	if (root != anon_vma && atomic_dec_and_test(&root->refcount))
 		anon_vma_free(root);
-
-	anon_vma_free(anon_vma);
 }
 
 #ifdef CONFIG_MIGRATION
diff -ruw linux-3.11.10/mm/slub.c linux-3.11.10-fbx/mm/slub.c
--- linux-3.11.10/mm/slub.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/slub.c	2015-09-29 11:15:08.941756048 +0200
@@ -1210,8 +1210,8 @@
 	/*
 	 * Enable debugging if selected on the kernel commandline.
 	 */
-	if (slub_debug && (!slub_debug_slabs ||
-		!strncmp(slub_debug_slabs, name, strlen(slub_debug_slabs))))
+	if (slub_debug && (!slub_debug_slabs || (name &&
+		!strncmp(slub_debug_slabs, name, strlen(slub_debug_slabs)))))
 		flags |= slub_debug;
 
 	return flags;
@@ -4300,7 +4300,13 @@
 
 			page = ACCESS_ONCE(c->partial);
 			if (page) {
-				x = page->pobjects;
+				node = page_to_nid(page);
+				if (flags & SO_TOTAL)
+					WARN_ON_ONCE(1);
+				else if (flags & SO_OBJECTS)
+					WARN_ON_ONCE(1);
+				else
+					x = page->pages;
 				total += x;
 				nodes[node] += x;
 			}
diff -ruw linux-3.11.10/mm/swap.c linux-3.11.10-fbx/mm/swap.c
--- linux-3.11.10/mm/swap.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/swap.c	2015-09-29 11:15:08.941756048 +0200
@@ -82,19 +82,6 @@
 
 static void put_compound_page(struct page *page)
 {
-	/*
-	 * hugetlbfs pages cannot be split from under us.  If this is a
-	 * hugetlbfs page, check refcount on head page and release the page if
-	 * the refcount becomes zero.
-	 */
-	if (PageHuge(page)) {
-		page = compound_head(page);
-		if (put_page_testzero(page))
-			__put_compound_page(page);
-
-		return;
-	}
-
 	if (unlikely(PageTail(page))) {
 		/* __split_huge_page_refcount can run under us */
 		struct page *page_head = compound_trans_head(page);
@@ -111,14 +98,31 @@
 			 * still hot on arches that do not support
 			 * this_cpu_cmpxchg_double().
 			 */
-			if (PageSlab(page_head)) {
-				if (PageTail(page)) {
+			if (PageSlab(page_head) || PageHeadHuge(page_head)) {
+				if (likely(PageTail(page))) {
+					/*
+					 * __split_huge_page_refcount
+					 * cannot race here.
+					 */
+					VM_BUG_ON(!PageHead(page_head));
+					atomic_dec(&page->_mapcount);
 					if (put_page_testzero(page_head))
 						VM_BUG_ON(1);
-
-					atomic_dec(&page->_mapcount);
-					goto skip_lock_tail;
+					if (put_page_testzero(page_head))
+						__put_compound_page(page_head);
+					return;
 				} else
+					/*
+					 * __split_huge_page_refcount
+					 * run before us, "page" was a
+					 * THP tail. The split
+					 * page_head has been freed
+					 * and reallocated as slab or
+					 * hugetlbfs page of smaller
+					 * order (only possible if
+					 * reallocated as slab on
+					 * x86).
+					 */
 					goto skip_lock;
 			}
 			/*
@@ -132,8 +136,27 @@
 				/* __split_huge_page_refcount run before us */
 				compound_unlock_irqrestore(page_head, flags);
 skip_lock:
-				if (put_page_testzero(page_head))
+				if (put_page_testzero(page_head)) {
+					/*
+					 * The head page may have been
+					 * freed and reallocated as a
+					 * compound page of smaller
+					 * order and then freed again.
+					 * All we know is that it
+					 * cannot have become: a THP
+					 * page, a compound page of
+					 * higher order, a tail page.
+					 * That is because we still
+					 * hold the refcount of the
+					 * split THP tail and
+					 * page_head was the THP head
+					 * before the split.
+					 */
+					if (PageHead(page_head))
+						__put_compound_page(page_head);
+					else
 					__put_single_page(page_head);
+				}
 out_put_single:
 				if (put_page_testzero(page))
 					__put_single_page(page);
@@ -155,7 +178,6 @@
 			VM_BUG_ON(atomic_read(&page->_count) != 0);
 			compound_unlock_irqrestore(page_head, flags);
 
-skip_lock_tail:
 			if (put_page_testzero(page_head)) {
 				if (PageHead(page_head))
 					__put_compound_page(page_head);
@@ -198,30 +220,32 @@
 	 * proper PT lock that already serializes against
 	 * split_huge_page().
 	 */
-	bool got = false;
-	struct page *page_head;
-
-	/*
-	 * If this is a hugetlbfs page it cannot be split under us.  Simply
-	 * increment refcount for the head page.
-	 */
-	if (PageHuge(page)) {
-		page_head = compound_head(page);
-		atomic_inc(&page_head->_count);
-		got = true;
-	} else {
 		unsigned long flags;
+	bool got = false;
+	struct page *page_head = compound_trans_head(page);
 
-		page_head = compound_trans_head(page);
-		if (likely(page != page_head &&
-					get_page_unless_zero(page_head))) {
-
+	if (likely(page != page_head && get_page_unless_zero(page_head))) {
 			/* Ref to put_compound_page() comment. */
-			if (PageSlab(page_head)) {
+		if (PageSlab(page_head) || PageHeadHuge(page_head)) {
 				if (likely(PageTail(page))) {
+				/*
+				 * This is a hugetlbfs page or a slab
+				 * page. __split_huge_page_refcount
+				 * cannot race here.
+				 */
+				VM_BUG_ON(!PageHead(page_head));
 					__get_page_tail_foll(page, false);
 					return true;
 				} else {
+				/*
+				 * __split_huge_page_refcount run
+				 * before us, "page" was a THP
+				 * tail. The split page_head has been
+				 * freed and reallocated as slab or
+				 * hugetlbfs page of smaller order
+				 * (only possible if reallocated as
+				 * slab on x86).
+				 */
 					put_page(page_head);
 					return false;
 				}
@@ -243,7 +267,6 @@
 			if (unlikely(!got))
 				put_page(page_head);
 		}
-	}
 	return got;
 }
 EXPORT_SYMBOL(__get_page_tail);
diff -ruw linux-3.11.10/mm/swapfile.c linux-3.11.10-fbx/mm/swapfile.c
--- linux-3.11.10/mm/swapfile.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/swapfile.c	2015-09-29 11:15:08.941756048 +0200
@@ -1675,7 +1675,6 @@
 	p->max = 0;
 	swap_map = p->swap_map;
 	p->swap_map = NULL;
-	p->flags = 0;
 	frontswap_map = frontswap_map_get(p);
 	frontswap_map_set(p, NULL);
 	spin_unlock(&p->lock);
@@ -1698,6 +1697,16 @@
 		mutex_unlock(&inode->i_mutex);
 	}
 	filp_close(swap_file, NULL);
+
+	/*
+	 * Clear the SWP_USED flag after all resources are freed so that swapon
+	 * can reuse this swap_info in alloc_swap_info() safely.  It is ok to
+	 * not hold p->lock after we cleared its SWP_WRITEOK.
+	 */
+	spin_lock(&swap_lock);
+	p->flags = 0;
+	spin_unlock(&swap_lock);
+
 	err = 0;
 	atomic_inc(&proc_poll_event);
 	wake_up_interruptible(&proc_poll_wait);
diff -ruw linux-3.11.10/mm/util.c linux-3.11.10-fbx/mm/util.c
--- linux-3.11.10/mm/util.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/util.c	2015-09-29 11:15:08.945756085 +0200
@@ -387,7 +387,10 @@
 {
 	struct address_space *mapping = page->mapping;
 
-	VM_BUG_ON(PageSlab(page));
+	/* This happens if someone calls flush_dcache_page on slab page */
+	if (unlikely(PageSlab(page)))
+		return NULL;
+
 #ifdef CONFIG_SWAP
 	if (unlikely(PageSwapCache(page))) {
 		swp_entry_t entry;
diff -ruw linux-3.11.10/mm/vmscan.c linux-3.11.10-fbx/mm/vmscan.c
--- linux-3.11.10/mm/vmscan.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/vmscan.c	2015-09-29 11:15:08.945756085 +0200
@@ -1468,19 +1468,18 @@
 		 * If dirty pages are scanned that are not queued for IO, it
 		 * implies that flushers are not keeping up. In this case, flag
 		 * the zone ZONE_TAIL_LRU_DIRTY and kswapd will start writing
-		 * pages from reclaim context. It will forcibly stall in the
-		 * next check.
+		 * pages from reclaim context.
 		 */
 		if (nr_unqueued_dirty == nr_taken)
 			zone_set_flag(zone, ZONE_TAIL_LRU_DIRTY);
 
 		/*
-		 * In addition, if kswapd scans pages marked marked for
-		 * immediate reclaim and under writeback (nr_immediate), it
-		 * implies that pages are cycling through the LRU faster than
+		 * If kswapd scans pages marked marked for immediate
+		 * reclaim and under writeback (nr_immediate), it implies
+		 * that pages are cycling through the LRU faster than
 		 * they are written so also forcibly stall.
 		 */
-		if (nr_unqueued_dirty == nr_taken || nr_immediate)
+		if (nr_immediate)
 			congestion_wait(BLK_RW_ASYNC, HZ/10);
 	}
 
@@ -2431,10 +2430,17 @@
 
 	for (i = 0; i <= ZONE_NORMAL; i++) {
 		zone = &pgdat->node_zones[i];
+		if (!populated_zone(zone))
+			continue;
+
 		pfmemalloc_reserve += min_wmark_pages(zone);
 		free_pages += zone_page_state(zone, NR_FREE_PAGES);
 	}
 
+	/* If there are no reserves (unexpected config) then do not throttle */
+	if (!pfmemalloc_reserve)
+		return true;
+
 	wmark_ok = free_pages > pfmemalloc_reserve / 2;
 
 	/* kswapd must be awake if processes are being throttled */
@@ -2459,9 +2465,9 @@
 static bool throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist,
 					nodemask_t *nodemask)
 {
+	struct zoneref *z;
 	struct zone *zone;
-	int high_zoneidx = gfp_zone(gfp_mask);
-	pg_data_t *pgdat;
+	pg_data_t *pgdat = NULL;
 
 	/*
 	 * Kernel threads should not be throttled as they may be indirectly
@@ -2480,11 +2486,35 @@
 	if (fatal_signal_pending(current))
 		goto out;
 
-	/* Check if the pfmemalloc reserves are ok */
-	first_zones_zonelist(zonelist, high_zoneidx, NULL, &zone);
+	/*
+	 * Check if the pfmemalloc reserves are ok by finding the first node
+	 * with a usable ZONE_NORMAL or lower zone. The expectation is that
+	 * GFP_KERNEL will be required for allocating network buffers when
+	 * swapping over the network so ZONE_HIGHMEM is unusable.
+	 *
+	 * Throttling is based on the first usable node and throttled processes
+	 * wait on a queue until kswapd makes progress and wakes them. There
+	 * is an affinity then between processes waking up and where reclaim
+	 * progress has been made assuming the process wakes on the same node.
+	 * More importantly, processes running on remote nodes will not compete
+	 * for remote pfmemalloc reserves and processes on different nodes
+	 * should make reasonable progress.
+	 */
+	for_each_zone_zonelist_nodemask(zone, z, zonelist,
+					gfp_mask, nodemask) {
+		if (zone_idx(zone) > ZONE_NORMAL)
+			continue;
+
+		/* Throttle based on the first usable node */
 	pgdat = zone->zone_pgdat;
 	if (pfmemalloc_watermark_ok(pgdat))
 		goto out;
+		break;
+	}
+
+	/* If no zone was usable by the allocation flags then do not throttle */
+	if (!pgdat)
+		goto out;
 
 	/* Account for the throttling */
 	count_vm_event(PGSCAN_DIRECT_THROTTLE);
@@ -3216,7 +3246,10 @@
 		}
 	}
 
+	tsk->flags &= ~(PF_MEMALLOC | PF_SWAPWRITE | PF_KSWAPD);
 	current->reclaim_state = NULL;
+	lockdep_clear_current_reclaim_state();
+
 	return 0;
 }
 
diff -ruw linux-3.11.10/mm/vmstat.c linux-3.11.10-fbx/mm/vmstat.c
--- linux-3.11.10/mm/vmstat.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/mm/vmstat.c	2015-09-29 11:15:08.945756085 +0200
@@ -779,6 +779,7 @@
 
 #ifdef CONFIG_NUMA_BALANCING
 	"numa_pte_updates",
+	"numa_huge_pte_updates",
 	"numa_hint_faults",
 	"numa_hint_faults_local",
 	"numa_pages_migrated",
diff -ruw linux-3.11.10/net/8021q/vlan.c linux-3.11.10-fbx/net/8021q/vlan.c
--- linux-3.11.10/net/8021q/vlan.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/8021q/vlan.c	2015-09-29 11:15:08.945756085 +0200
@@ -207,7 +207,7 @@
 /*  Attach a VLAN device to a mac address (ie Ethernet Card).
  *  Returns 0 if the device was created or a negative error code otherwise.
  */
-static int register_vlan_device(struct net_device *real_dev, u16 vlan_id)
+int register_vlan_device(struct net_device *real_dev, u16 vlan_id)
 {
 	struct net_device *new_dev;
 	struct net *net = dev_net(real_dev);
@@ -305,9 +305,11 @@
 static void vlan_transfer_features(struct net_device *dev,
 				   struct net_device *vlandev)
 {
+	struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev);
+
 	vlandev->gso_max_size = dev->gso_max_size;
 
-	if (dev->features & NETIF_F_HW_VLAN_CTAG_TX)
+	if (vlan_hw_offload_capable(dev->features, vlan->vlan_proto))
 		vlandev->hard_header_len = dev->hard_header_len;
 	else
 		vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN;
diff -ruw linux-3.11.10/net/8021q/vlan_core.c linux-3.11.10-fbx/net/8021q/vlan_core.c
--- linux-3.11.10/net/8021q/vlan_core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/8021q/vlan_core.c	2015-09-29 11:15:08.949756111 +0200
@@ -89,6 +89,12 @@
 }
 EXPORT_SYMBOL(__vlan_find_dev_deep);
 
+struct net_device *vlan_dev_upper_dev(const struct net_device *dev)
+{
+	return vlan_dev_priv(dev)->real_dev;
+}
+EXPORT_SYMBOL(vlan_dev_upper_dev);
+
 struct net_device *vlan_dev_real_dev(const struct net_device *dev)
 {
 	struct net_device *ret = vlan_dev_priv(dev)->real_dev;
diff -ruw linux-3.11.10/net/8021q/vlan_dev.c linux-3.11.10-fbx/net/8021q/vlan_dev.c
--- linux-3.11.10/net/8021q/vlan_dev.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/8021q/vlan_dev.c	2015-09-29 11:15:08.949756111 +0200
@@ -28,6 +28,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
+#include <linux/pkt_sched.h>
 #include <net/arp.h>
 
 #include "vlan.h"
@@ -77,7 +78,7 @@
 
 	mp = vlan_dev_priv(dev)->egress_priority_map[(skb->priority & 0xF)];
 	while (mp) {
-		if (mp->priority == skb->priority) {
+		if (mp->priority == (skb->priority & TC_H_MIN_MASK)) {
 			return mp->vlan_qos; /* This should already be shifted
 					      * to mask correctly with the
 					      * VLAN's TCI */
@@ -512,10 +513,48 @@
 	}
 }
 
+static int vlan_calculate_locking_subclass(struct net_device *real_dev)
+{
+	int subclass = 0;
+
+	while (is_vlan_dev(real_dev)) {
+		subclass++;
+		real_dev = vlan_dev_priv(real_dev)->real_dev;
+	}
+
+	return subclass;
+}
+
+static void vlan_dev_mc_sync(struct net_device *to, struct net_device *from)
+{
+	int err = 0, subclass;
+
+	subclass = vlan_calculate_locking_subclass(to);
+
+	spin_lock_nested(&to->addr_list_lock, subclass);
+	err = __hw_addr_sync(&to->mc, &from->mc, to->addr_len);
+	if (!err)
+		__dev_set_rx_mode(to);
+	spin_unlock(&to->addr_list_lock);
+}
+
+static void vlan_dev_uc_sync(struct net_device *to, struct net_device *from)
+{
+	int err = 0, subclass;
+
+	subclass = vlan_calculate_locking_subclass(to);
+
+	spin_lock_nested(&to->addr_list_lock, subclass);
+	err = __hw_addr_sync(&to->uc, &from->uc, to->addr_len);
+	if (!err)
+		__dev_set_rx_mode(to);
+	spin_unlock(&to->addr_list_lock);
+}
+
 static void vlan_dev_set_rx_mode(struct net_device *vlan_dev)
 {
-	dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev);
-	dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev);
+	vlan_dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev);
+	vlan_dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev);
 }
 
 /*
@@ -549,6 +588,26 @@
 	.parse	 = eth_header_parse,
 };
 
+static int vlan_passthru_hard_header(struct sk_buff *skb, struct net_device *dev,
+				     unsigned short type,
+				     const void *daddr, const void *saddr,
+				     unsigned int len)
+{
+	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
+	struct net_device *real_dev = vlan->real_dev;
+
+	if (saddr == NULL)
+		saddr = dev->dev_addr;
+
+	return dev_hard_header(skb, real_dev, type, daddr, saddr, len);
+}
+
+static const struct header_ops vlan_passthru_header_ops = {
+	.create	 = vlan_passthru_hard_header,
+	.rebuild = dev_rebuild_header,
+	.parse	 = eth_header_parse,
+};
+
 static struct device_type vlan_type = {
 	.name	= "vlan",
 };
@@ -591,8 +650,9 @@
 #endif
 
 	dev->needed_headroom = real_dev->needed_headroom;
-	if (real_dev->features & NETIF_F_HW_VLAN_CTAG_TX) {
-		dev->header_ops      = real_dev->header_ops;
+	if (vlan_hw_offload_capable(real_dev->features,
+				    vlan_dev_priv(dev)->vlan_proto)) {
+		dev->header_ops      = &vlan_passthru_header_ops;
 		dev->hard_header_len = real_dev->hard_header_len;
 	} else {
 		dev->header_ops      = &vlan_header_ops;
@@ -603,9 +663,7 @@
 
 	SET_NETDEV_DEVTYPE(dev, &vlan_type);
 
-	if (is_vlan_dev(real_dev))
-		subclass = 1;
-
+	subclass = vlan_calculate_locking_subclass(dev);
 	vlan_dev_set_lockdep_class(dev, subclass);
 
 	vlan_dev_priv(dev)->vlan_pcpu_stats = alloc_percpu(struct vlan_pcpu_stats);
diff -ruw linux-3.11.10/net/bluetooth/af_bluetooth.c linux-3.11.10-fbx/net/bluetooth/af_bluetooth.c
--- linux-3.11.10/net/bluetooth/af_bluetooth.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/bluetooth/af_bluetooth.c	2015-09-29 11:15:08.961756202 +0200
@@ -221,8 +221,6 @@
 	if (flags & (MSG_OOB))
 		return -EOPNOTSUPP;
 
-	msg->msg_namelen = 0;
-
 	skb = skb_recv_datagram(sk, flags, noblock, &err);
 	if (!skb) {
 		if (sk->sk_shutdown & RCV_SHUTDOWN)
@@ -287,8 +285,6 @@
 	if (flags & MSG_OOB)
 		return -EOPNOTSUPP;
 
-	msg->msg_namelen = 0;
-
 	BT_DBG("sk %p size %zu", sk, size);
 
 	lock_sock(sk);
diff -ruw linux-3.11.10/net/bluetooth/hci_conn.c linux-3.11.10-fbx/net/bluetooth/hci_conn.c
--- linux-3.11.10/net/bluetooth/hci_conn.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/bluetooth/hci_conn.c	2015-09-29 11:15:08.965756231 +0200
@@ -652,14 +652,17 @@
 	if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
 		struct hci_cp_auth_requested cp;
 
-		/* encrypt must be pending if auth is also pending */
-		set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
-
 		cp.handle = cpu_to_le16(conn->handle);
 		hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
 			     sizeof(cp), &cp);
-		if (conn->key_type != 0xff)
+
+		/* If we're already encrypted set the REAUTH_PEND flag,
+		 * otherwise set the ENCRYPT_PEND.
+		 */
+		if (conn->link_mode & HCI_LM_ENCRYPT)
 			set_bit(HCI_CONN_REAUTH_PEND, &conn->flags);
+		else
+			set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
 	}
 
 	return 0;
diff -ruw linux-3.11.10/net/bluetooth/hci_event.c linux-3.11.10-fbx/net/bluetooth/hci_event.c
--- linux-3.11.10/net/bluetooth/hci_event.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/bluetooth/hci_event.c	2015-09-29 11:15:08.965756231 +0200
@@ -47,6 +47,10 @@
 	smp_mb__after_clear_bit(); /* wake_up_bit advises about this barrier */
 	wake_up_bit(&hdev->flags, HCI_INQUIRY);
 
+	hci_dev_lock(hdev);
+	hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+	hci_dev_unlock(hdev);
+
 	hci_conn_check_pending(hdev);
 }
 
@@ -2992,6 +2996,12 @@
 	if (!conn)
 		goto unlock;
 
+	/* For BR/EDR the necessary steps are taken through the
+	 * auth_complete event.
+	 */
+	if (conn->type != LE_LINK)
+		goto unlock;
+
 	if (!ev->status)
 		conn->sec_level = conn->pending_sec_level;
 
@@ -3153,8 +3163,11 @@
 
 		/* If we're not the initiators request authorization to
 		 * proceed from user space (mgmt_user_confirm with
-		 * confirm_hint set to 1). */
-		if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
+		 * confirm_hint set to 1). The exception is if neither
+		 * side had MITM in which case we do auto-accept.
+		 */
+		if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) &&
+		    (loc_mitm || rem_mitm)) {
 			BT_DBG("Confirming auto-accept as acceptor");
 			confirm_hint = 1;
 			goto confirm;
@@ -3560,7 +3573,13 @@
 
 	hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
 
-	if (ltk->type & HCI_SMP_STK) {
+	/* Ref. Bluetooth Core SPEC pages 1975 and 2004. STK is a
+	 * temporary key used to encrypt a connection following
+	 * pairing. It is used during the Encrypted Session Setup to
+	 * distribute the keys. Later, security can be re-established
+	 * using a distributed LTK.
+	 */
+	if (ltk->type == HCI_SMP_STK_SLAVE) {
 		list_del(&ltk->list);
 		kfree(ltk);
 	}
diff -ruw linux-3.11.10/net/bluetooth/hci_sock.c linux-3.11.10-fbx/net/bluetooth/hci_sock.c
--- linux-3.11.10/net/bluetooth/hci_sock.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/bluetooth/hci_sock.c	2015-09-29 11:15:08.965756231 +0200
@@ -752,8 +752,6 @@
 	if (!skb)
 		return err;
 
-	msg->msg_namelen = 0;
-
 	copied = skb->len;
 	if (len < copied) {
 		msg->msg_flags |= MSG_TRUNC;
@@ -928,6 +926,7 @@
 		{
 			struct hci_filter *f = &hci_pi(sk)->filter;
 
+			memset(&uf, 0, sizeof(uf));
 			uf.type_mask = f->type_mask;
 			uf.opcode    = f->opcode;
 			uf.event_mask[0] = *((u32 *) f->event_mask + 0);
diff -ruw linux-3.11.10/net/bluetooth/l2cap_sock.c linux-3.11.10-fbx/net/bluetooth/l2cap_sock.c
--- linux-3.11.10/net/bluetooth/l2cap_sock.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/bluetooth/l2cap_sock.c	2015-09-29 11:15:08.969756268 +0200
@@ -631,11 +631,6 @@
 
 		/*change security for LE channels */
 		if (chan->scid == L2CAP_CID_ATT) {
-			if (!conn->hcon->out) {
-				err = -EINVAL;
-				break;
-			}
-
 			if (smp_conn_security(conn->hcon, sec.level))
 				break;
 			sk->sk_state = BT_CONFIG;
@@ -949,13 +944,16 @@
 	/* Check for backlog size */
 	if (sk_acceptq_is_full(parent)) {
 		BT_DBG("backlog full %d", parent->sk_ack_backlog);
+		release_sock(parent);
 		return NULL;
 	}
 
 	sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
 			      GFP_ATOMIC);
-	if (!sk)
+	if (!sk) {
+		release_sock(parent);
 		return NULL;
+        }
 
 	bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
 
diff -ruw linux-3.11.10/net/bluetooth/mgmt.c linux-3.11.10-fbx/net/bluetooth/mgmt.c
--- linux-3.11.10/net/bluetooth/mgmt.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/bluetooth/mgmt.c	2015-09-29 11:15:08.969756268 +0200
@@ -2319,8 +2319,13 @@
 	}
 
 	if (addr->type == BDADDR_LE_PUBLIC || addr->type == BDADDR_LE_RANDOM) {
-		/* Continue with pairing via SMP */
+		/* Continue with pairing via SMP. The hdev lock must be
+		 * released as SMP may try to recquire it for crypto
+		 * purposes.
+		 */
+		hci_dev_unlock(hdev);
 		err = smp_user_confirm_reply(conn, mgmt_op, passkey);
+		hci_dev_lock(hdev);
 
 		if (!err)
 			err = cmd_complete(sk, hdev->id, mgmt_op,
diff -ruw linux-3.11.10/net/bluetooth/sco.c linux-3.11.10-fbx/net/bluetooth/sco.c
--- linux-3.11.10/net/bluetooth/sco.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/bluetooth/sco.c	2015-09-29 11:15:08.973756294 +0200
@@ -700,7 +700,6 @@
 	    test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
 		sco_conn_defer_accept(pi->conn->hcon, 0);
 		sk->sk_state = BT_CONFIG;
-		msg->msg_namelen = 0;
 
 		release_sock(sk);
 		return 0;
diff -ruw linux-3.11.10/net/bridge/br_if.c linux-3.11.10-fbx/net/bridge/br_if.c
--- linux-3.11.10/net/bridge/br_if.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/bridge/br_if.c	2015-09-29 11:15:08.973756294 +0200
@@ -172,6 +172,8 @@
 		del_nbp(p);
 	}
 
+	br_fdb_delete_by_port(br, NULL, 1);
+
 	del_timer_sync(&br->gc_timer);
 
 	br_sysfs_delbr(br->dev);
diff -ruw linux-3.11.10/net/bridge/br_input.c linux-3.11.10-fbx/net/bridge/br_input.c
--- linux-3.11.10/net/bridge/br_input.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/bridge/br_input.c	2015-09-29 11:15:08.973756294 +0200
@@ -72,7 +72,7 @@
 		goto drop;
 
 	if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb, &vid))
-		goto drop;
+		goto out;
 
 	/* insert into forwarding database after filtering to avoid spoofing */
 	br = p->br;
diff -ruw linux-3.11.10/net/bridge/br_netlink.c linux-3.11.10-fbx/net/bridge/br_netlink.c
--- linux-3.11.10/net/bridge/br_netlink.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/bridge/br_netlink.c	2015-09-29 11:15:08.973756294 +0200
@@ -446,6 +446,20 @@
 	return 0;
 }
 
+static int br_dev_newlink(struct net *src_net, struct net_device *dev,
+			  struct nlattr *tb[], struct nlattr *data[])
+{
+	struct net_bridge *br = netdev_priv(dev);
+
+	if (tb[IFLA_ADDRESS]) {
+		spin_lock_bh(&br->lock);
+		br_stp_change_bridge_id(br, nla_data(tb[IFLA_ADDRESS]));
+		spin_unlock_bh(&br->lock);
+	}
+
+	return register_netdevice(dev);
+}
+
 static size_t br_get_link_af_size(const struct net_device *dev)
 {
 	struct net_port_vlans *pv;
@@ -474,6 +488,7 @@
 	.priv_size	= sizeof(struct net_bridge),
 	.setup		= br_dev_setup,
 	.validate	= br_validate,
+	.newlink	= br_dev_newlink,
 	.dellink	= br_dev_delete,
 };
 
diff -ruw linux-3.11.10/net/bridge/br_private.h linux-3.11.10-fbx/net/bridge/br_private.h
--- linux-3.11.10/net/bridge/br_private.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/bridge/br_private.h	2015-09-29 11:15:08.973756294 +0200
@@ -452,6 +452,16 @@
 extern int br_handle_frame_finish(struct sk_buff *skb);
 extern rx_handler_result_t br_handle_frame(struct sk_buff **pskb);
 
+static inline bool br_rx_handler_check_rcu(const struct net_device *dev)
+{
+	return rcu_dereference(dev->rx_handler) == br_handle_frame;
+}
+
+static inline struct net_bridge_port *br_port_get_check_rcu(const struct net_device *dev)
+{
+	return br_rx_handler_check_rcu(dev) ? br_port_get_rcu(dev) : NULL;
+}
+
 /* br_ioctl.c */
 extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 extern int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *arg);
diff -ruw linux-3.11.10/net/bridge/br_stp_bpdu.c linux-3.11.10-fbx/net/bridge/br_stp_bpdu.c
--- linux-3.11.10/net/bridge/br_stp_bpdu.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/bridge/br_stp_bpdu.c	2015-09-29 11:15:08.973756294 +0200
@@ -153,7 +153,7 @@
 	if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0)
 		goto err;
 
-	p = br_port_get_rcu(dev);
+	p = br_port_get_check_rcu(dev);
 	if (!p)
 		goto err;
 
diff -ruw linux-3.11.10/net/core/dev.c linux-3.11.10-fbx/net/core/dev.c
--- linux-3.11.10/net/core/dev.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/core/dev.c	2015-09-29 11:15:08.985756385 +0200
@@ -131,6 +131,7 @@
 #include <linux/static_key.h>
 #include <linux/hashtable.h>
 #include <linux/vmalloc.h>
+#include <linux/kthread.h>
 
 #include "net-sysfs.h"
 
@@ -146,6 +147,19 @@
 struct list_head ptype_all __read_mostly;	/* Taps */
 static struct list_head offload_base __read_mostly;
 
+#ifdef CONFIG_NETRXTHREAD
+
+#define RXTHREAD_MAX_PROCESS    CONFIG_NETRXTHREAD_MAX_PROCESS
+#define RXTHREAD_MAX_PKTS       128
+
+static struct task_struct *krxd;
+static struct sk_buff_head krxd_pkt_queue[CONFIG_NETRXTHREAD_RX_QUEUE];
+unsigned int krxd_stats_pkts[CONFIG_NETRXTHREAD_RX_QUEUE];
+unsigned int krxd_stats_dropped[CONFIG_NETRXTHREAD_RX_QUEUE];
+static wait_queue_head_t krxd_wq;
+static unsigned int krxd_pkts_count;
+#endif
+
 /*
  * The @dev_base_head list is protected by @dev_base_lock and the rtnl
  * semaphore.
@@ -2401,7 +2415,7 @@
  * 2. No high memory really exists on this machine.
  */
 
-static int illegal_highdma(struct net_device *dev, struct sk_buff *skb)
+static int illegal_highdma(const struct net_device *dev, struct sk_buff *skb)
 {
 #ifdef CONFIG_HIGHMEM
 	int i;
@@ -2481,34 +2495,36 @@
 }
 
 static netdev_features_t harmonize_features(struct sk_buff *skb,
+					    const struct net_device *dev,
 	netdev_features_t features)
 {
 	if (skb->ip_summed != CHECKSUM_NONE &&
 	    !can_checksum_protocol(features, skb_network_protocol(skb))) {
 		features &= ~NETIF_F_ALL_CSUM;
-	} else if (illegal_highdma(skb->dev, skb)) {
+	} else if (illegal_highdma(dev, skb)) {
 		features &= ~NETIF_F_SG;
 	}
 
 	return features;
 }
 
-netdev_features_t netif_skb_features(struct sk_buff *skb)
+netdev_features_t netif_skb_dev_features(struct sk_buff *skb,
+					 const struct net_device *dev)
 {
 	__be16 protocol = skb->protocol;
-	netdev_features_t features = skb->dev->features;
+	netdev_features_t features = dev->features;
 
-	if (skb_shinfo(skb)->gso_segs > skb->dev->gso_max_segs)
+	if (skb_shinfo(skb)->gso_segs > dev->gso_max_segs)
 		features &= ~NETIF_F_GSO_MASK;
 
 	if (protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD)) {
 		struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
 		protocol = veh->h_vlan_encapsulated_proto;
 	} else if (!vlan_tx_tag_present(skb)) {
-		return harmonize_features(skb, features);
+		return harmonize_features(skb, dev, features);
 	}
 
-	features &= (skb->dev->vlan_features | NETIF_F_HW_VLAN_CTAG_TX |
+	features &= (dev->vlan_features | NETIF_F_HW_VLAN_CTAG_TX |
 					       NETIF_F_HW_VLAN_STAG_TX);
 
 	if (protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD))
@@ -2516,9 +2532,9 @@
 				NETIF_F_GEN_CSUM | NETIF_F_HW_VLAN_CTAG_TX |
 				NETIF_F_HW_VLAN_STAG_TX;
 
-	return harmonize_features(skb, features);
+	return harmonize_features(skb, dev, features);
 }
-EXPORT_SYMBOL(netif_skb_features);
+EXPORT_SYMBOL(netif_skb_dev_features);
 
 /*
  * Returns true if either:
@@ -3211,6 +3227,23 @@
 	return NET_RX_DROP;
 }
 
+/* Start Freebox added code */
+#if defined(CONFIG_FREEBOX_DIVERTER) || defined(CONFIG_FREEBOX_DIVERTER_MODULE)
+int (*fbxdiverter_hook)(struct sk_buff *);
+
+static int handle_fbxdiverter(struct sk_buff *skb)
+{
+	/* try_module_get is missing here, so there is a race on
+	 * fbxdiverter module deletion */
+	if (!fbxdiverter_hook)
+		return 0;
+	return fbxdiverter_hook(skb);
+}
+
+EXPORT_SYMBOL(fbxdiverter_hook);
+#endif
+
+
 /**
  *	netif_rx	-	post buffer to the network code
  *	@skb: buffer to post
@@ -3344,6 +3377,36 @@
 EXPORT_SYMBOL_GPL(br_fdb_test_addr_hook);
 #endif
 
+#ifdef CONFIG_FBXBRIDGE
+struct sk_buff *(*fbxbridge_handle_frame_hook)(struct fbxbridge *p, struct sk_buff *skb);
+
+struct fbxbridge;
+
+static inline struct sk_buff *handle_fbxbridge(struct sk_buff *skb,
+					       struct packet_type **pt_prev, int *ret,
+					       struct net_device *orig_dev)
+{
+	struct fbxbridge *fbxbr;
+
+	if (skb->pkt_type == PACKET_LOOPBACK ||
+	    (fbxbr = skb->dev->fbx_bridge_port) == NULL)
+		return skb;
+
+	if (skb->protocol != __constant_htons(ETH_P_IP) &&
+	    skb->protocol != __constant_htons(ETH_P_ARP))
+		return skb;
+
+	if (*pt_prev) {
+		*ret = deliver_skb(skb, *pt_prev, orig_dev);
+		*pt_prev = NULL;
+	}
+
+	return fbxbridge_handle_frame_hook(fbxbr, skb);
+}
+#else
+#define handle_fbxbridge(skb, pt_prev, ret, orig_dev)   (skb)
+#endif
+
 #ifdef CONFIG_NET_CLS_ACT
 /* TODO: Maybe we should just force sch_ingress to be compiled in
  * when CONFIG_NET_CLS_ACT is? otherwise some useless instructions
@@ -3488,21 +3551,7 @@
 	int ret = NET_RX_DROP;
 	__be16 type;
 
-	net_timestamp_check(!netdev_tstamp_prequeue, skb);
-
-	trace_netif_receive_skb(skb);
-
-	/* if we've gotten here through NAPI, check netpoll */
-	if (netpoll_receive_skb(skb))
-		goto out;
-
 	orig_dev = skb->dev;
-
-	skb_reset_network_header(skb);
-	if (!skb_transport_header_was_set(skb))
-		skb_reset_transport_header(skb);
-	skb_reset_mac_len(skb);
-
 	pt_prev = NULL;
 
 	rcu_read_lock();
@@ -3538,6 +3587,10 @@
 	}
 
 skip_taps:
+	skb = handle_fbxbridge(skb, &pt_prev, &ret, orig_dev);
+	if (!skb)
+		goto unlock;
+
 #ifdef CONFIG_NET_CLS_ACT
 	skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
 	if (!skb)
@@ -3622,11 +3675,10 @@
 
 unlock:
 	rcu_read_unlock();
-out:
 	return ret;
 }
 
-static int __netif_receive_skb(struct sk_buff *skb)
+static int __netif_receive_skb_end(struct sk_buff *skb)
 {
 	int ret;
 
@@ -3651,6 +3703,101 @@
 	return ret;
 }
 
+#ifdef CONFIG_NETRXTHREAD
+
+
+static int krxd_action(void *unused)
+{
+	struct sk_buff *skb;
+
+	set_user_nice(current, -5);
+	current->flags |= PF_NOFREEZE;
+	__set_current_state(TASK_RUNNING);
+
+	local_bh_disable();
+	while (1) {
+		unsigned int i, queue, count;
+
+		count = CONFIG_NETRXTHREAD_RX_QUEUE;
+		for (i = 0; i < count; i++) {
+			queue = count - i - 1;
+			skb = skb_dequeue(&krxd_pkt_queue[queue]);
+			if (!skb)
+				continue;
+			krxd_pkts_count--;
+			break;
+		}
+
+		if (!skb) {
+			local_bh_enable();
+			wait_event_interruptible(krxd_wq,
+						 krxd_pkts_count != 0);
+			set_current_state(TASK_RUNNING);
+			local_bh_disable();
+			continue;
+		}
+
+		__netif_receive_skb_end(skb);
+
+		/* only schedule when working on lowest prio queue */
+		if (queue == 0) {
+			if (need_resched()) {
+				local_bh_enable();
+				schedule();
+				local_bh_disable();
+			}
+		}
+	}
+	return 0;
+}
+#endif /* RXTHREAD */
+
+static int __netif_receive_skb(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETRXTHREAD
+	unsigned int len, queue;
+#endif
+	net_timestamp_check(!netdev_tstamp_prequeue, skb);
+
+	trace_netif_receive_skb(skb);
+
+	/* if we've gotten here through NAPI, check netpoll */
+	if (netpoll_receive_skb(skb))
+		return NET_RX_DROP;
+
+	skb_reset_network_header(skb);
+	if (!skb_transport_header_was_set(skb))
+		skb_reset_transport_header(skb);
+	skb_reset_mac_len(skb);
+
+#if defined(CONFIG_FREEBOX_DIVERTER) || defined(CONFIG_FREEBOX_DIVERTER_MODULE)
+	if (handle_fbxdiverter(skb))
+		return NET_RX_SUCCESS;
+#endif
+
+#ifndef CONFIG_NETRXTHREAD
+	return __netif_receive_skb_end(skb);
+#else
+	queue = skb->rxthread_prio & 1;
+
+	/* queue the packet to the rx thread */
+	local_bh_disable();
+	len = skb_queue_len(&krxd_pkt_queue[queue]);
+	if (len < RXTHREAD_MAX_PKTS) {
+		__skb_queue_tail(&krxd_pkt_queue[queue], skb);
+		krxd_pkts_count++;
+		krxd_stats_pkts[queue]++;
+		if (!len)
+			wake_up(&krxd_wq);
+	} else {
+		krxd_stats_dropped[queue]++;
+		dev_kfree_skb(skb);
+	}
+	local_bh_enable();
+	return NET_RX_SUCCESS;
+#endif
+}
+
 /**
  *	netif_receive_skb - process receive buffer from network
  *	@skb: buffer to process
@@ -3960,6 +4107,7 @@
 	skb->vlan_tci = 0;
 	skb->dev = napi->dev;
 	skb->skb_iif = 0;
+	skb->truesize = SKB_TRUESIZE(skb_end_offset(skb));
 
 	napi->skb = skb;
 }
@@ -4598,7 +4746,7 @@
 {
 	const struct net_device_ops *ops = dev->netdev_ops;
 
-	if ((dev->flags & IFF_UP) && ops->ndo_change_rx_flags)
+	if (ops->ndo_change_rx_flags)
 		ops->ndo_change_rx_flags(dev, flags);
 }
 
@@ -4749,6 +4897,7 @@
 	if (ops->ndo_set_rx_mode)
 		ops->ndo_set_rx_mode(dev);
 }
+EXPORT_SYMBOL(__dev_set_rx_mode);
 
 void dev_set_rx_mode(struct net_device *dev)
 {
@@ -5599,7 +5748,7 @@
 			rebroadcast_time = jiffies;
 		}
 
-		msleep(250);
+		msleep(1);
 
 		refcnt = netdev_refcnt_read(dev);
 
@@ -5960,6 +6109,9 @@
 /**
  *	unregister_netdevice_many - unregister many devices
  *	@head: list of devices
+ *
+ *  Note: As most callers use a stack allocated list_head,
+ *  we force a list_del() to make sure stack wont be corrupted later.
  */
 void unregister_netdevice_many(struct list_head *head)
 {
@@ -5969,6 +6121,7 @@
 		rollback_registered_many(head);
 		list_for_each_entry(dev, head, unreg_list)
 			net_set_todo(dev);
+		list_del(head);
 	}
 }
 EXPORT_SYMBOL(unregister_netdevice_many);
@@ -6385,7 +6538,6 @@
 		}
 	}
 	unregister_netdevice_many(&dev_kill_list);
-	list_del(&dev_kill_list);
 	rtnl_unlock();
 }
 
@@ -6477,6 +6629,19 @@
 	open_softirq(NET_TX_SOFTIRQ, net_tx_action);
 	open_softirq(NET_RX_SOFTIRQ, net_rx_action);
 
+#ifdef CONFIG_NETRXTHREAD
+	for (i = 0; i < CONFIG_NETRXTHREAD_RX_QUEUE; i++)
+		skb_queue_head_init(&krxd_pkt_queue[i]);
+	krxd_pkts_count = 0;
+	init_waitqueue_head(&krxd_wq);
+	krxd = kthread_create(krxd_action, NULL, "krxthread");
+	if (IS_ERR(krxd)) {
+		printk(KERN_ERR "unable to create krxd\n");
+		return -ENOMEM;
+	}
+	wake_up_process(krxd);
+#endif
+
 	hotcpu_notifier(dev_cpu_callback, 0);
 	dst_init();
 	rc = 0;
@@ -6485,3 +6650,7 @@
 }
 
 subsys_initcall(net_dev_init);
+
+#if defined(CONFIG_FBXBRIDGE_MODULE)
+EXPORT_SYMBOL(fbxbridge_handle_frame_hook);
+#endif
diff -ruw linux-3.11.10/net/core/fib_rules.c linux-3.11.10-fbx/net/core/fib_rules.c
--- linux-3.11.10/net/core/fib_rules.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/core/fib_rules.c	2015-09-29 11:15:08.985756385 +0200
@@ -445,7 +445,8 @@
 		if (frh->action && (frh->action != rule->action))
 			continue;
 
-		if (frh->table && (frh_get_table(frh, tb) != rule->table))
+		if (frh_get_table(frh, tb) &&
+		    (frh_get_table(frh, tb) != rule->table))
 			continue;
 
 		if (tb[FRA_PRIORITY] &&
@@ -719,6 +720,13 @@
 			attach_rules(&ops->rules_list, dev);
 		break;
 
+	case NETDEV_CHANGENAME:
+		list_for_each_entry(ops, &net->rules_ops, list) {
+			detach_rules(&ops->rules_list, dev);
+			attach_rules(&ops->rules_list, dev);
+		}
+		break;
+
 	case NETDEV_UNREGISTER:
 		list_for_each_entry(ops, &net->rules_ops, list)
 			detach_rules(&ops->rules_list, dev);
diff -ruw linux-3.11.10/net/core/filter.c linux-3.11.10-fbx/net/core/filter.c
--- linux-3.11.10/net/core/filter.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/core/filter.c	2015-09-29 11:15:08.989756414 +0200
@@ -36,7 +36,6 @@
 #include <asm/uaccess.h>
 #include <asm/unaligned.h>
 #include <linux/filter.h>
-#include <linux/reciprocal_div.h>
 #include <linux/ratelimit.h>
 #include <linux/seccomp.h>
 #include <linux/if_vlan.h>
@@ -166,7 +165,7 @@
 			A /= X;
 			continue;
 		case BPF_S_ALU_DIV_K:
-			A = reciprocal_divide(A, K);
+			A /= K;
 			continue;
 		case BPF_S_ALU_MOD_X:
 			if (X == 0)
@@ -356,6 +355,8 @@
 
 			if (skb_is_nonlinear(skb))
 				return 0;
+			if (skb->len < sizeof(struct nlattr))
+				return 0;
 			if (A > skb->len - sizeof(struct nlattr))
 				return 0;
 
@@ -372,11 +373,13 @@
 
 			if (skb_is_nonlinear(skb))
 				return 0;
+			if (skb->len < sizeof(struct nlattr))
+				return 0;
 			if (A > skb->len - sizeof(struct nlattr))
 				return 0;
 
 			nla = (struct nlattr *)&skb->data[A];
-			if (nla->nla_len > A - skb->len)
+			if (nla->nla_len > skb->len - A)
 				return 0;
 
 			nla = nla_find_nested(nla, X);
@@ -553,11 +556,6 @@
 		/* Some instructions need special checks */
 		switch (code) {
 		case BPF_S_ALU_DIV_K:
-			/* check for division by zero */
-			if (ftest->k == 0)
-				return -EINVAL;
-			ftest->k = reciprocal_value(ftest->k);
-			break;
 		case BPF_S_ALU_MOD_K:
 			/* check for division by zero */
 			if (ftest->k == 0)
@@ -853,26 +851,6 @@
 	to->code = decodes[code];
 	to->jt = filt->jt;
 	to->jf = filt->jf;
-
-	if (code == BPF_S_ALU_DIV_K) {
-		/*
-		 * When loaded this rule user gave us X, which was
-		 * translated into R = r(X). Now we calculate the
-		 * RR = r(R) and report it back. If next time this
-		 * value is loaded and RRR = r(RR) is calculated
-		 * then the R == RRR will be true.
-		 *
-		 * One exception. X == 1 translates into R == 0 and
-		 * we can't calculate RR out of it with r().
-		 */
-
-		if (filt->k == 0)
-			to->k = 1;
-		else
-			to->k = reciprocal_value(filt->k);
-
-		BUG_ON(reciprocal_value(to->k) != filt->k);
-	} else
 		to->k = filt->k;
 }
 
diff -ruw linux-3.11.10/net/core/iovec.c linux-3.11.10-fbx/net/core/iovec.c
--- linux-3.11.10/net/core/iovec.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/core/iovec.c	2015-09-29 11:15:08.989756414 +0200
@@ -48,6 +48,7 @@
 			if (err < 0)
 				return err;
 		}
+		if (m->msg_name)
 		m->msg_name = address;
 	} else {
 		m->msg_name = NULL;
diff -ruw linux-3.11.10/net/core/neighbour.c linux-3.11.10-fbx/net/core/neighbour.c
--- linux-3.11.10/net/core/neighbour.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/core/neighbour.c	2015-09-29 11:15:08.989756414 +0200
@@ -764,9 +764,6 @@
 	nht = rcu_dereference_protected(tbl->nht,
 					lockdep_is_held(&tbl->lock));
 
-	if (atomic_read(&tbl->entries) < tbl->gc_thresh1)
-		goto out;
-
 	/*
 	 *	periodically recompute ReachableTime from random function
 	 */
@@ -779,6 +776,9 @@
 				neigh_rand_reach_time(p->base_reachable_time);
 	}
 
+	if (atomic_read(&tbl->entries) < tbl->gc_thresh1)
+		goto out;
+
 	for (i = 0 ; i < (1 << nht->hash_shift); i++) {
 		np = &nht->hash_buckets[i];
 
@@ -1274,7 +1274,7 @@
 
 	if (dev_hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL,
 			    skb->len) < 0 &&
-	    dev->header_ops->rebuild(skb))
+	    dev_rebuild_header(skb))
 		return 0;
 
 	return dev_queue_xmit(skb);
diff -ruw linux-3.11.10/net/core/net-procfs.c linux-3.11.10-fbx/net/core/net-procfs.c
--- linux-3.11.10/net/core/net-procfs.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/core/net-procfs.c	2015-06-12 14:02:27.770997106 +0200
@@ -311,6 +311,86 @@
 	.release = seq_release_net,
 };
 
+#ifdef CONFIG_NETRXTHREAD
+extern unsigned int krxd_stats_pkts[CONFIG_NETRXTHREAD_RX_QUEUE];
+extern unsigned int krxd_stats_dropped[CONFIG_NETRXTHREAD_RX_QUEUE];
+
+/*
+ *	This is invoked by the /proc filesystem handler to display a device
+ *	in detail.
+ */
+static void *krxthread_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	int *queue;
+
+	if (*pos > CONFIG_NETRXTHREAD_RX_QUEUE)
+		return NULL;
+
+	queue = kmalloc(sizeof(*queue), GFP_KERNEL);
+	if (!queue)
+		return NULL;
+	*queue = ((int)*pos - 1);
+
+	return queue;
+}
+
+static void *krxthread_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	int *queue = v;
+
+	if (*pos == CONFIG_NETRXTHREAD_RX_QUEUE)
+		return NULL;
+
+	++*queue;
+	*pos = *queue + 1;
+	return queue;
+}
+
+static void krxthread_seq_stop(struct seq_file *seq, void *v)
+{
+	kfree(v);
+}
+
+static void krxthread_seq_printf_stats(struct seq_file *seq, int queue)
+{
+	seq_printf(seq, "%8u %12u %12u\n",
+		   queue,
+		   krxd_stats_pkts[queue],
+		   krxd_stats_dropped[queue]);
+}
+
+static int krxthread_seq_show(struct seq_file *seq, void *v)
+{
+	int *queue = v;
+
+	if (*queue == -1)
+		seq_printf(seq, "%8s %12s %12s\n",
+			   "queue", "packets", "drops");
+	else
+		krxthread_seq_printf_stats(seq, *queue);
+	return 0;
+}
+
+static const struct seq_operations krxthread_seq_ops = {
+	.start = krxthread_seq_start,
+	.next  = krxthread_seq_next,
+	.stop  = krxthread_seq_stop,
+	.show  = krxthread_seq_show,
+};
+
+static int krxthread_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &krxthread_seq_ops);
+}
+
+static const struct file_operations krxthread_seq_fops = {
+	.owner	 = THIS_MODULE,
+	.open    = krxthread_seq_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+#endif /* KRXTHREAD */
 
 static int __net_init dev_proc_net_init(struct net *net)
 {
@@ -323,9 +403,13 @@
 		goto out_dev;
 	if (!proc_create("ptype", S_IRUGO, net->proc_net, &ptype_seq_fops))
 		goto out_softnet;
-
 	if (wext_proc_init(net))
 		goto out_ptype;
+#ifdef CONFIG_NETRXTHREAD
+	if (!proc_create("krxthread", S_IRUGO, net->proc_net,
+			 &krxthread_seq_fops))
+		goto out_ptype;
+#endif
 	rc = 0;
 out:
 	return rc;
diff -ruw linux-3.11.10/net/core/rtnetlink.c linux-3.11.10-fbx/net/core/rtnetlink.c
--- linux-3.11.10/net/core/rtnetlink.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/core/rtnetlink.c	2015-09-29 11:15:08.993756451 +0200
@@ -714,7 +714,8 @@
 		return 0;
 }
 
-static size_t rtnl_port_size(const struct net_device *dev)
+static size_t rtnl_port_size(const struct net_device *dev,
+			     u32 ext_filter_mask)
 {
 	size_t port_size = nla_total_size(4)		/* PORT_VF */
 		+ nla_total_size(PORT_PROFILE_MAX)	/* PORT_PROFILE */
@@ -730,7 +731,8 @@
 	size_t port_self_size = nla_total_size(sizeof(struct nlattr))
 		+ port_size;
 
-	if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent)
+	if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent ||
+	    !(ext_filter_mask & RTEXT_FILTER_VF))
 		return 0;
 	if (dev_num_vf(dev->dev.parent))
 		return port_self_size + vf_ports_size +
@@ -765,7 +767,7 @@
 	       + nla_total_size(ext_filter_mask
 			        & RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */
 	       + rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */
-	       + rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */
+	       + rtnl_port_size(dev, ext_filter_mask) /* IFLA_VF_PORTS + IFLA_PORT_SELF */
 	       + rtnl_link_get_size(dev) /* IFLA_LINKINFO */
 	       + rtnl_link_get_af_size(dev); /* IFLA_AF_SPEC */
 }
@@ -826,11 +828,13 @@
 	return 0;
 }
 
-static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev)
+static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev,
+			  u32 ext_filter_mask)
 {
 	int err;
 
-	if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent)
+	if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent ||
+	    !(ext_filter_mask & RTEXT_FILTER_VF))
 		return 0;
 
 	err = rtnl_port_self_fill(skb, dev);
@@ -994,7 +998,7 @@
 		nla_nest_end(skb, vfinfo);
 	}
 
-	if (rtnl_port_fill(skb, dev))
+	if (rtnl_port_fill(skb, dev, ext_filter_mask))
 		goto nla_put_failure;
 
 	if (dev->rtnl_link_ops) {
@@ -1048,6 +1052,8 @@
 	struct hlist_head *head;
 	struct nlattr *tb[IFLA_MAX+1];
 	u32 ext_filter_mask = 0;
+	int err;
+	int hdrlen;
 
 	s_h = cb->args[0];
 	s_idx = cb->args[1];
@@ -1055,8 +1061,17 @@
 	rcu_read_lock();
 	cb->seq = net->dev_base_seq;
 
-	if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
-			ifla_policy) >= 0) {
+	/* A hack to preserve kernel<->userspace interface.
+	 * The correct header is ifinfomsg. It is consistent with rtnl_getlink.
+	 * However, before Linux v3.9 the code here assumed rtgenmsg and that's
+	 * what iproute2 < v3.9.0 used.
+	 * We can detect the old iproute2. Even including the IFLA_EXT_MASK
+	 * attribute, its netlink message is shorter than struct ifinfomsg.
+	 */
+	hdrlen = nlmsg_len(cb->nlh) < sizeof(struct ifinfomsg) ?
+		 sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg);
+
+	if (nlmsg_parse(cb->nlh, hdrlen, tb, IFLA_MAX, ifla_policy) >= 0) {
 
 		if (tb[IFLA_EXT_MASK])
 			ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
@@ -1068,11 +1083,17 @@
 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
 			if (idx < s_idx)
 				goto cont;
-			if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
+			err = rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
 					     NETLINK_CB(cb->skb).portid,
 					     cb->nlh->nlmsg_seq, 0,
 					     NLM_F_MULTI,
-					     ext_filter_mask) <= 0)
+					       ext_filter_mask);
+			/* If we ran out of room on the first message,
+			 * we're in trouble
+			 */
+			WARN_ON((err == -EMSGSIZE) && (skb->len == 0));
+
+			if (err <= 0)
 				goto out;
 
 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
@@ -1301,7 +1322,8 @@
 	return 0;
 }
 
-static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
+static int do_setlink(const struct sk_buff *skb,
+		      struct net_device *dev, struct ifinfomsg *ifm,
 		      struct nlattr **tb, char *ifname, int modified)
 {
 	const struct net_device_ops *ops = dev->netdev_ops;
@@ -1313,7 +1335,7 @@
 			err = PTR_ERR(net);
 			goto errout;
 		}
-		if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) {
+		if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
 			err = -EPERM;
 			goto errout;
 		}
@@ -1567,7 +1589,7 @@
 	if (err < 0)
 		goto errout;
 
-	err = do_setlink(dev, ifm, tb, ifname, 0);
+	err = do_setlink(skb, dev, ifm, tb, ifname, 0);
 errout:
 	return err;
 }
@@ -1607,7 +1629,6 @@
 
 	ops->dellink(dev, &list_kill);
 	unregister_netdevice_many(&list_kill);
-	list_del(&list_kill);
 	return 0;
 }
 
@@ -1685,7 +1706,8 @@
 }
 EXPORT_SYMBOL(rtnl_create_link);
 
-static int rtnl_group_changelink(struct net *net, int group,
+static int rtnl_group_changelink(const struct sk_buff *skb,
+		struct net *net, int group,
 		struct ifinfomsg *ifm,
 		struct nlattr **tb)
 {
@@ -1694,7 +1716,7 @@
 
 	for_each_netdev(net, dev) {
 		if (dev->group == group) {
-			err = do_setlink(dev, ifm, tb, NULL, 0);
+			err = do_setlink(skb, dev, ifm, tb, NULL, 0);
 			if (err < 0)
 				return err;
 		}
@@ -1796,12 +1818,12 @@
 				modified = 1;
 			}
 
-			return do_setlink(dev, ifm, tb, ifname, modified);
+			return do_setlink(skb, dev, ifm, tb, ifname, modified);
 		}
 
 		if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
 			if (ifm->ifi_index == 0 && tb[IFLA_GROUP])
-				return rtnl_group_changelink(net,
+				return rtnl_group_changelink(skb, net,
 						nla_get_u32(tb[IFLA_GROUP]),
 						ifm, tb);
 			return -ENODEV;
@@ -1913,9 +1935,13 @@
 	struct nlattr *tb[IFLA_MAX+1];
 	u32 ext_filter_mask = 0;
 	u16 min_ifinfo_dump_size = 0;
+	int hdrlen;
+
+	/* Same kernel<->userspace interface hack as in rtnl_dump_ifinfo. */
+	hdrlen = nlmsg_len(nlh) < sizeof(struct ifinfomsg) ?
+		 sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg);
 
-	if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
-			ifla_policy) >= 0) {
+	if (nlmsg_parse(nlh, hdrlen, tb, IFLA_MAX, ifla_policy) >= 0) {
 		if (tb[IFLA_EXT_MASK])
 			ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
 	}
@@ -1991,12 +2017,13 @@
 static int nlmsg_populate_fdb_fill(struct sk_buff *skb,
 				   struct net_device *dev,
 				   u8 *addr, u32 pid, u32 seq,
-				   int type, unsigned int flags)
+				   int type, unsigned int flags,
+				   int nlflags)
 {
 	struct nlmsghdr *nlh;
 	struct ndmsg *ndm;
 
-	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), NLM_F_MULTI);
+	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), nlflags);
 	if (!nlh)
 		return -EMSGSIZE;
 
@@ -2034,7 +2061,7 @@
 	if (!skb)
 		goto errout;
 
-	err = nlmsg_populate_fdb_fill(skb, dev, addr, 0, 0, type, NTF_SELF);
+	err = nlmsg_populate_fdb_fill(skb, dev, addr, 0, 0, type, NTF_SELF, 0);
 	if (err < 0) {
 		kfree_skb(skb);
 		goto errout;
@@ -2181,7 +2208,7 @@
 	int err = -EINVAL;
 	__u8 *addr;
 
-	if (!capable(CAP_NET_ADMIN))
+	if (!netlink_capable(skb, CAP_NET_ADMIN))
 		return -EPERM;
 
 	err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
@@ -2259,7 +2286,8 @@
 
 		err = nlmsg_populate_fdb_fill(skb, dev, ha->addr,
 					      portid, seq,
-					      RTM_NEWNEIGH, NTF_SELF);
+					      RTM_NEWNEIGH, NTF_SELF,
+					      NLM_F_MULTI);
 		if (err < 0)
 			return err;
 skip:
@@ -2632,7 +2660,7 @@
 	sz_idx = type>>2;
 	kind = type&3;
 
-	if (kind != 2 && !ns_capable(net->user_ns, CAP_NET_ADMIN))
+	if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN))
 		return -EPERM;
 
 	if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
diff -ruw linux-3.11.10/net/core/skbuff.c linux-3.11.10-fbx/net/core/skbuff.c
--- linux-3.11.10/net/core/skbuff.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/core/skbuff.c	2015-09-29 11:15:08.993756451 +0200
@@ -47,6 +47,8 @@
 #include <linux/in.h>
 #include <linux/inet.h>
 #include <linux/slab.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
 #include <linux/netdevice.h>
 #ifdef CONFIG_NET_CLS_ACT
 #include <net/pkt_sched.h>
@@ -74,36 +76,6 @@
 struct kmem_cache *skbuff_head_cache __read_mostly;
 static struct kmem_cache *skbuff_fclone_cache __read_mostly;
 
-static void sock_pipe_buf_release(struct pipe_inode_info *pipe,
-				  struct pipe_buffer *buf)
-{
-	put_page(buf->page);
-}
-
-static void sock_pipe_buf_get(struct pipe_inode_info *pipe,
-				struct pipe_buffer *buf)
-{
-	get_page(buf->page);
-}
-
-static int sock_pipe_buf_steal(struct pipe_inode_info *pipe,
-			       struct pipe_buffer *buf)
-{
-	return 1;
-}
-
-
-/* Pipe buffer operations for a socket. */
-static const struct pipe_buf_operations sock_pipe_buf_ops = {
-	.can_merge = 0,
-	.map = generic_pipe_buf_map,
-	.unmap = generic_pipe_buf_unmap,
-	.confirm = generic_pipe_buf_confirm,
-	.release = sock_pipe_buf_release,
-	.steal = sock_pipe_buf_steal,
-	.get = sock_pipe_buf_get,
-};
-
 /**
  *	skb_panic - private function for out-of-line support
  *	@skb:	buffer
@@ -580,9 +552,6 @@
 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
 	nf_conntrack_put(skb->nfct);
 #endif
-#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
-	nf_conntrack_put_reasm(skb->nfct_reasm);
-#endif
 #ifdef CONFIG_BRIDGE_NETFILTER
 	nf_bridge_put(skb->nf_bridge);
 #endif
@@ -722,6 +691,9 @@
 	new->ipvs_property	= old->ipvs_property;
 #endif
 	new->pfmemalloc		= old->pfmemalloc;
+#ifdef CONFIG_IP_FFN
+	new->ffn_state		= FFN_STATE_INIT;
+#endif
 	new->protocol		= old->protocol;
 	new->mark		= old->mark;
 	new->skb_iif		= old->skb_iif;
@@ -762,6 +734,9 @@
 	C(mac_len);
 	n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len;
 	n->cloned = 1;
+#ifdef CONFIG_FBXBRIDGE
+	n->fbxbridge_state = 0;
+#endif
 	n->nohdr = 0;
 	n->destructor = NULL;
 	C(tail);
@@ -1115,6 +1090,9 @@
 	skb->cloned   = 0;
 	skb->hdr_len  = 0;
 	skb->nohdr    = 0;
+#ifdef CONFIG_FBXBRIDGE
+	skb->fbxbridge_state = 0;
+#endif
 	atomic_set(&skb_shinfo(skb)->dataref, 1);
 	return 0;
 
@@ -1552,6 +1530,10 @@
 	skb->tail     += delta;
 	skb->data_len -= delta;
 
+#ifdef CONFIG_FBXBRIDGE
+	skb->fbxbridge_state = 0;
+#endif
+
 	return skb_tail_pointer(skb);
 }
 EXPORT_SYMBOL(__pskb_pull_tail);
@@ -1803,7 +1785,7 @@
 		.partial = partial,
 		.nr_pages_max = MAX_SKB_FRAGS,
 		.flags = flags,
-		.ops = &sock_pipe_buf_ops,
+		.ops = &nosteal_pipe_buf_ops,
 		.spd_release = sock_spd_release,
 	};
 	struct sk_buff *frag_iter;
@@ -2869,6 +2851,9 @@
 		skb_shinfo(nskb)->tx_flags = skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG;
 
 		while (pos < offset + len && i < nfrags) {
+			if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
+				goto err;
+
 			*frag = skb_shinfo(skb)->frags[i];
 			__skb_frag_ref(frag);
 			size = skb_frag_size(frag);
@@ -3514,6 +3499,7 @@
 	skb->tstamp.tv64 = 0;
 	skb->pkt_type = PACKET_HOST;
 	skb->skb_iif = 0;
+	skb->local_df = 0;
 	skb_dst_drop(skb);
 	skb->mark = 0;
 	secpath_reset(skb);
@@ -3521,3 +3507,28 @@
 	nf_reset_trace(skb);
 }
 EXPORT_SYMBOL_GPL(skb_scrub_packet);
+
+/**
+ * skb_gso_transport_seglen - Return length of individual segments of a gso packet
+ *
+ * @skb: GSO skb
+ *
+ * skb_gso_transport_seglen is used to determine the real size of the
+ * individual segments, including Layer4 headers (TCP/UDP).
+ *
+ * The MAC/L2 or network (IP, IPv6) headers are not accounted for.
+ */
+unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
+{
+	const struct skb_shared_info *shinfo = skb_shinfo(skb);
+
+	if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))
+		return tcp_hdrlen(skb) + shinfo->gso_size;
+
+	/* UFO sets gso_size to the size of the fragmentation
+	 * payload, i.e. the size of the L4 (UDP) header is already
+	 * accounted for.
+	 */
+	return shinfo->gso_size;
+}
+EXPORT_SYMBOL_GPL(skb_gso_transport_seglen);
diff -ruw linux-3.11.10/net/core/sock.c linux-3.11.10-fbx/net/core/sock.c
--- linux-3.11.10/net/core/sock.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/core/sock.c	2015-09-29 11:15:08.993756451 +0200
@@ -144,6 +144,55 @@
 static DEFINE_MUTEX(proto_list_mutex);
 static LIST_HEAD(proto_list);
 
+/**
+ * sk_ns_capable - General socket capability test
+ * @sk: Socket to use a capability on or through
+ * @user_ns: The user namespace of the capability to use
+ * @cap: The capability to use
+ *
+ * Test to see if the opener of the socket had when the socket was
+ * created and the current process has the capability @cap in the user
+ * namespace @user_ns.
+ */
+bool sk_ns_capable(const struct sock *sk,
+		   struct user_namespace *user_ns, int cap)
+{
+	return file_ns_capable(sk->sk_socket->file, user_ns, cap) &&
+		ns_capable(user_ns, cap);
+}
+EXPORT_SYMBOL(sk_ns_capable);
+
+/**
+ * sk_capable - Socket global capability test
+ * @sk: Socket to use a capability on or through
+ * @cap: The global capbility to use
+ *
+ * Test to see if the opener of the socket had when the socket was
+ * created and the current process has the capability @cap in all user
+ * namespaces.
+ */
+bool sk_capable(const struct sock *sk, int cap)
+{
+	return sk_ns_capable(sk, &init_user_ns, cap);
+}
+EXPORT_SYMBOL(sk_capable);
+
+/**
+ * sk_net_capable - Network namespace socket capability test
+ * @sk: Socket to use a capability on or through
+ * @cap: The capability to use
+ *
+ * Test to see if the opener of the socket had when the socke was created
+ * and the current process has the capability @cap over the network namespace
+ * the socket is a member of.
+ */
+bool sk_net_capable(const struct sock *sk, int cap)
+{
+	return sk_ns_capable(sk, sock_net(sk)->user_ns, cap);
+}
+EXPORT_SYMBOL(sk_net_capable);
+
+
 #ifdef CONFIG_MEMCG_KMEM
 int mem_cgroup_sockets_init(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
 {
@@ -887,7 +936,7 @@
 
 	case SO_PEEK_OFF:
 		if (sock->ops->set_peek_off)
-			sock->ops->set_peek_off(sk, val);
+			ret = sock->ops->set_peek_off(sk, val);
 		else
 			ret = -EOPNOTSUPP;
 		break;
@@ -913,6 +962,11 @@
 		}
 		break;
 #endif
+
+	case SO_UDP_DUP_UNICAST:
+		sock_valbool_flag(sk, SOCK_UDP_DUP_UNICAST, valbool);
+		break;
+
 	default:
 		ret = -ENOPROTOOPT;
 		break;
@@ -1176,6 +1230,10 @@
 		break;
 #endif
 
+	case SO_UDP_DUP_UNICAST:
+		v.val = sock_flag(sk, SOCK_UDP_DUP_UNICAST);
+		break;
+
 	default:
 		return -ENOPROTOOPT;
 	}
@@ -1835,7 +1893,7 @@
 		gfp_t gfp = sk->sk_allocation;
 
 		if (order)
-			gfp |= __GFP_COMP | __GFP_NOWARN;
+			gfp |= __GFP_COMP | __GFP_NOWARN | __GFP_NORETRY;
 		pfrag->page = alloc_pages(gfp, order);
 		if (likely(pfrag->page)) {
 			pfrag->offset = 0;
@@ -2335,10 +2393,13 @@
 	if (sk->sk_backlog.tail)
 		__release_sock(sk);
 
+	/* Warning : release_cb() might need to release sk ownership,
+	 * ie call sock_release_ownership(sk) before us.
+	 */
 	if (sk->sk_prot->release_cb)
 		sk->sk_prot->release_cb(sk);
 
-	sk->sk_lock.owned = 0;
+	sock_release_ownership(sk);
 	if (waitqueue_active(&sk->sk_lock.wq))
 		wake_up(&sk->sk_lock.wq);
 	spin_unlock_bh(&sk->sk_lock.slock);
diff -ruw linux-3.11.10/net/core/sock_diag.c linux-3.11.10-fbx/net/core/sock_diag.c
--- linux-3.11.10/net/core/sock_diag.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/core/sock_diag.c	2015-09-29 11:15:08.993756451 +0200
@@ -49,7 +49,7 @@
 }
 EXPORT_SYMBOL_GPL(sock_diag_put_meminfo);
 
-int sock_diag_put_filterinfo(struct user_namespace *user_ns, struct sock *sk,
+int sock_diag_put_filterinfo(bool may_report_filterinfo, struct sock *sk,
 			     struct sk_buff *skb, int attrtype)
 {
 	struct nlattr *attr;
@@ -57,7 +57,7 @@
 	unsigned int len;
 	int err = 0;
 
-	if (!ns_capable(user_ns, CAP_NET_ADMIN)) {
+	if (!may_report_filterinfo) {
 		nla_reserve(skb, attrtype, 0);
 		return 0;
 	}
diff -ruw linux-3.11.10/net/ipv4/datagram.c linux-3.11.10-fbx/net/ipv4/datagram.c
--- linux-3.11.10/net/ipv4/datagram.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/datagram.c	2015-09-29 11:15:09.005756542 +0200
@@ -57,7 +57,7 @@
 	if (IS_ERR(rt)) {
 		err = PTR_ERR(rt);
 		if (err == -ENETUNREACH)
-			IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
+			IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
 		goto out;
 	}
 
@@ -86,18 +86,26 @@
 }
 EXPORT_SYMBOL(ip4_datagram_connect);
 
+/* Because UDP xmit path can manipulate sk_dst_cache without holding
+ * socket lock, we need to use sk_dst_set() here,
+ * even if we own the socket lock.
+ */
 void ip4_datagram_release_cb(struct sock *sk)
 {
 	const struct inet_sock *inet = inet_sk(sk);
 	const struct ip_options_rcu *inet_opt;
 	__be32 daddr = inet->inet_daddr;
+	struct dst_entry *dst;
 	struct flowi4 fl4;
 	struct rtable *rt;
 
-	if (! __sk_dst_get(sk) || __sk_dst_check(sk, 0))
-		return;
-
 	rcu_read_lock();
+
+	dst = __sk_dst_get(sk);
+	if (!dst || !dst->obsolete || dst->ops->check(dst, 0)) {
+		rcu_read_unlock();
+		return;
+	}
 	inet_opt = rcu_dereference(inet->inet_opt);
 	if (inet_opt && inet_opt->opt.srr)
 		daddr = inet_opt->opt.faddr;
@@ -105,8 +113,10 @@
 				   inet->inet_saddr, inet->inet_dport,
 				   inet->inet_sport, sk->sk_protocol,
 				   RT_CONN_FLAGS(sk), sk->sk_bound_dev_if);
-	if (!IS_ERR(rt))
-		__sk_dst_set(sk, &rt->dst);
+
+	dst = !IS_ERR(rt) ? &rt->dst : NULL;
+	sk_dst_set(sk, dst);
+
 	rcu_read_unlock();
 }
 EXPORT_SYMBOL_GPL(ip4_datagram_release_cb);
diff -ruw linux-3.11.10/net/ipv4/devinet.c linux-3.11.10-fbx/net/ipv4/devinet.c
--- linux-3.11.10/net/ipv4/devinet.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/devinet.c	2015-09-29 11:15:09.009756568 +0200
@@ -1434,7 +1434,8 @@
 	       + nla_total_size(4) /* IFA_ADDRESS */
 	       + nla_total_size(4) /* IFA_LOCAL */
 	       + nla_total_size(4) /* IFA_BROADCAST */
-	       + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
+	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
+	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
 }
 
 static inline u32 cstamp_delta(unsigned long cstamp)
diff -ruw linux-3.11.10/net/ipv4/fib_frontend.c linux-3.11.10-fbx/net/ipv4/fib_frontend.c
--- linux-3.11.10/net/ipv4/fib_frontend.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/fib_frontend.c	2015-09-29 11:15:09.009756568 +0200
@@ -1049,6 +1049,8 @@
 	}
 
 	in_dev = __in_dev_get_rtnl(dev);
+	if (!in_dev)
+		return NOTIFY_DONE;
 
 	switch (event) {
 	case NETDEV_UP:
diff -ruw linux-3.11.10/net/ipv4/fib_semantics.c linux-3.11.10-fbx/net/ipv4/fib_semantics.c
--- linux-3.11.10/net/ipv4/fib_semantics.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/fib_semantics.c	2015-09-29 11:15:09.009756568 +0200
@@ -819,13 +819,13 @@
 	fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
 	if (fi == NULL)
 		goto failure;
+	fib_info_cnt++;
 	if (cfg->fc_mx) {
 		fi->fib_metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
 		if (!fi->fib_metrics)
 			goto failure;
 	} else
 		fi->fib_metrics = (u32 *) dst_default_metrics;
-	fib_info_cnt++;
 
 	fi->fib_net = hold_net(net);
 	fi->fib_protocol = cfg->fc_protocol;
diff -ruw linux-3.11.10/net/ipv4/gre_offload.c linux-3.11.10-fbx/net/ipv4/gre_offload.c
--- linux-3.11.10/net/ipv4/gre_offload.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/gre_offload.c	2015-09-29 11:15:09.009756568 +0200
@@ -28,6 +28,7 @@
 	netdev_features_t enc_features;
 	int ghl = GRE_HEADER_SECTION;
 	struct gre_base_hdr *greh;
+	u16 mac_offset = skb->mac_header;
 	int mac_len = skb->mac_len;
 	__be16 protocol = skb->protocol;
 	int tnl_hlen;
@@ -57,13 +58,13 @@
 	} else
 		csum = false;
 
+	if (unlikely(!pskb_may_pull(skb, ghl)))
+		goto out;
+
 	/* setup inner skb. */
 	skb->protocol = greh->protocol;
 	skb->encapsulation = 0;
 
-	if (unlikely(!pskb_may_pull(skb, ghl)))
-		goto out;
-
 	__skb_pull(skb, ghl);
 	skb_reset_mac_header(skb);
 	skb_set_network_header(skb, skb_inner_network_offset(skb));
@@ -72,8 +73,10 @@
 	/* segment inner packet. */
 	enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
 	segs = skb_mac_gso_segment(skb, enc_features);
-	if (!segs || IS_ERR(segs))
+	if (!segs || IS_ERR(segs)) {
+		skb_gso_error_unwind(skb, protocol, ghl, mac_offset, mac_len);
 		goto out;
+	}
 
 	skb = segs;
 	tnl_hlen = skb_tnl_header_len(skb);
diff -ruw linux-3.11.10/net/ipv4/inet_diag.c linux-3.11.10-fbx/net/ipv4/inet_diag.c
--- linux-3.11.10/net/ipv4/inet_diag.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/inet_diag.c	2015-09-29 11:15:09.009756568 +0200
@@ -106,6 +106,10 @@
 
 	r->id.idiag_sport = inet->inet_sport;
 	r->id.idiag_dport = inet->inet_dport;
+
+	memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src));
+	memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst));
+
 	r->id.idiag_src[0] = inet->inet_rcv_saddr;
 	r->id.idiag_dst[0] = inet->inet_daddr;
 
@@ -240,12 +244,19 @@
 
 	r->idiag_family	      = tw->tw_family;
 	r->idiag_retrans      = 0;
+
 	r->id.idiag_if	      = tw->tw_bound_dev_if;
 	sock_diag_save_cookie(tw, r->id.idiag_cookie);
+
 	r->id.idiag_sport     = tw->tw_sport;
 	r->id.idiag_dport     = tw->tw_dport;
+
+	memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src));
+	memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst));
+
 	r->id.idiag_src[0]    = tw->tw_rcv_saddr;
 	r->id.idiag_dst[0]    = tw->tw_daddr;
+
 	r->idiag_state	      = tw->tw_substate;
 	r->idiag_timer	      = 3;
 	r->idiag_expires      = DIV_ROUND_UP(tmo * 1000, HZ);
@@ -732,8 +743,13 @@
 
 	r->id.idiag_sport = inet->inet_sport;
 	r->id.idiag_dport = ireq->rmt_port;
+
+	memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src));
+	memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst));
+
 	r->id.idiag_src[0] = ireq->loc_addr;
 	r->id.idiag_dst[0] = ireq->rmt_addr;
+
 	r->idiag_expires = jiffies_to_msecs(tmo);
 	r->idiag_rqueue = 0;
 	r->idiag_wqueue = 0;
@@ -945,7 +961,7 @@
 			++num;
 		}
 
-		if (r->idiag_states & TCPF_TIME_WAIT) {
+		if (r->idiag_states & (TCPF_TIME_WAIT | TCPF_FIN_WAIT2)) {
 			struct inet_timewait_sock *tw;
 
 			inet_twsk_for_each(tw, node,
@@ -955,6 +971,8 @@
 
 				if (num < s_num)
 					goto next_dying;
+				if (!(r->idiag_states & (1 << tw->tw_substate)))
+					goto next_dying;
 				if (r->sdiag_family != AF_UNSPEC &&
 						tw->tw_family != r->sdiag_family)
 					goto next_dying;
diff -ruw linux-3.11.10/net/ipv4/inet_fragment.c linux-3.11.10-fbx/net/ipv4/inet_fragment.c
--- linux-3.11.10/net/ipv4/inet_fragment.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/inet_fragment.c	2015-09-29 11:15:09.009756568 +0200
@@ -211,7 +211,7 @@
 	}
 
 	work = frag_mem_limit(nf) - nf->low_thresh;
-	while (work > 0) {
+	while (work > 0 || force) {
 		spin_lock(&nf->lru_lock);
 
 		if (list_empty(&nf->lru_list)) {
@@ -281,9 +281,10 @@
 
 	atomic_inc(&qp->refcnt);
 	hlist_add_head(&qp->list, &hb->chain);
+	inet_frag_lru_add(nf, qp);
 	spin_unlock(&hb->chain_lock);
 	read_unlock(&f->lock);
-	inet_frag_lru_add(nf, qp);
+
 	return qp;
 }
 
diff -ruw linux-3.11.10/net/ipv4/ipconfig.c linux-3.11.10-fbx/net/ipv4/ipconfig.c
--- linux-3.11.10/net/ipv4/ipconfig.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/ipconfig.c	2015-09-29 11:15:09.013756597 +0200
@@ -191,15 +191,61 @@
 static struct ic_device *ic_first_dev __initdata = NULL;/* List of open device */
 static struct net_device *ic_dev __initdata = NULL;	/* Selected device */
 
-static bool __init ic_is_init_dev(struct net_device *dev)
+static bool __init ic_is_init_dev(struct net_device *dev, bool partial)
 {
+	char *p = NULL;
+	bool ret;
+
 	if (dev->flags & IFF_LOOPBACK)
 		return false;
-	return user_dev_name[0] ? !strcmp(dev->name, user_dev_name) :
+
+	if (partial) {
+		p = strchr(user_dev_name, '.');
+		if (p)
+			*p = 0;
+	}
+
+	ret = false;
+	if (user_dev_name[0] ? !strcmp(dev->name, user_dev_name) :
 	    (!(dev->flags & IFF_LOOPBACK) &&
 	     (dev->flags & (IFF_POINTOPOINT|IFF_BROADCAST)) &&
-	     strncmp(dev->name, "dummy", 5));
+	     strncmp(dev->name, "dummy", 5)))
+		ret = true;
+	if (p)
+		*p = '.';
+	return ret;
+}
+
+#ifdef CONFIG_VLAN_8021Q
+int register_vlan_device(struct net_device *real_dev, u16 vlan_id);
+
+static void __init prepare_vlan(void)
+{
+	unsigned short oflags;
+	struct net_device *dev;
+	char *p;
+	u16 vid;
+
+	if (!strchr(user_dev_name, '.'))
+		return;
+
+	p = strchr(user_dev_name, '.');
+	*p = 0;
+	vid = simple_strtoul(p + 1, NULL, 10);
+	dev = __dev_get_by_name(&init_net, user_dev_name);
+	if (!dev)
+		goto fail;
+
+	oflags = dev->flags;
+	if (dev_change_flags(dev, oflags | IFF_UP) < 0)
+		goto fail;
+
+	register_vlan_device(dev, vid);
+
+fail:
+	*p = '.';
 }
+#endif
 
 static int __init ic_open_devs(void)
 {
@@ -219,8 +265,13 @@
 			pr_err("IP-Config: Failed to open %s\n", dev->name);
 	}
 
+#ifdef CONFIG_VLAN_8021Q
+	/* register vlan device if needed */
+	prepare_vlan();
+#endif
+
 	for_each_netdev(&init_net, dev) {
-		if (ic_is_init_dev(dev)) {
+		if (ic_is_init_dev(dev, false)) {
 			int able = 0;
 			if (dev->mtu >= 364)
 				able |= IC_BOOTP;
@@ -268,7 +319,7 @@
 		int wait, elapsed;
 
 		for_each_netdev(&init_net, dev)
-			if (ic_is_init_dev(dev) && netif_carrier_ok(dev))
+			if (ic_is_init_dev(dev, false) && netif_carrier_ok(dev))
 				goto have_carrier;
 
 		msleep(1);
@@ -720,8 +771,10 @@
 			e += len;
 		}
 		if (*vendor_class_identifier) {
+#ifdef IPCONFIG_DEBUG
 			pr_info("DHCP: sending class identifier \"%s\"\n",
 				vendor_class_identifier);
+#endif
 			*e++ = 60;	/* Class-identifier */
 			len = strlen(vendor_class_identifier);
 			*e++ = len;
@@ -1378,7 +1431,7 @@
 
 		rtnl_lock();
 		for_each_netdev(&init_net, dev) {
-			if (ic_is_init_dev(dev)) {
+			if (ic_is_init_dev(dev, true)) {
 				found = 1;
 				break;
 			}
diff -ruw linux-3.11.10/net/ipv4/ip_forward.c linux-3.11.10-fbx/net/ipv4/ip_forward.c
--- linux-3.11.10/net/ipv4/ip_forward.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/ip_forward.c	2015-09-29 11:15:09.013756597 +0200
@@ -39,6 +39,71 @@
 #include <net/route.h>
 #include <net/xfrm.h>
 
+static bool ip_may_fragment(const struct sk_buff *skb)
+{
+	return unlikely((ip_hdr(skb)->frag_off & htons(IP_DF)) == 0) ||
+		skb->local_df;
+}
+
+static bool ip_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
+{
+	if (skb->len <= mtu)
+		return false;
+
+	if (skb_is_gso(skb) && skb_gso_network_seglen(skb) <= mtu)
+		return false;
+
+	return true;
+}
+
+static bool ip_gso_exceeds_dst_mtu(const struct sk_buff *skb)
+{
+	unsigned int mtu;
+
+	if (skb->local_df || !skb_is_gso(skb))
+		return false;
+
+	mtu = dst_mtu(skb_dst(skb));
+
+	/* if seglen > mtu, do software segmentation for IP fragmentation on
+	 * output.  DF bit cannot be set since ip_forward would have sent
+	 * icmp error.
+	 */
+	return skb_gso_network_seglen(skb) > mtu;
+}
+
+/* called if GSO skb needs to be fragmented on forward */
+static int ip_forward_finish_gso(struct sk_buff *skb)
+{
+	struct dst_entry *dst = skb_dst(skb);
+	netdev_features_t features;
+	struct sk_buff *segs;
+	int ret = 0;
+
+	features = netif_skb_dev_features(skb, dst->dev);
+	segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
+	if (IS_ERR(segs)) {
+		kfree_skb(skb);
+		return -ENOMEM;
+	}
+
+	consume_skb(skb);
+
+	do {
+		struct sk_buff *nskb = segs->next;
+		int err;
+
+		segs->next = NULL;
+		err = dst_output(segs);
+
+		if (err && ret == 0)
+			ret = err;
+		segs = nskb;
+	} while (segs);
+
+	return ret;
+}
+
 static int ip_forward_finish(struct sk_buff *skb)
 {
 	struct ip_options *opt	= &(IPCB(skb)->opt);
@@ -49,6 +114,9 @@
 	if (unlikely(opt->optlen))
 		ip_forward_options(skb);
 
+	if (ip_gso_exceeds_dst_mtu(skb))
+		return ip_forward_finish_gso(skb);
+
 	return dst_output(skb);
 }
 
@@ -88,8 +156,7 @@
 	if (opt->is_strictroute && rt->rt_uses_gateway)
 		goto sr_failed;
 
-	if (unlikely(skb->len > dst_mtu(&rt->dst) && !skb_is_gso(skb) &&
-		     (ip_hdr(skb)->frag_off & htons(IP_DF))) && !skb->local_df) {
+	if (!ip_may_fragment(skb) && ip_exceeds_mtu(skb, dst_mtu(&rt->dst))) {
 		IP_INC_STATS(dev_net(rt->dst.dev), IPSTATS_MIB_FRAGFAILS);
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
 			  htonl(dst_mtu(&rt->dst)));
diff -ruw linux-3.11.10/net/ipv4/ip_fragment.c linux-3.11.10-fbx/net/ipv4/ip_fragment.c
--- linux-3.11.10/net/ipv4/ip_fragment.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/ip_fragment.c	2015-09-29 11:15:09.013756597 +0200
@@ -623,8 +623,7 @@
 	IPCB(head)->frag_max_size = qp->q.max_size;
 
 	iph = ip_hdr(head);
-	/* max_size != 0 implies at least one fragment had IP_DF set */
-	iph->frag_off = qp->q.max_size ? htons(IP_DF) : 0;
+	iph->frag_off = 0;
 	iph->tot_len = htons(len);
 	iph->tos |= ecn;
 	IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS);
diff -ruw linux-3.11.10/net/ipv4/ip_input.c linux-3.11.10-fbx/net/ipv4/ip_input.c
--- linux-3.11.10/net/ipv4/ip_input.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/ip_input.c	2015-09-29 11:15:09.013756597 +0200
@@ -186,10 +186,15 @@
 	return false;
 }
 
-static int ip_local_deliver_finish(struct sk_buff *skb)
+int ip_local_deliver_finish(struct sk_buff *skb)
 {
 	struct net *net = dev_net(skb->dev);
 
+#ifdef CONFIG_IP_FFN
+	if (skb->ffn_state == FFN_STATE_FORWARDABLE)
+		ip_ffn_add(skb, IP_FFN_LOCAL_IN);
+#endif
+
 	__skb_pull(skb, skb_network_header_len(skb));
 
 	rcu_read_lock();
@@ -313,7 +318,7 @@
 	const struct iphdr *iph = ip_hdr(skb);
 	struct rtable *rt;
 
-	if (sysctl_ip_early_demux && !skb_dst(skb)) {
+	if (sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) {
 		const struct net_protocol *ipprot;
 		int protocol = iph->protocol;
 
@@ -442,6 +447,11 @@
 	/* Must drop socket now because of tproxy. */
 	skb_orphan(skb);
 
+#ifdef CONFIG_IP_FFN
+	if (!ip_ffn_process(skb))
+		return NET_RX_SUCCESS;
+#endif
+
 	return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL,
 		       ip_rcv_finish);
 
diff -ruw linux-3.11.10/net/ipv4/ip_output.c linux-3.11.10-fbx/net/ipv4/ip_output.c
--- linux-3.11.10/net/ipv4/ip_output.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/ip_output.c	2015-09-29 11:15:09.013756597 +0200
@@ -192,6 +192,11 @@
 		skb = skb2;
 	}
 
+#ifdef CONFIG_IP_FFN
+	if (skb->ffn_state == FFN_STATE_FORWARDABLE)
+		ip_ffn_add(skb, IP_FFN_FINISH_OUT);
+#endif
+
 	rcu_read_lock_bh();
 	nexthop = (__force u32) rt_nexthop(rt, ip_hdr(skb)->daddr);
 	neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
@@ -199,7 +204,6 @@
 		neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
 	if (!IS_ERR(neigh)) {
 		int res = dst_neigh_output(dst, neigh, skb);
-
 		rcu_read_unlock_bh();
 		return res;
 	}
@@ -296,6 +300,11 @@
 	skb->dev = dev;
 	skb->protocol = htons(ETH_P_IP);
 
+#ifdef CONFIG_IP_FFN
+	if (skb->ffn_state == FFN_STATE_FAST_FORWARDED)
+		return ip_finish_output(skb);
+#endif
+
 	return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, dev,
 			    ip_finish_output,
 			    !(IPCB(skb)->flags & IPSKB_REROUTED));
@@ -1537,4 +1546,7 @@
 #if defined(CONFIG_IP_MULTICAST) && defined(CONFIG_PROC_FS)
 	igmp_mc_proc_init();
 #endif
+#ifdef CONFIG_IP_FFN
+	ip_ffn_init();
+#endif
 }
diff -ruw linux-3.11.10/net/ipv4/ip_sockglue.c linux-3.11.10-fbx/net/ipv4/ip_sockglue.c
--- linux-3.11.10/net/ipv4/ip_sockglue.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/ip_sockglue.c	2015-09-29 11:15:09.013756597 +0200
@@ -368,7 +368,7 @@
 /*
  *	Handle MSG_ERRQUEUE
  */
-int ip_recv_error(struct sock *sk, struct msghdr *msg, int len)
+int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
 {
 	struct sock_exterr_skb *serr;
 	struct sk_buff *skb, *skb2;
@@ -405,6 +405,7 @@
 						   serr->addr_offset);
 		sin->sin_port = serr->port;
 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
+		*addr_len = sizeof(*sin);
 	}
 
 	memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
diff -ruw linux-3.11.10/net/ipv4/ip_tunnel.c linux-3.11.10-fbx/net/ipv4/ip_tunnel.c
--- linux-3.11.10/net/ipv4/ip_tunnel.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/ip_tunnel.c	2015-09-29 11:15:09.013756597 +0200
@@ -621,6 +621,7 @@
 				tunnel->err_time + IPTUNNEL_ERR_TIMEO)) {
 			tunnel->err_count--;
 
+			memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
 			dst_link_failure(skb);
 		} else
 			tunnel->err_count = 0;
diff -ruw linux-3.11.10/net/ipv4/ip_tunnel_core.c linux-3.11.10-fbx/net/ipv4/ip_tunnel_core.c
--- linux-3.11.10/net/ipv4/ip_tunnel_core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/ip_tunnel_core.c	2015-09-29 11:15:09.013756597 +0200
@@ -45,6 +45,9 @@
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
 #include <net/rtnetlink.h>
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+#include <net/netfilter/nf_conntrack.h>
+#endif
 
 int iptunnel_xmit(struct net *net, struct rtable *rt,
 		  struct sk_buff *skb,
@@ -56,6 +59,13 @@
 	int err;
 
 	nf_reset(skb);
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+	if (proto == IPPROTO_IPV6) {
+		skb->nfct = &nf_ct_untracked_get()->ct_general;
+		skb->nfctinfo = IP_CT_NEW;
+		nf_conntrack_get(skb->nfct);
+	}
+#endif
 	secpath_reset(skb);
 	skb->rxhash = 0;
 	skb_dst_drop(skb);
diff -ruw linux-3.11.10/net/ipv4/Kconfig linux-3.11.10-fbx/net/ipv4/Kconfig
--- linux-3.11.10/net/ipv4/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/Kconfig	2015-09-29 11:15:09.005756542 +0200
@@ -369,6 +369,11 @@
 	tristate
 	default n
 
+config INET_XFRM_GC_THRESH
+	int "IP: xfrm garbage collect threshold"
+	depends on XFRM
+	default 1024
+
 config INET_XFRM_MODE_TRANSPORT
 	tristate "IP: IPsec transport mode"
 	default y
diff -ruw linux-3.11.10/net/ipv4/Makefile linux-3.11.10-fbx/net/ipv4/Makefile
--- linux-3.11.10/net/ipv4/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/Makefile	2015-09-29 11:15:09.005756542 +0200
@@ -15,6 +15,8 @@
 
 obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o
 obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o
+
+obj-$(CONFIG_IP_FFN) += ip_ffn.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o
 obj-$(CONFIG_IP_MROUTE) += ipmr.o
diff -ruw linux-3.11.10/net/ipv4/netfilter/ip_tables.c linux-3.11.10-fbx/net/ipv4/netfilter/ip_tables.c
--- linux-3.11.10/net/ipv4/netfilter/ip_tables.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/netfilter/ip_tables.c	2015-09-29 11:15:09.017756634 +0200
@@ -30,6 +30,9 @@
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <net/netfilter/nf_log.h>
 #include "../../netfilter/xt_repldata.h"
+#ifdef CONFIG_FBXBRIDGE
+#include <linux/fbxbridge.h>
+#endif
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
@@ -1226,8 +1229,10 @@
 
 	xt_free_table_info(oldinfo);
 	if (copy_to_user(counters_ptr, counters,
-			 sizeof(struct xt_counters) * num_counters) != 0)
-		ret = -EFAULT;
+			 sizeof(struct xt_counters) * num_counters) != 0) {
+		/* Silent error, can't fail, new table is already in place */
+		net_warn_ratelimited("iptables: counters copy to user failed while replacing table\n");
+	}
 	vfree(counters);
 	xt_table_unlock(t);
 	return ret;
@@ -1280,6 +1285,14 @@
 			   tmp.num_counters, tmp.counters);
 	if (ret)
 		goto free_newinfo_untrans;
+
+#ifdef CONFIG_IP_FFN
+	ip_ffn_flush_all();
+#endif
+
+#if defined(CONFIG_FBXBRIDGE) || defined(CONFIG_FBXBRIDGE_MODULE)
+	fbxbridge_fp_flush_all();
+#endif
 	return 0;
 
  free_newinfo_untrans:
diff -ruw linux-3.11.10/net/ipv4/netfilter/Kconfig linux-3.11.10-fbx/net/ipv4/netfilter/Kconfig
--- linux-3.11.10/net/ipv4/netfilter/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/netfilter/Kconfig	2015-09-29 11:15:09.017756634 +0200
@@ -5,6 +5,13 @@
 menu "IP: Netfilter Configuration"
 	depends on INET && NETFILTER
 
+config IP_FFN
+	bool "IP: Fast forwarding and NAT"
+
+config IP_FFN_PROCFS
+	bool "IP: Fast forwarding and NAT /proc/net entries"
+	depends on IP_FFN
+
 config NF_DEFRAG_IPV4
 	tristate
 	default n
diff -ruw linux-3.11.10/net/ipv4/netfilter/nf_defrag_ipv4.c linux-3.11.10-fbx/net/ipv4/netfilter/nf_defrag_ipv4.c
--- linux-3.11.10/net/ipv4/netfilter/nf_defrag_ipv4.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/netfilter/nf_defrag_ipv4.c	2015-09-29 11:15:09.021756659 +0200
@@ -22,7 +22,6 @@
 #endif
 #include <net/netfilter/nf_conntrack_zones.h>
 
-/* Returns new sk_buff, or NULL */
 static int nf_ct_ipv4_gather_frags(struct sk_buff *skb, u_int32_t user)
 {
 	int err;
@@ -33,8 +32,10 @@
 	err = ip_defrag(skb, user);
 	local_bh_enable();
 
-	if (!err)
+	if (!err) {
 		ip_send_check(ip_hdr(skb));
+		skb->local_df = 1;
+	}
 
 	return err;
 }
diff -ruw linux-3.11.10/net/ipv4/ping.c linux-3.11.10-fbx/net/ipv4/ping.c
--- linux-3.11.10/net/ipv4/ping.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/ping.c	2015-09-29 11:15:09.021756659 +0200
@@ -249,26 +249,33 @@
 {
 	struct net *net = sock_net(sk);
 	kgid_t group = current_egid();
-	struct group_info *group_info = get_current_groups();
-	int i, j, count = group_info->ngroups;
+	struct group_info *group_info;
+	int i, j, count;
 	kgid_t low, high;
+	int ret = 0;
 
 	inet_get_ping_group_range_net(net, &low, &high);
 	if (gid_lte(low, group) && gid_lte(group, high))
 		return 0;
 
+	group_info = get_current_groups();
+	count = group_info->ngroups;
 	for (i = 0; i < group_info->nblocks; i++) {
 		int cp_count = min_t(int, NGROUPS_PER_BLOCK, count);
 		for (j = 0; j < cp_count; j++) {
 			kgid_t gid = group_info->blocks[i][j];
 			if (gid_lte(low, gid) && gid_lte(gid, high))
-				return 0;
+				goto out_release_group;
 		}
 
 		count -= cp_count;
 	}
 
-	return -EACCES;
+	ret = -EACCES;
+
+out_release_group:
+	put_group_info(group_info);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(ping_init_sock);
 
@@ -769,7 +776,7 @@
 		err = PTR_ERR(rt);
 		rt = NULL;
 		if (err == -ENETUNREACH)
-			IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES);
+			IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
 		goto out;
 	}
 
@@ -827,8 +834,6 @@
 {
 	struct inet_sock *isk = inet_sk(sk);
 	int family = sk->sk_family;
-	struct sockaddr_in *sin;
-	struct sockaddr_in6 *sin6;
 	struct sk_buff *skb;
 	int copied, err;
 
@@ -838,19 +843,13 @@
 	if (flags & MSG_OOB)
 		goto out;
 
-	if (addr_len) {
-		if (family == AF_INET)
-			*addr_len = sizeof(*sin);
-		else if (family == AF_INET6 && addr_len)
-			*addr_len = sizeof(*sin6);
-	}
-
 	if (flags & MSG_ERRQUEUE) {
 		if (family == AF_INET) {
-			return ip_recv_error(sk, msg, len);
+			return ip_recv_error(sk, msg, len, addr_len);
 #if IS_ENABLED(CONFIG_IPV6)
 		} else if (family == AF_INET6) {
-			return pingv6_ops.ipv6_recv_error(sk, msg, len);
+			return pingv6_ops.ipv6_recv_error(sk, msg, len,
+							  addr_len);
 #endif
 		}
 	}
@@ -874,11 +873,15 @@
 
 	/* Copy the address and add cmsg data. */
 	if (family == AF_INET) {
-		sin = (struct sockaddr_in *) msg->msg_name;
+		struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
+
+		if (sin) {
 		sin->sin_family = AF_INET;
 		sin->sin_port = 0 /* skb->h.uh->source */;
 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
 		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+			*addr_len = sizeof(*sin);
+		}
 
 		if (isk->cmsg_flags)
 			ip_cmsg_recv(msg, skb);
@@ -887,17 +890,21 @@
 	} else if (family == AF_INET6) {
 		struct ipv6_pinfo *np = inet6_sk(sk);
 		struct ipv6hdr *ip6 = ipv6_hdr(skb);
-		sin6 = (struct sockaddr_in6 *) msg->msg_name;
+		struct sockaddr_in6 *sin6 =
+			(struct sockaddr_in6 *)msg->msg_name;
+
+		if (sin6) {
 		sin6->sin6_family = AF_INET6;
 		sin6->sin6_port = 0;
 		sin6->sin6_addr = ip6->saddr;
-
 		sin6->sin6_flowinfo = 0;
 		if (np->sndflow)
 			sin6->sin6_flowinfo = ip6_flowinfo(ip6);
-
-		sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr,
+			sin6->sin6_scope_id =
+				ipv6_iface_scope_id(&sin6->sin6_addr,
 							  IP6CB(skb)->iif);
+			*addr_len = sizeof(*sin6);
+		}
 
 		if (inet6_sk(sk)->rxopt.all)
 			pingv6_ops.ip6_datagram_recv_ctl(sk, msg, skb);
diff -ruw linux-3.11.10/net/ipv4/raw.c linux-3.11.10-fbx/net/ipv4/raw.c
--- linux-3.11.10/net/ipv4/raw.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/raw.c	2015-09-29 11:15:09.021756659 +0200
@@ -692,11 +692,8 @@
 	if (flags & MSG_OOB)
 		goto out;
 
-	if (addr_len)
-		*addr_len = sizeof(*sin);
-
 	if (flags & MSG_ERRQUEUE) {
-		err = ip_recv_error(sk, msg, len);
+		err = ip_recv_error(sk, msg, len, addr_len);
 		goto out;
 	}
 
@@ -722,6 +719,7 @@
 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
 		sin->sin_port = 0;
 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
+		*addr_len = sizeof(*sin);
 	}
 	if (inet->cmsg_flags)
 		ip_cmsg_recv(msg, skb);
diff -ruw linux-3.11.10/net/ipv4/route.c linux-3.11.10-fbx/net/ipv4/route.c
--- linux-3.11.10/net/ipv4/route.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/route.c	2015-09-29 11:15:09.021756659 +0200
@@ -1527,7 +1527,7 @@
 	struct in_device *out_dev;
 	unsigned int flags = 0;
 	bool do_cache;
-	u32 itag;
+	u32 itag = 0;
 
 	/* get a working reference to the output device */
 	out_dev = __in_dev_get_rcu(FIB_RES_DEV(*res));
@@ -1598,6 +1598,7 @@
 	rth->rt_gateway	= 0;
 	rth->rt_uses_gateway = 0;
 	INIT_LIST_HEAD(&rth->rt_uncached);
+	RT_CACHE_STAT_INC(in_slow_tot);
 
 	rth->dst.input = ip_forward;
 	rth->dst.output = ip_output;
@@ -1699,8 +1700,6 @@
 	if (err != 0)
 		goto no_route;
 
-	RT_CACHE_STAT_INC(in_slow_tot);
-
 	if (res.type == RTN_BROADCAST)
 		goto brd_input;
 
@@ -1769,13 +1768,18 @@
 	rth->rt_gateway	= 0;
 	rth->rt_uses_gateway = 0;
 	INIT_LIST_HEAD(&rth->rt_uncached);
+	RT_CACHE_STAT_INC(in_slow_tot);
 	if (res.type == RTN_UNREACHABLE) {
 		rth->dst.input= ip_error;
 		rth->dst.error= -err;
 		rth->rt_flags 	&= ~RTCF_LOCAL;
 	}
-	if (do_cache)
-		rt_cache_route(&FIB_RES_NH(res), rth);
+	if (do_cache) {
+		if (unlikely(!rt_cache_route(&FIB_RES_NH(res), rth))) {
+			rth->dst.flags |= DST_NOCACHE;
+			rt_add_uncached_list(rth);
+		}
+	}
 	skb_dst_set(skb, &rth->dst);
 	err = 0;
 	goto out;
@@ -2356,7 +2360,7 @@
 			}
 		} else
 #endif
-			if (nla_put_u32(skb, RTA_IIF, rt->rt_iif))
+			if (nla_put_u32(skb, RTA_IIF, skb->dev->ifindex))
 				goto nla_put_failure;
 	}
 
diff -ruw linux-3.11.10/net/ipv4/tcp.c linux-3.11.10-fbx/net/ipv4/tcp.c
--- linux-3.11.10/net/ipv4/tcp.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/tcp.c	2015-09-29 11:15:09.025756688 +0200
@@ -810,12 +810,6 @@
 		xmit_size_goal = min_t(u32, gso_size,
 				       sk->sk_gso_max_size - 1 - hlen);
 
-		/* TSQ : try to have at least two segments in flight
-		 * (one in NIC TX ring, another in Qdisc)
-		 */
-		xmit_size_goal = min_t(u32, xmit_size_goal,
-				       sysctl_tcp_limit_output_bytes >> 1);
-
 		xmit_size_goal = tcp_bound_to_half_wnd(tp, xmit_size_goal);
 
 		/* We try hard to avoid divides here */
@@ -1010,7 +1004,8 @@
 	}
 }
 
-static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg, int *size)
+static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg,
+				int *copied, size_t size)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
 	int err, flags;
@@ -1025,11 +1020,12 @@
 	if (unlikely(tp->fastopen_req == NULL))
 		return -ENOBUFS;
 	tp->fastopen_req->data = msg;
+	tp->fastopen_req->size = size;
 
 	flags = (msg->msg_flags & MSG_DONTWAIT) ? O_NONBLOCK : 0;
 	err = __inet_stream_connect(sk->sk_socket, msg->msg_name,
 				    msg->msg_namelen, flags);
-	*size = tp->fastopen_req->copied;
+	*copied = tp->fastopen_req->copied;
 	tcp_free_fastopen_req(tp);
 	return err;
 }
@@ -1049,7 +1045,7 @@
 
 	flags = msg->msg_flags;
 	if (flags & MSG_FASTOPEN) {
-		err = tcp_sendmsg_fastopen(sk, msg, &copied_syn);
+		err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size);
 		if (err == -EINPROGRESS && copied_syn > 0)
 			goto out;
 		else if (err)
diff -ruw linux-3.11.10/net/ipv4/tcp_cubic.c linux-3.11.10-fbx/net/ipv4/tcp_cubic.c
--- linux-3.11.10/net/ipv4/tcp_cubic.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/tcp_cubic.c	2015-09-29 11:15:09.025756688 +0200
@@ -408,7 +408,7 @@
 		ratio -= ca->delayed_ack >> ACK_RATIO_SHIFT;
 		ratio += cnt;
 
-		ca->delayed_ack = min(ratio, ACK_RATIO_LIMIT);
+		ca->delayed_ack = clamp(ratio, 1U, ACK_RATIO_LIMIT);
 	}
 
 	/* Some calls are for duplicates without timetamps */
diff -ruw linux-3.11.10/net/ipv4/tcp_input.c linux-3.11.10-fbx/net/ipv4/tcp_input.c
--- linux-3.11.10/net/ipv4/tcp_input.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/tcp_input.c	2015-09-29 11:15:09.025756688 +0200
@@ -2622,13 +2622,12 @@
 	bool recovered = !before(tp->snd_una, tp->high_seq);
 
 	if (tp->frto) { /* F-RTO RFC5682 sec 3.1 (sack enhanced version). */
-		if (flag & FLAG_ORIG_SACK_ACKED) {
 			/* Step 3.b. A timeout is spurious if not all data are
 			 * lost, i.e., never-retransmitted data are (s)acked.
 			 */
-			tcp_try_undo_loss(sk, true);
+		if (tcp_try_undo_loss(sk, flag & FLAG_ORIG_SACK_ACKED))
 			return;
-		}
+
 		if (after(tp->snd_nxt, tp->high_seq) &&
 		    (flag & FLAG_DATA_SACKED || is_dupack)) {
 			tp->frto = 0; /* Loss was real: 2nd part of step 3.a */
diff -ruw linux-3.11.10/net/ipv4/tcp_ipv4.c linux-3.11.10-fbx/net/ipv4/tcp_ipv4.c
--- linux-3.11.10/net/ipv4/tcp_ipv4.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/tcp_ipv4.c	2015-09-29 11:15:09.025756688 +0200
@@ -177,7 +177,7 @@
 	if (IS_ERR(rt)) {
 		err = PTR_ERR(rt);
 		if (err == -ENETUNREACH)
-			IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
+			IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
 		return err;
 	}
 
diff -ruw linux-3.11.10/net/ipv4/tcp_metrics.c linux-3.11.10-fbx/net/ipv4/tcp_metrics.c
--- linux-3.11.10/net/ipv4/tcp_metrics.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/tcp_metrics.c	2015-09-29 11:15:09.029756725 +0200
@@ -22,6 +22,9 @@
 
 int sysctl_tcp_nometrics_save __read_mostly;
 
+static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *addr,
+						   struct net *net, unsigned int hash);
+
 struct tcp_fastopen_metrics {
 	u16	mss;
 	u16	syn_loss:10;		/* Recurring Fast Open SYN losses */
@@ -130,16 +133,41 @@
 	}
 }
 
+#define TCP_METRICS_TIMEOUT		(60 * 60 * HZ)
+
+static void tcpm_check_stamp(struct tcp_metrics_block *tm, struct dst_entry *dst)
+{
+	if (tm && unlikely(time_after(jiffies, tm->tcpm_stamp + TCP_METRICS_TIMEOUT)))
+		tcpm_suck_dst(tm, dst, false);
+}
+
+#define TCP_METRICS_RECLAIM_DEPTH	5
+#define TCP_METRICS_RECLAIM_PTR		(struct tcp_metrics_block *) 0x1UL
+
 static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst,
 					  struct inetpeer_addr *addr,
-					  unsigned int hash,
-					  bool reclaim)
+					  unsigned int hash)
 {
 	struct tcp_metrics_block *tm;
 	struct net *net;
+	bool reclaim = false;
 
 	spin_lock_bh(&tcp_metrics_lock);
 	net = dev_net(dst->dev);
+
+	/* While waiting for the spin-lock the cache might have been populated
+	 * with this entry and so we have to check again.
+	 */
+	tm = __tcp_get_metrics(addr, net, hash);
+	if (tm == TCP_METRICS_RECLAIM_PTR) {
+		reclaim = true;
+		tm = NULL;
+	}
+	if (tm) {
+		tcpm_check_stamp(tm, dst);
+		goto out_unlock;
+	}
+
 	if (unlikely(reclaim)) {
 		struct tcp_metrics_block *oldest;
 
@@ -169,17 +197,6 @@
 	return tm;
 }
 
-#define TCP_METRICS_TIMEOUT		(60 * 60 * HZ)
-
-static void tcpm_check_stamp(struct tcp_metrics_block *tm, struct dst_entry *dst)
-{
-	if (tm && unlikely(time_after(jiffies, tm->tcpm_stamp + TCP_METRICS_TIMEOUT)))
-		tcpm_suck_dst(tm, dst, false);
-}
-
-#define TCP_METRICS_RECLAIM_DEPTH	5
-#define TCP_METRICS_RECLAIM_PTR		(struct tcp_metrics_block *) 0x1UL
-
 static struct tcp_metrics_block *tcp_get_encode(struct tcp_metrics_block *tm, int depth)
 {
 	if (tm)
@@ -280,7 +297,6 @@
 	struct inetpeer_addr addr;
 	unsigned int hash;
 	struct net *net;
-	bool reclaim;
 
 	addr.family = sk->sk_family;
 	switch (addr.family) {
@@ -300,13 +316,10 @@
 	hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log);
 
 	tm = __tcp_get_metrics(&addr, net, hash);
-	reclaim = false;
-	if (tm == TCP_METRICS_RECLAIM_PTR) {
-		reclaim = true;
+	if (tm == TCP_METRICS_RECLAIM_PTR)
 		tm = NULL;
-	}
 	if (!tm && create)
-		tm = tcpm_new(dst, &addr, hash, reclaim);
+		tm = tcpm_new(dst, &addr, hash);
 	else
 		tcpm_check_stamp(tm, dst);
 
@@ -665,10 +678,13 @@
 void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
 			    struct tcp_fastopen_cookie *cookie, bool syn_lost)
 {
+	struct dst_entry *dst = __sk_dst_get(sk);
 	struct tcp_metrics_block *tm;
 
+	if (!dst)
+		return;
 	rcu_read_lock();
-	tm = tcp_get_metrics(sk, __sk_dst_get(sk), true);
+	tm = tcp_get_metrics(sk, dst, true);
 	if (tm) {
 		struct tcp_fastopen_metrics *tfom = &tm->tcpm_fastopen;
 
diff -ruw linux-3.11.10/net/ipv4/tcp_offload.c linux-3.11.10-fbx/net/ipv4/tcp_offload.c
--- linux-3.11.10/net/ipv4/tcp_offload.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/tcp_offload.c	2015-09-29 11:15:09.029756725 +0200
@@ -272,33 +272,32 @@
 {
 	const struct iphdr *iph = skb_gro_network_header(skb);
 	__wsum wsum;
-	__sum16 sum;
+
+	/* Don't bother verifying checksum if we're going to flush anyway. */
+	if (NAPI_GRO_CB(skb)->flush)
+		goto skip_csum;
+
+	wsum = skb->csum;
 
 	switch (skb->ip_summed) {
+	case CHECKSUM_NONE:
+		wsum = skb_checksum(skb, skb_gro_offset(skb), skb_gro_len(skb),
+				    0);
+
+		/* fall through */
+
 	case CHECKSUM_COMPLETE:
 		if (!tcp_v4_check(skb_gro_len(skb), iph->saddr, iph->daddr,
-				  skb->csum)) {
+				  wsum)) {
 			skb->ip_summed = CHECKSUM_UNNECESSARY;
 			break;
 		}
-flush:
+
 		NAPI_GRO_CB(skb)->flush = 1;
 		return NULL;
-
-	case CHECKSUM_NONE:
-		wsum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
-					  skb_gro_len(skb), IPPROTO_TCP, 0);
-		sum = csum_fold(skb_checksum(skb,
-					     skb_gro_offset(skb),
-					     skb_gro_len(skb),
-					     wsum));
-		if (sum)
-			goto flush;
-
-		skb->ip_summed = CHECKSUM_UNNECESSARY;
-		break;
 	}
 
+skip_csum:
 	return tcp_gro_receive(head, skb);
 }
 
diff -ruw linux-3.11.10/net/ipv4/tcp_output.c linux-3.11.10-fbx/net/ipv4/tcp_output.c
--- linux-3.11.10/net/ipv4/tcp_output.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/tcp_output.c	2015-09-29 11:15:09.029756725 +0200
@@ -691,7 +691,8 @@
 	if ((1 << sk->sk_state) &
 	    (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_CLOSING |
 	     TCPF_CLOSE_WAIT  | TCPF_LAST_ACK))
-		tcp_write_xmit(sk, tcp_current_mss(sk), 0, 0, GFP_ATOMIC);
+		tcp_write_xmit(sk, tcp_current_mss(sk), tcp_sk(sk)->nonagle,
+			       0, GFP_ATOMIC);
 }
 /*
  * One tasklest per cpu tries to send more skbs.
@@ -759,6 +760,17 @@
 	if (flags & (1UL << TCP_TSQ_DEFERRED))
 		tcp_tsq_handler(sk);
 
+	/* Here begins the tricky part :
+	 * We are called from release_sock() with :
+	 * 1) BH disabled
+	 * 2) sk_lock.slock spinlock held
+	 * 3) socket owned by us (sk->sk_lock.owned == 1)
+	 *
+	 * But following code is meant to be called from BH handlers,
+	 * so we should keep BH disabled, but early release socket ownership
+	 */
+	sock_release_ownership(sk);
+
 	if (flags & (1UL << TCP_WRITE_TIMER_DEFERRED)) {
 		tcp_write_timer_handler(sk);
 		__sock_put(sk);
@@ -1871,11 +1883,23 @@
 		 *  - better RTT estimation and ACK scheduling
 		 *  - faster recovery
 		 *  - high rates
+		 * Alas, some drivers / subsystems require a fair amount
+		 * of queued bytes to ensure line rate.
+		 * One example is wifi aggregation (802.11 AMPDU)
 		 */
-		limit = max(skb->truesize, sk->sk_pacing_rate >> 10);
+		limit = max_t(unsigned int, sysctl_tcp_limit_output_bytes,
+			      sk->sk_pacing_rate >> 10);
 
 		if (atomic_read(&sk->sk_wmem_alloc) > limit) {
 			set_bit(TSQ_THROTTLED, &tp->tsq_flags);
+			/* It is possible TX completion already happened
+			 * before we set TSQ_THROTTLED, so we must
+			 * test again the condition.
+			 * We abuse smp_mb__after_clear_bit() because
+			 * there is no smp_mb__after_set_bit() yet
+			 */
+			smp_mb__after_clear_bit();
+			if (atomic_read(&sk->sk_wmem_alloc) > limit)
 			break;
 		}
 
@@ -2885,7 +2909,12 @@
 	space = __tcp_mtu_to_mss(sk, inet_csk(sk)->icsk_pmtu_cookie) -
 		MAX_TCP_OPTION_SPACE;
 
-	syn_data = skb_copy_expand(syn, skb_headroom(syn), space,
+	space = min_t(size_t, space, fo->size);
+
+	/* limit to order-0 allocations */
+	space = min_t(size_t, space, SKB_MAX_HEAD(MAX_TCP_HEADER));
+
+	syn_data = skb_copy_expand(syn, MAX_TCP_HEADER, space,
 				   sk->sk_allocation);
 	if (syn_data == NULL)
 		goto fallback;
@@ -3104,7 +3133,6 @@
 {
 	if (sk->sk_state == TCP_ESTABLISHED) {
 		tcp_sk(sk)->snd_wl1 = tcp_sk(sk)->rcv_nxt - 1;
-		tcp_sk(sk)->snd_nxt = tcp_sk(sk)->write_seq;
 		tcp_xmit_probe_skb(sk, 0);
 	}
 }
diff -ruw linux-3.11.10/net/ipv4/udp.c linux-3.11.10-fbx/net/ipv4/udp.c
--- linux-3.11.10/net/ipv4/udp.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/udp.c	2015-09-29 11:15:09.029756725 +0200
@@ -208,6 +208,8 @@
 					 const struct sock *sk2),
 		     unsigned int hash2_nulladdr)
 {
+	struct sock *sk2;
+	struct hlist_nulls_node *node;
 	struct udp_hslot *hslot, *hslot2;
 	struct udp_table *udptable = sk->sk_prot->h.udp_table;
 	int    error = 1;
@@ -287,6 +289,48 @@
 	inet_sk(sk)->inet_num = snum;
 	udp_sk(sk)->udp_port_hash = snum;
 	udp_sk(sk)->udp_portaddr_hash ^= snum;
+
+	/* resolve udp reuse conflict */
+	if (sk->sk_reuse) {
+		bool found;
+
+		found = false;
+		sk_nulls_for_each(sk2, node, &hslot->head) {
+			if (!net_eq(sock_net(sk2), net) ||
+			    sk2 == sk ||
+			    (udp_sk(sk2)->udp_port_hash != snum))
+				continue;
+
+			if (sk2->sk_bound_dev_if &&
+			    sk->sk_bound_dev_if &&
+			    sk2->sk_bound_dev_if != sk->sk_bound_dev_if)
+				continue;
+
+			if (!(*saddr_comp)(sk, sk2))
+				continue;
+
+			found = true;
+			break;
+		}
+
+		sk_nulls_for_each(sk2, node, &hslot->head) {
+			if (!net_eq(sock_net(sk2), net) ||
+			    sk2 == sk ||
+			    (udp_sk(sk2)->udp_port_hash != snum))
+				continue;
+
+			if (sk2->sk_bound_dev_if &&
+			    sk->sk_bound_dev_if &&
+			    sk2->sk_bound_dev_if != sk->sk_bound_dev_if)
+				continue;
+
+			if (!(*saddr_comp)(sk, sk2))
+				continue;
+
+			sk->sk_reuse_conflict = found;
+		}
+	}
+
 	if (sk_unhashed(sk)) {
 		sk_nulls_add_node_rcu(sk, &hslot->head);
 		hslot->count++;
@@ -972,7 +1016,7 @@
 			err = PTR_ERR(rt);
 			rt = NULL;
 			if (err == -ENETUNREACH)
-				IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES);
+				IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
 			goto out;
 		}
 
@@ -1071,6 +1115,9 @@
 	struct udp_sock *up = udp_sk(sk);
 	int ret;
 
+	if (flags & MSG_SENDPAGE_NOTLAST)
+		flags |= MSG_MORE;
+
 	if (!up->pending) {
 		struct msghdr msg = {	.msg_flags = flags|MSG_MORE };
 
@@ -1208,14 +1255,8 @@
 	int is_udplite = IS_UDPLITE(sk);
 	bool slow;
 
-	/*
-	 *	Check any passed addresses
-	 */
-	if (addr_len)
-		*addr_len = sizeof(*sin);
-
 	if (flags & MSG_ERRQUEUE)
-		return ip_recv_error(sk, msg, len);
+		return ip_recv_error(sk, msg, len, addr_len);
 
 try_again:
 	skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
@@ -1275,6 +1316,7 @@
 		sin->sin_port = udp_hdr(skb)->source;
 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
 		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+		*addr_len = sizeof(*sin);
 	}
 	if (inet->cmsg_flags)
 		ip_cmsg_recv(msg, skb);
@@ -1628,6 +1670,64 @@
 	return 0;
 }
 
+/*
+ *	Unicast goes to one listener and all sockets with dup flag
+ *
+ *	Note: called only from the BH handler context.
+ */
+static int __udp4_lib_uc_conflict_deliver(struct net *net, struct sk_buff *skb,
+					  struct udphdr  *uh,
+					  __be32 saddr, __be32 daddr,
+					  struct udp_table *udptable)
+{
+	struct sock *sk, *stack[256 / sizeof(struct sock *)];
+	struct udp_hslot *hslot = udp_hashslot(udptable, net, ntohs(uh->dest));
+	int dif;
+	unsigned int i, count = 0, non_dup_count = 0;
+
+	spin_lock(&hslot->lock);
+	sk = sk_nulls_head(&hslot->head);
+	dif = skb->dev->ifindex;
+	sk = udp_v4_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif);
+	while (sk) {
+		if (!sock_flag(sk, SOCK_UDP_DUP_UNICAST)) {
+			if (!non_dup_count)
+				stack[count++] = sk;
+			non_dup_count = 1;
+		} else
+			stack[count++] = sk;
+
+		sk = udp_v4_mcast_next(net, sk_nulls_next(sk), uh->dest,
+				       daddr, uh->source, saddr, dif);
+		if (unlikely(count == ARRAY_SIZE(stack))) {
+			if (!sk)
+				break;
+			flush_stack(stack, count, skb, ~0);
+			count = 0;
+		}
+	}
+	/*
+	 * before releasing chain lock, we must take a reference on sockets
+	 */
+	for (i = 0; i < count; i++)
+		sock_hold(stack[i]);
+
+	spin_unlock(&hslot->lock);
+
+	/*
+	 * do the slow work with no lock held
+	 */
+	if (count) {
+		flush_stack(stack, count, skb, count - 1);
+
+		for (i = 0; i < count; i++)
+			sock_put(stack[i]);
+	} else {
+		kfree_skb(skb);
+	}
+	return 0;
+}
+
 /* Initialize UDP checksum. If exited with zero value (success),
  * CHECKSUM_UNNECESSARY means, that no more checks are required.
  * Otherwise, csum completion requires chacksumming packet body,
@@ -1714,6 +1814,13 @@
 		int ret;
 
 		sk_mark_napi_id(sk, skb);
+		if (sk->sk_reuse_conflict) {
+			sock_put(sk);
+			return __udp4_lib_uc_conflict_deliver(net, skb, uh,
+							      saddr, daddr,
+							      udptable);
+		}
+
 		ret = udp_queue_rcv_skb(sk, skb);
 		sock_put(sk);
 
@@ -2295,6 +2402,7 @@
 				       netdev_features_t features)
 {
 	struct sk_buff *segs = ERR_PTR(-EINVAL);
+	u16 mac_offset = skb->mac_header;
 	int mac_len = skb->mac_len;
 	int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
 	__be16 protocol = skb->protocol;
@@ -2314,8 +2422,11 @@
 	/* segment inner packet. */
 	enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
 	segs = skb_mac_gso_segment(skb, enc_features);
-	if (!segs || IS_ERR(segs))
+	if (!segs || IS_ERR(segs)) {
+		skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset,
+				     mac_len);
 		goto out;
+	}
 
 	outer_hlen = skb_tnl_header_len(skb);
 	skb = segs;
diff -ruw linux-3.11.10/net/ipv4/udp_offload.c linux-3.11.10-fbx/net/ipv4/udp_offload.c
--- linux-3.11.10/net/ipv4/udp_offload.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/udp_offload.c	2015-09-29 11:15:09.029756725 +0200
@@ -41,6 +41,14 @@
 {
 	struct sk_buff *segs = ERR_PTR(-EINVAL);
 	unsigned int mss;
+	int offset;
+	__wsum csum;
+
+	if (skb->encapsulation &&
+	    skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) {
+		segs = skb_udp_tunnel_segment(skb, features);
+		goto out;
+	}
 
 	mss = skb_shinfo(skb)->gso_size;
 	if (unlikely(skb->len <= mss))
@@ -62,15 +70,6 @@
 		goto out;
 	}
 
-	/* Fragment the skb. IP headers of the fragments are updated in
-	 * inet_gso_segment()
-	 */
-	if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
-		segs = skb_udp_tunnel_segment(skb, features);
-	else {
-		int offset;
-		__wsum csum;
-
 		/* Do software UFO. Complete and fill in the UDP checksum as
 		 * HW cannot do checksum of UDP packets sent as multiple
 		 * IP fragments.
@@ -81,8 +80,10 @@
 		*(__sum16 *)(skb->data + offset) = csum_fold(csum);
 		skb->ip_summed = CHECKSUM_NONE;
 
+	/* Fragment the skb. IP headers of the fragments are updated in
+	 * inet_gso_segment()
+	 */
 		segs = skb_segment(skb, features);
-	}
 out:
 	return segs;
 }
diff -ruw linux-3.11.10/net/ipv4/xfrm4_policy.c linux-3.11.10-fbx/net/ipv4/xfrm4_policy.c
--- linux-3.11.10/net/ipv4/xfrm4_policy.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv4/xfrm4_policy.c	2015-09-29 11:15:09.033756751 +0200
@@ -235,7 +235,7 @@
 	.destroy =		xfrm4_dst_destroy,
 	.ifdown =		xfrm4_dst_ifdown,
 	.local_out =		__ip_local_out,
-	.gc_thresh =		1024,
+	.gc_thresh =		CONFIG_INET_XFRM_GC_THRESH,
 };
 
 static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
diff -ruw linux-3.11.10/net/ipv6/addrconf.c linux-3.11.10-fbx/net/ipv6/addrconf.c
--- linux-3.11.10/net/ipv6/addrconf.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/addrconf.c	2015-09-29 11:15:09.033756751 +0200
@@ -1113,8 +1113,11 @@
 	 * Lifetime is greater than REGEN_ADVANCE time units.  In particular,
 	 * an implementation must not create a temporary address with a zero
 	 * Preferred Lifetime.
+	 * Use age calculation as in addrconf_verify to avoid unnecessary
+	 * temporary addresses being generated.
 	 */
-	if (tmp_prefered_lft <= regen_advance) {
+	age = (now - tmp_tstamp + ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
+	if (tmp_prefered_lft <= regen_advance + age) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
 		ret = -1;
diff -ruw linux-3.11.10/net/ipv6/datagram.c linux-3.11.10-fbx/net/ipv6/datagram.c
--- linux-3.11.10/net/ipv6/datagram.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/datagram.c	2015-09-29 11:15:09.033756751 +0200
@@ -318,7 +318,7 @@
 /*
  *	Handle MSG_ERRQUEUE
  */
-int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
+int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
 {
 	struct ipv6_pinfo *np = inet6_sk(sk);
 	struct sock_exterr_skb *serr;
@@ -369,6 +369,7 @@
 					       &sin->sin6_addr);
 			sin->sin6_scope_id = 0;
 		}
+		*addr_len = sizeof(*sin);
 	}
 
 	memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
@@ -377,6 +378,7 @@
 	if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL) {
 		sin->sin6_family = AF_INET6;
 		sin->sin6_flowinfo = 0;
+		sin->sin6_port = 0;
 		if (skb->protocol == htons(ETH_P_IPV6)) {
 			sin->sin6_addr = ipv6_hdr(skb)->saddr;
 			if (np->rxopt.all)
@@ -423,7 +425,8 @@
 /*
  *	Handle IPV6_RECVPATHMTU
  */
-int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len)
+int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len,
+		     int *addr_len)
 {
 	struct ipv6_pinfo *np = inet6_sk(sk);
 	struct sk_buff *skb;
@@ -457,6 +460,7 @@
 		sin->sin6_port = 0;
 		sin->sin6_scope_id = mtu_info.ip6m_addr.sin6_scope_id;
 		sin->sin6_addr = mtu_info.ip6m_addr.sin6_addr;
+		*addr_len = sizeof(*sin);
 	}
 
 	put_cmsg(msg, SOL_IPV6, IPV6_PATHMTU, sizeof(mtu_info), &mtu_info);
diff -ruw linux-3.11.10/net/ipv6/exthdrs_core.c linux-3.11.10-fbx/net/ipv6/exthdrs_core.c
--- linux-3.11.10/net/ipv6/exthdrs_core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/exthdrs_core.c	2015-09-29 11:15:09.033756751 +0200
@@ -212,7 +212,7 @@
 		found = (nexthdr == target);
 
 		if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
-			if (target < 0)
+			if (target < 0 || found)
 				break;
 			return -ENOENT;
 		}
diff -ruw linux-3.11.10/net/ipv6/exthdrs_offload.c linux-3.11.10-fbx/net/ipv6/exthdrs_offload.c
--- linux-3.11.10/net/ipv6/exthdrs_offload.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/exthdrs_offload.c	2014-07-29 21:29:45.545870221 +0200
@@ -25,11 +25,11 @@
 	int ret;
 
 	ret = inet6_add_offload(&rthdr_offload, IPPROTO_ROUTING);
-	if (!ret)
+	if (ret)
 		goto out;
 
 	ret = inet6_add_offload(&dstopt_offload, IPPROTO_DSTOPTS);
-	if (!ret)
+	if (ret)
 		goto out_rt;
 
 out:
diff -ruw linux-3.11.10/net/ipv6/icmp.c linux-3.11.10-fbx/net/ipv6/icmp.c
--- linux-3.11.10/net/ipv6/icmp.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/icmp.c	2015-09-29 11:15:09.033756751 +0200
@@ -516,7 +516,7 @@
 			      np->tclass, NULL, &fl6, (struct rt6_info *)dst,
 			      MSG_DONTWAIT, np->dontfrag);
 	if (err) {
-		ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS);
+		ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS);
 		ip6_flush_pending_frames(sk);
 	} else {
 		err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr,
diff -ruw linux-3.11.10/net/ipv6/ip6_fib.c linux-3.11.10-fbx/net/ipv6/ip6_fib.c
--- linux-3.11.10/net/ipv6/ip6_fib.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/ip6_fib.c	2015-09-29 11:15:09.037756780 +0200
@@ -1418,7 +1418,7 @@
 
 				if (w->skip) {
 					w->skip--;
-					continue;
+					goto skip;
 				}
 
 				err = w->func(w);
@@ -1428,6 +1428,7 @@
 				w->count++;
 				continue;
 			}
+skip:
 			w->state = FWS_U;
 		case FWS_U:
 			if (fn == w->root)
diff -ruw linux-3.11.10/net/ipv6/ip6_flowlabel.c linux-3.11.10-fbx/net/ipv6/ip6_flowlabel.c
--- linux-3.11.10/net/ipv6/ip6_flowlabel.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/ip6_flowlabel.c	2015-09-29 11:15:09.037756780 +0200
@@ -453,8 +453,10 @@
 	if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK)
 		return 0;
 
+	rcu_read_lock_bh();
 	for_each_sk_fl_rcu(np, sfl)
 		count++;
+	rcu_read_unlock_bh();
 
 	if (room <= 0 ||
 	    ((count >= FL_MAX_PER_SOCK ||
diff -ruw linux-3.11.10/net/ipv6/ip6_input.c linux-3.11.10-fbx/net/ipv6/ip6_input.c
--- linux-3.11.10/net/ipv6/ip6_input.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/ip6_input.c	2015-09-29 11:15:09.037756780 +0200
@@ -49,7 +49,7 @@
 
 int ip6_rcv_finish(struct sk_buff *skb)
 {
-	if (sysctl_ip_early_demux && !skb_dst(skb)) {
+	if (sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) {
 		const struct inet6_protocol *ipprot;
 
 		ipprot = rcu_dereference(inet6_protos[ipv6_hdr(skb)->nexthdr]);
diff -ruw linux-3.11.10/net/ipv6/ip6_output.c linux-3.11.10-fbx/net/ipv6/ip6_output.c
--- linux-3.11.10/net/ipv6/ip6_output.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/ip6_output.c	2015-09-29 11:15:09.037756780 +0200
@@ -141,7 +141,7 @@
 	}
 	rcu_read_unlock_bh();
 
-	IP6_INC_STATS_BH(dev_net(dst->dev),
+	IP6_INC_STATS(dev_net(dst->dev),
 			 ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
 	kfree_skb(skb);
 	return -EINVAL;
@@ -150,7 +150,8 @@
 static int ip6_finish_output(struct sk_buff *skb)
 {
 	if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
-	    dst_allfrag(skb_dst(skb)))
+	    dst_allfrag(skb_dst(skb)) ||
+	    (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size))
 		return ip6_fragment(skb, ip6_finish_output2);
 	else
 		return ip6_finish_output2(skb);
@@ -345,6 +346,24 @@
 	return dst_output(skb);
 }
 
+static bool ip6_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)
+{
+	if (skb->len <= mtu)
+		return false;
+
+	/* ipv6 conntrack defrag sets max_frag_size + local_df */
+	if (IP6CB(skb)->frag_max_size && IP6CB(skb)->frag_max_size > mtu)
+		return true;
+
+	if (skb->local_df)
+		return false;
+
+	if (skb_is_gso(skb) && skb_gso_network_seglen(skb) <= mtu)
+		return false;
+
+	return true;
+}
+
 int ip6_forward(struct sk_buff *skb)
 {
 	struct dst_entry *dst = skb_dst(skb);
@@ -467,8 +486,7 @@
 	if (mtu < IPV6_MIN_MTU)
 		mtu = IPV6_MIN_MTU;
 
-	if ((!skb->local_df && skb->len > mtu && !skb_is_gso(skb)) ||
-	    (IP6CB(skb)->frag_max_size && IP6CB(skb)->frag_max_size > mtu)) {
+	if (ip6_pkt_too_big(skb, mtu)) {
 		/* Again, force OUTPUT device used as source address */
 		skb->dev = dst->dev;
 		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
@@ -1096,21 +1114,19 @@
 				unsigned int fragheaderlen,
 				struct sk_buff *skb,
 				struct rt6_info *rt,
-				bool pmtuprobe)
+				unsigned int orig_mtu)
 {
 	if (!(rt->dst.flags & DST_XFRM_TUNNEL)) {
 		if (skb == NULL) {
 			/* first fragment, reserve header_len */
-			*mtu = *mtu - rt->dst.header_len;
+			*mtu = orig_mtu - rt->dst.header_len;
 
 		} else {
 			/*
 			 * this fragment is not first, the headers
 			 * space is regarded as data space.
 			 */
-			*mtu = min(*mtu, pmtuprobe ?
-				   rt->dst.dev->mtu :
-				   dst_mtu(rt->dst.path));
+			*mtu = orig_mtu;
 		}
 		*maxfraglen = ((*mtu - fragheaderlen) & ~7)
 			      + fragheaderlen - sizeof(struct frag_hdr);
@@ -1127,7 +1143,7 @@
 	struct ipv6_pinfo *np = inet6_sk(sk);
 	struct inet_cork *cork;
 	struct sk_buff *skb, *skb_prev = NULL;
-	unsigned int maxfraglen, fragheaderlen, mtu;
+	unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu;
 	int exthdrlen;
 	int dst_exthdrlen;
 	int hh_len;
@@ -1209,6 +1225,7 @@
 		dst_exthdrlen = 0;
 		mtu = cork->fragsize;
 	}
+	orig_mtu = mtu;
 
 	hh_len = LL_RESERVED_SPACE(rt->dst.dev);
 
@@ -1288,8 +1305,7 @@
 			if (skb == NULL || skb_prev == NULL)
 				ip6_append_data_mtu(&mtu, &maxfraglen,
 						    fragheaderlen, skb, rt,
-						    np->pmtudisc ==
-						    IPV6_PMTUDISC_PROBE);
+						    orig_mtu);
 
 			skb_prev = skb;
 
@@ -1545,8 +1561,8 @@
 	if (proto == IPPROTO_ICMPV6) {
 		struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
 
-		ICMP6MSGOUT_INC_STATS_BH(net, idev, icmp6_hdr(skb)->icmp6_type);
-		ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTMSGS);
+		ICMP6MSGOUT_INC_STATS(net, idev, icmp6_hdr(skb)->icmp6_type);
+		ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
 	}
 
 	err = ip6_local_out(skb);
diff -ruw linux-3.11.10/net/ipv6/ip6_tunnel.c linux-3.11.10-fbx/net/ipv6/ip6_tunnel.c
--- linux-3.11.10/net/ipv6/ip6_tunnel.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/ip6_tunnel.c	2015-09-29 11:15:09.037756780 +0200
@@ -61,6 +61,7 @@
 MODULE_AUTHOR("Ville Nuorvala");
 MODULE_DESCRIPTION("IPv6 tunneling device");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("ip6tnl");
 MODULE_ALIAS_NETDEV("ip6tnl0");
 
 #ifdef IP6_TNL_DEBUG
@@ -1537,7 +1538,7 @@
 {
 	u8 proto;
 
-	if (!data)
+	if (!data || !data[IFLA_IPTUN_PROTO])
 		return 0;
 
 	proto = nla_get_u8(data[IFLA_IPTUN_PROTO]);
@@ -1623,6 +1624,15 @@
 	return ip6_tnl_update(t, &p);
 }
 
+static void ip6_tnl_dellink(struct net_device *dev, struct list_head *head)
+{
+	struct net *net = dev_net(dev);
+	struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+
+	if (dev != ip6n->fb_tnl_dev)
+		unregister_netdevice_queue(dev, head);
+}
+
 static size_t ip6_tnl_get_size(const struct net_device *dev)
 {
 	return
@@ -1687,6 +1697,7 @@
 	.validate	= ip6_tnl_validate,
 	.newlink	= ip6_tnl_newlink,
 	.changelink	= ip6_tnl_changelink,
+	.dellink	= ip6_tnl_dellink,
 	.get_size	= ip6_tnl_get_size,
 	.fill_info	= ip6_tnl_fill_info,
 };
diff -ruw linux-3.11.10/net/ipv6/mcast.c linux-3.11.10-fbx/net/ipv6/mcast.c
--- linux-3.11.10/net/ipv6/mcast.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/mcast.c	2015-09-29 11:15:09.037756780 +0200
@@ -1448,11 +1448,12 @@
 		      dst_output);
 out:
 	if (!err) {
-		ICMP6MSGOUT_INC_STATS_BH(net, idev, ICMPV6_MLD2_REPORT);
-		ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTMSGS);
-		IP6_UPD_PO_STATS_BH(net, idev, IPSTATS_MIB_OUTMCAST, payload_len);
-	} else
-		IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_OUTDISCARDS);
+		ICMP6MSGOUT_INC_STATS(net, idev, ICMPV6_MLD2_REPORT);
+		ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
+		IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, payload_len);
+	} else {
+		IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
+	}
 
 	rcu_read_unlock();
 	return;
diff -ruw linux-3.11.10/net/ipv6/netfilter/ip6_tables.c linux-3.11.10-fbx/net/ipv6/netfilter/ip6_tables.c
--- linux-3.11.10/net/ipv6/netfilter/ip6_tables.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/netfilter/ip6_tables.c	2015-09-29 11:15:09.041756817 +0200
@@ -1236,8 +1236,10 @@
 
 	xt_free_table_info(oldinfo);
 	if (copy_to_user(counters_ptr, counters,
-			 sizeof(struct xt_counters) * num_counters) != 0)
-		ret = -EFAULT;
+			 sizeof(struct xt_counters) * num_counters) != 0) {
+		/* Silent error, can't fail, new table is already in place */
+		net_warn_ratelimited("ip6tables: counters copy to user failed while replacing table\n");
+	}
 	vfree(counters);
 	xt_table_unlock(t);
 	return ret;
diff -ruw linux-3.11.10/net/ipv6/netfilter/nf_conntrack_reasm.c linux-3.11.10-fbx/net/ipv6/netfilter/nf_conntrack_reasm.c
--- linux-3.11.10/net/ipv6/netfilter/nf_conntrack_reasm.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/netfilter/nf_conntrack_reasm.c	2015-09-29 11:15:09.041756817 +0200
@@ -621,31 +621,16 @@
 	return skb;
 }
 
-void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
-			struct net_device *in, struct net_device *out,
-			int (*okfn)(struct sk_buff *))
+void nf_ct_frag6_consume_orig(struct sk_buff *skb)
 {
 	struct sk_buff *s, *s2;
-	unsigned int ret = 0;
 
 	for (s = NFCT_FRAG6_CB(skb)->orig; s;) {
-		nf_conntrack_put_reasm(s->nfct_reasm);
-		nf_conntrack_get_reasm(skb);
-		s->nfct_reasm = skb;
-
 		s2 = s->next;
 		s->next = NULL;
-
-		if (ret != -ECANCELED)
-			ret = NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, s,
-					     in, out, okfn,
-					     NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
-		else
-			kfree_skb(s);
-
+		consume_skb(s);
 		s = s2;
 	}
-	nf_conntrack_put_reasm(skb);
 }
 
 static int nf_ct_net_init(struct net *net)
diff -ruw linux-3.11.10/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c linux-3.11.10-fbx/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
--- linux-3.11.10/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c	2015-09-29 11:15:09.041756817 +0200
@@ -75,8 +75,11 @@
 	if (reasm == skb)
 		return NF_ACCEPT;
 
-	nf_ct_frag6_output(hooknum, reasm, (struct net_device *)in,
-			   (struct net_device *)out, okfn);
+	nf_ct_frag6_consume_orig(reasm);
+
+	NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, reasm,
+		       (struct net_device *) in, (struct net_device *) out,
+		       okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
 
 	return NF_STOLEN;
 }
diff -ruw linux-3.11.10/net/ipv6/netfilter.c linux-3.11.10-fbx/net/ipv6/netfilter.c
--- linux-3.11.10/net/ipv6/netfilter.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/netfilter.c	2015-09-29 11:15:09.041756817 +0200
@@ -30,13 +30,15 @@
 		.daddr = iph->daddr,
 		.saddr = iph->saddr,
 	};
+	int err;
 
 	dst = ip6_route_output(net, skb->sk, &fl6);
-	if (dst->error) {
+	err = dst->error;
+	if (err) {
 		IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
 		LIMIT_NETDEBUG(KERN_DEBUG "ip6_route_me_harder: No more route.\n");
 		dst_release(dst);
-		return dst->error;
+		return err;
 	}
 
 	/* Drop old route. */
diff -ruw linux-3.11.10/net/ipv6/output_core.c linux-3.11.10-fbx/net/ipv6/output_core.c
--- linux-3.11.10/net/ipv6/output_core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/output_core.c	2015-09-29 11:15:09.041756817 +0200
@@ -9,7 +9,7 @@
 void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
 {
 	static atomic_t ipv6_fragmentation_id;
-	int old, new;
+	int ident;
 
 #if IS_ENABLED(CONFIG_IPV6)
 	if (rt && !(rt->dst.flags & DST_NOPEER)) {
@@ -25,13 +25,8 @@
 		}
 	}
 #endif
-	do {
-		old = atomic_read(&ipv6_fragmentation_id);
-		new = old + 1;
-		if (!new)
-			new = 1;
-	} while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old);
-	fhdr->identification = htonl(new);
+	ident = atomic_inc_return(&ipv6_fragmentation_id);
+	fhdr->identification = htonl(ident);
 }
 EXPORT_SYMBOL(ipv6_select_ident);
 
diff -ruw linux-3.11.10/net/ipv6/ping.c linux-3.11.10-fbx/net/ipv6/ping.c
--- linux-3.11.10/net/ipv6/ping.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/ping.c	2015-09-29 11:15:09.041756817 +0200
@@ -57,7 +57,8 @@
 
 
 /* Compatibility glue so we can support IPv6 when it's compiled as a module */
-static int dummy_ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
+static int dummy_ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len,
+				 int *addr_len)
 {
 	return -EAFNOSUPPORT;
 }
@@ -181,7 +182,7 @@
 			      MSG_DONTWAIT, np->dontfrag);
 
 	if (err) {
-		ICMP6_INC_STATS_BH(sock_net(sk), rt->rt6i_idev,
+		ICMP6_INC_STATS(sock_net(sk), rt->rt6i_idev,
 				   ICMP6_MIB_OUTERRORS);
 		ip6_flush_pending_frames(sk);
 	} else {
diff -ruw linux-3.11.10/net/ipv6/raw.c linux-3.11.10-fbx/net/ipv6/raw.c
--- linux-3.11.10/net/ipv6/raw.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/raw.c	2015-09-29 11:15:09.045756842 +0200
@@ -459,14 +459,11 @@
 	if (flags & MSG_OOB)
 		return -EOPNOTSUPP;
 
-	if (addr_len)
-		*addr_len=sizeof(*sin6);
-
 	if (flags & MSG_ERRQUEUE)
-		return ipv6_recv_error(sk, msg, len);
+		return ipv6_recv_error(sk, msg, len, addr_len);
 
 	if (np->rxpmtu && np->rxopt.bits.rxpmtu)
-		return ipv6_recv_rxpmtu(sk, msg, len);
+		return ipv6_recv_rxpmtu(sk, msg, len, addr_len);
 
 	skb = skb_recv_datagram(sk, flags, noblock, &err);
 	if (!skb)
@@ -500,6 +497,7 @@
 		sin6->sin6_flowinfo = 0;
 		sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr,
 							  IP6CB(skb)->iif);
+		*addr_len = sizeof(*sin6);
 	}
 
 	sock_recv_ts_and_drops(msg, sk, skb);
diff -ruw linux-3.11.10/net/ipv6/route.c linux-3.11.10-fbx/net/ipv6/route.c
--- linux-3.11.10/net/ipv6/route.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/route.c	2015-09-29 11:15:09.045756842 +0200
@@ -84,6 +84,8 @@
 
 static int		ip6_pkt_discard(struct sk_buff *skb);
 static int		ip6_pkt_discard_out(struct sk_buff *skb);
+static int		ip6_pkt_prohibit(struct sk_buff *skb);
+static int		ip6_pkt_prohibit_out(struct sk_buff *skb);
 static void		ip6_link_failure(struct sk_buff *skb);
 static void		ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
 					   struct sk_buff *skb, u32 mtu);
@@ -234,9 +236,6 @@
 
 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
 
-static int ip6_pkt_prohibit(struct sk_buff *skb);
-static int ip6_pkt_prohibit_out(struct sk_buff *skb);
-
 static const struct rt6_info ip6_prohibit_entry_template = {
 	.dst = {
 		.__refcnt	= ATOMIC_INIT(1),
@@ -732,8 +731,11 @@
 		prefix = &prefix_buf;
 	}
 
-	rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
-				dev->ifindex);
+	if (rinfo->prefix_len == 0)
+		rt = rt6_get_dflt_router(gwaddr, dev);
+	else
+		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
+					gwaddr, dev->ifindex);
 
 	if (rt && !lifetime) {
 		ip6_del_rt(rt);
@@ -1259,7 +1261,7 @@
 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
 
 	if (mtu)
-		return mtu;
+		goto out;
 
 	mtu = IPV6_MIN_MTU;
 
@@ -1269,7 +1271,8 @@
 		mtu = idev->cnf.mtu6;
 	rcu_read_unlock();
 
-	return mtu;
+out:
+	return min_t(unsigned int, mtu, IP6_MAX_MTU);
 }
 
 static struct dst_entry *icmp6_dst_gc_list;
@@ -1449,7 +1452,7 @@
 	if (!table)
 		goto out;
 
-	rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
+	rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT, table);
 
 	if (!rt) {
 		err = -ENOMEM;
@@ -1518,21 +1521,24 @@
 				goto out;
 			}
 		}
-		rt->dst.output = ip6_pkt_discard_out;
-		rt->dst.input = ip6_pkt_discard;
 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
 		switch (cfg->fc_type) {
 		case RTN_BLACKHOLE:
 			rt->dst.error = -EINVAL;
+			rt->dst.output = dst_discard;
+			rt->dst.input = dst_discard;
 			break;
 		case RTN_PROHIBIT:
 			rt->dst.error = -EACCES;
+			rt->dst.output = ip6_pkt_prohibit_out;
+			rt->dst.input = ip6_pkt_prohibit;
 			break;
 		case RTN_THROW:
-			rt->dst.error = -EAGAIN;
-			break;
 		default:
-			rt->dst.error = -ENETUNREACH;
+			rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN
+					: -ENETUNREACH;
+			rt->dst.output = ip6_pkt_discard_out;
+			rt->dst.input = ip6_pkt_discard;
 			break;
 		}
 		goto install_route;
@@ -1856,8 +1862,6 @@
 		else
 			rt->rt6i_gateway = *dest;
 		rt->rt6i_flags = ort->rt6i_flags;
-		if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
-		    (RTF_DEFAULT | RTF_ADDRCONF))
 			rt6_set_from(rt, ort);
 		rt->rt6i_metric = 0;
 
@@ -2097,8 +2101,6 @@
 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
 }
 
-#ifdef CONFIG_IPV6_MULTIPLE_TABLES
-
 static int ip6_pkt_prohibit(struct sk_buff *skb)
 {
 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
@@ -2110,8 +2112,6 @@
 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
 }
 
-#endif
-
 /*
  *	Allocate a dst for local (unicast / anycast) address.
  */
@@ -2121,12 +2121,10 @@
 				    bool anycast)
 {
 	struct net *net = dev_net(idev->dev);
-	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
-
-	if (!rt) {
-		net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
+	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev,
+					    DST_NOCOUNT, NULL);
+	if (!rt)
 		return ERR_PTR(-ENOMEM);
-	}
 
 	in6_dev_hold(idev);
 
diff -ruw linux-3.11.10/net/ipv6/sit.c linux-3.11.10-fbx/net/ipv6/sit.c
--- linux-3.11.10/net/ipv6/sit.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/sit.c	2015-09-29 11:15:09.045756842 +0200
@@ -530,12 +530,12 @@
 
 	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
 		ipv4_update_pmtu(skb, dev_net(skb->dev), info,
-				 t->dev->ifindex, 0, IPPROTO_IPV6, 0);
+				 t->parms.link, 0, IPPROTO_IPV6, 0);
 		err = 0;
 		goto out;
 	}
 	if (type == ICMP_REDIRECT) {
-		ipv4_redirect(skb, dev_net(skb->dev), t->dev->ifindex, 0,
+		ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0,
 			      IPPROTO_IPV6, 0);
 		err = 0;
 		goto out;
@@ -1601,6 +1601,15 @@
 #endif
 };
 
+static void ipip6_dellink(struct net_device *dev, struct list_head *head)
+{
+	struct net *net = dev_net(dev);
+	struct sit_net *sitn = net_generic(net, sit_net_id);
+
+	if (dev != sitn->fb_tunnel_dev)
+		unregister_netdevice_queue(dev, head);
+}
+
 static struct rtnl_link_ops sit_link_ops __read_mostly = {
 	.kind		= "sit",
 	.maxtype	= IFLA_IPTUN_MAX,
@@ -1612,6 +1621,7 @@
 	.changelink	= ipip6_changelink,
 	.get_size	= ipip6_get_size,
 	.fill_info	= ipip6_fill_info,
+	.dellink	= ipip6_dellink,
 };
 
 static struct xfrm_tunnel sit_handler __read_mostly = {
@@ -1767,4 +1777,5 @@
 module_init(sit_init);
 module_exit(sit_cleanup);
 MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("sit");
 MODULE_ALIAS_NETDEV("sit0");
diff -ruw linux-3.11.10/net/ipv6/tcpv6_offload.c linux-3.11.10-fbx/net/ipv6/tcpv6_offload.c
--- linux-3.11.10/net/ipv6/tcpv6_offload.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/tcpv6_offload.c	2015-09-29 11:15:09.045756842 +0200
@@ -37,34 +37,32 @@
 {
 	const struct ipv6hdr *iph = skb_gro_network_header(skb);
 	__wsum wsum;
-	__sum16 sum;
+
+	/* Don't bother verifying checksum if we're going to flush anyway. */
+	if (NAPI_GRO_CB(skb)->flush)
+		goto skip_csum;
+
+	wsum = skb->csum;
 
 	switch (skb->ip_summed) {
+	case CHECKSUM_NONE:
+		wsum = skb_checksum(skb, skb_gro_offset(skb), skb_gro_len(skb),
+				    wsum);
+
+		/* fall through */
+
 	case CHECKSUM_COMPLETE:
 		if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr,
-				  skb->csum)) {
+				  wsum)) {
 			skb->ip_summed = CHECKSUM_UNNECESSARY;
 			break;
 		}
-flush:
+
 		NAPI_GRO_CB(skb)->flush = 1;
 		return NULL;
-
-	case CHECKSUM_NONE:
-		wsum = ~csum_unfold(csum_ipv6_magic(&iph->saddr, &iph->daddr,
-						    skb_gro_len(skb),
-						    IPPROTO_TCP, 0));
-		sum = csum_fold(skb_checksum(skb,
-					     skb_gro_offset(skb),
-					     skb_gro_len(skb),
-					     wsum));
-		if (sum)
-			goto flush;
-
-		skb->ip_summed = CHECKSUM_UNNECESSARY;
-		break;
 	}
 
+skip_csum:
 	return tcp_gro_receive(head, skb);
 }
 
diff -ruw linux-3.11.10/net/ipv6/udp.c linux-3.11.10-fbx/net/ipv6/udp.c
--- linux-3.11.10/net/ipv6/udp.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/udp.c	2015-09-29 11:15:09.045756842 +0200
@@ -374,14 +374,11 @@
 	int is_udp4;
 	bool slow;
 
-	if (addr_len)
-		*addr_len = sizeof(struct sockaddr_in6);
-
 	if (flags & MSG_ERRQUEUE)
-		return ipv6_recv_error(sk, msg, len);
+		return ipv6_recv_error(sk, msg, len, addr_len);
 
 	if (np->rxpmtu && np->rxopt.bits.rxpmtu)
-		return ipv6_recv_rxpmtu(sk, msg, len);
+		return ipv6_recv_rxpmtu(sk, msg, len, addr_len);
 
 try_again:
 	skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
@@ -462,7 +459,7 @@
 				ipv6_iface_scope_id(&sin6->sin6_addr,
 						    IP6CB(skb)->iif);
 		}
-
+		*addr_len = sizeof(*sin6);
 	}
 	if (is_udp4) {
 		if (inet->cmsg_flags)
@@ -785,6 +782,60 @@
 	return 0;
 }
 
+/*
+ * Note: called only from the BH handler context,
+ * so we don't need to lock the hashes.
+ */
+static int __udp6_lib_uc_conflict_deliver(struct net *net, struct sk_buff *skb,
+		const struct in6_addr *saddr, const struct in6_addr *daddr,
+		struct udp_table *udptable)
+{
+	struct sock *sk, *stack[256 / sizeof(struct sock *)];
+	const struct udphdr *uh = udp_hdr(skb);
+	struct udp_hslot *hslot = udp_hashslot(udptable, net, ntohs(uh->dest));
+	int dif;
+	unsigned int i, count = 0, non_dup_count = 0;
+
+	spin_lock(&hslot->lock);
+	sk = sk_nulls_head(&hslot->head);
+	dif = inet6_iif(skb);
+	sk = udp_v6_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif);
+	while (sk) {
+		if (!sock_flag(sk, SOCK_UDP_DUP_UNICAST)) {
+			if (!non_dup_count)
+				stack[count++] = sk;
+			non_dup_count = 1;
+		} else
+			stack[count++] = sk;
+
+		sk = udp_v6_mcast_next(net, sk_nulls_next(sk), uh->dest, daddr,
+				       uh->source, saddr, dif);
+		if (unlikely(count == ARRAY_SIZE(stack))) {
+			if (!sk)
+				break;
+			flush_stack(stack, count, skb, ~0);
+			count = 0;
+		}
+	}
+	/*
+	 * before releasing the lock, we must take reference on sockets
+	 */
+	for (i = 0; i < count; i++)
+		sock_hold(stack[i]);
+
+	spin_unlock(&hslot->lock);
+
+	if (count) {
+		flush_stack(stack, count, skb, count - 1);
+
+		for (i = 0; i < count; i++)
+			sock_put(stack[i]);
+	} else {
+		kfree_skb(skb);
+	}
+	return 0;
+}
+
 int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
 		   int proto)
 {
@@ -845,6 +896,13 @@
 		int ret;
 
 		sk_mark_napi_id(sk, skb);
+		if (sk->sk_reuse_conflict) {
+			sock_put(sk);
+			return __udp6_lib_uc_conflict_deliver(net, skb,
+							      saddr, daddr,
+							      udptable);
+		}
+
 		ret = udpv6_queue_rcv_skb(sk, skb);
 		sock_put(sk);
 
diff -ruw linux-3.11.10/net/ipv6/udp_offload.c linux-3.11.10-fbx/net/ipv6/udp_offload.c
--- linux-3.11.10/net/ipv6/udp_offload.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/ipv6/udp_offload.c	2015-09-29 11:15:09.045756842 +0200
@@ -86,7 +86,7 @@
 
 	/* Check if there is enough headroom to insert fragment header. */
 	tnl_hlen = skb_tnl_header_len(skb);
-	if (skb_headroom(skb) < (tnl_hlen + frag_hdr_sz)) {
+	if (skb_mac_header(skb) < skb->head + tnl_hlen + frag_hdr_sz) {
 		if (gso_pskb_expand_head(skb, tnl_hlen + frag_hdr_sz))
 			goto out;
 	}
@@ -109,7 +109,7 @@
 	fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
 	fptr->nexthdr = nexthdr;
 	fptr->reserved = 0;
-	ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
+	fptr->identification = skb_shinfo(skb)->ip6_frag_id;
 
 	/* Fragment the skb. ipv6 header and the remaining fields of the
 	 * fragment header are updated in ipv6_gso_segment()
diff -ruw linux-3.11.10/net/Kconfig linux-3.11.10-fbx/net/Kconfig
--- linux-3.11.10/net/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/Kconfig	2015-09-29 11:15:08.949756111 +0200
@@ -46,6 +46,18 @@
 
 menu "Networking options"
 
+config NETSKBPAD
+	int "Size reserved by dev_alloc_skb"
+	default 16
+
+config NETRXTHREAD
+	bool "Do rx network processing in kernel thread"
+
+config NETRXTHREAD_RX_QUEUE
+	int "Number of rx queues"
+	default 1
+	depends on NETRXTHREAD
+
 source "net/packet/Kconfig"
 source "net/unix/Kconfig"
 source "net/xfrm/Kconfig"
@@ -199,6 +211,8 @@
 source "net/tipc/Kconfig"
 source "net/atm/Kconfig"
 source "net/l2tp/Kconfig"
+source "net/fbxatm/Kconfig"
+source "net/fbxbridge/Kconfig"
 source "net/802/Kconfig"
 source "net/bridge/Kconfig"
 source "net/dsa/Kconfig"
diff -ruw linux-3.11.10/net/key/af_key.c linux-3.11.10-fbx/net/key/af_key.c
--- linux-3.11.10/net/key/af_key.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/key/af_key.c	2015-09-29 11:15:09.057756933 +0200
@@ -3623,7 +3623,6 @@
 	if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT))
 		goto out;
 
-	msg->msg_namelen = 0;
 	skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);
 	if (skb == NULL)
 		goto out;
diff -ruw linux-3.11.10/net/mac80211/aes_ccm.c linux-3.11.10-fbx/net/mac80211/aes_ccm.c
--- linux-3.11.10/net/mac80211/aes_ccm.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/aes_ccm.c	2015-09-29 11:15:09.061756962 +0200
@@ -2,6 +2,8 @@
  * Copyright 2003-2004, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
  *
+ * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
@@ -17,134 +19,75 @@
 #include "key.h"
 #include "aes_ccm.h"
 
-static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a)
+void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+			       u8 *data, size_t data_len, u8 *mic)
 {
-	int i;
-	u8 *b_0, *aad, *b, *s_0;
+	struct scatterlist assoc, pt, ct[2];
+	struct {
+		struct aead_request	req;
+		u8			priv[crypto_aead_reqsize(tfm)];
+	} aead_req;
 
-	b_0 = scratch + 3 * AES_BLOCK_SIZE;
-	aad = scratch + 4 * AES_BLOCK_SIZE;
-	b = scratch;
-	s_0 = scratch + AES_BLOCK_SIZE;
-
-	crypto_cipher_encrypt_one(tfm, b, b_0);
-
-	/* Extra Authenticate-only data (always two AES blocks) */
-	for (i = 0; i < AES_BLOCK_SIZE; i++)
-		aad[i] ^= b[i];
-	crypto_cipher_encrypt_one(tfm, b, aad);
-
-	aad += AES_BLOCK_SIZE;
-
-	for (i = 0; i < AES_BLOCK_SIZE; i++)
-		aad[i] ^= b[i];
-	crypto_cipher_encrypt_one(tfm, a, aad);
-
-	/* Mask out bits from auth-only-b_0 */
-	b_0[0] &= 0x07;
-
-	/* S_0 is used to encrypt T (= MIC) */
-	b_0[14] = 0;
-	b_0[15] = 0;
-	crypto_cipher_encrypt_one(tfm, s_0, b_0);
-}
+	memset(&aead_req, 0, sizeof(aead_req));
 
+	sg_init_one(&pt, data, data_len);
+	sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
+	sg_init_table(ct, 2);
+	sg_set_buf(&ct[0], data, data_len);
+	sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
 
-void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
-			       u8 *data, size_t data_len,
-			       u8 *cdata, u8 *mic)
-{
-	int i, j, last_len, num_blocks;
-	u8 *pos, *cpos, *b, *s_0, *e, *b_0;
+	aead_request_set_tfm(&aead_req.req, tfm);
+	aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
+	aead_request_set_crypt(&aead_req.req, &pt, ct, data_len, b_0);
 
-	b = scratch;
-	s_0 = scratch + AES_BLOCK_SIZE;
-	e = scratch + 2 * AES_BLOCK_SIZE;
-	b_0 = scratch + 3 * AES_BLOCK_SIZE;
-
-	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
-	last_len = data_len % AES_BLOCK_SIZE;
-	aes_ccm_prepare(tfm, scratch, b);
-
-	/* Process payload blocks */
-	pos = data;
-	cpos = cdata;
-	for (j = 1; j <= num_blocks; j++) {
-		int blen = (j == num_blocks && last_len) ?
-			last_len : AES_BLOCK_SIZE;
-
-		/* Authentication followed by encryption */
-		for (i = 0; i < blen; i++)
-			b[i] ^= pos[i];
-		crypto_cipher_encrypt_one(tfm, b, b);
-
-		b_0[14] = (j >> 8) & 0xff;
-		b_0[15] = j & 0xff;
-		crypto_cipher_encrypt_one(tfm, e, b_0);
-		for (i = 0; i < blen; i++)
-			*cpos++ = *pos++ ^ e[i];
+	crypto_aead_encrypt(&aead_req.req);
 	}
 
-	for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++)
-		mic[i] = b[i] ^ s_0[i];
-}
-
-
-int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
-			      u8 *cdata, size_t data_len, u8 *mic, u8 *data)
+int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+			      u8 *data, size_t data_len, u8 *mic)
 {
-	int i, j, last_len, num_blocks;
-	u8 *pos, *cpos, *b, *s_0, *a, *b_0;
+	struct scatterlist assoc, pt, ct[2];
+	struct {
+		struct aead_request	req;
+		u8			priv[crypto_aead_reqsize(tfm)];
+	} aead_req;
 
-	b = scratch;
-	s_0 = scratch + AES_BLOCK_SIZE;
-	a = scratch + 2 * AES_BLOCK_SIZE;
-	b_0 = scratch + 3 * AES_BLOCK_SIZE;
-
-	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
-	last_len = data_len % AES_BLOCK_SIZE;
-	aes_ccm_prepare(tfm, scratch, a);
-
-	/* Process payload blocks */
-	cpos = cdata;
-	pos = data;
-	for (j = 1; j <= num_blocks; j++) {
-		int blen = (j == num_blocks && last_len) ?
-			last_len : AES_BLOCK_SIZE;
-
-		/* Decryption followed by authentication */
-		b_0[14] = (j >> 8) & 0xff;
-		b_0[15] = j & 0xff;
-		crypto_cipher_encrypt_one(tfm, b, b_0);
-		for (i = 0; i < blen; i++) {
-			*pos = *cpos++ ^ b[i];
-			a[i] ^= *pos++;
-		}
-		crypto_cipher_encrypt_one(tfm, a, a);
-	}
+	memset(&aead_req, 0, sizeof(aead_req));
 
-	for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) {
-		if ((mic[i] ^ s_0[i]) != a[i])
-			return -1;
-	}
+	sg_init_one(&pt, data, data_len);
+	sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
+	sg_init_table(ct, 2);
+	sg_set_buf(&ct[0], data, data_len);
+	sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
 
-	return 0;
-}
+	aead_request_set_tfm(&aead_req.req, tfm);
+	aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
+	aead_request_set_crypt(&aead_req.req, ct, &pt,
+			       data_len + IEEE80211_CCMP_MIC_LEN, b_0);
 
+	return crypto_aead_decrypt(&aead_req.req);
+}
 
-struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[])
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])
 {
-	struct crypto_cipher *tfm;
+	struct crypto_aead *tfm;
+	int err;
 
-	tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
-	if (!IS_ERR(tfm))
-		crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
+	tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm))
+		return tfm;
 
+	err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
+	if (!err)
+		err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN);
+	if (!err)
 	return tfm;
-}
 
+	crypto_free_aead(tfm);
+	return ERR_PTR(err);
+}
 
-void ieee80211_aes_key_free(struct crypto_cipher *tfm)
+void ieee80211_aes_key_free(struct crypto_aead *tfm)
 {
-	crypto_free_cipher(tfm);
+	crypto_free_aead(tfm);
 }
diff -ruw linux-3.11.10/net/mac80211/aes_ccm.h linux-3.11.10-fbx/net/mac80211/aes_ccm.h
--- linux-3.11.10/net/mac80211/aes_ccm.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/aes_ccm.h	2015-09-29 11:15:09.061756962 +0200
@@ -12,13 +12,11 @@
 
 #include <linux/crypto.h>
 
-struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]);
-void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
-			       u8 *data, size_t data_len,
-			       u8 *cdata, u8 *mic);
-int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
-			      u8 *cdata, size_t data_len,
-			      u8 *mic, u8 *data);
-void ieee80211_aes_key_free(struct crypto_cipher *tfm);
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]);
+void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+			       u8 *data, size_t data_len, u8 *mic);
+int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+			      u8 *data, size_t data_len, u8 *mic);
+void ieee80211_aes_key_free(struct crypto_aead *tfm);
 
 #endif /* AES_CCM_H */
diff -ruw linux-3.11.10/net/mac80211/cfg.c linux-3.11.10-fbx/net/mac80211/cfg.c
--- linux-3.11.10/net/mac80211/cfg.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/cfg.c	2015-09-29 11:15:09.065756999 +0200
@@ -133,7 +133,9 @@
 			     struct key_params *params)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
 	struct sta_info *sta = NULL;
+	const struct ieee80211_cipher_scheme *cs = NULL;
 	struct ieee80211_key *key;
 	int err;
 
@@ -145,22 +147,28 @@
 	case WLAN_CIPHER_SUITE_WEP40:
 	case WLAN_CIPHER_SUITE_TKIP:
 	case WLAN_CIPHER_SUITE_WEP104:
-		if (IS_ERR(sdata->local->wep_tx_tfm))
+		if (IS_ERR(local->wep_tx_tfm))
 			return -EINVAL;
 		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_GCMP:
+		break;
 	default:
+		cs = ieee80211_cs_get(local, params->cipher, sdata->vif.type);
 		break;
 	}
 
 	key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len,
-				  params->key, params->seq_len, params->seq);
+				  params->key, params->seq_len, params->seq,
+				  cs);
 	if (IS_ERR(key))
 		return PTR_ERR(key);
 
 	if (pairwise)
 		key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
 
-	mutex_lock(&sdata->local->sta_mtx);
+	mutex_lock(&local->sta_mtx);
 
 	if (mac_addr) {
 		if (ieee80211_vif_is_mesh(&sdata->vif))
@@ -216,10 +224,13 @@
 		break;
 	}
 
+	if (sta)
+		sta->cipher_scheme = cs;
+
 	err = ieee80211_key_link(key, sdata, sta);
 
  out_unlock:
-	mutex_unlock(&sdata->local->sta_mtx);
+	mutex_unlock(&local->sta_mtx);
 
 	return err;
 }
@@ -244,7 +255,7 @@
 			goto out_unlock;
 
 		if (pairwise)
-			key = key_mtx_dereference(local, sta->ptk);
+			key = key_mtx_dereference(local, sta->ptk[key_idx]);
 		else
 			key = key_mtx_dereference(local, sta->gtk[key_idx]);
 	} else
@@ -291,7 +302,7 @@
 			goto out;
 
 		if (pairwise)
-			key = rcu_dereference(sta->ptk);
+			key = rcu_dereference(sta->ptk[key_idx]);
 		else if (key_idx < NUM_DEFAULT_KEYS)
 			key = rcu_dereference(sta->gtk[key_idx]);
 	} else
@@ -395,9 +406,13 @@
 		rinfo->nss = ieee80211_rate_get_vht_nss(rate);
 	} else {
 		struct ieee80211_supported_band *sband;
+		int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+		u16 brate;
+
 		sband = sta->local->hw.wiphy->bands[
 				ieee80211_get_sdata_band(sta->sdata)];
-		rinfo->legacy = sband->bitrates[rate->idx].bitrate;
+		brate = sband->bitrates[rate->idx].bitrate;
+		rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
 	}
 	if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
 		rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
@@ -422,11 +437,13 @@
 		rinfo->mcs = sta->last_rx_rate_idx;
 	} else {
 		struct ieee80211_supported_band *sband;
+		int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+		u16 brate;
 
 		sband = sta->local->hw.wiphy->bands[
 				ieee80211_get_sdata_band(sta->sdata)];
-		rinfo->legacy =
-			sband->bitrates[sta->last_rx_rate_idx].bitrate;
+		brate = sband->bitrates[sta->last_rx_rate_idx].bitrate;
+		rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
 	}
 
 	if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
@@ -810,6 +827,7 @@
 	if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))
 		return 0;
 
+	mutex_lock(&local->mtx);
 	mutex_lock(&local->iflist_mtx);
 	if (local->use_chanctx) {
 		sdata = rcu_dereference_protected(
@@ -828,6 +846,7 @@
 	if (ret == 0)
 		local->monitor_chandef = *chandef;
 	mutex_unlock(&local->iflist_mtx);
+	mutex_unlock(&local->mtx);
 
 	return ret;
 }
@@ -840,7 +859,7 @@
 	if (!resp || !resp_len)
 		return 1;
 
-	old = rtnl_dereference(sdata->u.ap.probe_resp);
+	old = sdata_dereference(sdata->u.ap.probe_resp, sdata);
 
 	new = kzalloc(sizeof(struct probe_resp) + resp_len, GFP_KERNEL);
 	if (!new)
@@ -856,7 +875,7 @@
 	return 0;
 }
 
-static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
 				   struct cfg80211_beacon_data *params)
 {
 	struct beacon_data *new, *old;
@@ -864,7 +883,8 @@
 	int size, err;
 	u32 changed = BSS_CHANGED_BEACON;
 
-	old = rtnl_dereference(sdata->u.ap.beacon);
+	old = sdata_dereference(sdata->u.ap.beacon, sdata);
+
 
 	/* Need to have a beacon head if we don't have one yet */
 	if (!params->head && !old)
@@ -932,6 +952,7 @@
 			      struct cfg80211_ap_settings *params)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
 	struct beacon_data *old;
 	struct ieee80211_sub_if_data *vlan;
 	u32 changed = BSS_CHANGED_BEACON_INT |
@@ -941,17 +962,19 @@
 		      BSS_CHANGED_P2P_PS;
 	int err;
 
-	old = rtnl_dereference(sdata->u.ap.beacon);
+	old = sdata_dereference(sdata->u.ap.beacon, sdata);
 	if (old)
 		return -EALREADY;
 
 	/* TODO: make hostapd tell us what it wants */
 	sdata->smps_mode = IEEE80211_SMPS_OFF;
 	sdata->needed_rx_chains = sdata->local->rx_chains;
-	sdata->radar_required = params->radar_required;
 
+	mutex_lock(&local->mtx);
+	sdata->radar_required = params->radar_required;
 	err = ieee80211_vif_use_channel(sdata, &params->chandef,
 					IEEE80211_CHANCTX_SHARED);
+	mutex_unlock(&local->mtx);
 	if (err)
 		return err;
 	ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
@@ -962,11 +985,19 @@
 	 */
 	sdata->control_port_protocol = params->crypto.control_port_ethertype;
 	sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt;
+	sdata->encrypt_headroom = ieee80211_cs_headroom(sdata->local,
+							&params->crypto,
+							sdata->vif.type);
+
 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
 		vlan->control_port_protocol =
 			params->crypto.control_port_ethertype;
 		vlan->control_port_no_encrypt =
 			params->crypto.control_port_no_encrypt;
+		vlan->encrypt_headroom =
+			ieee80211_cs_headroom(sdata->local,
+					      &params->crypto,
+					      vlan->vif.type);
 	}
 
 	sdata->vif.bss_conf.beacon_int = params->beacon_interval;
@@ -989,16 +1020,20 @@
 					IEEE80211_P2P_OPPPS_ENABLE_BIT;
 
 	err = ieee80211_assign_beacon(sdata, &params->beacon);
-	if (err < 0)
+	if (err < 0) {
+		ieee80211_vif_release_channel(sdata);
 		return err;
+	}
 	changed |= err;
 
 	err = drv_start_ap(sdata->local, sdata);
 	if (err) {
-		old = rtnl_dereference(sdata->u.ap.beacon);
+		old = sdata_dereference(sdata->u.ap.beacon, sdata);
+
 		if (old)
 			kfree_rcu(old, rcu_head);
 		RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+		ieee80211_vif_release_channel(sdata);
 		return err;
 	}
 
@@ -1019,8 +1054,15 @@
 	int err;
 
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	sdata_assert_lock(sdata);
 
-	old = rtnl_dereference(sdata->u.ap.beacon);
+	/* don't allow changing the beacon while CSA is in place - offset
+	 * of channel switch counter may change
+	 */
+	if (sdata->vif.csa_active)
+		return -EBUSY;
+
+	old = sdata_dereference(sdata->u.ap.beacon, sdata);
 	if (!old)
 		return -ENOENT;
 
@@ -1038,11 +1080,22 @@
 	struct ieee80211_local *local = sdata->local;
 	struct beacon_data *old_beacon;
 	struct probe_resp *old_probe_resp;
+	struct cfg80211_chan_def chandef;
+
+	sdata_assert_lock(sdata);
 
-	old_beacon = rtnl_dereference(sdata->u.ap.beacon);
+	old_beacon = sdata_dereference(sdata->u.ap.beacon, sdata);
 	if (!old_beacon)
 		return -ENOENT;
-	old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
+	old_probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata);
+
+	/* abort any running channel switch */
+	mutex_lock(&local->mtx);
+	ieee80211_csa_clear(sdata);
+	ieee80211_csa_free(sdata);
+	mutex_unlock(&local->mtx);
+
+	cancel_work_sync(&sdata->u.ap.request_smps_work);
 
 	/* turn off carrier for this interface and dependent VLANs */
 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
@@ -1074,8 +1127,10 @@
 	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
 
 	if (sdata->wdev.cac_started) {
+		chandef = sdata->vif.bss_conf.chandef;
 		cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
-		cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED,
+		cfg80211_cac_event(sdata->dev, &chandef,
+				   NL80211_RADAR_CAC_ABORTED,
 				   GFP_KERNEL);
 	}
 
@@ -1086,7 +1141,9 @@
 	skb_queue_purge(&sdata->u.ap.ps.bc_buf);
 
 	ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
+	mutex_lock(&local->mtx);
 	ieee80211_vif_release_channel(sdata);
+	mutex_unlock(&local->mtx);
 
 	return 0;
 }
@@ -1192,8 +1249,6 @@
 				struct station_parameters *params)
 {
 	int ret = 0;
-	u32 rates;
-	int i, j;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
 	enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
@@ -1286,16 +1341,10 @@
 		sta->listen_interval = params->listen_interval;
 
 	if (params->supported_rates) {
-		rates = 0;
-
-		for (i = 0; i < params->supported_rates_len; i++) {
-			int rate = (params->supported_rates[i] & 0x7f) * 5;
-			for (j = 0; j < sband->n_bitrates; j++) {
-				if (sband->bitrates[j].bitrate == rate)
-					rates |= BIT(j);
-			}
-		}
-		sta->sta.supp_rates[band] = rates;
+		ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
+					 sband, params->supported_rates,
+					 params->supported_rates_len,
+					 &sta->sta.supp_rates[band]);
 	}
 
 	if (params->ht_capa)
@@ -1306,6 +1355,18 @@
 		ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
 						    params->vht_capa, sta);
 
+	if (params->opmode_notif_used) {
+		enum ieee80211_band band =
+			ieee80211_get_sdata_band(sdata);
+
+		/* returned value is only needed for rc update, but the
+		 * rc isn't initialized here yet, so ignore it
+		 */
+		__ieee80211_vht_handle_opmode(sdata, sta,
+					      params->opmode_notif,
+					      band, false);
+	}
+
 	if (ieee80211_vif_is_mesh(&sdata->vif)) {
 #ifdef CONFIG_MAC80211_MESH
 		u32 changed = 0;
@@ -1334,8 +1395,8 @@
 				sta->plink_state = params->plink_state;
 
 				ieee80211_mps_sta_status_update(sta);
-				changed |=
-				      ieee80211_mps_local_status_update(sdata);
+				changed |= ieee80211_mps_set_sta_local_pm(sta,
+						NL80211_MESH_POWER_UNKNOWN);
 				break;
 			default:
 				/*  nothing  */
@@ -1545,6 +1606,20 @@
 
 	mutex_unlock(&local->sta_mtx);
 
+	if ((sdata->vif.type == NL80211_IFTYPE_AP ||
+	     sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
+	    sta->known_smps_mode != sta->sdata->bss->req_smps &&
+	    test_sta_flag(sta, WLAN_STA_AUTHORIZED) &&
+	    sta_info_tx_streams(sta) != 1) {
+		ht_dbg(sta->sdata,
+		       "%pM just authorized and MIMO capable - update SMPS\n",
+		       sta->sta.addr);
+		ieee80211_send_smps_action(sta->sdata,
+			sta->sdata->bss->req_smps,
+			sta->sta.addr,
+			sta->sdata->vif.bss_conf.bssid);
+	}
+
 	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
 	    params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
 		ieee80211_recalc_ps(local, -1);
@@ -1903,8 +1978,10 @@
 	sdata->smps_mode = IEEE80211_SMPS_OFF;
 	sdata->needed_rx_chains = sdata->local->rx_chains;
 
+	mutex_lock(&sdata->local->mtx);
 	err = ieee80211_vif_use_channel(sdata, &setup->chandef,
 					IEEE80211_CHANCTX_SHARED);
+	mutex_unlock(&sdata->local->mtx);
 	if (err)
 		return err;
 
@@ -1916,7 +1993,9 @@
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
 	ieee80211_stop_mesh(sdata);
+	mutex_lock(&sdata->local->mtx);
 	ieee80211_vif_release_channel(sdata);
+	mutex_unlock(&sdata->local->mtx);
 
 	return 0;
 }
@@ -1930,7 +2009,7 @@
 	enum ieee80211_band band;
 	u32 changed = 0;
 
-	if (!rtnl_dereference(sdata->u.ap.beacon))
+	if (!sdata_dereference(sdata->u.ap.beacon, sdata))
 		return -ENOENT;
 
 	band = ieee80211_get_sdata_band(sdata);
@@ -1958,18 +2037,11 @@
 	}
 
 	if (params->basic_rates) {
-		int i, j;
-		u32 rates = 0;
-		struct ieee80211_supported_band *sband = wiphy->bands[band];
-
-		for (i = 0; i < params->basic_rates_len; i++) {
-			int rate = (params->basic_rates[i] & 0x7f) * 5;
-			for (j = 0; j < sband->n_bitrates; j++) {
-				if (sband->bitrates[j].bitrate == rate)
-					rates |= BIT(j);
-			}
-		}
-		sdata->vif.bss_conf.basic_rates = rates;
+		ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
+					 wiphy->bands[band],
+					 params->basic_rates,
+					 params->basic_rates_len,
+					 &sdata->vif.bss_conf.basic_rates);
 		changed |= BSS_CHANGED_BASIC_RATES;
 	}
 
@@ -2301,14 +2373,25 @@
 }
 
 #ifdef CONFIG_NL80211_TESTMODE
-static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len)
+static int ieee80211_testmode_cmd(struct wiphy *wiphy,
+				  struct wireless_dev *wdev,
+				  void *data, int len)
 {
 	struct ieee80211_local *local = wiphy_priv(wiphy);
+	struct ieee80211_vif *vif = NULL;
 
 	if (!local->ops->testmode_cmd)
 		return -EOPNOTSUPP;
 
-	return local->ops->testmode_cmd(&local->hw, data, len);
+	if (wdev) {
+		struct ieee80211_sub_if_data *sdata;
+
+		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+		if (sdata->flags & IEEE80211_SDATA_IN_DRIVER)
+			vif = &sdata->vif;
+	}
+
+	return local->ops->testmode_cmd(&local->hw, vif, data, len);
 }
 
 static int ieee80211_testmode_dump(struct wiphy *wiphy,
@@ -2325,7 +2408,91 @@
 }
 #endif
 
-int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
+int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
+				enum ieee80211_smps_mode smps_mode)
+{
+	struct sta_info *sta;
+	enum ieee80211_smps_mode old_req;
+	int i;
+
+	if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP))
+		return -EINVAL;
+
+	if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
+		return 0;
+
+	old_req = sdata->u.ap.req_smps;
+	sdata->u.ap.req_smps = smps_mode;
+
+	/* AUTOMATIC doesn't mean much for AP - don't allow it */
+	if (old_req == smps_mode ||
+	    smps_mode == IEEE80211_SMPS_AUTOMATIC)
+		return 0;
+
+	 /* If no associated stations, there's no need to do anything */
+	if (!atomic_read(&sdata->u.ap.num_mcast_sta)) {
+		sdata->smps_mode = smps_mode;
+		ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
+		return 0;
+	}
+
+	ht_dbg(sdata,
+	       "SMSP %d requested in AP mode, sending Action frame to %d stations\n",
+	       smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta));
+
+	mutex_lock(&sdata->local->sta_mtx);
+	for (i = 0; i < STA_HASH_SIZE; i++) {
+		for (sta = rcu_dereference_protected(sdata->local->sta_hash[i],
+				lockdep_is_held(&sdata->local->sta_mtx));
+		     sta;
+		     sta = rcu_dereference_protected(sta->hnext,
+				lockdep_is_held(&sdata->local->sta_mtx))) {
+			/*
+			 * Only stations associated to our AP and
+			 * associated VLANs
+			 */
+			if (sta->sdata->bss != &sdata->u.ap)
+				continue;
+
+			/* This station doesn't support MIMO - skip it */
+			if (sta_info_tx_streams(sta) == 1)
+				continue;
+
+			/*
+			 * Don't wake up a STA just to send the action frame
+			 * unless we are getting more restrictive.
+			 */
+			if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
+			    !ieee80211_smps_is_restrictive(sta->known_smps_mode,
+							   smps_mode)) {
+				ht_dbg(sdata,
+				       "Won't send SMPS to sleeping STA %pM\n",
+				       sta->sta.addr);
+				continue;
+			}
+
+			/*
+			 * If the STA is not authorized, wait until it gets
+			 * authorized and the action frame will be sent then.
+			 */
+			if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+				continue;
+
+			ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
+			ieee80211_send_smps_action(sdata, smps_mode,
+						   sta->sta.addr,
+						   sdata->vif.bss_conf.bssid);
+		}
+	}
+	mutex_unlock(&sdata->local->sta_mtx);
+
+	sdata->smps_mode = smps_mode;
+	ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
+
+	return 0;
+}
+
+int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
 			     enum ieee80211_smps_mode smps_mode)
 {
 	const u8 *ap;
@@ -2334,6 +2501,9 @@
 
 	lockdep_assert_held(&sdata->wdev.mtx);
 
+	if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
+		return -EINVAL;
+
 	old_req = sdata->u.mgd.req_smps;
 	sdata->u.mgd.req_smps = smps_mode;
 
@@ -2374,8 +2544,7 @@
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 
-	if (sdata->vif.type != NL80211_IFTYPE_STATION &&
-	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+	if (sdata->vif.type != NL80211_IFTYPE_STATION)
 		return -EOPNOTSUPP;
 
 	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
@@ -2390,7 +2559,7 @@
 
 	/* no change, but if automatic follow powersave */
 	sdata_lock(sdata);
-	__ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
+	__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps);
 	sdata_unlock(sdata);
 
 	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
@@ -2448,8 +2617,8 @@
 		int j;
 
 		sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
-		memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].mcs,
-		       sizeof(mask->control[i].mcs));
+		memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs,
+		       sizeof(mask->control[i].ht_mcs));
 
 		sdata->rc_has_mcs_mask[i] = false;
 		if (!sband)
@@ -2495,6 +2664,24 @@
 	INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
 	INIT_LIST_HEAD(&roc->dependents);
 
+	/*
+	 * cookie is either the roc cookie (for normal roc)
+	 * or the SKB (for mgmt TX)
+	 */
+	if (!txskb) {
+		/* local->mtx protects this */
+		local->roc_cookie_counter++;
+		roc->cookie = local->roc_cookie_counter;
+		/* wow, you wrapped 64 bits ... more likely a bug */
+		if (WARN_ON(roc->cookie == 0)) {
+			roc->cookie = 1;
+			local->roc_cookie_counter++;
+		}
+		*cookie = roc->cookie;
+	} else {
+		*cookie = (unsigned long)txskb;
+	}
+
 	/* if there's one pending or we're scanning, queue this one */
 	if (!list_empty(&local->roc_list) ||
 	    local->scanning || local->radar_detect_enabled)
@@ -2629,24 +2816,6 @@
 	if (!queued)
 		list_add_tail(&roc->list, &local->roc_list);
 
-	/*
-	 * cookie is either the roc cookie (for normal roc)
-	 * or the SKB (for mgmt TX)
-	 */
-	if (!txskb) {
-		/* local->mtx protects this */
-		local->roc_cookie_counter++;
-		roc->cookie = local->roc_cookie_counter;
-		/* wow, you wrapped 64 bits ... more likely a bug */
-		if (WARN_ON(roc->cookie == 0)) {
-			roc->cookie = 1;
-			local->roc_cookie_counter++;
-		}
-		*cookie = roc->cookie;
-	} else {
-		*cookie = (unsigned long)txskb;
-	}
-
 	return 0;
 }
 
@@ -2757,31 +2926,667 @@
 
 static int ieee80211_start_radar_detection(struct wiphy *wiphy,
 					   struct net_device *dev,
-					   struct cfg80211_chan_def *chandef)
+					   struct cfg80211_chan_def *chandef,
+					   u32 cac_time_ms)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct ieee80211_local *local = sdata->local;
-	unsigned long timeout;
 	int err;
 
-	if (!list_empty(&local->roc_list) || local->scanning)
-		return -EBUSY;
+	mutex_lock(&local->mtx);
+	if (!list_empty(&local->roc_list) || local->scanning) {
+		err = -EBUSY;
+		goto out_unlock;
+	}
 
 	/* whatever, but channel contexts should not complain about that one */
 	sdata->smps_mode = IEEE80211_SMPS_OFF;
 	sdata->needed_rx_chains = local->rx_chains;
 	sdata->radar_required = true;
 
-	mutex_lock(&local->iflist_mtx);
 	err = ieee80211_vif_use_channel(sdata, chandef,
 					IEEE80211_CHANCTX_SHARED);
+	if (err)
+		goto out_unlock;
+
+	ieee80211_queue_delayed_work(&sdata->local->hw,
+				     &sdata->dfs_cac_timer_work,
+				     msecs_to_jiffies(cac_time_ms));
+
+ out_unlock:
+	mutex_unlock(&local->mtx);
+	return err;
+}
+
+static struct cfg80211_beacon_data *
+cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
+{
+	struct cfg80211_beacon_data *new_beacon;
+	u8 *pos;
+	int len;
+
+	len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
+	      beacon->proberesp_ies_len + beacon->assocresp_ies_len +
+	      beacon->probe_resp_len;
+
+	new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
+	if (!new_beacon)
+		return NULL;
+
+	pos = (u8 *)(new_beacon + 1);
+	if (beacon->head_len) {
+		new_beacon->head_len = beacon->head_len;
+		new_beacon->head = pos;
+		memcpy(pos, beacon->head, beacon->head_len);
+		pos += beacon->head_len;
+	}
+	if (beacon->tail_len) {
+		new_beacon->tail_len = beacon->tail_len;
+		new_beacon->tail = pos;
+		memcpy(pos, beacon->tail, beacon->tail_len);
+		pos += beacon->tail_len;
+	}
+	if (beacon->beacon_ies_len) {
+		new_beacon->beacon_ies_len = beacon->beacon_ies_len;
+		new_beacon->beacon_ies = pos;
+		memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
+		pos += beacon->beacon_ies_len;
+	}
+	if (beacon->proberesp_ies_len) {
+		new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
+		new_beacon->proberesp_ies = pos;
+		memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
+		pos += beacon->proberesp_ies_len;
+	}
+	if (beacon->assocresp_ies_len) {
+		new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
+		new_beacon->assocresp_ies = pos;
+		memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
+		pos += beacon->assocresp_ies_len;
+	}
+	if (beacon->probe_resp_len) {
+		new_beacon->probe_resp_len = beacon->probe_resp_len;
+		beacon->probe_resp = pos;
+		memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
+		pos += beacon->probe_resp_len;
+	}
+
+	return new_beacon;
+}
+
+static int ieee80211_ap_beacon_presp_backup(struct ieee80211_sub_if_data *sdata)
+{
+	struct beacon_data *beacon;
+	struct probe_resp *probe_resp;
+
+	beacon = sdata_dereference(sdata->u.ap.beacon, sdata);
+	if (beacon) {
+		int head_pos, tail_pos, meshconf_pos;
+		struct beacon_data *prev_beacon;
+
+		prev_beacon = kmemdup(beacon, sizeof(beacon) +
+				      beacon->head_len + beacon->tail_len,
+				      GFP_KERNEL);
+		if (!prev_beacon)
+			return -ENOMEM;
+
+		head_pos = (void *)beacon->head - (void *)beacon;
+		tail_pos = (void *)beacon->tail - (void *)beacon;
+		meshconf_pos = (void *)beacon->meshconf - (void *)beacon;
+
+		prev_beacon->head = (void *)prev_beacon + head_pos;
+		prev_beacon->tail = (void *)prev_beacon + tail_pos;
+		prev_beacon->meshconf = (void *)prev_beacon + meshconf_pos;
+
+		sdata->u.ap.prev_beacon = prev_beacon;
+	}
+
+	probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata);
+	if (probe_resp) {
+		sdata->u.ap.prev_presp = kmemdup(probe_resp,
+						 sizeof(probe_resp) +
+						 probe_resp->len, GFP_KERNEL);
+		if (!sdata->u.ap.prev_presp) {
+			kfree(sdata->u.ap.prev_beacon);
+			sdata->u.ap.prev_beacon = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+static int
+ieee80211_ap_beacon_presp_restore(struct ieee80211_sub_if_data *sdata)
+{
+	struct beacon_data *beacon;
+	struct probe_resp *probe_resp;
+	int changed = 0;
+
+	if (sdata->u.ap.prev_beacon) {
+		beacon = sdata_dereference(sdata->u.ap.beacon, sdata);
+		rcu_assign_pointer(sdata->u.ap.beacon, sdata->u.ap.prev_beacon);
+		if (beacon)
+			kfree_rcu(beacon, rcu_head);
+		sdata->u.ap.prev_beacon = NULL;
+		changed |= BSS_CHANGED_BEACON;
+	}
+
+	if (sdata->u.ap.prev_presp) {
+		probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata);
+		rcu_assign_pointer(sdata->u.ap.probe_resp,
+				   sdata->u.ap.prev_presp);
+		if (probe_resp)
+			kfree_rcu(probe_resp, rcu_head);
+		sdata->u.ap.prev_presp = NULL;
+		changed |= BSS_CHANGED_AP_PROBE_RESP;
+	}
+
+	return changed;
+}
+
+static int ieee80211_ap_finish_csa(struct ieee80211_sub_if_data *sdata)
+{
+	int err = 0;
+
+	lockdep_assert_held(&sdata->local->mtx);
+
+	if (sdata->u.ap.next_beacon)
+		err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
+	else
+		err = ieee80211_ap_beacon_presp_restore(sdata);
+
+	kfree(sdata->u.ap.next_beacon);
+	kfree(sdata->u.ap.prev_beacon);
+	kfree(sdata->u.ap.prev_presp);
+	sdata->u.ap.next_beacon = NULL;
+	sdata->u.ap.prev_beacon = NULL;
+	sdata->u.ap.prev_presp = NULL;
+
+	if (err < 0)
+		return err;
+
+	ieee80211_bss_info_change_notify(sdata, err);
+	return 0;
+}
+
+void ieee80211_csa_finish(struct ieee80211_vif *vif)
+{
+	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+	ieee80211_queue_work(&sdata->local->hw,
+			     &sdata->csa_complete_work);
+}
+EXPORT_SYMBOL(ieee80211_csa_finish);
+
+void ieee80211_csa_clear(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_local *local = sdata->local;
+
+	sdata_assert_lock(sdata);
+	lockdep_assert_held(&local->mtx);
+
+	sdata->vif.csa_active = false;
+	sdata->csa_complete = false;
+
+	/* unblock queues when last CSA interface is cleared (either finalizes
+	 * or is cancelled) */
+	if (ieee80211_is_csa_active(local))
+		return;
+
+	ieee80211_wake_queues_by_reason(&local->hw,
+					IEEE80211_MAX_QUEUE_MAP,
+					IEEE80211_QUEUE_STOP_REASON_CSA);
+}
+
+void ieee80211_csa_free(struct ieee80211_sub_if_data *sdata)
+{
+	sdata_assert_lock(sdata);
+	lockdep_assert_held(&sdata->local->mtx);
+
+	if (sdata->vif.type != NL80211_IFTYPE_AP)
+		return;
+
+	kfree(sdata->u.ap.next_beacon);
+	kfree(sdata->u.ap.prev_beacon);
+	kfree(sdata->u.ap.prev_presp);
+	sdata->u.ap.next_beacon = NULL;
+	sdata->u.ap.prev_beacon = NULL;
+	sdata->u.ap.prev_presp = NULL;
+}
+
+static int ieee80211_csa_finish_beacon(struct ieee80211_sub_if_data *sdata)
+{
+	int err;
+
+	sdata_assert_lock(sdata);
+	lockdep_assert_held(&sdata->local->mtx);
+
+	switch (sdata->vif.type) {
+	case NL80211_IFTYPE_AP:
+		err = ieee80211_ap_finish_csa(sdata);
+		if (err < 0)
+			return err;
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		err = ieee80211_ibss_finish_csa(sdata);
+		if (err < 0)
+			return err;
+		break;
+#ifdef CONFIG_MAC80211_MESH
+	case NL80211_IFTYPE_MESH_POINT:
+		err = ieee80211_mesh_finish_csa(sdata);
+		if (err < 0)
+			return err;
+		break;
+#endif
+	default:
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_local *local = sdata->local;
+	int err, changed = 0;
+
+	sdata_assert_lock(sdata);
+	lockdep_assert_held(&local->mtx);
+
+	if (!ieee80211_sdata_running(sdata))
+		return;
+
+	if (!sdata->vif.csa_active)
+		return;
+
+	if (sdata->vif.bss_conf.chandef.width != sdata->csa_chandef.width)
+		changed |= BSS_CHANGED_BANDWIDTH;
+
+	/* channel switch is called for each sdata csa is being performed, but
+	 * this shouldn't be a problem */
+	mutex_lock(&local->chanctx_mtx);
+	err = ieee80211_chanctx_chswitch(local);
+	mutex_unlock(&local->chanctx_mtx);
+
+	if (WARN_ON(err < 0))
+		return;
+
+	if (!local->use_chanctx) {
+		local->_oper_chandef = sdata->csa_chandef;
+		ieee80211_hw_config(local, 0);
+	}
+
+	ieee80211_bss_info_change_notify(sdata, changed);
+
+	err = ieee80211_csa_finish_beacon(sdata);
+	if (err)
+		return;
+
+	cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
+}
+
+void ieee80211_csa_finalize_work(struct work_struct *work)
+{
+	struct ieee80211_sub_if_data *sdata =
+		container_of(work, struct ieee80211_sub_if_data,
+			     csa_finalize_work);
+
+	sdata_lock(sdata);
+	mutex_lock(&sdata->local->mtx);
+
+	/* AP might have been stopped while waiting for the lock. */
+	if (!sdata->vif.csa_active)
+		goto unlock;
+
+	if (!ieee80211_sdata_running(sdata))
+		goto unlock;
+
+	ieee80211_csa_finalize(sdata);
+
+unlock:
+	ieee80211_csa_clear(sdata);
+	mutex_unlock(&sdata->local->mtx);
+	sdata_unlock(sdata);
+}
+
+static bool ieee80211_is_csa_complete(struct ieee80211_local *local)
+{
+	struct ieee80211_sub_if_data *sdata;
+	int num_active = 0;
+	int num_complete = 0;
+
+	lockdep_assert_held(&local->mtx);
+	lockdep_assert_held(&local->iflist_mtx);
+
+	list_for_each_entry(sdata, &local->interfaces, list) {
+		if (sdata->vif.csa_active)
+			num_active++;
+		if (sdata->csa_complete)
+			num_complete++;
+	}
+
+	if (num_active == 0)
+		return false;
+	if (num_active != num_complete)
+		return false;
+
+	return true;
+}
+
+static void ieee80211_queue_csa_finalize(struct ieee80211_local *local)
+{
+	struct ieee80211_sub_if_data *sdata;
+
+	lockdep_assert_held(&local->mtx);
+	lockdep_assert_held(&local->iflist_mtx);
+
+	list_for_each_entry(sdata, &local->interfaces, list) {
+		if (!ieee80211_sdata_running(sdata))
+			continue;
+
+		if (!sdata->vif.csa_active)
+			continue;
+
+		ieee80211_queue_work(&local->hw, &sdata->csa_finalize_work);
+	}
+}
+
+void ieee80211_csa_complete_work(struct work_struct *work)
+{
+	struct ieee80211_sub_if_data *sdata =
+		container_of(work, struct ieee80211_sub_if_data,
+			     csa_complete_work);
+	struct ieee80211_local *local = sdata->local;
+
+	mutex_lock(&local->mtx);
+	mutex_lock(&local->iflist_mtx);
+
+	if (sdata->vif.csa_active)
+		sdata->csa_complete = true;
+
+	if (ieee80211_is_csa_complete(sdata->local))
+		ieee80211_queue_csa_finalize(sdata->local);
+
 	mutex_unlock(&local->iflist_mtx);
+	mutex_unlock(&local->mtx);
+}
+
+static void ieee80211_channel_switch_abort(struct wiphy *wiphy,
+					   struct net_device *dev,
+					   struct cfg80211_csa_settings *params)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_if_mesh __maybe_unused *ifmsh;
+
+	sdata_assert_lock(sdata);
+	lockdep_assert_held(&local->mtx);
+
+	ieee80211_csa_clear(sdata);
+
+	/* force to switch to previous AP beacon */
+	kfree(sdata->u.ap.next_beacon);
+	sdata->u.ap.next_beacon = NULL;
+
+	ieee80211_csa_finish_beacon(sdata);
+}
+
+int __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+			       struct cfg80211_csa_settings *params,
+			       int num_ifaces)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_chanctx *chanctx;
+	struct ieee80211_if_mesh __maybe_unused *ifmsh;
+	int err, changed = 0;
+
+	sdata_assert_lock(sdata);
+	lockdep_assert_held(&local->mtx);
+
+	/* Only first CSA call sets num_ifaces > 0. It's used to validate
+	 * chanctx->refcount and start CSA sequence (thus reserving subsequenct
+	 * CSA for each interface */
+
+	if (num_ifaces && (!list_empty(&local->roc_list) ||
+			    local->scanning ||
+			    ieee80211_is_csa_active(local)))
+		return -EBUSY;
+
+	if (!ieee80211_sdata_running(sdata))
+		return -ENETDOWN;
+
+	if (sdata->wdev.cac_started)
+		return -EBUSY;
+
+	if (sdata->vif.csa_active)
+		return -EBUSY;
+
+	if (cfg80211_chandef_identical(&params->chandef,
+				       &sdata->vif.bss_conf.chandef))
+		return -EINVAL;
+
+	mutex_lock(&local->chanctx_mtx);
+
+	chanctx = ieee80211_get_csa_chanctx(local);
+	if (!chanctx) {
+		mutex_unlock(&local->chanctx_mtx);
+		return -EBUSY;
+	}
+
+	if (num_ifaces && chanctx->refcount != num_ifaces) {
+		mutex_unlock(&local->chanctx_mtx);
+		return -EBUSY;
+	}
+
+	mutex_unlock(&local->chanctx_mtx);
+
+	/* don't allow another channel switch if one is already active. */
+	if (sdata->vif.csa_active)
+		return -EBUSY;
+
+	switch (sdata->vif.type) {
+	case NL80211_IFTYPE_AP:
+		sdata->u.ap.next_beacon =
+			cfg80211_beacon_dup(&params->beacon_after);
+		if (!sdata->u.ap.next_beacon)
+			return -ENOMEM;
+
+		/*
+		 * With a count of 0, we don't have to wait for any
+		 * TBTT before switching, so complete the CSA
+		 * immediately.  In theory, with a count == 1 we
+		 * should delay the switch until just before the next
+		 * TBTT, but that would complicate things so we switch
+		 * immediately too.  If we would delay the switch
+		 * until the next TBTT, we would have to set the probe
+		 * response here.
+		 *
+		 * TODO: A channel switch with count <= 1 without
+		 * sending a CSA action frame is kind of useless,
+		 * because the clients won't know we're changing
+		 * channels.  The action frame must be implemented
+		 * either here or in the userspace.
+		 */
+		if (params->count <= 1)
+			break;
+
+		sdata->csa_counter_offset_beacon =
+			params->counter_offset_beacon;
+		sdata->csa_counter_offset_presp = params->counter_offset_presp;
+
+		err = ieee80211_ap_beacon_presp_backup(sdata);
+		if (err) {
+			ieee80211_csa_free(sdata);
+			return -ENOMEM;
+		}
+
+		err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
+		if (err < 0) {
+			ieee80211_csa_free(sdata);
+			return err;
+		}
+		changed |= err;
+
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		if (!sdata->vif.bss_conf.ibss_joined)
+			return -EINVAL;
+
+		if (params->chandef.width != sdata->u.ibss.chandef.width)
+			return -EINVAL;
+
+		switch (params->chandef.width) {
+		case NL80211_CHAN_WIDTH_40:
+			if (cfg80211_get_chandef_type(&params->chandef) !=
+			    cfg80211_get_chandef_type(&sdata->u.ibss.chandef))
+				return -EINVAL;
+		case NL80211_CHAN_WIDTH_5:
+		case NL80211_CHAN_WIDTH_10:
+		case NL80211_CHAN_WIDTH_20_NOHT:
+		case NL80211_CHAN_WIDTH_20:
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/* changes into another band are not supported */
+		if (sdata->u.ibss.chandef.chan->band !=
+		    params->chandef.chan->band)
+			return -EINVAL;
+
+		/* see comments in the NL80211_IFTYPE_AP block */
+		if (params->count > 1) {
+			err = ieee80211_ibss_csa_beacon(sdata, params);
+			if (err < 0)
+				return err;
+			changed |= err;
+		}
+
+		ieee80211_send_action_csa(sdata, params);
+
+		break;
+#ifdef CONFIG_MAC80211_MESH
+	case NL80211_IFTYPE_MESH_POINT:
+		ifmsh = &sdata->u.mesh;
+
+		if (!ifmsh->mesh_id)
+			return -EINVAL;
+
+		if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
+			return -EINVAL;
+
+		/* changes into another band are not supported */
+		if (sdata->vif.bss_conf.chandef.chan->band !=
+		    params->chandef.chan->band)
+			return -EINVAL;
+
+		if (!ifmsh->pre_value)
+			ifmsh->pre_value = 1;
+		else
+			ifmsh->pre_value++;
+
+		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE)
+			ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT;
+
+		/* see comments in the NL80211_IFTYPE_AP block */
+		if (params->count > 1) {
+			err = ieee80211_mesh_csa_beacon(sdata, params);
+			if (err < 0) {
+				ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
+				return err;
+			}
+			changed |= err;
+		}
+
+		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT)
+			ieee80211_send_action_csa(sdata, params);
+
+		break;
+#endif
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	sdata->csa_radar_required = params->radar_required;
+
+	if (params->block_tx)
+		ieee80211_stop_queues_by_reason(&local->hw,
+				IEEE80211_MAX_QUEUE_MAP,
+				IEEE80211_QUEUE_STOP_REASON_CSA);
+
+	sdata->csa_chandef = params->chandef;
+	sdata->vif.csa_active = true;
+
+	if (changed) {
+		ieee80211_bss_info_change_notify(sdata, changed);
+		drv_channel_switch_beacon(sdata, &params->chandef);
+	} else {
+		/* if the beacon didn't change, we can finalize immediately */
+		ieee80211_csa_finalize(sdata);
+	}
+
+	return 0;
+}
+
+static int ieee80211_csa_allowed_settings(struct cfg80211_csa_settings *params,
+					  int num_params)
+{
+	const struct cfg80211_chan_def *chandef;
+	int i;
+
+	if (num_params == 0)
+		return -EINVAL;
+
+	chandef = &params[0].chandef;
+	for (i = 1; i < num_params; i++) {
+		chandef = cfg80211_chandef_compatible(chandef,
+						      &params[i].chandef);
+		if (!chandef)
+			return -EBUSY;
+	}
+
+	return 0;
+}
+
+int ieee80211_channel_switch(struct wiphy *wiphy,
+			     struct cfg80211_csa_settings *params,
+			     int num_params)
+{
+	struct ieee80211_sub_if_data *sdata;
+	int err, i;
+
+	err = ieee80211_csa_allowed_settings(params, num_params);
 	if (err)
 		return err;
 
-	timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
-	ieee80211_queue_delayed_work(&sdata->local->hw,
-				     &sdata->dfs_cac_timer_work, timeout);
+	for (i = 0; i < num_params; i++) {
+		sdata = IEEE80211_DEV_TO_SUB_IF(params[i].dev);
+
+		sdata_lock(sdata);
+		mutex_lock(&sdata->local->mtx);
+		err = __ieee80211_channel_switch(wiphy, params[i].dev,
+						 &params[i],
+						 i == 0 ? num_params : 0);
+		mutex_unlock(&sdata->local->mtx);
+		sdata_unlock(sdata);
+
+		if (err) {
+			for (i--; i >= 0; i--) {
+				sdata_lock(sdata);
+				mutex_lock(&sdata->local->mtx);
+				ieee80211_channel_switch_abort(wiphy,
+							       params[i].dev,
+							       &params[i]);
+				mutex_unlock(&sdata->local->mtx);
+				sdata_unlock(sdata);
+			}
+			return err;
+		}
+	}
 
 	return 0;
 }
@@ -2830,7 +3635,8 @@
 			need_offchan = true;
 		if (!ieee80211_is_action(mgmt->frame_control) ||
 		    mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
-		    mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED)
+		    mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED ||
+		    mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)
 			break;
 		rcu_read_lock();
 		sta = sta_info_get(sdata, mgmt->da);
@@ -3503,4 +4309,5 @@
 	.get_et_strings = ieee80211_get_et_strings,
 	.get_channel = ieee80211_cfg_get_channel,
 	.start_radar_detection = ieee80211_start_radar_detection,
+	.channel_switch = ieee80211_channel_switch,
 };
diff -ruw linux-3.11.10/net/mac80211/chan.c linux-3.11.10-fbx/net/mac80211/chan.c
--- linux-3.11.10/net/mac80211/chan.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/chan.c	2015-09-29 11:15:09.065756999 +0200
@@ -9,6 +9,140 @@
 #include "ieee80211_i.h"
 #include "driver-ops.h"
 
+static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
+{
+	switch (sta->bandwidth) {
+	case IEEE80211_STA_RX_BW_20:
+		if (sta->ht_cap.ht_supported)
+			return NL80211_CHAN_WIDTH_20;
+		else
+			return NL80211_CHAN_WIDTH_20_NOHT;
+	case IEEE80211_STA_RX_BW_40:
+		return NL80211_CHAN_WIDTH_40;
+	case IEEE80211_STA_RX_BW_80:
+		return NL80211_CHAN_WIDTH_80;
+	case IEEE80211_STA_RX_BW_160:
+		/*
+		 * This applied for both 160 and 80+80. since we use
+		 * the returned value to consider degradation of
+		 * ctx->conf.min_def, we have to make sure to take
+		 * the bigger one (NL80211_CHAN_WIDTH_160).
+		 * Otherwise we might try degrading even when not
+		 * needed, as the max required sta_bw returned (80+80)
+		 * might be smaller than the configured bw (160).
+		 */
+		return NL80211_CHAN_WIDTH_160;
+	default:
+		WARN_ON(1);
+		return NL80211_CHAN_WIDTH_20;
+	}
+}
+
+static enum nl80211_chan_width
+ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata)
+{
+	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
+	struct sta_info *sta;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
+		if (sdata != sta->sdata &&
+		    !(sta->sdata->bss && sta->sdata->bss == sdata->bss))
+			continue;
+
+		if (!sta->uploaded)
+			continue;
+
+		max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta));
+	}
+	rcu_read_unlock();
+
+	return max_bw;
+}
+
+static enum nl80211_chan_width
+ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
+				      struct ieee80211_chanctx_conf *conf)
+{
+	struct ieee80211_sub_if_data *sdata;
+	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+		struct ieee80211_vif *vif = &sdata->vif;
+		enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT;
+
+		if (!ieee80211_sdata_running(sdata))
+			continue;
+
+		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
+			continue;
+
+		switch (vif->type) {
+		case NL80211_IFTYPE_AP:
+		case NL80211_IFTYPE_AP_VLAN:
+			width = ieee80211_get_max_required_bw(sdata);
+			break;
+		case NL80211_IFTYPE_P2P_DEVICE:
+			continue;
+		case NL80211_IFTYPE_STATION:
+		case NL80211_IFTYPE_ADHOC:
+		case NL80211_IFTYPE_WDS:
+		case NL80211_IFTYPE_MESH_POINT:
+			width = vif->bss_conf.chandef.width;
+			break;
+		case NL80211_IFTYPE_UNSPECIFIED:
+		case NUM_NL80211_IFTYPES:
+		case NL80211_IFTYPE_MONITOR:
+		case NL80211_IFTYPE_P2P_CLIENT:
+		case NL80211_IFTYPE_P2P_GO:
+			WARN_ON_ONCE(1);
+		}
+		max_bw = max(max_bw, width);
+	}
+	rcu_read_unlock();
+
+	return max_bw;
+}
+
+/*
+ * recalc the min required chan width of the channel context, which is
+ * the max of min required widths of all the interfaces bound to this
+ * channel context.
+ */
+void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
+				      struct ieee80211_chanctx *ctx)
+{
+	enum nl80211_chan_width max_bw;
+	struct cfg80211_chan_def min_def;
+
+	lockdep_assert_held(&local->chanctx_mtx);
+
+	/* don't optimize 5MHz, 10MHz, and radar_enabled confs */
+	if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 ||
+	    ctx->conf.def.width == NL80211_CHAN_WIDTH_10 ||
+	    ctx->conf.radar_enabled) {
+		ctx->conf.min_def = ctx->conf.def;
+		return;
+	}
+
+	max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf);
+
+	/* downgrade chandef up to max_bw */
+	min_def = ctx->conf.def;
+	while (min_def.width > max_bw)
+		ieee80211_chandef_downgrade(&min_def);
+
+	if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def))
+		return;
+
+	ctx->conf.min_def = min_def;
+	if (!ctx->driver_present)
+		return;
+
+	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH);
+}
+
 static void ieee80211_change_chanctx(struct ieee80211_local *local,
 				     struct ieee80211_chanctx *ctx,
 				     const struct cfg80211_chan_def *chandef)
@@ -20,6 +154,7 @@
 
 	ctx->conf.def = *chandef;
 	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
+	ieee80211_recalc_chanctx_min_def(local, ctx);
 
 	if (!local->use_chanctx) {
 		local->_oper_chandef = *chandef;
@@ -93,11 +228,12 @@
 	ctx->conf.rx_chains_dynamic = 1;
 	ctx->mode = mode;
 	ctx->conf.radar_enabled = ieee80211_is_radar_required(local);
+	ieee80211_recalc_chanctx_min_def(local, ctx);
 	if (!local->use_chanctx)
 		local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
 
-	/* acquire mutex to prevent idle from changing */
-	mutex_lock(&local->mtx);
+	/* we hold the mutex to prevent idle from changing */
+	lockdep_assert_held(&local->mtx);
 	/* turn idle off *before* setting channel -- some drivers need that */
 	changed = ieee80211_idle_off(local);
 	if (changed)
@@ -110,19 +246,14 @@
 		err = drv_add_chanctx(local, ctx);
 		if (err) {
 			kfree(ctx);
-			ctx = ERR_PTR(err);
-
 			ieee80211_recalc_idle(local);
-			goto out;
+			return ERR_PTR(err);
 		}
 	}
 
 	/* and keep the mutex held until the new chanctx is on the list */
 	list_add_rcu(&ctx->list, &local->chanctx_list);
 
- out:
-	mutex_unlock(&local->mtx);
-
 	return ctx;
 }
 
@@ -158,9 +289,7 @@
 	/* throw a warning if this wasn't the only channel context. */
 	WARN_ON(check_single_channel && !list_empty(&local->chanctx_list));
 
-	mutex_lock(&local->mtx);
 	ieee80211_recalc_idle(local);
-	mutex_unlock(&local->mtx);
 }
 
 static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
@@ -179,6 +308,7 @@
 	ctx->refcount++;
 
 	ieee80211_recalc_txpower(sdata);
+	ieee80211_recalc_chanctx_min_def(local, ctx);
 	sdata->vif.bss_conf.idle = false;
 
 	if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
@@ -221,6 +351,31 @@
 	ieee80211_change_chanctx(local, ctx, compat);
 }
 
+static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
+					   struct ieee80211_chanctx *chanctx)
+{
+	bool radar_enabled;
+
+	lockdep_assert_held(&local->chanctx_mtx);
+	/* for setting local->radar_detect_enabled */
+	lockdep_assert_held(&local->mtx);
+
+	radar_enabled = ieee80211_is_radar_required(local);
+
+	if (radar_enabled == chanctx->conf.radar_enabled)
+		return;
+
+	chanctx->conf.radar_enabled = radar_enabled;
+	local->radar_detect_enabled = chanctx->conf.radar_enabled;
+
+	if (!local->use_chanctx) {
+		local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
+		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+	}
+
+	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
+}
+
 static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
 					   struct ieee80211_chanctx *ctx)
 {
@@ -243,6 +398,7 @@
 		ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
 		ieee80211_recalc_smps_chanctx(local, ctx);
 		ieee80211_recalc_radar_chanctx(local, ctx);
+		ieee80211_recalc_chanctx_min_def(local, ctx);
 	}
 }
 
@@ -266,29 +422,6 @@
 		ieee80211_free_chanctx(local, ctx);
 }
 
-void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
-				    struct ieee80211_chanctx *chanctx)
-{
-	bool radar_enabled;
-
-	lockdep_assert_held(&local->chanctx_mtx);
-
-	radar_enabled = ieee80211_is_radar_required(local);
-
-	if (radar_enabled == chanctx->conf.radar_enabled)
-		return;
-
-	chanctx->conf.radar_enabled = radar_enabled;
-	local->radar_detect_enabled = chanctx->conf.radar_enabled;
-
-	if (!local->use_chanctx) {
-		local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
-		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
-	}
-
-	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
-}
-
 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
 				   struct ieee80211_chanctx *chanctx)
 {
@@ -380,8 +513,17 @@
 	struct ieee80211_chanctx *ctx;
 	int ret;
 
+	lockdep_assert_held(&local->mtx);
+
 	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
 
+	/* Do not allow an interface to bind to channel contexts during CSA as
+	 * it would end up with the interface being dragged to a different
+	 * channel once CSA completes. It's safe to unbind from channel
+	 * contexts though. */
+	if (ieee80211_is_csa_active(local))
+		return -EBUSY;
+
 	mutex_lock(&local->chanctx_mtx);
 	__ieee80211_vif_release_channel(sdata);
 
@@ -410,6 +552,113 @@
 	return ret;
 }
 
+const struct cfg80211_chan_def *
+ieee80211_get_csa_chandef(struct ieee80211_local *local)
+{
+	struct ieee80211_sub_if_data *sdata;
+	const struct cfg80211_chan_def *chandef = NULL;
+
+	lockdep_assert_held(&local->mtx);
+	lockdep_assert_held(&local->chanctx_mtx);
+
+	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+		if (!sdata->vif.csa_active)
+			continue;
+
+		if (!sdata->csa_complete)
+			return NULL;
+
+		if (chandef == NULL)
+			chandef = &sdata->csa_chandef;
+		else
+			chandef = cfg80211_chandef_compatible(
+					chandef, &sdata->csa_chandef);
+
+		if (!chandef)
+			return NULL;
+	}
+
+	return chandef;
+}
+
+static void ieee80211_use_csa_chandef(struct ieee80211_local *local)
+{
+	struct ieee80211_sub_if_data *sdata;
+
+	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+		if (!sdata->vif.csa_active)
+			continue;
+
+		sdata->radar_required = sdata->csa_radar_required;
+		sdata->vif.bss_conf.chandef = sdata->csa_chandef;
+	}
+}
+
+struct ieee80211_chanctx *
+ieee80211_get_csa_chanctx(struct ieee80211_local *local)
+{
+	struct ieee80211_chanctx *chanctx = NULL, *ctx;
+	int num_chanctx = 0;
+
+	lockdep_assert_held(&local->chanctx_mtx);
+
+	list_for_each_entry(ctx, &local->chanctx_list, list) {
+		chanctx = ctx;
+		num_chanctx++;
+	}
+
+	/* multi-channel is not supported, multi-vif is */
+	if (num_chanctx > 1)
+		return NULL;
+
+	return chanctx;
+}
+
+int ieee80211_chanctx_chswitch(struct ieee80211_local *local)
+{
+	u32 chanctx_changed = 0;
+	struct ieee80211_chanctx *ctx;
+	const struct cfg80211_chan_def *chandef;
+
+	lockdep_assert_held(&local->mtx);
+	lockdep_assert_held(&local->chanctx_mtx);
+
+	ctx = ieee80211_get_csa_chanctx(local);
+	if (!ctx)
+		return -EBUSY;
+
+	rcu_read_lock();
+	chandef = ieee80211_get_csa_chandef(local);
+	if (!chandef) {
+		rcu_read_unlock();
+		return -EINVAL;
+	}
+
+	if (!cfg80211_chandef_usable(local->hw.wiphy, chandef,
+				     IEEE80211_CHAN_DISABLED)) {
+		rcu_read_unlock();
+		return -EINVAL;
+	}
+
+	ieee80211_use_csa_chandef(local);
+	rcu_read_unlock();
+
+	chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
+
+	if (ctx->conf.def.width != chandef->width)
+		chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
+
+	ctx->conf.def = *chandef;
+	drv_change_chanctx(local, ctx, chanctx_changed);
+
+	ieee80211_recalc_chanctx_chantype(local, ctx);
+	ieee80211_recalc_smps_chanctx(local, ctx);
+	ieee80211_recalc_radar_chanctx(local, ctx);
+	ieee80211_recalc_chanctx_min_def(local, ctx);
+
+	return 0;
+}
+
 int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
 				   const struct cfg80211_chan_def *chandef,
 				   u32 *changed)
@@ -463,6 +712,8 @@
 {
 	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
 
+	lockdep_assert_held(&sdata->local->mtx);
+
 	mutex_lock(&sdata->local->chanctx_mtx);
 	__ieee80211_vif_release_channel(sdata);
 	mutex_unlock(&sdata->local->chanctx_mtx);
diff -ruw linux-3.11.10/net/mac80211/debug.h linux-3.11.10-fbx/net/mac80211/debug.h
--- linux-3.11.10/net/mac80211/debug.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/debug.h	2015-09-29 11:15:09.065756999 +0200
@@ -44,6 +44,12 @@
 #define MAC80211_MESH_SYNC_DEBUG 0
 #endif
 
+#ifdef CONFIG_MAC80211_MESH_CSA_DEBUG
+#define MAC80211_MESH_CSA_DEBUG 1
+#else
+#define MAC80211_MESH_CSA_DEBUG 0
+#endif
+
 #ifdef CONFIG_MAC80211_MESH_PS_DEBUG
 #define MAC80211_MESH_PS_DEBUG 1
 #else
@@ -157,6 +163,10 @@
 	_sdata_dbg(MAC80211_MESH_SYNC_DEBUG,				\
 		   sdata, fmt, ##__VA_ARGS__)
 
+#define mcsa_dbg(sdata, fmt, ...)					\
+	_sdata_dbg(MAC80211_MESH_CSA_DEBUG,				\
+		   sdata, fmt, ##__VA_ARGS__)
+
 #define mps_dbg(sdata, fmt, ...)					\
 	_sdata_dbg(MAC80211_MESH_PS_DEBUG,				\
 		   sdata, fmt, ##__VA_ARGS__)
diff -ruw linux-3.11.10/net/mac80211/driver-ops.h linux-3.11.10-fbx/net/mac80211/driver-ops.h
--- linux-3.11.10/net/mac80211/driver-ops.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/driver-ops.h	2015-09-29 11:15:09.065756999 +0200
@@ -1072,4 +1072,44 @@
 }
 #endif
 
+static inline void
+drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
+			  struct cfg80211_chan_def *chandef)
+{
+	struct ieee80211_local *local = sdata->local;
+
+	if (local->ops->channel_switch_beacon) {
+		trace_drv_channel_switch_beacon(local, sdata, chandef);
+		local->ops->channel_switch_beacon(&local->hw, &sdata->vif,
+						  chandef);
+	}
+}
+
+static inline int drv_join_ibss(struct ieee80211_local *local,
+				struct ieee80211_sub_if_data *sdata)
+{
+	int ret = 0;
+
+	might_sleep();
+	check_sdata_in_driver(sdata);
+
+	trace_drv_join_ibss(local, sdata, &sdata->vif.bss_conf);
+	if (local->ops->join_ibss)
+		ret = local->ops->join_ibss(&local->hw, &sdata->vif);
+	trace_drv_return_int(local, ret);
+	return ret;
+}
+
+static inline void drv_leave_ibss(struct ieee80211_local *local,
+				  struct ieee80211_sub_if_data *sdata)
+{
+	might_sleep();
+	check_sdata_in_driver(sdata);
+
+	trace_drv_leave_ibss(local, sdata);
+	if (local->ops->leave_ibss)
+		local->ops->leave_ibss(&local->hw, &sdata->vif);
+	trace_drv_return_void(local);
+}
+
 #endif /* __MAC80211_DRIVER_OPS */
diff -ruw linux-3.11.10/net/mac80211/ht.c linux-3.11.10-fbx/net/mac80211/ht.c
--- linux-3.11.10/net/mac80211/ht.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/ht.c	2015-09-29 11:15:09.065756999 +0200
@@ -19,13 +19,14 @@
 #include "ieee80211_i.h"
 #include "rate.h"
 
-static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata,
+static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa,
+				  struct ieee80211_ht_cap *ht_capa_mask,
 				  struct ieee80211_sta_ht_cap *ht_cap,
 				  u16 flag)
 {
 	__le16 le_flag = cpu_to_le16(flag);
-	if (sdata->u.mgd.ht_capa_mask.cap_info & le_flag) {
-		if (!(sdata->u.mgd.ht_capa.cap_info & le_flag))
+	if (ht_capa_mask->cap_info & le_flag) {
+		if (!(ht_capa->cap_info & le_flag))
 			ht_cap->cap &= ~flag;
 	}
 }
@@ -33,13 +34,30 @@
 void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
 				     struct ieee80211_sta_ht_cap *ht_cap)
 {
-	u8 *scaps = (u8 *)(&sdata->u.mgd.ht_capa.mcs.rx_mask);
-	u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask);
+	struct ieee80211_ht_cap *ht_capa, *ht_capa_mask;
+	u8 *scaps, *smask;
 	int i;
 
 	if (!ht_cap->ht_supported)
 		return;
 
+	switch (sdata->vif.type) {
+	case NL80211_IFTYPE_STATION:
+		ht_capa = &sdata->u.mgd.ht_capa;
+		ht_capa_mask = &sdata->u.mgd.ht_capa_mask;
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		ht_capa = &sdata->u.ibss.ht_capa;
+		ht_capa_mask = &sdata->u.ibss.ht_capa_mask;
+		break;
+	default:
+		WARN_ON_ONCE(1);
+		return;
+	}
+
+	scaps = (u8 *)(&ht_capa->mcs.rx_mask);
+	smask = (u8 *)(&ht_capa_mask->mcs.rx_mask);
+
 	/* NOTE:  If you add more over-rides here, update register_hw
 	 * ht_capa_mod_msk logic in main.c as well.
 	 * And, if this method can ever change ht_cap.ht_supported, fix
@@ -55,28 +73,32 @@
 	}
 
 	/* Force removal of HT-40 capabilities? */
-	__check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40);
-	__check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40);
+	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+			      IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+			      IEEE80211_HT_CAP_SGI_40);
 
 	/* Allow user to disable SGI-20 (SGI-40 is handled above) */
-	__check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_20);
+	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+			      IEEE80211_HT_CAP_SGI_20);
 
 	/* Allow user to disable the max-AMSDU bit. */
-	__check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU);
+	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+			      IEEE80211_HT_CAP_MAX_AMSDU);
 
 	/* Allow user to decrease AMPDU factor */
-	if (sdata->u.mgd.ht_capa_mask.ampdu_params_info &
+	if (ht_capa_mask->ampdu_params_info &
 	    IEEE80211_HT_AMPDU_PARM_FACTOR) {
-		u8 n = sdata->u.mgd.ht_capa.ampdu_params_info
-			& IEEE80211_HT_AMPDU_PARM_FACTOR;
+		u8 n = ht_capa->ampdu_params_info &
+		       IEEE80211_HT_AMPDU_PARM_FACTOR;
 		if (n < ht_cap->ampdu_factor)
 			ht_cap->ampdu_factor = n;
 	}
 
 	/* Allow the user to increase AMPDU density. */
-	if (sdata->u.mgd.ht_capa_mask.ampdu_params_info &
+	if (ht_capa_mask->ampdu_params_info &
 	    IEEE80211_HT_AMPDU_PARM_DENSITY) {
-		u8 n = (sdata->u.mgd.ht_capa.ampdu_params_info &
+		u8 n = (ht_capa->ampdu_params_info &
 			IEEE80211_HT_AMPDU_PARM_DENSITY)
 			>> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT;
 		if (n > ht_cap->ampdu_density)
@@ -112,7 +134,8 @@
 	 * we advertised a restricted capability set to. Override
 	 * our own capabilities and then use those below.
 	 */
-	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+	if ((sdata->vif.type == NL80211_IFTYPE_STATION ||
+	     sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
 	    !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
 		ieee80211_apply_htcap_overrides(sdata, &own_cap);
 
@@ -425,14 +448,25 @@
 	return 0;
 }
 
-void ieee80211_request_smps_work(struct work_struct *work)
+void ieee80211_request_smps_mgd_work(struct work_struct *work)
 {
 	struct ieee80211_sub_if_data *sdata =
 		container_of(work, struct ieee80211_sub_if_data,
 			     u.mgd.request_smps_work);
 
 	sdata_lock(sdata);
-	__ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode);
+	__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode);
+	sdata_unlock(sdata);
+}
+
+void ieee80211_request_smps_ap_work(struct work_struct *work)
+{
+	struct ieee80211_sub_if_data *sdata =
+		container_of(work, struct ieee80211_sub_if_data,
+			     u.ap.request_smps_work);
+
+	sdata_lock(sdata);
+	__ieee80211_request_smps_ap(sdata, sdata->u.ap.driver_smps_mode);
 	sdata_unlock(sdata);
 }
 
@@ -441,19 +475,29 @@
 {
 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 
-	if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
+	if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION &&
+			 vif->type != NL80211_IFTYPE_AP))
 		return;
 
 	if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
 		smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
+	if (vif->type == NL80211_IFTYPE_STATION) {
 	if (sdata->u.mgd.driver_smps_mode == smps_mode)
 		return;
-
 	sdata->u.mgd.driver_smps_mode = smps_mode;
-
 	ieee80211_queue_work(&sdata->local->hw,
 			     &sdata->u.mgd.request_smps_work);
+	} else {
+		/* AUTOMATIC is meaningless in AP mode */
+		if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC))
+			return;
+		if (sdata->u.ap.driver_smps_mode == smps_mode)
+			return;
+		sdata->u.ap.driver_smps_mode = smps_mode;
+		ieee80211_queue_work(&sdata->local->hw,
+				     &sdata->u.ap.request_smps_work);
+	}
 }
 /* this might change ... don't want non-open drivers using it */
 EXPORT_SYMBOL_GPL(ieee80211_request_smps);
diff -ruw linux-3.11.10/net/mac80211/ibss.c linux-3.11.10-fbx/net/mac80211/ibss.c
--- linux-3.11.10/net/mac80211/ibss.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/ibss.c	2015-09-29 11:15:09.065756999 +0200
@@ -30,78 +30,28 @@
 
 #define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)
 #define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ)
+#define IEEE80211_IBSS_RSN_INACTIVITY_LIMIT (10 * HZ)
 
 #define IEEE80211_IBSS_MAX_STA_ENTRIES 128
 
-
-static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
-				      const u8 *bssid, const int beacon_int,
-				      struct cfg80211_chan_def *req_chandef,
-				      const u32 basic_rates,
+static struct beacon_data *
+ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
+			   const int beacon_int, const u32 basic_rates,
 				      const u16 capability, u64 tsf,
-				      bool creator)
+			   struct cfg80211_chan_def *chandef,
+			   bool *have_higher_than_11mbit,
+			   struct cfg80211_csa_settings *csa_settings)
 {
 	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 	struct ieee80211_local *local = sdata->local;
-	int rates, i;
+	int rates_n = 0, i, ri;
 	struct ieee80211_mgmt *mgmt;
 	u8 *pos;
 	struct ieee80211_supported_band *sband;
-	struct cfg80211_bss *bss;
-	u32 bss_change;
-	u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
-	struct cfg80211_chan_def chandef;
-	struct ieee80211_channel *chan;
+	u32 rate_flags, rates = 0, rates_added = 0;
 	struct beacon_data *presp;
 	int frame_len;
-
-	sdata_assert_lock(sdata);
-
-	/* Reset own TSF to allow time synchronization work. */
-	drv_reset_tsf(local, sdata);
-
-	if (!ether_addr_equal(ifibss->bssid, bssid))
-		sta_info_flush(sdata);
-
-	/* if merging, indicate to driver that we leave the old IBSS */
-	if (sdata->vif.bss_conf.ibss_joined) {
-		sdata->vif.bss_conf.ibss_joined = false;
-		sdata->vif.bss_conf.ibss_creator = false;
-		sdata->vif.bss_conf.enable_beacon = false;
-		netif_carrier_off(sdata->dev);
-		ieee80211_bss_info_change_notify(sdata,
-						 BSS_CHANGED_IBSS |
-						 BSS_CHANGED_BEACON_ENABLED);
-	}
-
-	presp = rcu_dereference_protected(ifibss->presp,
-					  lockdep_is_held(&sdata->wdev.mtx));
-	rcu_assign_pointer(ifibss->presp, NULL);
-	if (presp)
-		kfree_rcu(presp, rcu_head);
-
-	sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
-
-	/* make a copy of the chandef, it could be modified below. */
-	chandef = *req_chandef;
-	chan = chandef.chan;
-	if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
-		chandef.width = NL80211_CHAN_WIDTH_20;
-		chandef.center_freq1 = chan->center_freq;
-	}
-
-	ieee80211_vif_release_channel(sdata);
-	if (ieee80211_vif_use_channel(sdata, &chandef,
-				      ifibss->fixed_channel ?
-					IEEE80211_CHANCTX_SHARED :
-					IEEE80211_CHANCTX_EXCLUSIVE)) {
-		sdata_info(sdata, "Failed to join IBSS, no channel context\n");
-		return;
-	}
-
-	memcpy(ifibss->bssid, bssid, ETH_ALEN);
-
-	sband = local->hw.wiphy->bands[chan->band];
+	int shift;
 
 	/* Build IBSS probe response */
 	frame_len = sizeof(struct ieee80211_hdr_3addr) +
@@ -110,13 +60,14 @@
 		    2 + 8 /* max Supported Rates */ +
 		    3 /* max DS params */ +
 		    4 /* IBSS params */ +
+		    5 /* Channel Switch Announcement */ +
 		    2 + (IEEE80211_MAX_SUPP_RATES - 8) +
 		    2 + sizeof(struct ieee80211_ht_cap) +
 		    2 + sizeof(struct ieee80211_ht_operation) +
 		    ifibss->ie_len;
 	presp = kzalloc(sizeof(*presp) + frame_len, GFP_KERNEL);
 	if (!presp)
-		return;
+		return NULL;
 
 	presp->head = (void *)(presp + 1);
 
@@ -137,21 +88,47 @@
 	memcpy(pos, ifibss->ssid, ifibss->ssid_len);
 	pos += ifibss->ssid_len;
 
-	rates = min_t(int, 8, sband->n_bitrates);
+	sband = local->hw.wiphy->bands[chandef->chan->band];
+	rate_flags = ieee80211_chandef_rate_flags(chandef);
+	shift = ieee80211_chandef_get_shift(chandef);
+	rates_n = 0;
+	if (have_higher_than_11mbit)
+		*have_higher_than_11mbit = false;
+
+	for (i = 0; i < sband->n_bitrates; i++) {
+		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+			continue;
+		if (sband->bitrates[i].bitrate > 110 &&
+		    have_higher_than_11mbit)
+			*have_higher_than_11mbit = true;
+
+		rates |= BIT(i);
+		rates_n++;
+	}
+
 	*pos++ = WLAN_EID_SUPP_RATES;
-	*pos++ = rates;
-	for (i = 0; i < rates; i++) {
-		int rate = sband->bitrates[i].bitrate;
+	*pos++ = min_t(int, 8, rates_n);
+	for (ri = 0; ri < sband->n_bitrates; ri++) {
+		int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
+					5 * (1 << shift));
 		u8 basic = 0;
-		if (basic_rates & BIT(i))
+		if (!(rates & BIT(ri)))
+			continue;
+
+		if (basic_rates & BIT(ri))
 			basic = 0x80;
-		*pos++ = basic | (u8) (rate / 5);
+		*pos++ = basic | (u8) rate;
+		if (++rates_added == 8) {
+			ri++; /* continue at next rate for EXT_SUPP_RATES */
+			break;
+		}
 	}
 
 	if (sband->band == IEEE80211_BAND_2GHZ) {
 		*pos++ = WLAN_EID_DS_PARAMS;
 		*pos++ = 1;
-		*pos++ = ieee80211_frequency_to_channel(chan->center_freq);
+		*pos++ = ieee80211_frequency_to_channel(
+				chandef->chan->center_freq);
 	}
 
 	*pos++ = WLAN_EID_IBSS_PARAMS;
@@ -160,15 +137,30 @@
 	*pos++ = 0;
 	*pos++ = 0;
 
-	if (sband->n_bitrates > 8) {
+	if (csa_settings) {
+		*pos++ = WLAN_EID_CHANNEL_SWITCH;
+		*pos++ = 3;
+		*pos++ = csa_settings->block_tx ? 1 : 0;
+		*pos++ = ieee80211_frequency_to_channel(
+				csa_settings->chandef.chan->center_freq);
+		sdata->csa_counter_offset_beacon = (pos - presp->head);
+		*pos++ = csa_settings->count;
+	}
+
+	/* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */
+	if (rates_n > 8) {
 		*pos++ = WLAN_EID_EXT_SUPP_RATES;
-		*pos++ = sband->n_bitrates - 8;
-		for (i = 8; i < sband->n_bitrates; i++) {
-			int rate = sband->bitrates[i].bitrate;
+		*pos++ = rates_n - 8;
+		for (; ri < sband->n_bitrates; ri++) {
+			int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
+						5 * (1 << shift));
 			u8 basic = 0;
-			if (basic_rates & BIT(i))
+			if (!(rates & BIT(ri)))
+				continue;
+
+			if (basic_rates & BIT(ri))
 				basic = 0x80;
-			*pos++ = basic | (u8) (rate / 5);
+			*pos++ = basic | (u8) rate;
 		}
 	}
 
@@ -178,19 +170,23 @@
 	}
 
 	/* add HT capability and information IEs */
-	if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
-	    chandef.width != NL80211_CHAN_WIDTH_5 &&
-	    chandef.width != NL80211_CHAN_WIDTH_10 &&
+	if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT &&
+	    chandef->width != NL80211_CHAN_WIDTH_5 &&
+	    chandef->width != NL80211_CHAN_WIDTH_10 &&
 	    sband->ht_cap.ht_supported) {
-		pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
-						sband->ht_cap.cap);
+		struct ieee80211_sta_ht_cap ht_cap;
+
+		memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+		ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+
+		pos = ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
 		/*
 		 * Note: According to 802.11n-2009 9.13.3.1, HT Protection
 		 * field and RIFS Mode are reserved in IBSS mode, therefore
 		 * keep them at 0
 		 */
 		pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
-						 &chandef, 0);
+						 chandef, 0);
 	}
 
 	if (local->hw.queues >= IEEE80211_NUM_ACS) {
@@ -207,9 +203,121 @@
 
 	presp->head_len = pos - presp->head;
 	if (WARN_ON(presp->head_len > frame_len))
+		goto error;
+
+	return presp;
+error:
+	kfree(presp);
+	return NULL;
+}
+
+static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
+				      const u8 *bssid, const int beacon_int,
+				      struct cfg80211_chan_def *req_chandef,
+				      const u32 basic_rates,
+				      const u16 capability, u64 tsf,
+				      bool creator)
+{
+	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_supported_band *sband;
+	struct ieee80211_mgmt *mgmt;
+	struct cfg80211_bss *bss;
+	u32 bss_change;
+	struct cfg80211_chan_def chandef;
+	struct ieee80211_channel *chan;
+	struct beacon_data *presp;
+	enum nl80211_bss_scan_width scan_width;
+	bool have_higher_than_11mbit;
+	bool radar_required = false;
+	int err;
+
+	sdata_assert_lock(sdata);
+
+	/* Reset own TSF to allow time synchronization work. */
+	drv_reset_tsf(local, sdata);
+
+	if (!ether_addr_equal(ifibss->bssid, bssid))
+		sta_info_flush(sdata);
+
+	/* if merging, indicate to driver that we leave the old IBSS */
+	if (sdata->vif.bss_conf.ibss_joined) {
+		sdata->vif.bss_conf.ibss_joined = false;
+		sdata->vif.bss_conf.ibss_creator = false;
+		sdata->vif.bss_conf.enable_beacon = false;
+		netif_carrier_off(sdata->dev);
+		ieee80211_bss_info_change_notify(sdata,
+						 BSS_CHANGED_IBSS |
+						 BSS_CHANGED_BEACON_ENABLED);
+		drv_leave_ibss(local, sdata);
+	}
+
+	presp = rcu_dereference_protected(ifibss->presp,
+					  lockdep_is_held(&sdata->wdev.mtx));
+	rcu_assign_pointer(ifibss->presp, NULL);
+	if (presp)
+		kfree_rcu(presp, rcu_head);
+
+	sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
+
+	/* make a copy of the chandef, it could be modified below. */
+	chandef = *req_chandef;
+	chan = chandef.chan;
+	if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+		if (chandef.width == NL80211_CHAN_WIDTH_5 ||
+		    chandef.width == NL80211_CHAN_WIDTH_10 ||
+		    chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
+		    chandef.width == NL80211_CHAN_WIDTH_20) {
+			sdata_info(sdata,
+				   "Failed to join IBSS, beacons forbidden\n");
+			return;
+		}
+		chandef.width = NL80211_CHAN_WIDTH_20;
+		chandef.center_freq1 = chan->center_freq;
+		/* check again for downgraded chandef */
+		if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+			sdata_info(sdata,
+				   "Failed to join IBSS, beacons forbidden\n");
+			return;
+		}
+	}
+
+	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
+					    &chandef);
+	if (err > 0) {
+		if (!ifibss->userspace_handles_dfs) {
+			sdata_info(sdata,
+				   "Failed to join IBSS, DFS channel without control program\n");
+			return;
+		}
+		radar_required = true;
+	}
+
+	mutex_lock(&local->mtx);
+	ieee80211_vif_release_channel(sdata);
+	if (ieee80211_vif_use_channel(sdata, &chandef,
+				      ifibss->fixed_channel ?
+					IEEE80211_CHANCTX_SHARED :
+					IEEE80211_CHANCTX_EXCLUSIVE)) {
+		sdata_info(sdata, "Failed to join IBSS, no channel context\n");
+		mutex_unlock(&local->mtx);
+		return;
+	}
+	sdata->radar_required = radar_required;
+	mutex_unlock(&local->mtx);
+
+	memcpy(ifibss->bssid, bssid, ETH_ALEN);
+
+	sband = local->hw.wiphy->bands[chan->band];
+
+	presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates,
+					   capability, tsf, &chandef,
+					   &have_higher_than_11mbit, NULL);
+	if (!presp)
 		return;
 
 	rcu_assign_pointer(ifibss->presp, presp);
+	mgmt = (void *)presp->head;
 
 	sdata->vif.bss_conf.enable_beacon = true;
 	sdata->vif.bss_conf.beacon_int = beacon_int;
@@ -239,171 +347,605 @@
 	sdata->vif.bss_conf.use_short_slot = chan->band == IEEE80211_BAND_5GHZ;
 	bss_change |= BSS_CHANGED_ERP_SLOT;
 
+	/* cf. IEEE 802.11 9.2.12 */
+	if (chan->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit)
+		sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
+	else
+		sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+
+	ieee80211_set_wmm_default(sdata, true);
+
 	sdata->vif.bss_conf.ibss_joined = true;
 	sdata->vif.bss_conf.ibss_creator = creator;
+
+	err = drv_join_ibss(local, sdata);
+	if (err) {
+		sdata->vif.bss_conf.ibss_joined = false;
+		sdata->vif.bss_conf.ibss_creator = false;
+		sdata->vif.bss_conf.enable_beacon = false;
+		sdata->vif.bss_conf.ssid_len = 0;
+		RCU_INIT_POINTER(ifibss->presp, NULL);
+		kfree_rcu(presp, rcu_head);
+		mutex_lock(&local->mtx);
+		ieee80211_vif_release_channel(sdata);
+		mutex_unlock(&local->mtx);
+		sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n",
+			   err);
+		return;
+	}
+
 	ieee80211_bss_info_change_notify(sdata, bss_change);
 
-	ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates);
+	ifibss->state = IEEE80211_IBSS_MLME_JOINED;
+	mod_timer(&ifibss->timer,
+		  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
+
+	scan_width = cfg80211_chandef_to_scan_width(&chandef);
+	bss = cfg80211_inform_bss_width_frame(local->hw.wiphy, chan,
+					      scan_width, mgmt,
+					      presp->head_len, 0, GFP_KERNEL);
+	cfg80211_put_bss(local->hw.wiphy, bss);
+	netif_carrier_on(sdata->dev);
+	cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
+}
+
+static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
+				    struct ieee80211_bss *bss)
+{
+	struct cfg80211_bss *cbss =
+		container_of((void *)bss, struct cfg80211_bss, priv);
+	struct ieee80211_supported_band *sband;
+	struct cfg80211_chan_def chandef;
+	u32 basic_rates;
+	int i, j;
+	u16 beacon_int = cbss->beacon_interval;
+	const struct cfg80211_bss_ies *ies;
+	enum nl80211_channel_type chan_type;
+	u64 tsf;
+	u32 rate_flags;
+	int shift;
+
+	sdata_assert_lock(sdata);
+
+	if (beacon_int < 10)
+		beacon_int = 10;
+
+	switch (sdata->u.ibss.chandef.width) {
+	case NL80211_CHAN_WIDTH_20_NOHT:
+	case NL80211_CHAN_WIDTH_20:
+	case NL80211_CHAN_WIDTH_40:
+		chan_type = cfg80211_get_chandef_type(&sdata->u.ibss.chandef);
+		cfg80211_chandef_create(&chandef, cbss->channel, chan_type);
+		break;
+	case NL80211_CHAN_WIDTH_5:
+	case NL80211_CHAN_WIDTH_10:
+		cfg80211_chandef_create(&chandef, cbss->channel,
+					NL80211_CHAN_WIDTH_20_NOHT);
+		chandef.width = sdata->u.ibss.chandef.width;
+		break;
+	default:
+		/* fall back to 20 MHz for unsupported modes */
+		cfg80211_chandef_create(&chandef, cbss->channel,
+					NL80211_CHAN_WIDTH_20_NOHT);
+		break;
+	}
+
+	sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
+	rate_flags = ieee80211_chandef_rate_flags(&sdata->u.ibss.chandef);
+	shift = ieee80211_vif_get_shift(&sdata->vif);
+
+	basic_rates = 0;
+
+	for (i = 0; i < bss->supp_rates_len; i++) {
+		int rate = bss->supp_rates[i] & 0x7f;
+		bool is_basic = !!(bss->supp_rates[i] & 0x80);
+
+		for (j = 0; j < sband->n_bitrates; j++) {
+			int brate;
+			if ((rate_flags & sband->bitrates[j].flags)
+			    != rate_flags)
+				continue;
+
+			brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
+					     5 * (1 << shift));
+			if (brate == rate) {
+				if (is_basic)
+					basic_rates |= BIT(j);
+				break;
+			}
+		}
+	}
+
+	rcu_read_lock();
+	ies = rcu_dereference(cbss->ies);
+	tsf = ies->tsf;
+	rcu_read_unlock();
+
+	__ieee80211_sta_join_ibss(sdata, cbss->bssid,
+				  beacon_int,
+				  &chandef,
+				  basic_rates,
+				  cbss->capability,
+				  tsf, false);
+}
+
+int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
+			      struct cfg80211_csa_settings *csa_settings)
+{
+	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+	struct beacon_data *presp, *old_presp;
+	struct cfg80211_bss *cbss;
+	const struct cfg80211_bss_ies *ies;
+	u16 capability;
+	u64 tsf;
+	int ret = 0;
+
+	sdata_assert_lock(sdata);
+
+	capability = WLAN_CAPABILITY_IBSS;
+
+	if (ifibss->privacy)
+		capability |= WLAN_CAPABILITY_PRIVACY;
+
+	cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan,
+				ifibss->bssid, ifibss->ssid,
+				ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
+				WLAN_CAPABILITY_PRIVACY,
+				capability);
+
+	if (WARN_ON(!cbss)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	rcu_read_lock();
+	ies = rcu_dereference(cbss->ies);
+	tsf = ies->tsf;
+	rcu_read_unlock();
+	cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
+
+	old_presp = rcu_dereference_protected(ifibss->presp,
+					  lockdep_is_held(&sdata->wdev.mtx));
+
+	presp = ieee80211_ibss_build_presp(sdata,
+					   sdata->vif.bss_conf.beacon_int,
+					   sdata->vif.bss_conf.basic_rates,
+					   capability, tsf, &ifibss->chandef,
+					   NULL, csa_settings);
+	if (!presp) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	rcu_assign_pointer(ifibss->presp, presp);
+	if (old_presp)
+		kfree_rcu(old_presp, rcu_head);
+
+	return BSS_CHANGED_BEACON;
+ out:
+	return ret;
+}
+
+int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+	struct cfg80211_bss *cbss;
+	int err;
+	u16 capability;
+
+	sdata_assert_lock(sdata);
+
+	/* update cfg80211 bss information with the new channel */
+	if (!is_zero_ether_addr(ifibss->bssid)) {
+		capability = WLAN_CAPABILITY_IBSS;
+
+		if (ifibss->privacy)
+			capability |= WLAN_CAPABILITY_PRIVACY;
+
+		cbss = cfg80211_get_bss(sdata->local->hw.wiphy,
+					ifibss->chandef.chan,
+					ifibss->bssid, ifibss->ssid,
+					ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
+					WLAN_CAPABILITY_PRIVACY,
+					capability);
+		/* XXX: should not really modify cfg80211 data */
+		if (cbss) {
+			cbss->channel = sdata->csa_chandef.chan;
+			cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
+		}
+	}
+
+	ifibss->chandef = sdata->csa_chandef;
+
+	/* generate the beacon */
+	err = ieee80211_ibss_csa_beacon(sdata, NULL);
+	if (err < 0)
+		return err;
+
+	if (err)
+		ieee80211_bss_info_change_notify(sdata, err);
+
+	return 0;
+}
+
+void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+	cancel_work_sync(&ifibss->csa_connection_drop_work);
+}
+
+static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
+	__acquires(RCU)
+{
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+	u8 addr[ETH_ALEN];
+
+	memcpy(addr, sta->sta.addr, ETH_ALEN);
+
+	ibss_dbg(sdata, "Adding new IBSS station %pM\n", addr);
+
+	sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+	sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+	/* authorize the station only if the network is not RSN protected. If
+	 * not wait for the userspace to authorize it */
+	if (!sta->sdata->u.ibss.control_port)
+		sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
+
+	rate_control_rate_init(sta);
+
+	/* If it fails, maybe we raced another insertion? */
+	if (sta_info_insert_rcu(sta))
+		return sta_info_get(sdata, addr);
+	return sta;
+}
+
+static struct sta_info *
+ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
+		       const u8 *addr, u32 supp_rates)
+	__acquires(RCU)
+{
+	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+	struct ieee80211_local *local = sdata->local;
+	struct sta_info *sta;
+	struct ieee80211_chanctx_conf *chanctx_conf;
+	struct ieee80211_supported_band *sband;
+	enum nl80211_bss_scan_width scan_width;
+	int band;
+
+	/*
+	 * XXX: Consider removing the least recently used entry and
+	 * 	allow new one to be added.
+	 */
+	if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
+		net_info_ratelimited("%s: No room for a new IBSS STA entry %pM\n",
+				    sdata->name, addr);
+		rcu_read_lock();
+		return NULL;
+	}
+
+	if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH) {
+		rcu_read_lock();
+		return NULL;
+	}
+
+	if (!ether_addr_equal(bssid, sdata->u.ibss.bssid)) {
+		rcu_read_lock();
+		return NULL;
+	}
+
+	rcu_read_lock();
+	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+	if (WARN_ON_ONCE(!chanctx_conf))
+		return NULL;
+	band = chanctx_conf->def.chan->band;
+	scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
+	rcu_read_unlock();
+
+	sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
+	if (!sta) {
+		rcu_read_lock();
+		return NULL;
+	}
+
+	sta->last_rx = jiffies;
+
+	/* make sure mandatory rates are always added */
+	sband = local->hw.wiphy->bands[band];
+	sta->sta.supp_rates[band] = supp_rates |
+			ieee80211_mandatory_rates(sband, scan_width);
+
+	return ieee80211_ibss_finish_sta(sta);
+}
+
+static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_local *local = sdata->local;
+	int active = 0;
+	struct sta_info *sta;
+
+	sdata_assert_lock(sdata);
+
+	rcu_read_lock();
+
+	list_for_each_entry_rcu(sta, &local->sta_list, list) {
+		if (sta->sdata == sdata &&
+		    time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
+			       jiffies)) {
+			active++;
+			break;
+		}
+	}
+
+	rcu_read_unlock();
+
+	return active;
+}
+
+static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+	struct ieee80211_local *local = sdata->local;
+	struct cfg80211_bss *cbss;
+	struct beacon_data *presp;
+	struct sta_info *sta;
+	int active_ibss;
+	u16 capability;
+
+	active_ibss = ieee80211_sta_active_ibss(sdata);
+
+	if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
+		capability = WLAN_CAPABILITY_IBSS;
+
+		if (ifibss->privacy)
+			capability |= WLAN_CAPABILITY_PRIVACY;
+
+		cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
+					ifibss->bssid, ifibss->ssid,
+					ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
+					WLAN_CAPABILITY_PRIVACY,
+					capability);
+
+		if (cbss) {
+			cfg80211_unlink_bss(local->hw.wiphy, cbss);
+			cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
+		}
+	}
+
+	ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
+
+	sta_info_flush(sdata);
+
+	spin_lock_bh(&ifibss->incomplete_lock);
+	while (!list_empty(&ifibss->incomplete_stations)) {
+		sta = list_first_entry(&ifibss->incomplete_stations,
+				       struct sta_info, list);
+		list_del(&sta->list);
+		spin_unlock_bh(&ifibss->incomplete_lock);
+
+		sta_info_free(local, sta);
+		spin_lock_bh(&ifibss->incomplete_lock);
+	}
+	spin_unlock_bh(&ifibss->incomplete_lock);
+
+	netif_carrier_off(sdata->dev);
+
+	sdata->vif.bss_conf.ibss_joined = false;
+	sdata->vif.bss_conf.ibss_creator = false;
+	sdata->vif.bss_conf.enable_beacon = false;
+	sdata->vif.bss_conf.ssid_len = 0;
+
+	/* remove beacon */
+	presp = rcu_dereference_protected(ifibss->presp,
+					  lockdep_is_held(&sdata->wdev.mtx));
+	RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
+	if (presp)
+		kfree_rcu(presp, rcu_head);
+
+	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
+	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
+						BSS_CHANGED_IBSS);
+	drv_leave_ibss(local, sdata);
+	mutex_lock(&local->mtx);
+	ieee80211_vif_release_channel(sdata);
+	mutex_unlock(&local->mtx);
+}
+
+static void ieee80211_csa_connection_drop_work(struct work_struct *work)
+{
+	struct ieee80211_sub_if_data *sdata =
+		container_of(work, struct ieee80211_sub_if_data,
+			     u.ibss.csa_connection_drop_work);
+
+	sdata_lock(sdata);
+
+	ieee80211_ibss_disconnect(sdata);
+	synchronize_rcu();
+	skb_queue_purge(&sdata->skb_queue);
+
+	/* trigger a scan to find another IBSS network to join */
+	ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 
-	ifibss->state = IEEE80211_IBSS_MLME_JOINED;
-	mod_timer(&ifibss->timer,
-		  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
+	sdata_unlock(sdata);
+}
 
-	bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan,
-					mgmt, presp->head_len, 0, GFP_KERNEL);
-	cfg80211_put_bss(local->hw.wiphy, bss);
-	netif_carrier_on(sdata->dev);
-	cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
+static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+	int err;
+
+	/* if the current channel is a DFS channel, mark the channel as
+	 * unavailable.
+	 */
+	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
+					    &ifibss->chandef);
+	if (err > 0)
+		cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef,
+				     GFP_ATOMIC);
 }
 
-static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
-				    struct ieee80211_bss *bss)
+static bool
+ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
+				  struct ieee802_11_elems *elems,
+				  bool beacon)
 {
-	struct cfg80211_bss *cbss =
-		container_of((void *)bss, struct cfg80211_bss, priv);
-	struct ieee80211_supported_band *sband;
-	struct cfg80211_chan_def chandef;
-	u32 basic_rates;
-	int i, j;
-	u16 beacon_int = cbss->beacon_interval;
-	const struct cfg80211_bss_ies *ies;
-	enum nl80211_channel_type chan_type;
-	u64 tsf;
+	struct cfg80211_csa_settings params;
+	struct ieee80211_csa_ie csa_ie;
+	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+	enum nl80211_channel_type ch_type;
+	int err;
+	u32 sta_flags;
 
 	sdata_assert_lock(sdata);
+	lockdep_assert_held(&sdata->local->mtx);
 
-	if (beacon_int < 10)
-		beacon_int = 10;
+	if (sdata->vif.csa_active)
+		return true;
 
-	switch (sdata->u.ibss.chandef.width) {
-	case NL80211_CHAN_WIDTH_20_NOHT:
-	case NL80211_CHAN_WIDTH_20:
-	case NL80211_CHAN_WIDTH_40:
-		chan_type = cfg80211_get_chandef_type(&sdata->u.ibss.chandef);
-		cfg80211_chandef_create(&chandef, cbss->channel, chan_type);
-		break;
+	sta_flags = IEEE80211_STA_DISABLE_VHT;
+	switch (ifibss->chandef.width) {
 	case NL80211_CHAN_WIDTH_5:
 	case NL80211_CHAN_WIDTH_10:
-		cfg80211_chandef_create(&chandef, cbss->channel,
-					NL80211_CHAN_WIDTH_20_NOHT);
-		chandef.width = sdata->u.ibss.chandef.width;
+	case NL80211_CHAN_WIDTH_20_NOHT:
+		sta_flags |= IEEE80211_STA_DISABLE_HT;
+		/* fall through */
+	case NL80211_CHAN_WIDTH_20:
+		sta_flags |= IEEE80211_STA_DISABLE_40MHZ;
 		break;
 	default:
-		/* fall back to 20 MHz for unsupported modes */
-		cfg80211_chandef_create(&chandef, cbss->channel,
-					NL80211_CHAN_WIDTH_20_NOHT);
 		break;
 	}
 
-	sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
-
-	basic_rates = 0;
+	memset(&params, 0, sizeof(params));
+	memset(&csa_ie, 0, sizeof(csa_ie));
+	err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
+					   ifibss->chandef.chan->band,
+					   sta_flags, ifibss->bssid, &csa_ie);
+	/* can't switch to destination channel, fail */
+	if (err < 0)
+		goto disconnect;
+
+	/* did not contain a CSA */
+	if (err)
+		return false;
+
+	/* channel switch is not supported, disconnect */
+	if (!(sdata->local->hw.wiphy->flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
+		goto disconnect;
 
-	for (i = 0; i < bss->supp_rates_len; i++) {
-		int rate = (bss->supp_rates[i] & 0x7f) * 5;
-		bool is_basic = !!(bss->supp_rates[i] & 0x80);
+	params.count = csa_ie.count;
+	params.chandef = csa_ie.chandef;
 
-		for (j = 0; j < sband->n_bitrates; j++) {
-			if (sband->bitrates[j].bitrate == rate) {
-				if (is_basic)
-					basic_rates |= BIT(j);
+	switch (ifibss->chandef.width) {
+	case NL80211_CHAN_WIDTH_20_NOHT:
+	case NL80211_CHAN_WIDTH_20:
+	case NL80211_CHAN_WIDTH_40:
+		/* keep our current HT mode (HT20/HT40+/HT40-), even if
+		 * another mode  has been announced. The mode is not adopted
+		 * within the beacon while doing CSA and we should therefore
+		 * keep the mode which we announce.
+		 */
+		ch_type = cfg80211_get_chandef_type(&ifibss->chandef);
+		cfg80211_chandef_create(&params.chandef, params.chandef.chan,
+					ch_type);
 				break;
+	case NL80211_CHAN_WIDTH_5:
+	case NL80211_CHAN_WIDTH_10:
+		if (params.chandef.width != ifibss->chandef.width) {
+			sdata_info(sdata,
+				   "IBSS %pM received channel switch from incompatible channel width (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
+				   ifibss->bssid,
+				   params.chandef.chan->center_freq,
+				   params.chandef.width,
+				   params.chandef.center_freq1,
+				   params.chandef.center_freq2);
+			goto disconnect;
 			}
-		}
+		break;
+	default:
+		/* should not happen, sta_flags should prevent VHT modes. */
+		WARN_ON(1);
+		goto disconnect;
 	}
 
-	rcu_read_lock();
-	ies = rcu_dereference(cbss->ies);
-	tsf = ies->tsf;
-	rcu_read_unlock();
+	if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, &params.chandef)) {
+		sdata_info(sdata,
+			   "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
+			   ifibss->bssid,
+			   params.chandef.chan->center_freq,
+			   params.chandef.width,
+			   params.chandef.center_freq1,
+			   params.chandef.center_freq2);
+		goto disconnect;
+	}
+
+	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
+					    &params.chandef);
+	if (err < 0)
+		goto disconnect;
+	if (err) {
+		/* IBSS-DFS only allowed with a control program */
+		if (!ifibss->userspace_handles_dfs)
+			goto disconnect;
 
-	__ieee80211_sta_join_ibss(sdata, cbss->bssid,
-				  beacon_int,
-				  &chandef,
-				  basic_rates,
-				  cbss->capability,
-				  tsf, false);
+		params.radar_required = true;
 }
 
-static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
-	__acquires(RCU)
-{
-	struct ieee80211_sub_if_data *sdata = sta->sdata;
-	u8 addr[ETH_ALEN];
-
-	memcpy(addr, sta->sta.addr, ETH_ALEN);
-
-	ibss_dbg(sdata, "Adding new IBSS station %pM\n", addr);
+	if (cfg80211_chandef_identical(&params.chandef,
+				       &sdata->vif.bss_conf.chandef)) {
+		ibss_dbg(sdata,
+			 "received csa with an identical chandef, ignoring\n");
+		return true;
+	}
 
-	sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
-	sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
-	/* authorize the station only if the network is not RSN protected. If
-	 * not wait for the userspace to authorize it */
-	if (!sta->sdata->u.ibss.control_port)
-		sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
+	/* all checks done, now perform the channel switch. */
+	ibss_dbg(sdata,
+		 "received channel switch announcement to go to channel %d MHz\n",
+		 params.chandef.chan->center_freq);
 
-	rate_control_rate_init(sta);
+	params.block_tx = !!csa_ie.mode;
 
-	/* If it fails, maybe we raced another insertion? */
-	if (sta_info_insert_rcu(sta))
-		return sta_info_get(sdata, addr);
-	return sta;
-}
+	if (__ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev,
+				       &params, 1))
+		goto disconnect;
 
-static struct sta_info *
-ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
-		       const u8 *addr, u32 supp_rates)
-	__acquires(RCU)
-{
-	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
-	struct ieee80211_local *local = sdata->local;
-	struct sta_info *sta;
-	struct ieee80211_chanctx_conf *chanctx_conf;
-	struct ieee80211_supported_band *sband;
-	int band;
+	ieee80211_ibss_csa_mark_radar(sdata);
 
-	/*
-	 * XXX: Consider removing the least recently used entry and
-	 * 	allow new one to be added.
-	 */
-	if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
-		net_info_ratelimited("%s: No room for a new IBSS STA entry %pM\n",
-				    sdata->name, addr);
-		rcu_read_lock();
-		return NULL;
-	}
+	return true;
+disconnect:
+	ibss_dbg(sdata, "Can't handle channel switch, disconnect\n");
+	ieee80211_queue_work(&sdata->local->hw,
+			     &ifibss->csa_connection_drop_work);
 
-	if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH) {
-		rcu_read_lock();
-		return NULL;
-	}
+	ieee80211_ibss_csa_mark_radar(sdata);
 
-	if (!ether_addr_equal(bssid, sdata->u.ibss.bssid)) {
-		rcu_read_lock();
-		return NULL;
+	return true;
 	}
 
-	rcu_read_lock();
-	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-	if (WARN_ON_ONCE(!chanctx_conf))
-		return NULL;
-	band = chanctx_conf->def.chan->band;
-	rcu_read_unlock();
+static void
+ieee80211_rx_mgmt_spectrum_mgmt(struct ieee80211_sub_if_data *sdata,
+				struct ieee80211_mgmt *mgmt, size_t len,
+				struct ieee80211_rx_status *rx_status,
+				struct ieee802_11_elems *elems)
+{
+	int required_len;
 
-	sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
-	if (!sta) {
-		rcu_read_lock();
-		return NULL;
-	}
+	if (len < IEEE80211_MIN_ACTION_SIZE + 1)
+		return;
 
-	sta->last_rx = jiffies;
+	/* CSA is the only action we handle for now */
+	if (mgmt->u.action.u.measurement.action_code !=
+	    WLAN_ACTION_SPCT_CHL_SWITCH)
+		return;
 
-	/* make sure mandatory rates are always added */
-	sband = local->hw.wiphy->bands[band];
-	sta->sta.supp_rates[band] = supp_rates |
-			ieee80211_mandatory_rates(sband);
+	required_len = IEEE80211_MIN_ACTION_SIZE +
+		       sizeof(mgmt->u.action.u.chan_switch);
+	if (len < required_len)
+		return;
 
-	return ieee80211_ibss_finish_sta(sta);
+	mutex_lock(&sdata->local->mtx);
+	ieee80211_ibss_process_chanswitch(sdata, elems, false);
+	mutex_unlock(&sdata->local->mtx);
 }
 
 static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
@@ -465,6 +1007,7 @@
 	u64 beacon_timestamp, rx_timestamp;
 	u32 supp_rates = 0;
 	enum ieee80211_band band = rx_status->band;
+	enum nl80211_bss_scan_width scan_width;
 	struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
 	bool rates_updated = false;
 
@@ -486,16 +1029,22 @@
 		sta = sta_info_get(sdata, mgmt->sa);
 
 		if (elems->supp_rates) {
-			supp_rates = ieee80211_sta_get_rates(local, elems,
+			supp_rates = ieee80211_sta_get_rates(sdata, elems,
 							     band, NULL);
 			if (sta) {
 				u32 prev_rates;
 
 				prev_rates = sta->sta.supp_rates[band];
 				/* make sure mandatory rates are always added */
-				sta->sta.supp_rates[band] = supp_rates |
-					ieee80211_mandatory_rates(sband);
+				scan_width = NL80211_BSS_CHAN_WIDTH_20;
+				if (rx_status->flag & RX_FLAG_5MHZ)
+					scan_width = NL80211_BSS_CHAN_WIDTH_5;
+				if (rx_status->flag & RX_FLAG_10MHZ)
+					scan_width = NL80211_BSS_CHAN_WIDTH_10;
 
+				sta->sta.supp_rates[band] = supp_rates |
+					ieee80211_mandatory_rates(sband,
+								  scan_width);
 				if (sta->sta.supp_rates[band] != prev_rates) {
 					ibss_dbg(sdata,
 						 "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
@@ -561,10 +1110,6 @@
 
 	/* check if we need to merge IBSS */
 
-	/* we use a fixed BSSID */
-	if (sdata->u.ibss.fixed_bssid)
-		goto put_bss;
-
 	/* not an IBSS */
 	if (!(cbss->capability & WLAN_CAPABILITY_IBSS))
 		goto put_bss;
@@ -580,10 +1125,22 @@
 				sdata->u.ibss.ssid_len))
 		goto put_bss;
 
+	/* process channel switch */
+	mutex_lock(&local->mtx);
+	if (ieee80211_ibss_process_chanswitch(sdata, elems, true)) {
+		mutex_unlock(&local->mtx);
+		goto put_bss;
+	}
+	mutex_unlock(&local->mtx);
+
 	/* same BSSID */
 	if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid))
 		goto put_bss;
 
+	/* we use a fixed BSSID */
+	if (sdata->u.ibss.fixed_bssid)
+		goto put_bss;
+
 	if (ieee80211_have_rx_timestamp(rx_status)) {
 		/* time when timestamp field was received */
 		rx_timestamp =
@@ -610,7 +1167,7 @@
 			 "beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n",
 			 mgmt->bssid);
 		ieee80211_sta_join_ibss(sdata, bss);
-		supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL);
+		supp_rates = ieee80211_sta_get_rates(sdata, elems, band, NULL);
 		ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
 				       supp_rates);
 		rcu_read_unlock();
@@ -629,6 +1186,7 @@
 	struct sta_info *sta;
 	struct ieee80211_chanctx_conf *chanctx_conf;
 	struct ieee80211_supported_band *sband;
+	enum nl80211_bss_scan_width scan_width;
 	int band;
 
 	/*
@@ -654,6 +1212,7 @@
 		return;
 	}
 	band = chanctx_conf->def.chan->band;
+	scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
 	rcu_read_unlock();
 
 	sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
@@ -665,7 +1224,7 @@
 	/* make sure mandatory rates are always added */
 	sband = local->hw.wiphy->bands[band];
 	sta->sta.supp_rates[band] = supp_rates |
-			ieee80211_mandatory_rates(sband);
+			ieee80211_mandatory_rates(sband, scan_width);
 
 	spin_lock(&ifibss->incomplete_lock);
 	list_add(&sta->list, &ifibss->incomplete_stations);
@@ -673,28 +1232,31 @@
 	ieee80211_queue_work(&local->hw, &sdata->work);
 }
 
-static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
+static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_local *local = sdata->local;
-	int active = 0;
-	struct sta_info *sta;
+	struct sta_info *sta, *tmp;
+	unsigned long exp_time = IEEE80211_IBSS_INACTIVITY_LIMIT;
+	unsigned long exp_rsn_time = IEEE80211_IBSS_RSN_INACTIVITY_LIMIT;
 
-	sdata_assert_lock(sdata);
+	mutex_lock(&local->sta_mtx);
 
-	rcu_read_lock();
+	list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
+		if (sdata != sta->sdata)
+			continue;
 
-	list_for_each_entry_rcu(sta, &local->sta_list, list) {
-		if (sta->sdata == sdata &&
-		    time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
-			       jiffies)) {
-			active++;
-			break;
+		if (time_after(jiffies, sta->last_rx + exp_time) ||
+		    (time_after(jiffies, sta->last_rx + exp_rsn_time) &&
+		     sta->sta_state != IEEE80211_STA_AUTHORIZED)) {
+			sta_dbg(sta->sdata, "expiring inactive %sSTA %pM\n",
+				sta->sta_state != IEEE80211_STA_AUTHORIZED ?
+				"not authorized " : "", sta->sta.addr);
+
+			WARN_ON(__sta_info_destroy(sta));
 		}
 	}
 
-	rcu_read_unlock();
-
-	return active;
+	mutex_unlock(&local->sta_mtx);
 }
 
 /*
@@ -704,13 +1266,14 @@
 static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+	enum nl80211_bss_scan_width scan_width;
 
 	sdata_assert_lock(sdata);
 
 	mod_timer(&ifibss->timer,
 		  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
 
-	ieee80211_sta_expire(sdata, IEEE80211_IBSS_INACTIVITY_LIMIT);
+	ieee80211_ibss_sta_expire(sdata);
 
 	if (time_before(jiffies, ifibss->last_scan_completed +
 		       IEEE80211_IBSS_MERGE_INTERVAL))
@@ -725,8 +1288,9 @@
 	sdata_info(sdata,
 		   "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n");
 
+	scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
 	ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
-				    NULL);
+				    NULL, scan_width);
 }
 
 static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@@ -776,6 +1340,7 @@
 	struct cfg80211_bss *cbss;
 	struct ieee80211_channel *chan = NULL;
 	const u8 *bssid = NULL;
+	enum nl80211_bss_scan_width scan_width;
 	int active_ibss;
 	u16 capability;
 
@@ -817,6 +1382,17 @@
 		return;
 	}
 
+	/* if a fixed bssid and a fixed freq have been provided create the IBSS
+	 * directly and do not waste time scanning
+	 */
+	if (ifibss->fixed_bssid && ifibss->fixed_channel) {
+		sdata_info(sdata, "Created IBSS using preconfigured BSSID %pM\n",
+			   bssid);
+		ieee80211_sta_create_ibss(sdata);
+		return;
+	}
+
+
 	ibss_dbg(sdata, "sta_find_ibss: did not try to join ibss\n");
 
 	/* Selected IBSS not found in current scan results - try to scan */
@@ -824,8 +1400,10 @@
 					IEEE80211_SCAN_INTERVAL)) {
 		sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
 
+		scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
 		ieee80211_request_ibss_scan(sdata, ifibss->ssid,
-					    ifibss->ssid_len, chan);
+					    ifibss->ssid_len, chan,
+					    scan_width);
 	} else {
 		int interval = IEEE80211_SCAN_INTERVAL;
 
@@ -931,6 +1509,8 @@
 	struct ieee80211_rx_status *rx_status;
 	struct ieee80211_mgmt *mgmt;
 	u16 fc;
+	struct ieee802_11_elems elems;
+	int ies_len;
 
 	rx_status = IEEE80211_SKB_RXCB(skb);
 	mgmt = (struct ieee80211_mgmt *) skb->data;
@@ -956,6 +1536,27 @@
 	case IEEE80211_STYPE_DEAUTH:
 		ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len);
 		break;
+	case IEEE80211_STYPE_ACTION:
+		switch (mgmt->u.action.category) {
+		case WLAN_CATEGORY_SPECTRUM_MGMT:
+			ies_len = skb->len -
+				  offsetof(struct ieee80211_mgmt,
+					   u.action.u.chan_switch.variable);
+
+			if (ies_len < 0)
+				break;
+
+			ieee802_11_parse_elems(
+				mgmt->u.action.u.chan_switch.variable,
+				ies_len, true, &elems);
+
+			if (elems.parse_error)
+				break;
+
+			ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, skb->len,
+							rx_status, &elems);
+			break;
+		}
 	}
 
  mgmt_out:
@@ -1022,6 +1623,8 @@
 		    (unsigned long) sdata);
 	INIT_LIST_HEAD(&ifibss->incomplete_stations);
 	spin_lock_init(&ifibss->incomplete_lock);
+	INIT_WORK(&ifibss->csa_connection_drop_work,
+		  ieee80211_csa_connection_drop_work);
 }
 
 /* scan finished notification */
@@ -1045,6 +1648,9 @@
 			struct cfg80211_ibss_params *params)
 {
 	u32 changed = 0;
+	u32 rate_flags;
+	struct ieee80211_supported_band *sband;
+	int i;
 
 	if (params->bssid) {
 		memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
@@ -1054,7 +1660,17 @@
 
 	sdata->u.ibss.privacy = params->privacy;
 	sdata->u.ibss.control_port = params->control_port;
+	sdata->u.ibss.userspace_handles_dfs = params->userspace_handles_dfs;
 	sdata->u.ibss.basic_rates = params->basic_rates;
+
+	/* fix basic_rates if channel does not support these rates */
+	rate_flags = ieee80211_chandef_rate_flags(&params->chandef);
+	sband = sdata->local->hw.wiphy->bands[params->chandef.chan->band];
+	for (i = 0; i < sband->n_bitrates; i++) {
+		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+			sdata->u.ibss.basic_rates &= ~BIT(i);
+	}
+	sdata->u.ibss.last_scan_completed = jiffies;
 	memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate,
 	       sizeof(params->mcast_rate));
 
@@ -1076,6 +1692,11 @@
 	memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len);
 	sdata->u.ibss.ssid_len = params->ssid_len;
 
+	memcpy(&sdata->u.ibss.ht_capa, &params->ht_capa,
+	       sizeof(sdata->u.ibss.ht_capa));
+	memcpy(&sdata->u.ibss.ht_capa_mask, &params->ht_capa_mask,
+	       sizeof(sdata->u.ibss.ht_capa_mask));
+
 	/*
 	 * 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is
 	 * reserved, but an HT STA shall protect HT transmissions as though
@@ -1104,68 +1725,19 @@
 int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
-	struct ieee80211_local *local = sdata->local;
-	struct cfg80211_bss *cbss;
-	u16 capability;
-	int active_ibss;
-	struct sta_info *sta;
-	struct beacon_data *presp;
-
-	active_ibss = ieee80211_sta_active_ibss(sdata);
-
-	if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
-		capability = WLAN_CAPABILITY_IBSS;
-
-		if (ifibss->privacy)
-			capability |= WLAN_CAPABILITY_PRIVACY;
-
-		cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
-					ifibss->bssid, ifibss->ssid,
-					ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
-					WLAN_CAPABILITY_PRIVACY,
-					capability);
-
-		if (cbss) {
-			cfg80211_unlink_bss(local->hw.wiphy, cbss);
-			cfg80211_put_bss(local->hw.wiphy, cbss);
-		}
-	}
 
-	ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
-	memset(ifibss->bssid, 0, ETH_ALEN);
+	ieee80211_ibss_disconnect(sdata);
 	ifibss->ssid_len = 0;
-
-	sta_info_flush(sdata);
-
-	spin_lock_bh(&ifibss->incomplete_lock);
-	while (!list_empty(&ifibss->incomplete_stations)) {
-		sta = list_first_entry(&ifibss->incomplete_stations,
-				       struct sta_info, list);
-		list_del(&sta->list);
-		spin_unlock_bh(&ifibss->incomplete_lock);
-
-		sta_info_free(local, sta);
-		spin_lock_bh(&ifibss->incomplete_lock);
-	}
-	spin_unlock_bh(&ifibss->incomplete_lock);
-
-	netif_carrier_off(sdata->dev);
+	memset(ifibss->bssid, 0, ETH_ALEN);
 
 	/* remove beacon */
 	kfree(sdata->u.ibss.ie);
-	presp = rcu_dereference_protected(ifibss->presp,
-					  lockdep_is_held(&sdata->wdev.mtx));
-	RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
-	sdata->vif.bss_conf.ibss_joined = false;
-	sdata->vif.bss_conf.ibss_creator = false;
-	sdata->vif.bss_conf.enable_beacon = false;
-	sdata->vif.bss_conf.ssid_len = 0;
-	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
-	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
-						BSS_CHANGED_IBSS);
-	ieee80211_vif_release_channel(sdata);
+
+	/* on the next join, re-program HT parameters */
+	memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
+	memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask));
+
 	synchronize_rcu();
-	kfree(presp);
 
 	skb_queue_purge(&sdata->skb_queue);
 
diff -ruw linux-3.11.10/net/mac80211/ieee80211_i.h linux-3.11.10-fbx/net/mac80211/ieee80211_i.h
--- linux-3.11.10/net/mac80211/ieee80211_i.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/ieee80211_i.h	2015-09-29 11:15:09.065756999 +0200
@@ -53,9 +53,6 @@
  * increased memory use (about 2 kB of RAM per entry). */
 #define IEEE80211_FRAGMENT_MAX 4
 
-#define TU_TO_JIFFIES(x)	(usecs_to_jiffies((x) * 1024))
-#define TU_TO_EXP_TIME(x)	(jiffies + TU_TO_JIFFIES(x))
-
 /* power level hasn't been configured (or set to automatic) */
 #define IEEE80211_UNSET_POWER_LEVEL	INT_MIN
 
@@ -235,6 +232,7 @@
 struct beacon_data {
 	u8 *head, *tail;
 	int head_len, tail_len;
+	struct ieee80211_meshconf_ie *meshconf;
 	struct rcu_head rcu_head;
 };
 
@@ -259,10 +257,20 @@
 	struct beacon_data __rcu *beacon;
 	struct probe_resp __rcu *probe_resp;
 
+	/* to be used after channel switch. */
+	struct cfg80211_beacon_data *next_beacon;
 	struct list_head vlans;
 
+	/* to be used if channel switch fails. */
+	struct beacon_data *prev_beacon;
+	struct probe_resp *prev_presp;
+
 	struct ps_data ps;
 	atomic_t num_mcast_sta; /* number of stations receiving multicast */
+	enum ieee80211_smps_mode req_smps, /* requested smps mode */
+			 driver_smps_mode; /* smps mode request */
+
+	struct work_struct request_smps_work;
 };
 
 struct ieee80211_if_wds {
@@ -312,6 +320,7 @@
 
 	bool started, abort, hw_begun, notified;
 	bool to_be_freed;
+	bool on_channel;
 
 	unsigned long hw_start_time;
 
@@ -323,7 +332,6 @@
 
 /* flags used in struct ieee80211_if_managed.flags */
 enum ieee80211_sta_flags {
-	IEEE80211_STA_BEACON_POLL	= BIT(0),
 	IEEE80211_STA_CONNECTION_POLL	= BIT(1),
 	IEEE80211_STA_CONTROL_PORT	= BIT(2),
 	IEEE80211_STA_DISABLE_HT	= BIT(4),
@@ -336,6 +344,7 @@
 	IEEE80211_STA_DISABLE_VHT	= BIT(11),
 	IEEE80211_STA_DISABLE_80P80MHZ	= BIT(12),
 	IEEE80211_STA_DISABLE_160MHZ	= BIT(13),
+	IEEE80211_STA_DISABLE_WMM	= BIT(14),
 };
 
 struct ieee80211_mgd_auth_data {
@@ -488,6 +497,7 @@
 
 struct ieee80211_if_ibss {
 	struct timer_list timer;
+	struct work_struct csa_connection_drop_work;
 
 	unsigned long last_scan_completed;
 
@@ -498,6 +508,7 @@
 	bool privacy;
 
 	bool control_port;
+	bool userspace_handles_dfs;
 
 	u8 bssid[ETH_ALEN] __aligned(2);
 	u8 ssid[IEEE80211_MAX_SSID_LEN];
@@ -509,6 +520,9 @@
 	/* probe response/beacon for IBSS */
 	struct beacon_data __rcu *presp;
 
+	struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */
+	struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
+
 	spinlock_t incomplete_lock;
 	struct list_head incomplete_stations;
 
@@ -532,10 +546,18 @@
 			     struct ieee80211_mgmt *mgmt,
 			     struct ieee802_11_elems *elems,
 			     struct ieee80211_rx_status *rx_status);
-	void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata);
+
+	/* should be called with beacon_data under RCU read lock */
+	void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata,
+			    struct beacon_data *beacon);
 	/* add other framework functions here */
 };
 
+struct mesh_csa_settings {
+	struct rcu_head rcu_head;
+	struct cfg80211_csa_settings settings;
+};
+
 struct ieee80211_if_mesh {
 	struct timer_list housekeeping_timer;
 	struct timer_list mesh_path_timer;
@@ -596,6 +618,18 @@
 	int ps_peers_light_sleep;
 	int ps_peers_deep_sleep;
 	struct ps_data ps;
+	/* Channel Switching Support */
+	struct mesh_csa_settings __rcu *csa;
+	enum {
+		IEEE80211_MESH_CSA_ROLE_NONE,
+		IEEE80211_MESH_CSA_ROLE_INIT,
+		IEEE80211_MESH_CSA_ROLE_REPEATER,
+	} csa_role;
+	u8 chsw_ttl;
+	u16 pre_value;
+
+	/* offset from skb->data while building IE */
+	int meshconf_offset;
 };
 
 #ifdef CONFIG_MAC80211_MESH
@@ -670,6 +704,7 @@
 
 struct ieee80211_sub_if_data {
 	struct list_head list;
+	struct list_head csa_list;
 
 	struct wireless_dev wdev;
 
@@ -710,9 +745,18 @@
 	u16 sequence_number;
 	__be16 control_port_protocol;
 	bool control_port_no_encrypt;
+	int encrypt_headroom;
 
 	struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
 
+	struct work_struct csa_finalize_work;
+	struct work_struct csa_complete_work;
+	int csa_counter_offset_beacon;
+	int csa_counter_offset_presp;
+	bool csa_radar_required;
+	bool csa_complete;
+	struct cfg80211_chan_def csa_chandef;
+
 	/* used to reconfigure hardware SM PS */
 	struct work_struct recalc_smps;
 
@@ -788,6 +832,9 @@
 	__release(&sdata->wdev.mtx);
 }
 
+#define sdata_dereference(p, sdata) \
+	rcu_dereference_protected(p, lockdep_is_held(&sdata->wdev.mtx))
+
 static inline void
 sdata_assert_lock(struct ieee80211_sub_if_data *sdata)
 {
@@ -809,6 +856,34 @@
 	return band;
 }
 
+static inline int
+ieee80211_chandef_get_shift(struct cfg80211_chan_def *chandef)
+{
+	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_5:
+		return 2;
+	case NL80211_CHAN_WIDTH_10:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static inline int
+ieee80211_vif_get_shift(struct ieee80211_vif *vif)
+{
+	struct ieee80211_chanctx_conf *chanctx_conf;
+	int shift = 0;
+
+	rcu_read_lock();
+	chanctx_conf = rcu_dereference(vif->chanctx_conf);
+	if (chanctx_conf)
+		shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
+	rcu_read_unlock();
+
+	return shift;
+}
+
 enum sdata_queue_type {
 	IEEE80211_SDATA_QUEUE_TYPE_FRAME	= 0,
 	IEEE80211_SDATA_QUEUE_AGG_START		= 1,
@@ -1029,7 +1104,7 @@
 	struct cfg80211_ssid scan_ssid;
 	struct cfg80211_scan_request *int_scan_req;
 	struct cfg80211_scan_request *scan_req, *hw_scan_req;
-	struct ieee80211_channel *scan_channel;
+	struct cfg80211_chan_def scan_chandef;
 	enum ieee80211_band hw_scan_band;
 	int scan_channel_idx;
 	int scan_ies_len;
@@ -1042,7 +1117,6 @@
 	enum mac80211_scan_state next_scan_state;
 	struct delayed_work scan_work;
 	struct ieee80211_sub_if_data __rcu *scan_sdata;
-	struct cfg80211_chan_def csa_chandef;
 	/* For backward compatibility only -- do not use */
 	struct cfg80211_chan_def _oper_chandef;
 
@@ -1066,7 +1140,6 @@
 	u32 dot11TransmittedFrameCount;
 
 #ifdef CONFIG_MAC80211_LEDS
-	int tx_led_counter, rx_led_counter;
 	struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
 	struct tpt_led_trigger *tpt_led_trigger;
 	char tx_led_name[32], rx_led_name[32],
@@ -1172,6 +1245,15 @@
 	u16 tid;
 };
 
+/* this struct holds the value parsing from channel switch IE  */
+struct ieee80211_csa_ie {
+	struct cfg80211_chan_def chandef;
+	u8 mode;
+	u8 count;
+	u8 ttl;
+	u16 pre_value;
+};
+
 /* Parsed Information Elements */
 struct ieee802_11_elems {
 	const u8 *ie_start;
@@ -1208,6 +1290,7 @@
 	const struct ieee80211_timeout_interval_ie *timeout_int;
 	const u8 *opmode_notif;
 	const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
+	const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;
 
 	/* length of them, respectively */
 	u8 ssid_len;
@@ -1286,6 +1369,7 @@
 void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata);
 void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
 				  __le16 fc, bool acked);
+void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata);
 void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
 
 /* IBSS code */
@@ -1299,17 +1383,25 @@
 void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata);
 void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 				   struct sk_buff *skb);
+int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
+			      struct cfg80211_csa_settings *csa_settings);
+int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
+void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
 
 /* mesh code */
 void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
 void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 				   struct sk_buff *skb);
+int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
+			      struct cfg80211_csa_settings *csa_settings);
+int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
 
 /* scan/BSS handling */
 void ieee80211_scan_work(struct work_struct *work);
 int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
 				const u8 *ssid, u8 ssid_len,
-				struct ieee80211_channel *chan);
+				struct ieee80211_channel *chan,
+				enum nl80211_bss_scan_width scan_width);
 int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
 			   struct cfg80211_scan_request *req);
 void ieee80211_scan_cancel(struct ieee80211_local *local);
@@ -1344,6 +1436,25 @@
 void ieee80211_sw_roc_work(struct work_struct *work);
 void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
 
+/* channel switch handling */
+void ieee80211_csa_finalize_work(struct work_struct *work);
+void ieee80211_csa_complete_work(struct work_struct *work);
+int __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+			       struct cfg80211_csa_settings *params,
+			       int num_ifaces);
+int ieee80211_channel_switch(struct wiphy *wiphy,
+			     struct cfg80211_csa_settings *params,
+			     int num_params);
+bool ieee80211_is_csa_active(struct ieee80211_local *local);
+void ieee80211_csa_clear(struct ieee80211_sub_if_data *sdata);
+void ieee80211_csa_free(struct ieee80211_sub_if_data *sdata);
+const struct cfg80211_chan_def *
+ieee80211_get_csa_chandef(struct ieee80211_local *local);
+struct ieee80211_chanctx *
+ieee80211_get_csa_chanctx(struct ieee80211_local *local);
+int ieee80211_chanctx_csa(struct ieee80211_local *local);
+int ieee80211_chanctx_chswitch(struct ieee80211_local *local);
+
 /* interface handling */
 int ieee80211_iface_init(void);
 void ieee80211_iface_exit(void);
@@ -1365,6 +1476,8 @@
 
 bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
 void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+			    struct cfg80211_beacon_data *params);
 
 static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
 {
@@ -1394,7 +1507,10 @@
 int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
 			       enum ieee80211_smps_mode smps, const u8 *da,
 			       const u8 *bssid);
-void ieee80211_request_smps_work(struct work_struct *work);
+void ieee80211_request_smps_ap_work(struct work_struct *work);
+void ieee80211_request_smps_mgd_work(struct work_struct *work);
+bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
+				   enum ieee80211_smps_mode smps_mode_new);
 
 void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
 				     u16 initiator, u16 reason, bool stop);
@@ -1434,6 +1550,9 @@
 				    struct sta_info *sta);
 enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta);
 void ieee80211_sta_set_rx_nss(struct sta_info *sta);
+u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
+                                  struct sta_info *sta, u8 opmode,
+                                  enum ieee80211_band band, bool nss_only);
 void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
 				 struct sta_info *sta, u8 opmode,
 				 enum ieee80211_band band, bool nss_only);
@@ -1444,6 +1563,28 @@
 void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
 				       struct ieee80211_mgmt *mgmt,
 				       size_t len);
+/**
+ * ieee80211_parse_ch_switch_ie - parses channel switch IEs
+ * @sdata: the sdata of the interface which has received the frame
+ * @elems: parsed 802.11 elements received with the frame
+ * @beacon: indicates if the frame was a beacon or probe response
+ * @current_band: indicates the current band
+ * @sta_flags: contains information about own capabilities and restrictions
+ *	to decide which channel switch announcements can be accepted. Only the
+ *	following subset of &enum ieee80211_sta_flags are evaluated:
+ *	%IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT,
+ *	%IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ,
+ *	%IEEE80211_STA_DISABLE_160MHZ.
+ * @bssid: the currently connected bssid (for reporting)
+ * @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl.
+	All of them will be filled with if success only.
+ * Return: 0 on success, <0 on error and >0 if there is nothing to parse.
+ */
+int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
+				 struct ieee802_11_elems *elems, bool beacon,
+				 enum ieee80211_band current_band,
+				 u32 sta_flags, u8 *bssid,
+				 struct ieee80211_csa_ie *csa_ie);
 
 /* Suspend/resume and hw reconfiguration */
 int ieee80211_reconfig(struct ieee80211_local *local);
@@ -1468,7 +1609,8 @@
 u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
 			enum nl80211_iftype type);
 int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
-			     int rate, int erp, int short_preamble);
+			     int rate, int erp, int short_preamble,
+			     int shift);
 void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
 				     struct ieee80211_hdr *hdr, const u8 *tsc,
 				     gfp_t gfp);
@@ -1572,7 +1714,7 @@
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
 			     size_t buffer_len, const u8 *ie, size_t ie_len,
 			     enum ieee80211_band band, u32 rate_mask,
-			     u8 channel);
+			     struct cfg80211_chan_def *chandef);
 struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
 					  u8 *dst, u32 ratemask,
 					  struct ieee80211_channel *chan,
@@ -1585,15 +1727,15 @@
 			      u32 ratemask, bool directed, u32 tx_flags,
 			      struct ieee80211_channel *channel, bool scan);
 
-void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
-				  const size_t supp_rates_len,
-				  const u8 *supp_rates);
-u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
+u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
 			    struct ieee802_11_elems *elems,
 			    enum ieee80211_band band, u32 *basic_rates);
-int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
+int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
+				 enum ieee80211_smps_mode smps_mode);
+int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
 			     enum ieee80211_smps_mode smps_mode);
 void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
+void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata);
 
 size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
 			  const u8 *ids, int n_ids, size_t offset);
@@ -1605,6 +1747,9 @@
 			       u16 prot_mode);
 u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
 			       u32 cap);
+int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
+			     const struct ieee80211_supported_band *sband,
+			     const u8 *srates, int srates_len, u32 *rates);
 int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
 			    struct sk_buff *skb, bool need_basic,
 			    enum ieee80211_band band);
@@ -1616,6 +1761,7 @@
 void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
 				  const struct ieee80211_ht_operation *ht_oper,
 				  struct cfg80211_chan_def *chandef);
+u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
 
 int __must_check
 ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
@@ -1632,13 +1778,24 @@
 
 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
 				   struct ieee80211_chanctx *chanctx);
-void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
-				    struct ieee80211_chanctx *chanctx);
+void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
+				      struct ieee80211_chanctx *ctx);
 
 void ieee80211_dfs_cac_timer(unsigned long data);
 void ieee80211_dfs_cac_timer_work(struct work_struct *work);
 void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
 void ieee80211_dfs_radar_detected_work(struct work_struct *work);
+int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
+			      struct cfg80211_csa_settings *csa_settings);
+
+bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs);
+bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n);
+const struct ieee80211_cipher_scheme *
+ieee80211_cs_get(struct ieee80211_local *local, u32 cipher,
+		 enum nl80211_iftype iftype);
+int ieee80211_cs_headroom(struct ieee80211_local *local,
+			  struct cfg80211_crypto_settings *crypto,
+			  enum nl80211_iftype iftype);
 
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
diff -ruw linux-3.11.10/net/mac80211/iface.c linux-3.11.10-fbx/net/mac80211/iface.c
--- linux-3.11.10/net/mac80211/iface.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/iface.c	2015-09-29 11:15:09.065756999 +0200
@@ -54,7 +54,7 @@
 		return false;
 	}
 
-	power = chanctx_conf->def.chan->max_power;
+	power = ieee80211_chandef_max_power(&chanctx_conf->def);
 	rcu_read_unlock();
 
 	if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
@@ -250,7 +250,8 @@
 			 type2 == NL80211_IFTYPE_AP_VLAN));
 }
 
-static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
+static int
+__ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
 					    enum nl80211_iftype iftype)
 {
 	struct ieee80211_local *local = sdata->local;
@@ -258,6 +259,10 @@
 
 	ASSERT_RTNL();
 
+	/* access to vif.csa_active must be protected by sdata or local->mtx.
+	 * this interates over interfaces so sdata lock won't do */
+	lockdep_assert_held(&local->mtx);
+
 	/* we hold the RTNL here so can safely walk the list */
 	list_for_each_entry(nsdata, &local->interfaces, list) {
 		if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {
@@ -274,6 +279,12 @@
 			if (iftype == NL80211_IFTYPE_ADHOC &&
 			    nsdata->vif.type == NL80211_IFTYPE_ADHOC)
 				return -EBUSY;
+			/*
+			 * will not add another interface while any channel
+			 * switch is active.
+			 */
+			if (nsdata->vif.csa_active)
+				return -EBUSY;
 
 			/*
 			 * The remaining checks are only performed for interfaces
@@ -302,12 +313,28 @@
 	return 0;
 }
 
-static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata)
+static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
+					    enum nl80211_iftype iftype)
+{
+	struct ieee80211_local *local = sdata->local;
+	int err;
+
+	ASSERT_RTNL();
+
+	mutex_lock(&local->mtx);
+	err = __ieee80211_check_concurrent_iface(sdata, iftype);
+	mutex_unlock(&local->mtx);
+
+	return err;
+}
+
+static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata,
+				  enum nl80211_iftype iftype)
 {
 	int n_queues = sdata->local->hw.queues;
 	int i;
 
-	if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) {
+	if (iftype != NL80211_IFTYPE_P2P_DEVICE) {
 		for (i = 0; i < IEEE80211_NUM_ACS; i++) {
 			if (WARN_ON_ONCE(sdata->vif.hw_queue[i] ==
 					 IEEE80211_INVAL_HW_QUEUE))
@@ -318,8 +345,9 @@
 		}
 	}
 
-	if ((sdata->vif.type != NL80211_IFTYPE_AP &&
-	     sdata->vif.type != NL80211_IFTYPE_MESH_POINT) ||
+	if ((iftype != NL80211_IFTYPE_AP &&
+	     iftype != NL80211_IFTYPE_P2P_GO &&
+	     iftype != NL80211_IFTYPE_MESH_POINT) ||
 	    !(sdata->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)) {
 		sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
 		return 0;
@@ -393,6 +421,8 @@
 	snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
 		 wiphy_name(local->hw.wiphy));
 
+	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
+
 	ieee80211_set_default_queues(sdata);
 
 	ret = drv_add_interface(local, sdata);
@@ -402,14 +432,16 @@
 		return ret;
 	}
 
-	ret = ieee80211_check_queues(sdata);
+	ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR);
 	if (ret) {
 		kfree(sdata);
 		return ret;
 	}
 
+	mutex_lock(&local->mtx);
 	ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef,
 					IEEE80211_CHANCTX_EXCLUSIVE);
+	mutex_unlock(&local->mtx);
 	if (ret) {
 		drv_remove_interface(local, sdata);
 		kfree(sdata);
@@ -446,7 +478,9 @@
 
 	synchronize_net();
 
+	mutex_lock(&local->mtx);
 	ieee80211_vif_release_channel(sdata);
+	mutex_unlock(&local->mtx);
 
 	drv_remove_interface(local, sdata);
 
@@ -586,7 +620,8 @@
 			res = drv_add_interface(local, sdata);
 			if (res)
 				goto err_stop;
-			res = ieee80211_check_queues(sdata);
+			res = ieee80211_check_queues(sdata,
+				ieee80211_vif_type_p2p(&sdata->vif));
 			if (res)
 				goto err_del_interface;
 		}
@@ -740,6 +775,7 @@
 	u32 hw_reconf_flags = 0;
 	int i, flushed;
 	struct ps_data *ps;
+	struct cfg80211_chan_def chandef;
 
 	clear_bit(SDATA_STATE_RUNNING, &sdata->state);
 
@@ -757,6 +793,10 @@
 	if (sdata->vif.type == NL80211_IFTYPE_STATION)
 		ieee80211_mgd_stop(sdata);
 
+	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+		ieee80211_ibss_stop(sdata);
+
+
 	/*
 	 * Remove all stations associated with this interface.
 	 *
@@ -804,15 +844,25 @@
 	cancel_work_sync(&local->dynamic_ps_enable_work);
 
 	cancel_work_sync(&sdata->recalc_smps);
+	sdata_lock(sdata);
+	mutex_lock(&local->mtx);
+	ieee80211_csa_clear(sdata);
+	ieee80211_csa_free(sdata);
+	mutex_unlock(&local->mtx);
+	sdata_unlock(sdata);
+	cancel_work_sync(&sdata->csa_complete_work);
+	cancel_work_sync(&sdata->csa_finalize_work);
 
 	cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
 
 	if (sdata->wdev.cac_started) {
+		chandef = sdata->vif.bss_conf.chandef;
 		WARN_ON(local->suspended);
-		mutex_lock(&local->iflist_mtx);
+		mutex_lock(&local->mtx);
 		ieee80211_vif_release_channel(sdata);
-		mutex_unlock(&local->iflist_mtx);
-		cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED,
+		mutex_unlock(&local->mtx);
+		cfg80211_cac_event(sdata->dev, &chandef,
+				   NL80211_RADAR_CAC_ABORTED,
 				   GFP_KERNEL);
 	}
 
@@ -1255,6 +1305,7 @@
 
 	sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE);
 	sdata->control_port_no_encrypt = false;
+	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
 
 	sdata->noack_map = 0;
 
@@ -1267,6 +1318,8 @@
 	skb_queue_head_init(&sdata->skb_queue);
 	INIT_WORK(&sdata->work, ieee80211_iface_work);
 	INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
+	INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
+	INIT_WORK(&sdata->csa_complete_work, ieee80211_csa_complete_work);
 
 	switch (type) {
 	case NL80211_IFTYPE_P2P_GO:
@@ -1277,7 +1330,10 @@
 	case NL80211_IFTYPE_AP:
 		skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
 		INIT_LIST_HEAD(&sdata->u.ap.vlans);
+		INIT_WORK(&sdata->u.ap.request_smps_work,
+			  ieee80211_request_smps_ap_work);
 		sdata->vif.bss_conf.bssid = sdata->vif.addr;
+		sdata->u.ap.req_smps = IEEE80211_SMPS_OFF;
 		break;
 	case NL80211_IFTYPE_P2P_CLIENT:
 		type = NL80211_IFTYPE_STATION;
@@ -1380,14 +1436,14 @@
 
 	ret = drv_change_interface(local, sdata, internal_type, p2p);
 	if (ret)
-		type = sdata->vif.type;
+		type = ieee80211_vif_type_p2p(&sdata->vif);
 
 	/*
 	 * Ignore return value here, there's not much we can do since
 	 * the driver changed the interface type internally already.
 	 * The warnings will hopefully make driver authors fix it :-)
 	 */
-	ieee80211_check_queues(sdata);
+	ieee80211_check_queues(sdata, type);
 
 	ieee80211_setup_sdata(sdata, type);
 
@@ -1667,6 +1723,8 @@
 	sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
 	sdata->user_power_level = local->user_power_level;
 
+	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
+
 	/* setup type-dependent data */
 	ieee80211_setup_sdata(sdata, type);
 
@@ -1754,7 +1812,6 @@
 	}
 	mutex_unlock(&local->iflist_mtx);
 	unregister_netdevice_many(&unreg_list);
-	list_del(&unreg_list);
 
 	list_for_each_entry_safe(sdata, tmp, &wdev_list, list) {
 		list_del(&sdata->list);
diff -ruw linux-3.11.10/net/mac80211/Kconfig linux-3.11.10-fbx/net/mac80211/Kconfig
--- linux-3.11.10/net/mac80211/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/Kconfig	2015-09-29 11:15:09.061756962 +0200
@@ -4,6 +4,7 @@
 	select CRYPTO
 	select CRYPTO_ARC4
 	select CRYPTO_AES
+	select CRYPTO_CCM
 	select CRC32
 	select AVERAGE
 	---help---
@@ -257,6 +258,17 @@
 	  mesh network).
 
 	  Do not select this option.
+
+config MAC80211_MESH_CSA_DEBUG
+	bool "Verbose mesh channel switch debugging"
+	depends on MAC80211_DEBUG_MENU
+	depends on MAC80211_MESH
+	---help---
+	  Selecting this option causes mac80211 to print out very verbose mesh
+	  channel switch debugging messages (when mac80211 is taking part in a
+	  mesh network).
+
+	  Do not select this option.
 
 config MAC80211_MESH_PS_DEBUG
 	bool "Verbose mesh powersave debugging"
diff -ruw linux-3.11.10/net/mac80211/key.c linux-3.11.10-fbx/net/mac80211/key.c
--- linux-3.11.10/net/mac80211/key.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/key.c	2015-09-29 11:15:09.065756999 +0200
@@ -93,6 +93,9 @@
 
 	might_sleep();
 
+	if (key->flags & KEY_FLAG_TAINTED)
+		return -EINVAL;
+
 	if (!key->local->ops->set_key)
 		goto out_unsupported;
 
@@ -260,15 +263,6 @@
 	if (new)
 		list_add_tail(&new->list, &sdata->key_list);
 
-	if (sta && pairwise) {
-		rcu_assign_pointer(sta->ptk, new);
-	} else if (sta) {
-		if (old)
-			idx = old->conf.keyidx;
-		else
-			idx = new->conf.keyidx;
-		rcu_assign_pointer(sta->gtk[idx], new);
-	} else {
 		WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
 
 		if (old)
@@ -276,6 +270,15 @@
 		else
 			idx = new->conf.keyidx;
 
+	if (sta) {
+		if (pairwise) {
+			rcu_assign_pointer(sta->ptk[idx], new);
+			sta->ptk_idx = idx;
+		} else {
+			rcu_assign_pointer(sta->gtk[idx], new);
+			sta->gtk_idx = idx;
+		}
+	} else {
 		defunikey = old &&
 			old == key_mtx_dereference(sdata->local,
 						sdata->default_unicast_key);
@@ -309,9 +312,11 @@
 		list_del(&old->list);
 }
 
-struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
+struct ieee80211_key *
+ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
 					  const u8 *key_data,
-					  size_t seq_len, const u8 *seq)
+		    size_t seq_len, const u8 *seq,
+		    const struct ieee80211_cipher_scheme *cs)
 {
 	struct ieee80211_key *key;
 	int i, j, err;
@@ -390,6 +395,18 @@
 			return ERR_PTR(err);
 		}
 		break;
+	default:
+		if (cs) {
+			size_t len = (seq_len > MAX_PN_LEN) ?
+						MAX_PN_LEN : seq_len;
+
+			key->conf.iv_len = cs->hdr_len;
+			key->conf.icv_len = cs->mic_len;
+			for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++)
+				for (j = 0; j < len; j++)
+					key->u.gen.rx_pn[i][j] =
+							seq[len - j - 1];
+		}
 	}
 	memcpy(key->conf.key, key_data, key_len);
 	INIT_LIST_HEAD(&key->list);
@@ -455,6 +472,7 @@
 		       struct ieee80211_sub_if_data *sdata,
 		       struct sta_info *sta)
 {
+	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_key *old_key;
 	int idx, ret;
 	bool pairwise;
@@ -471,7 +489,7 @@
 	mutex_lock(&sdata->local->key_mtx);
 
 	if (sta && pairwise)
-		old_key = key_mtx_dereference(sdata->local, sta->ptk);
+		old_key = key_mtx_dereference(sdata->local, sta->ptk[idx]);
 	else if (sta)
 		old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]);
 	else
@@ -484,10 +502,13 @@
 
 	ieee80211_debugfs_key_add(key);
 
+	if (!local->wowlan) {
 	ret = ieee80211_key_enable_hw_accel(key);
-
 	if (ret)
 		ieee80211_key_free(key, true);
+	} else {
+		ret = 0;
+	}
 
 	mutex_unlock(&sdata->local->key_mtx);
 
@@ -540,7 +561,7 @@
 			 void *iter_data)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
-	struct ieee80211_key *key;
+	struct ieee80211_key *key, *tmp;
 	struct ieee80211_sub_if_data *sdata;
 
 	ASSERT_RTNL();
@@ -548,13 +569,14 @@
 	mutex_lock(&local->key_mtx);
 	if (vif) {
 		sdata = vif_to_sdata(vif);
-		list_for_each_entry(key, &sdata->key_list, list)
+		list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
 			iter(hw, &sdata->vif,
 			     key->sta ? &key->sta->sta : NULL,
 			     &key->conf, iter_data);
 	} else {
 		list_for_each_entry(sdata, &local->interfaces, list)
-			list_for_each_entry(key, &sdata->key_list, list)
+			list_for_each_entry_safe(key, tmp,
+						 &sdata->key_list, list)
 				iter(hw, &sdata->vif,
 				     key->sta ? &key->sta->sta : NULL,
 				     &key->conf, iter_data);
@@ -617,8 +639,10 @@
 		list_add(&key->list, &keys);
 	}
 
-	key = key_mtx_dereference(local, sta->ptk);
-	if (key) {
+	for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+		key = key_mtx_dereference(local, sta->ptk[i]);
+		if (!key)
+			continue;
 		ieee80211_key_replace(key->sdata, key->sta,
 				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
 				key, NULL);
@@ -751,3 +775,135 @@
 	}
 }
 EXPORT_SYMBOL(ieee80211_get_key_rx_seq);
+
+void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf,
+			      struct ieee80211_key_seq *seq)
+{
+	struct ieee80211_key *key;
+	u64 pn64;
+
+	key = container_of(keyconf, struct ieee80211_key, conf);
+
+	switch (key->conf.cipher) {
+	case WLAN_CIPHER_SUITE_TKIP:
+		key->u.tkip.tx.iv32 = seq->tkip.iv32;
+		key->u.tkip.tx.iv16 = seq->tkip.iv16;
+		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+		pn64 = (u64)seq->ccmp.pn[5] |
+		       ((u64)seq->ccmp.pn[4] << 8) |
+		       ((u64)seq->ccmp.pn[3] << 16) |
+		       ((u64)seq->ccmp.pn[2] << 24) |
+		       ((u64)seq->ccmp.pn[1] << 32) |
+		       ((u64)seq->ccmp.pn[0] << 40);
+		atomic64_set(&key->u.ccmp.tx_pn, pn64);
+		break;
+	case WLAN_CIPHER_SUITE_AES_CMAC:
+		pn64 = (u64)seq->aes_cmac.pn[5] |
+		       ((u64)seq->aes_cmac.pn[4] << 8) |
+		       ((u64)seq->aes_cmac.pn[3] << 16) |
+		       ((u64)seq->aes_cmac.pn[2] << 24) |
+		       ((u64)seq->aes_cmac.pn[1] << 32) |
+		       ((u64)seq->aes_cmac.pn[0] << 40);
+		atomic64_set(&key->u.aes_cmac.tx_pn, pn64);
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+}
+EXPORT_SYMBOL_GPL(ieee80211_set_key_tx_seq);
+
+void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf,
+			      int tid, struct ieee80211_key_seq *seq)
+{
+	struct ieee80211_key *key;
+	u8 *pn;
+
+	key = container_of(keyconf, struct ieee80211_key, conf);
+
+	switch (key->conf.cipher) {
+	case WLAN_CIPHER_SUITE_TKIP:
+		if (WARN_ON(tid < 0 || tid >= IEEE80211_NUM_TIDS))
+			return;
+		key->u.tkip.rx[tid].iv32 = seq->tkip.iv32;
+		key->u.tkip.rx[tid].iv16 = seq->tkip.iv16;
+		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+		if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
+			return;
+		if (tid < 0)
+			pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS];
+		else
+			pn = key->u.ccmp.rx_pn[tid];
+		memcpy(pn, seq->ccmp.pn, IEEE80211_CCMP_PN_LEN);
+		break;
+	case WLAN_CIPHER_SUITE_AES_CMAC:
+		if (WARN_ON(tid != 0))
+			return;
+		pn = key->u.aes_cmac.rx_pn;
+		memcpy(pn, seq->aes_cmac.pn, IEEE80211_CMAC_PN_LEN);
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+}
+EXPORT_SYMBOL_GPL(ieee80211_set_key_rx_seq);
+
+void ieee80211_remove_key(struct ieee80211_key_conf *keyconf)
+{
+	struct ieee80211_key *key;
+
+	key = container_of(keyconf, struct ieee80211_key, conf);
+
+	assert_key_lock(key->local);
+
+	/*
+	 * if key was uploaded, we assume the driver will/has remove(d)
+	 * it, so adjust bookkeeping accordingly
+	 */
+	if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
+		key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
+
+		if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
+		      (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) ||
+		      (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)))
+			increment_tailroom_need_count(key->sdata);
+	}
+
+	ieee80211_key_free(key, false);
+}
+EXPORT_SYMBOL_GPL(ieee80211_remove_key);
+
+struct ieee80211_key_conf *
+ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
+			struct ieee80211_key_conf *keyconf)
+{
+	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_key *key;
+	int err;
+
+	if (WARN_ON(!local->wowlan))
+		return ERR_PTR(-EINVAL);
+
+	if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
+		return ERR_PTR(-EINVAL);
+
+	key = ieee80211_key_alloc(keyconf->cipher, keyconf->keyidx,
+				  keyconf->keylen, keyconf->key,
+				  0, NULL, NULL);
+	if (IS_ERR(key))
+		return ERR_CAST(key);
+
+	if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
+		key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
+
+	err = ieee80211_key_link(key, sdata, NULL);
+	if (err)
+		return ERR_PTR(err);
+
+	return &key->conf;
+}
+EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_add);
diff -ruw linux-3.11.10/net/mac80211/key.h linux-3.11.10-fbx/net/mac80211/key.h
--- linux-3.11.10/net/mac80211/key.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/key.h	2015-09-29 11:15:09.065756999 +0200
@@ -18,6 +18,7 @@
 
 #define NUM_DEFAULT_KEYS 4
 #define NUM_DEFAULT_MGMT_KEYS 2
+#define MAX_PN_LEN 16
 
 struct ieee80211_local;
 struct ieee80211_sub_if_data;
@@ -83,7 +84,7 @@
 			 * Management frames.
 			 */
 			u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN];
-			struct crypto_cipher *tfm;
+			struct crypto_aead *tfm;
 			u32 replays; /* dot11RSNAStatsCCMPReplays */
 		} ccmp;
 		struct {
@@ -93,6 +94,10 @@
 			u32 replays; /* dot11RSNAStatsCMACReplays */
 			u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
 		} aes_cmac;
+		struct {
+			/* generic cipher scheme */
+			u8 rx_pn[IEEE80211_NUM_TIDS + 1][MAX_PN_LEN];
+		} gen;
 	} u;
 
 	/* number of times this key has been used */
@@ -113,9 +118,11 @@
 	struct ieee80211_key_conf conf;
 };
 
-struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
+struct ieee80211_key *
+ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
 					  const u8 *key_data,
-					  size_t seq_len, const u8 *seq);
+		    size_t seq_len, const u8 *seq,
+		    const struct ieee80211_cipher_scheme *cs);
 /*
  * Insert a key into data structures (sdata, sta if necessary)
  * to make it used, free old key. On failure, also free the new key.
diff -ruw linux-3.11.10/net/mac80211/led.c linux-3.11.10-fbx/net/mac80211/led.c
--- linux-3.11.10/net/mac80211/led.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/led.c	2015-09-29 11:15:09.065756999 +0200
@@ -12,27 +12,22 @@
 #include <linux/export.h>
 #include "led.h"
 
+#define MAC80211_BLINK_DELAY 50 /* ms */
+
 void ieee80211_led_rx(struct ieee80211_local *local)
 {
+	unsigned long led_delay = MAC80211_BLINK_DELAY;
 	if (unlikely(!local->rx_led))
 		return;
-	if (local->rx_led_counter++ % 2 == 0)
-		led_trigger_event(local->rx_led, LED_OFF);
-	else
-		led_trigger_event(local->rx_led, LED_FULL);
+	led_trigger_blink_oneshot(local->rx_led, &led_delay, &led_delay, 0);
 }
 
-/* q is 1 if a packet was enqueued, 0 if it has been transmitted */
-void ieee80211_led_tx(struct ieee80211_local *local, int q)
+void ieee80211_led_tx(struct ieee80211_local *local)
 {
+	unsigned long led_delay = MAC80211_BLINK_DELAY;
 	if (unlikely(!local->tx_led))
 		return;
-	/* not sure how this is supposed to work ... */
-	local->tx_led_counter += 2*q-1;
-	if (local->tx_led_counter % 2 == 0)
-		led_trigger_event(local->tx_led, LED_OFF);
-	else
-		led_trigger_event(local->tx_led, LED_FULL);
+	led_trigger_blink_oneshot(local->tx_led, &led_delay, &led_delay, 0);
 }
 
 void ieee80211_led_assoc(struct ieee80211_local *local, bool associated)
diff -ruw linux-3.11.10/net/mac80211/led.h linux-3.11.10-fbx/net/mac80211/led.h
--- linux-3.11.10/net/mac80211/led.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/led.h	2015-09-29 11:15:09.065756999 +0200
@@ -13,7 +13,7 @@
 
 #ifdef CONFIG_MAC80211_LEDS
 void ieee80211_led_rx(struct ieee80211_local *local);
-void ieee80211_led_tx(struct ieee80211_local *local, int q);
+void ieee80211_led_tx(struct ieee80211_local *local);
 void ieee80211_led_assoc(struct ieee80211_local *local,
 			 bool associated);
 void ieee80211_led_radio(struct ieee80211_local *local,
@@ -27,7 +27,7 @@
 static inline void ieee80211_led_rx(struct ieee80211_local *local)
 {
 }
-static inline void ieee80211_led_tx(struct ieee80211_local *local, int q)
+static inline void ieee80211_led_tx(struct ieee80211_local *local)
 {
 }
 static inline void ieee80211_led_assoc(struct ieee80211_local *local,
diff -ruw linux-3.11.10/net/mac80211/main.c linux-3.11.10-fbx/net/mac80211/main.c
--- linux-3.11.10/net/mac80211/main.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/main.c	2015-09-29 11:15:09.069757025 +0200
@@ -102,17 +102,8 @@
 
 	offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
 
-	if (local->scan_channel) {
-		chandef.chan = local->scan_channel;
-		/* If scanning on oper channel, use whatever channel-type
-		 * is currently in use.
-		 */
-		if (chandef.chan == local->_oper_chandef.chan) {
-			chandef = local->_oper_chandef;
-		} else {
-			chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
-			chandef.center_freq1 = chandef.chan->center_freq;
-		}
+	if (local->scan_chandef.chan) {
+		chandef = local->scan_chandef;
 	} else if (local->tmp_channel) {
 		chandef.chan = local->tmp_channel;
 		chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
@@ -151,12 +142,14 @@
 		changed |= IEEE80211_CONF_CHANGE_SMPS;
 	}
 
-	power = chandef.chan->max_power;
+	power = ieee80211_chandef_max_power(&chandef);
 
 	rcu_read_lock();
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 		if (!rcu_access_pointer(sdata->vif.chanctx_conf))
 			continue;
+		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+			continue;
 		power = min(power, sdata->vif.bss_conf.txpower);
 	}
 	rcu_read_unlock();
@@ -660,15 +653,14 @@
 }
 EXPORT_SYMBOL(ieee80211_alloc_hw);
 
-int ieee80211_register_hw(struct ieee80211_hw *hw)
+static int ieee80211_init_cipher_suites(struct ieee80211_local *local)
 {
-	struct ieee80211_local *local = hw_to_local(hw);
-	int result, i;
-	enum ieee80211_band band;
-	int channels, max_bitrates;
-	bool supp_ht, supp_vht;
-	netdev_features_t feature_whitelist;
-	struct cfg80211_chan_def dflt_chandef = {};
+	bool have_wep = !(IS_ERR(local->wep_tx_tfm) ||
+			  IS_ERR(local->wep_rx_tfm));
+	bool have_mfp = local->hw.flags & IEEE80211_HW_MFP_CAPABLE;
+	const struct ieee80211_cipher_scheme *cs = local->hw.cipher_schemes;
+	int n_suites = 0, r = 0, w = 0;
+	u32 *suites;
 	static const u32 cipher_suites[] = {
 		/* keep WEP first, it may be removed below */
 		WLAN_CIPHER_SUITE_WEP40,
@@ -680,6 +672,93 @@
 		WLAN_CIPHER_SUITE_AES_CMAC
 	};
 
+	/* Driver specifies the ciphers, we have nothing to do... */
+	if (local->hw.wiphy->cipher_suites && have_wep)
+		return 0;
+
+	/* Set up cipher suites if driver relies on mac80211 cipher defs */
+	if (!local->hw.wiphy->cipher_suites && !cs) {
+		local->hw.wiphy->cipher_suites = cipher_suites;
+		local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+		if (!have_mfp)
+			local->hw.wiphy->n_cipher_suites--;
+
+		if (!have_wep) {
+			local->hw.wiphy->cipher_suites += 2;
+			local->hw.wiphy->n_cipher_suites -= 2;
+		}
+
+		return 0;
+	}
+
+	if (!local->hw.wiphy->cipher_suites) {
+		/*
+		 * Driver specifies cipher schemes only
+		 * We start counting ciphers defined by schemes, TKIP and CCMP
+		 */
+		n_suites = local->hw.n_cipher_schemes + 2;
+
+		/* check if we have WEP40 and WEP104 */
+		if (have_wep)
+			n_suites += 2;
+
+		/* check if we have AES_CMAC */
+		if (have_mfp)
+			n_suites++;
+
+		suites = kmalloc(sizeof(u32) * n_suites, GFP_KERNEL);
+		if (!suites)
+			return -ENOMEM;
+
+		suites[w++] = WLAN_CIPHER_SUITE_CCMP;
+		suites[w++] = WLAN_CIPHER_SUITE_TKIP;
+
+		if (have_wep) {
+			suites[w++] = WLAN_CIPHER_SUITE_WEP40;
+			suites[w++] = WLAN_CIPHER_SUITE_WEP104;
+		}
+
+		if (have_mfp)
+			suites[w++] = WLAN_CIPHER_SUITE_AES_CMAC;
+
+		for (r = 0; r < local->hw.n_cipher_schemes; r++)
+			suites[w++] = cs[r].cipher;
+	} else {
+		/* Driver provides cipher suites, but we need to exclude WEP */
+		suites = kmemdup(local->hw.wiphy->cipher_suites,
+				 sizeof(u32) * local->hw.wiphy->n_cipher_suites,
+				 GFP_KERNEL);
+		if (!suites)
+			return -ENOMEM;
+
+		for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
+			u32 suite = local->hw.wiphy->cipher_suites[r];
+
+			if (suite == WLAN_CIPHER_SUITE_WEP40 ||
+			    suite == WLAN_CIPHER_SUITE_WEP104)
+				continue;
+			suites[w++] = suite;
+		}
+	}
+
+	local->hw.wiphy->cipher_suites = suites;
+	local->hw.wiphy->n_cipher_suites = w;
+	local->wiphy_ciphers_allocated = true;
+
+	return 0;
+}
+
+int ieee80211_register_hw(struct ieee80211_hw *hw)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	int result, i;
+	enum ieee80211_band band;
+	int channels, max_bitrates;
+	bool supp_ht, supp_vht;
+	netdev_features_t feature_whitelist;
+	struct cfg80211_chan_def dflt_chandef = {};
+
 	if (hw->flags & IEEE80211_HW_QUEUE_CONTROL &&
 	    (local->hw.offchannel_tx_hw_queue == IEEE80211_INVAL_HW_QUEUE ||
 	     local->hw.offchannel_tx_hw_queue >= local->hw.queues))
@@ -860,54 +939,26 @@
 	if (local->hw.wiphy->max_scan_ie_len)
 		local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len;
 
-	/* Set up cipher suites unless driver already did */
-	if (!local->hw.wiphy->cipher_suites) {
-		local->hw.wiphy->cipher_suites = cipher_suites;
-		local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
-		if (!(local->hw.flags & IEEE80211_HW_MFP_CAPABLE))
-			local->hw.wiphy->n_cipher_suites--;
-	}
-	if (IS_ERR(local->wep_tx_tfm) || IS_ERR(local->wep_rx_tfm)) {
-		if (local->hw.wiphy->cipher_suites == cipher_suites) {
-			local->hw.wiphy->cipher_suites += 2;
-			local->hw.wiphy->n_cipher_suites -= 2;
-		} else {
-			u32 *suites;
-			int r, w = 0;
-
-			/* Filter out WEP */
+	WARN_ON(!ieee80211_cs_list_valid(local->hw.cipher_schemes,
+					 local->hw.n_cipher_schemes));
 
-			suites = kmemdup(
-				local->hw.wiphy->cipher_suites,
-				sizeof(u32) * local->hw.wiphy->n_cipher_suites,
-				GFP_KERNEL);
-			if (!suites) {
-				result = -ENOMEM;
+	result = ieee80211_init_cipher_suites(local);
+	if (result < 0)
 				goto fail_wiphy_register;
-			}
-			for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
-				u32 suite = local->hw.wiphy->cipher_suites[r];
-				if (suite == WLAN_CIPHER_SUITE_WEP40 ||
-				    suite == WLAN_CIPHER_SUITE_WEP104)
-					continue;
-				suites[w++] = suite;
-			}
-			local->hw.wiphy->cipher_suites = suites;
-			local->hw.wiphy->n_cipher_suites = w;
-			local->wiphy_ciphers_allocated = true;
-		}
-	}
 
 	if (!local->ops->remain_on_channel)
 		local->hw.wiphy->max_remain_on_channel_duration = 5000;
 
-	if (local->ops->sched_scan_start)
-		local->hw.wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
-
 	/* mac80211 based drivers don't support internal TDLS setup */
 	if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)
 		local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
 
+	/* it should be trivial for mac80211 based drivers to support
+	 * multi-interface channel switching as all the complex logic sits in
+	 * mac80211 already */
+	if (local->hw.wiphy->flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
+		local->hw.wiphy->flags |= WIPHY_FLAG_HAS_MULTI_IF_CHSWITCH;
+
 	result = wiphy_register(local->hw.wiphy);
 	if (result < 0)
 		goto fail_wiphy_register;
@@ -1059,6 +1110,7 @@
 
 	cancel_work_sync(&local->restart_work);
 	cancel_work_sync(&local->reconfig_filter);
+	flush_work(&local->sched_scan_stopped_work);
 
 	ieee80211_clear_tx_pending(local);
 	rate_control_deinitialize(local);
diff -ruw linux-3.11.10/net/mac80211/mlme.c linux-3.11.10-fbx/net/mac80211/mlme.c
--- linux-3.11.10/net/mac80211/mlme.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/mlme.c	2015-09-29 11:15:09.069757025 +0200
@@ -145,66 +145,6 @@
 	return (1 << ecw) - 1;
 }
 
-static u32 chandef_downgrade(struct cfg80211_chan_def *c)
-{
-	u32 ret;
-	int tmp;
-
-	switch (c->width) {
-	case NL80211_CHAN_WIDTH_20:
-		c->width = NL80211_CHAN_WIDTH_20_NOHT;
-		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
-		break;
-	case NL80211_CHAN_WIDTH_40:
-		c->width = NL80211_CHAN_WIDTH_20;
-		c->center_freq1 = c->chan->center_freq;
-		ret = IEEE80211_STA_DISABLE_40MHZ |
-		      IEEE80211_STA_DISABLE_VHT;
-		break;
-	case NL80211_CHAN_WIDTH_80:
-		tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
-		/* n_P40 */
-		tmp /= 2;
-		/* freq_P40 */
-		c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
-		c->width = NL80211_CHAN_WIDTH_40;
-		ret = IEEE80211_STA_DISABLE_VHT;
-		break;
-	case NL80211_CHAN_WIDTH_80P80:
-		c->center_freq2 = 0;
-		c->width = NL80211_CHAN_WIDTH_80;
-		ret = IEEE80211_STA_DISABLE_80P80MHZ |
-		      IEEE80211_STA_DISABLE_160MHZ;
-		break;
-	case NL80211_CHAN_WIDTH_160:
-		/* n_P20 */
-		tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
-		/* n_P80 */
-		tmp /= 4;
-		c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
-		c->width = NL80211_CHAN_WIDTH_80;
-		ret = IEEE80211_STA_DISABLE_80P80MHZ |
-		      IEEE80211_STA_DISABLE_160MHZ;
-		break;
-	default:
-	case NL80211_CHAN_WIDTH_20_NOHT:
-		WARN_ON_ONCE(1);
-		c->width = NL80211_CHAN_WIDTH_20_NOHT;
-		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
-		break;
-	case NL80211_CHAN_WIDTH_5:
-	case NL80211_CHAN_WIDTH_10:
-		WARN_ON_ONCE(1);
-		/* keep c->width */
-		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
-		break;
-	}
-
-	WARN_ON_ONCE(!cfg80211_chandef_valid(c));
-
-	return ret;
-}
-
 static u32
 ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
 			     struct ieee80211_supported_band *sband,
@@ -282,6 +222,7 @@
 	switch (vht_oper->chan_width) {
 	case IEEE80211_VHT_CHANWIDTH_USE_HT:
 		vht_chandef.width = chandef->width;
+		vht_chandef.center_freq1 = chandef->center_freq1;
 		break;
 	case IEEE80211_VHT_CHANWIDTH_80MHZ:
 		vht_chandef.width = NL80211_CHAN_WIDTH_80;
@@ -331,6 +272,28 @@
 	ret = 0;
 
 out:
+	/*
+	 * When tracking the current AP, don't do any further checks if the
+	 * new chandef is identical to the one we're currently using for the
+	 * connection. This keeps us from playing ping-pong with regulatory,
+	 * without it the following can happen (for example):
+	 *  - connect to an AP with 80 MHz, world regdom allows 80 MHz
+	 *  - AP advertises regdom US
+	 *  - CRDA loads regdom US with 80 MHz prohibited (old database)
+	 *  - the code below detects an unsupported channel, downgrades, and
+	 *    we disconnect from the AP in the caller
+	 *  - disconnect causes CRDA to reload world regdomain and the game
+	 *    starts anew.
+	 * (see https://bugzilla.kernel.org/show_bug.cgi?id=70881)
+	 *
+	 * It seems possible that there are still scenarios with CSA or real
+	 * bandwidth changes where a this could happen, but those cases are
+	 * less common and wouldn't completely prevent using the AP.
+	 */
+	if (tracking &&
+	    cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef))
+		return ret;
+
 	/* don't print the message below for VHT mismatch if VHT is disabled */
 	if (ret & IEEE80211_STA_DISABLE_VHT)
 		vht_chandef = *chandef;
@@ -352,7 +315,7 @@
 			break;
 		}
 
-		ret |= chandef_downgrade(chandef);
+		ret |= ieee80211_chandef_downgrade(chandef);
 	}
 
 	if (chandef->width != vht_chandef.width && !tracking)
@@ -406,13 +369,13 @@
 	 */
 	if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
 	    chandef.width == NL80211_CHAN_WIDTH_80P80)
-		flags |= chandef_downgrade(&chandef);
+		flags |= ieee80211_chandef_downgrade(&chandef);
 	if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
 	    chandef.width == NL80211_CHAN_WIDTH_160)
-		flags |= chandef_downgrade(&chandef);
+		flags |= ieee80211_chandef_downgrade(&chandef);
 	if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
 	    chandef.width > NL80211_CHAN_WIDTH_20)
-		flags |= chandef_downgrade(&chandef);
+		flags |= ieee80211_chandef_downgrade(&chandef);
 
 	if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef))
 		return 0;
@@ -489,27 +452,6 @@
 
 /* frame sending functions */
 
-static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
-				      struct ieee80211_supported_band *sband,
-				      u32 *rates)
-{
-	int i, j, count;
-	*rates = 0;
-	count = 0;
-	for (i = 0; i < supp_rates_len; i++) {
-		int rate = (supp_rates[i] & 0x7F) * 5;
-
-		for (j = 0; j < sband->n_bitrates; j++)
-			if (sband->bitrates[j].bitrate == rate) {
-				*rates |= BIT(j);
-				count++;
-				break;
-			}
-	}
-
-	return count;
-}
-
 static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
 				struct sk_buff *skb, u8 ap_ht_param,
 				struct ieee80211_supported_band *sband,
@@ -628,12 +570,12 @@
 	struct ieee80211_mgmt *mgmt;
 	u8 *pos, qos_info;
 	size_t offset = 0, noffset;
-	int i, count, rates_len, supp_rates_len;
+	int i, count, rates_len, supp_rates_len, shift;
 	u16 capab;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_chanctx_conf *chanctx_conf;
 	struct ieee80211_channel *chan;
-	u32 rates = 0;
+	u32 rate_flags, rates = 0;
 
 	sdata_assert_lock(sdata);
 
@@ -644,8 +586,10 @@
 		return;
 	}
 	chan = chanctx_conf->def.chan;
+	rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
 	rcu_read_unlock();
 	sband = local->hw.wiphy->bands[chan->band];
+	shift = ieee80211_vif_get_shift(&sdata->vif);
 
 	if (assoc_data->supp_rates_len) {
 		/*
@@ -654,17 +598,24 @@
 		 * in the association request (e.g. D-Link DAP 1353 in
 		 * b-only mode)...
 		 */
-		rates_len = ieee80211_compatible_rates(assoc_data->supp_rates,
+		rates_len = ieee80211_parse_bitrates(&chanctx_conf->def, sband,
+						     assoc_data->supp_rates,
 						       assoc_data->supp_rates_len,
-						       sband, &rates);
+						     &rates);
 	} else {
 		/*
 		 * In case AP not provide any supported rates information
 		 * before association, we send information element(s) with
 		 * all rates that we support.
 		 */
-		rates = ~0;
-		rates_len = sband->n_bitrates;
+		rates_len = 0;
+		for (i = 0; i < sband->n_bitrates; i++) {
+			if ((rate_flags & sband->bitrates[i].flags)
+			    != rate_flags)
+				continue;
+			rates |= BIT(i);
+			rates_len++;
+		}
 	}
 
 	skb = alloc_skb(local->hw.extra_tx_headroom +
@@ -741,8 +692,9 @@
 	count = 0;
 	for (i = 0; i < sband->n_bitrates; i++) {
 		if (BIT(i) & rates) {
-			int rate = sband->bitrates[i].bitrate;
-			*pos++ = (u8) (rate / 5);
+			int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+						5 * (1 << shift));
+			*pos++ = (u8) rate;
 			if (++count == 8)
 				break;
 		}
@@ -755,8 +707,10 @@
 
 		for (i++; i < sband->n_bitrates; i++) {
 			if (BIT(i) & rates) {
-				int rate = sband->bitrates[i].bitrate;
-				*pos++ = (u8) (rate / 5);
+				int rate;
+				rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+						    5 * (1 << shift));
+				*pos++ = (u8) rate;
 			}
 		}
 	}
@@ -767,7 +721,8 @@
 		*pos++ = WLAN_EID_PWR_CAPABILITY;
 		*pos++ = 2;
 		*pos++ = 0; /* min tx power */
-		*pos++ = chan->max_power; /* max tx power */
+		 /* max tx power */
+		*pos++ = ieee80211_chandef_max_power(&chanctx_conf->def);
 
 		/* 2. supported channels */
 		/* TODO: get this in reg domain format */
@@ -901,8 +856,7 @@
 	if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
 		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
 
-	if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
-			    IEEE80211_STA_CONNECTION_POLL))
+	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
 		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
 
 	ieee80211_tx_skb(sdata, skb);
@@ -945,33 +899,61 @@
 		container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+	u32 changed = 0;
+	int ret;
 
 	if (!ieee80211_sdata_running(sdata))
 		return;
 
 	sdata_lock(sdata);
+	mutex_lock(&local->mtx);
 	if (!ifmgd->associated)
 		goto out;
 
-	local->_oper_chandef = local->csa_chandef;
+	if (sdata->vif.bss_conf.chandef.width !=
+	    sdata->csa_chandef.width)
+		changed |= BSS_CHANGED_BANDWIDTH;
 
-	if (!local->ops->channel_switch) {
-		/* call "hw_config" only if doing sw channel switch */
-		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
-	} else {
-		/* update the device channel directly */
+	mutex_lock(&local->chanctx_mtx);
+	/* satisfy sanity checks */
+	sdata->csa_complete = true;
+
+	ret = ieee80211_chanctx_chswitch(local);
+	mutex_unlock(&local->chanctx_mtx);
+
+	if (ret) {
+		sdata_info(sdata,
+			   "vif channel switch failed, disconnecting\n");
+		ieee80211_queue_work(&sdata->local->hw,
+				     &ifmgd->csa_connection_drop_work);
+		goto out;
+	}
+
+	if (!local->use_chanctx) {
+		local->_oper_chandef = sdata->csa_chandef;
+		/* Call "hw_config" only if doing sw channel switch.
+		 * Otherwise update the channel directly
+		 */
+		if (!local->ops->channel_switch)
+			ieee80211_hw_config(local, 0);
+		else
 		local->hw.conf.chandef = local->_oper_chandef;
 	}
 
 	/* XXX: shouldn't really modify cfg80211-owned data! */
-	ifmgd->associated->channel = local->_oper_chandef.chan;
+	ifmgd->associated->channel = sdata->csa_chandef.chan;
 
 	/* XXX: wait for a beacon first? */
 	ieee80211_wake_queues_by_reason(&local->hw,
 					IEEE80211_MAX_QUEUE_MAP,
 					IEEE80211_QUEUE_STOP_REASON_CSA);
+
+	ieee80211_bss_info_change_notify(sdata, changed);
+
  out:
+	sdata->vif.csa_active = false;
 	ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+	mutex_unlock(&local->mtx);
 	sdata_unlock(sdata);
 }
 
@@ -1008,22 +990,13 @@
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct cfg80211_bss *cbss = ifmgd->associated;
-	struct ieee80211_bss *bss;
 	struct ieee80211_chanctx *chanctx;
-	enum ieee80211_band new_band;
-	int new_freq;
-	u8 new_chan_no;
-	u8 count;
-	u8 mode;
-	struct ieee80211_channel *new_chan;
-	struct cfg80211_chan_def new_chandef = {};
-	struct cfg80211_chan_def new_vht_chandef = {};
-	const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
-	const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
-	const struct ieee80211_ht_operation *ht_oper;
-	int secondary_channel_offset = -1;
+	enum ieee80211_band current_band;
+	struct ieee80211_csa_ie csa_ie;
+	int res;
 
 	sdata_assert_lock(sdata);
+	lockdep_assert_held(&sdata->local->mtx);
 
 	if (!cbss)
 		return;
@@ -1035,155 +1008,25 @@
 	if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
 		return;
 
-	sec_chan_offs = elems->sec_chan_offs;
-	wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
-	ht_oper = elems->ht_operation;
-
-	if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
-			    IEEE80211_STA_DISABLE_40MHZ)) {
-		sec_chan_offs = NULL;
-		wide_bw_chansw_ie = NULL;
-		/* only used for bandwidth here */
-		ht_oper = NULL;
-	}
-
-	if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
-		wide_bw_chansw_ie = NULL;
-
-	if (elems->ext_chansw_ie) {
-		if (!ieee80211_operating_class_to_band(
-				elems->ext_chansw_ie->new_operating_class,
-				&new_band)) {
-			sdata_info(sdata,
-				   "cannot understand ECSA IE operating class %d, disconnecting\n",
-				   elems->ext_chansw_ie->new_operating_class);
+	current_band = cbss->channel->band;
+	memset(&csa_ie, 0, sizeof(csa_ie));
+	res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
+					   ifmgd->flags,
+					   ifmgd->associated->bssid, &csa_ie);
+	if (res	< 0)
 			ieee80211_queue_work(&local->hw,
 					     &ifmgd->csa_connection_drop_work);
-		}
-		new_chan_no = elems->ext_chansw_ie->new_ch_num;
-		count = elems->ext_chansw_ie->count;
-		mode = elems->ext_chansw_ie->mode;
-	} else if (elems->ch_switch_ie) {
-		new_band = cbss->channel->band;
-		new_chan_no = elems->ch_switch_ie->new_ch_num;
-		count = elems->ch_switch_ie->count;
-		mode = elems->ch_switch_ie->mode;
-	} else {
-		/* nothing here we understand */
+	if (res)
 		return;
-	}
 
-	bss = (void *)cbss->priv;
-
-	new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
-	new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
-	if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
-		sdata_info(sdata,
-			   "AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
-			   ifmgd->associated->bssid, new_freq);
-		ieee80211_queue_work(&local->hw,
-				     &ifmgd->csa_connection_drop_work);
-		return;
-	}
-
-	if (!beacon && sec_chan_offs) {
-		secondary_channel_offset = sec_chan_offs->sec_chan_offs;
-	} else if (beacon && ht_oper) {
-		secondary_channel_offset =
-			ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
-	} else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
-		/*
-		 * If it's not a beacon, HT is enabled and the IE not present,
-		 * it's 20 MHz, 802.11-2012 8.5.2.6:
-		 *	This element [the Secondary Channel Offset Element] is
-		 *	present when switching to a 40 MHz channel. It may be
-		 *	present when switching to a 20 MHz channel (in which
-		 *	case the secondary channel offset is set to SCN).
-		 */
-		secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
-	}
-
-	switch (secondary_channel_offset) {
-	default:
-		/* secondary_channel_offset was present but is invalid */
-	case IEEE80211_HT_PARAM_CHA_SEC_NONE:
-		cfg80211_chandef_create(&new_chandef, new_chan,
-					NL80211_CHAN_HT20);
-		break;
-	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-		cfg80211_chandef_create(&new_chandef, new_chan,
-					NL80211_CHAN_HT40PLUS);
-		break;
-	case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-		cfg80211_chandef_create(&new_chandef, new_chan,
-					NL80211_CHAN_HT40MINUS);
-		break;
-	case -1:
-		cfg80211_chandef_create(&new_chandef, new_chan,
-					NL80211_CHAN_NO_HT);
-		break;
-	}
-
-	if (wide_bw_chansw_ie) {
-		new_vht_chandef.chan = new_chan;
-		new_vht_chandef.center_freq1 =
-			ieee80211_channel_to_frequency(
-				wide_bw_chansw_ie->new_center_freq_seg0,
-				new_band);
-
-		switch (wide_bw_chansw_ie->new_channel_width) {
-		default:
-			/* hmmm, ignore VHT and use HT if present */
-		case IEEE80211_VHT_CHANWIDTH_USE_HT:
-			new_vht_chandef.chan = NULL;
-			break;
-		case IEEE80211_VHT_CHANWIDTH_80MHZ:
-			new_vht_chandef.width = NL80211_CHAN_WIDTH_80;
-			break;
-		case IEEE80211_VHT_CHANWIDTH_160MHZ:
-			new_vht_chandef.width = NL80211_CHAN_WIDTH_160;
-			break;
-		case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
-			/* field is otherwise reserved */
-			new_vht_chandef.center_freq2 =
-				ieee80211_channel_to_frequency(
-					wide_bw_chansw_ie->new_center_freq_seg1,
-					new_band);
-			new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
-			break;
-		}
-		if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
-		    new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
-			chandef_downgrade(&new_vht_chandef);
-		if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
-		    new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
-			chandef_downgrade(&new_vht_chandef);
-		if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
-		    new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
-			chandef_downgrade(&new_vht_chandef);
-	}
-
-	/* if VHT data is there validate & use it */
-	if (new_vht_chandef.chan) {
-		if (!cfg80211_chandef_compatible(&new_vht_chandef,
-						 &new_chandef)) {
-			sdata_info(sdata,
-				   "AP %pM CSA has inconsistent channel data, disconnecting\n",
-				   ifmgd->associated->bssid);
-			ieee80211_queue_work(&local->hw,
-					     &ifmgd->csa_connection_drop_work);
-			return;
-		}
-		new_chandef = new_vht_chandef;
-	}
-
-	if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
+	if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef,
 				     IEEE80211_CHAN_DISABLED)) {
 		sdata_info(sdata,
 			   "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
-			   ifmgd->associated->bssid, new_freq,
-			   new_chandef.width, new_chandef.center_freq1,
-			   new_chandef.center_freq2);
+			   ifmgd->associated->bssid,
+			   csa_ie.chandef.chan->center_freq,
+			   csa_ie.chandef.width, csa_ie.chandef.center_freq1,
+			   csa_ie.chandef.center_freq2);
 		ieee80211_queue_work(&local->hw,
 				     &ifmgd->csa_connection_drop_work);
 		return;
@@ -1191,16 +1034,26 @@
 
 	ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
 
+	mutex_lock(&local->chanctx_mtx);
 	if (local->use_chanctx) {
+		u32 num_chanctx = 0;
+		list_for_each_entry(chanctx, &local->chanctx_list, list)
+		       num_chanctx++;
+
+		if (num_chanctx > 1 ||
+		    !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
 		sdata_info(sdata,
-			   "not handling channel switch with channel contexts\n");
+				   "not handling chan-switch with channel contexts\n");
 		ieee80211_queue_work(&local->hw,
 				     &ifmgd->csa_connection_drop_work);
+			mutex_unlock(&local->chanctx_mtx);
 		return;
 	}
+	}
 
-	mutex_lock(&local->chanctx_mtx);
 	if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {
+		ieee80211_queue_work(&local->hw,
+				     &ifmgd->csa_connection_drop_work);
 		mutex_unlock(&local->chanctx_mtx);
 		return;
 	}
@@ -1216,9 +1069,10 @@
 	}
 	mutex_unlock(&local->chanctx_mtx);
 
-	local->csa_chandef = new_chandef;
+	sdata->csa_chandef = csa_ie.chandef;
+	sdata->vif.csa_active = true;
 
-	if (mode)
+	if (csa_ie.mode)
 		ieee80211_stop_queues_by_reason(&local->hw,
 				IEEE80211_MAX_QUEUE_MAP,
 				IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -1227,9 +1081,9 @@
 		/* use driver's channel switch callback */
 		struct ieee80211_channel_switch ch_switch = {
 			.timestamp = timestamp,
-			.block_tx = mode,
-			.chandef = new_chandef,
-			.count = count,
+			.block_tx = csa_ie.mode,
+			.chandef = csa_ie.chandef,
+			.count = csa_ie.count,
 		};
 
 		drv_channel_switch(local, &ch_switch);
@@ -1237,11 +1091,11 @@
 	}
 
 	/* channel switch handled in software */
-	if (count <= 1)
+	if (csa_ie.count <= 1)
 		ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
 	else
 		mod_timer(&ifmgd->chswitch_timer,
-			  TU_TO_EXP_TIME(count * cbss->beacon_interval));
+			  TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
 }
 
 static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
@@ -1373,8 +1227,7 @@
 	if (!mgd->associated)
 		return false;
 
-	if (mgd->flags & (IEEE80211_STA_BEACON_POLL |
-			  IEEE80211_STA_CONNECTION_POLL))
+	if (mgd->flags & IEEE80211_STA_CONNECTION_POLL)
 		return false;
 
 	if (!mgd->have_beacon)
@@ -1580,10 +1433,16 @@
 	struct ieee80211_sub_if_data *sdata =
 		container_of(delayed_work, struct ieee80211_sub_if_data,
 			     dfs_cac_timer_work);
+	struct cfg80211_chan_def chandef = sdata->vif.bss_conf.chandef;
 
+	mutex_lock(&sdata->local->mtx);
+	if (sdata->wdev.cac_started) {
 	ieee80211_vif_release_channel(sdata);
-
-	cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+		cfg80211_cac_event(sdata->dev, &chandef,
+				   NL80211_RADAR_CAC_FINISHED,
+				   GFP_KERNEL);
+	}
+	mutex_unlock(&sdata->local->mtx);
 }
 
 /* MLME */
@@ -1690,8 +1549,7 @@
 {
 	lockdep_assert_held(&sdata->local->mtx);
 
-	sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
-				IEEE80211_STA_BEACON_POLL);
+	sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL;
 	ieee80211_run_deferred_scan(sdata->local);
 }
 
@@ -1842,7 +1700,7 @@
 	if (WARN_ON(!ifmgd->associated))
 		return;
 
-	ieee80211_stop_poll(sdata);
+	__ieee80211_stop_poll(sdata);
 
 	ifmgd->associated = NULL;
 	netif_carrier_off(sdata->dev);
@@ -1928,6 +1786,8 @@
 
 	ifmgd->flags = 0;
 	ieee80211_vif_release_channel(sdata);
+
+	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
 }
 
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -1953,11 +1813,8 @@
 	struct ieee80211_local *local = sdata->local;
 
 	mutex_lock(&local->mtx);
-	if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
-			      IEEE80211_STA_CONNECTION_POLL))) {
-		mutex_unlock(&local->mtx);
-		return;
-	}
+	if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL))
+		goto out;
 
 	__ieee80211_stop_poll(sdata);
 
@@ -2093,15 +1950,9 @@
 	 * because otherwise we would reset the timer every time and
 	 * never check whether we received a probe response!
 	 */
-	if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
-			    IEEE80211_STA_CONNECTION_POLL))
+	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
 		already = true;
 
-	if (beacon)
-		ifmgd->flags |= IEEE80211_STA_BEACON_POLL;
-	else
-		ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
-
 	mutex_unlock(&sdata->local->mtx);
 
 	if (already)
@@ -2164,21 +2015,23 @@
 	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
 	sdata_lock(sdata);
-	if (!ifmgd->associated) {
-		sdata_unlock(sdata);
-		return;
-	}
+	mutex_lock(&sdata->local->mtx);
+	if (!ifmgd->associated)
+		goto out;
 
 	ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
 			       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
 			       true, frame_buf);
 	ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+	sdata->vif.csa_active = false;
 	ieee80211_wake_queues_by_reason(&sdata->local->hw,
 					IEEE80211_MAX_QUEUE_MAP,
 					IEEE80211_QUEUE_STOP_REASON_CSA);
 
 	cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
 			      IEEE80211_DEAUTH_FRAME_LEN);
+out:
+	mutex_unlock(&sdata->local->mtx);
 	sdata_unlock(sdata);
 }
 
@@ -2254,7 +2107,9 @@
 		memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
 		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
 		sdata->u.mgd.flags = 0;
+		mutex_lock(&sdata->local->mtx);
 		ieee80211_vif_release_channel(sdata);
+		mutex_unlock(&sdata->local->mtx);
 	}
 
 	cfg80211_put_bss(sdata->local->hw.wiphy, auth_data->bss);
@@ -2443,15 +2298,16 @@
 				u8 *supp_rates, unsigned int supp_rates_len,
 				u32 *rates, u32 *basic_rates,
 				bool *have_higher_than_11mbit,
-				int *min_rate, int *min_rate_index)
+				int *min_rate, int *min_rate_index,
+				int shift, u32 rate_flags)
 {
 	int i, j;
 
 	for (i = 0; i < supp_rates_len; i++) {
-		int rate = (supp_rates[i] & 0x7f) * 5;
+		int rate = supp_rates[i] & 0x7f;
 		bool is_basic = !!(supp_rates[i] & 0x80);
 
-		if (rate > 110)
+		if ((rate * 5 * (1 << shift)) > 110)
 			*have_higher_than_11mbit = true;
 
 		/*
@@ -2467,12 +2323,20 @@
 			continue;
 
 		for (j = 0; j < sband->n_bitrates; j++) {
-			if (sband->bitrates[j].bitrate == rate) {
+			struct ieee80211_rate *br;
+			int brate;
+
+			br = &sband->bitrates[j];
+			if ((rate_flags & br->flags) != rate_flags)
+				continue;
+
+			brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
+			if (brate == rate) {
 				*rates |= BIT(j);
 				if (is_basic)
 					*basic_rates |= BIT(j);
-				if (rate < *min_rate) {
-					*min_rate = rate;
+				if ((rate * 5) < *min_rate) {
+					*min_rate = rate * 5;
 					*min_rate_index = j;
 				}
 				break;
@@ -2494,7 +2358,9 @@
 		memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
 		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
 		sdata->u.mgd.flags = 0;
+		mutex_lock(&sdata->local->mtx);
 		ieee80211_vif_release_channel(sdata);
+		mutex_unlock(&sdata->local->mtx);
 	}
 
 	kfree(assoc_data);
@@ -2707,7 +2573,7 @@
 	 */
 	ifmgd->wmm_last_param_set = -1;
 
-	if (elems.wmm_param)
+	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && elems.wmm_param)
 		ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
 					 elems.wmm_param_len);
 	else
@@ -2848,8 +2714,8 @@
 	bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
 					channel);
 	if (bss) {
-		ieee80211_rx_bss_put(local, bss);
 		sdata->vif.bss_conf.beacon_rate = bss->beacon_rate;
+		ieee80211_rx_bss_put(local, bss);
 	}
 }
 
@@ -3051,17 +2917,10 @@
 		}
 	}
 
-	if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
+	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) {
 		mlme_dbg_ratelimited(sdata,
 				     "cancelling AP probe due to a received beacon\n");
-		mutex_lock(&local->mtx);
-		ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
-		ieee80211_run_deferred_scan(local);
-		mutex_unlock(&local->mtx);
-
-		mutex_lock(&local->iflist_mtx);
-		ieee80211_recalc_ps(local, -1);
-		mutex_unlock(&local->iflist_mtx);
+		ieee80211_reset_ap_probe(sdata);
 	}
 
 	/*
@@ -3139,10 +2998,13 @@
 
 	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
 
+	mutex_lock(&local->mtx);
 	ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
 					 &elems, true);
+	mutex_unlock(&local->mtx);
 
-	if (ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
+	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
+	    ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
 				     elems.wmm_param_len))
 		changed |= BSS_CHANGED_QOS;
 
@@ -3270,9 +3132,11 @@
 			if (elems.parse_error)
 				break;
 
+			mutex_lock(&sdata->local->mtx);
 			ieee80211_sta_process_chanswitch(sdata,
 							 rx_status->mactime,
 							 &elems, false);
+			mutex_unlock(&sdata->local->mtx);
 		} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
 			ies_len = skb->len -
 				  offsetof(struct ieee80211_mgmt,
@@ -3292,9 +3156,11 @@
 			elems.ext_chansw_ie =
 				&mgmt->u.action.u.ext_chan_switch.data;
 
+			mutex_lock(&sdata->local->mtx);
 			ieee80211_sta_process_chanswitch(sdata,
 							 rx_status->mactime,
 							 &elems, false);
+			mutex_unlock(&sdata->local->mtx);
 		}
 		break;
 	}
@@ -3533,8 +3399,7 @@
 	} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
 		run_again(sdata, ifmgd->assoc_data->timeout);
 
-	if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
-			    IEEE80211_STA_CONNECTION_POLL) &&
+	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL &&
 	    ifmgd->associated) {
 		u8 bssid[ETH_ALEN];
 		int max_tries;
@@ -3651,6 +3516,38 @@
 }
 
 #ifdef CONFIG_PM
+void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
+
+	sdata_lock(sdata);
+
+	if (ifmgd->auth_data || ifmgd->assoc_data) {
+		const u8 *bssid = ifmgd->auth_data ?
+				ifmgd->auth_data->bss->bssid :
+				ifmgd->assoc_data->bss->bssid;
+
+		/*
+		 * If we are trying to authenticate / associate while suspending,
+		 * cfg80211 won't know and won't actually abort those attempts,
+		 * thus we need to do that ourselves.
+		 */
+		ieee80211_send_deauth_disassoc(sdata, bssid,
+					       IEEE80211_STYPE_DEAUTH,
+					       WLAN_REASON_DEAUTH_LEAVING,
+					       false, frame_buf);
+		if (ifmgd->assoc_data)
+			ieee80211_destroy_assoc_data(sdata, false);
+		if (ifmgd->auth_data)
+			ieee80211_destroy_auth_data(sdata, false);
+		cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+				      IEEE80211_DEAUTH_FRAME_LEN);
+	}
+
+	sdata_unlock(sdata);
+}
+
 void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -3687,7 +3584,7 @@
 		  ieee80211_beacon_connection_loss_work);
 	INIT_WORK(&ifmgd->csa_connection_drop_work,
 		  ieee80211_csa_connection_drop_work);
-	INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
+	INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
 	setup_timer(&ifmgd->timer, ieee80211_sta_timer,
 		    (unsigned long) sdata);
 	setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
@@ -3852,6 +3749,7 @@
 	/* will change later if needed */
 	sdata->smps_mode = IEEE80211_SMPS_OFF;
 
+	mutex_lock(&local->mtx);
 	/*
 	 * If this fails (possibly due to channel context sharing
 	 * on incompatible channels, e.g. 80+80 and 160 sharing the
@@ -3863,13 +3761,15 @@
 	/* don't downgrade for 5 and 10 MHz channels, though. */
 	if (chandef.width == NL80211_CHAN_WIDTH_5 ||
 	    chandef.width == NL80211_CHAN_WIDTH_10)
-		return ret;
+		goto out;
 
 	while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
-		ifmgd->flags |= chandef_downgrade(&chandef);
+		ifmgd->flags |= ieee80211_chandef_downgrade(&chandef);
 		ret = ieee80211_vif_use_channel(sdata, &chandef,
 						IEEE80211_CHANCTX_SHARED);
 	}
+ out:
+	mutex_unlock(&local->mtx);
 	return ret;
 }
 
@@ -3897,27 +3797,40 @@
 		if (!new_sta)
 			return -ENOMEM;
 	}
-
 	if (new_sta) {
 		u32 rates = 0, basic_rates = 0;
 		bool have_higher_than_11mbit;
 		int min_rate = INT_MAX, min_rate_index = -1;
+		struct ieee80211_chanctx_conf *chanctx_conf;
 		struct ieee80211_supported_band *sband;
 		const struct cfg80211_bss_ies *ies;
+		int shift;
+		u32 rate_flags;
 
 		sband = local->hw.wiphy->bands[cbss->channel->band];
 
 		err = ieee80211_prep_channel(sdata, cbss);
 		if (err) {
 			sta_info_free(local, new_sta);
-			return err;
+			return -EINVAL;
 		}
+		shift = ieee80211_vif_get_shift(&sdata->vif);
+
+		rcu_read_lock();
+		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+		if (WARN_ON(!chanctx_conf)) {
+			rcu_read_unlock();
+			return -EINVAL;
+		}
+		rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
+		rcu_read_unlock();
 
 		ieee80211_get_rates(sband, bss->supp_rates,
 				    bss->supp_rates_len,
 				    &rates, &basic_rates,
 				    &have_higher_than_11mbit,
-				    &min_rate, &min_rate_index);
+				    &min_rate, &min_rate_index,
+				    shift, rate_flags);
 
 		/*
 		 * This used to be a workaround for basic rates missing
@@ -4112,6 +4025,44 @@
 	return err;
 }
 
+static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata,
+					const u8 *wmm_param, int len)
+{
+	const u8 *pos;
+	size_t left;
+
+	if (len < 8)
+		return false;
+
+	if (wmm_param[5] != 1 /* version */)
+		return false;
+
+	pos = wmm_param + 8;
+	left = len - 8;
+
+	for (; left >= 4; left -= 4, pos += 4) {
+		u8 aifsn = pos[0] & 0x0f;
+		u8 ecwmin = pos[1] & 0x0f;
+		u8 ecwmax = (pos[1] & 0xf0) >> 4;
+		int aci = (pos[0] >> 5) & 0x03;
+
+		if (aifsn < 2) {
+			sdata_info(sdata,
+				   "AP has invalid WMM params (AIFSN=%d for ACI %d), disabling WMM\n",
+				   aifsn, aci);
+			return false;
+		}
+		if (ecwmin > ecwmax) {
+			sdata_info(sdata,
+				   "AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n",
+				   ecwmin, ecwmax, aci);
+			return false;
+		}
+	}
+
+	return true;
+}
+
 int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 			struct cfg80211_assoc_request *req)
 {
@@ -4172,6 +4123,42 @@
 	
 	ifmgd->beacon_crc_valid = false;
 
+	assoc_data->wmm = bss->wmm_used &&
+			  (local->hw.queues >= IEEE80211_NUM_ACS);
+	if (assoc_data->wmm) {
+		/* try to check validity of WMM params IE */
+		const struct cfg80211_bss_ies *ies;
+		const u8 *wp, *start, *end;
+
+		rcu_read_lock();
+		ies = rcu_dereference(req->bss->ies);
+		start = ies->data;
+		end = start + ies->len;
+
+		while (true) {
+			wp = cfg80211_find_vendor_ie(
+				WLAN_OUI_MICROSOFT,
+				WLAN_OUI_TYPE_MICROSOFT_WMM,
+				start, end - start);
+			if (!wp)
+				break;
+			start = wp + wp[1] + 2;
+			/* if this IE is too short, try the next */
+			if (wp[1] <= 4)
+				continue;
+			/* if this IE is WMM params, we found what we wanted */
+			if (wp[6] == 1)
+				break;
+		}
+
+		if (!wp || !ieee80211_usable_wmm_params(sdata, wp + 2,
+							wp[1] - 2)) {
+			assoc_data->wmm = false;
+			ifmgd->flags |= IEEE80211_STA_DISABLE_WMM;
+		}
+		rcu_read_unlock();
+	}
+
 	/*
 	 * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
 	 * We still associate in non-HT mode (11a/b/g) if any one of these
@@ -4201,18 +4188,22 @@
 	/* Also disable HT if we don't support it or the AP doesn't use WMM */
 	sband = local->hw.wiphy->bands[req->bss->channel->band];
 	if (!sband->ht_cap.ht_supported ||
-	    local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
+	    local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used ||
+	    ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
 		ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
-		if (!bss->wmm_used)
+		if (!bss->wmm_used &&
+		    !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM))
 			netdev_info(sdata->dev,
 				    "disabling HT as WMM/QoS is not supported by the AP\n");
 	}
 
 	/* disable VHT if we don't support it or the AP doesn't use WMM */
 	if (!sband->vht_cap.vht_supported ||
-	    local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
+	    local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used ||
+	    ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
 		ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
-		if (!bss->wmm_used)
+		if (!bss->wmm_used &&
+		    !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM))
 			netdev_info(sdata->dev,
 				    "disabling VHT as WMM/QoS is not supported by the AP\n");
 	}
@@ -4241,8 +4232,6 @@
 		sdata->smps_mode = ifmgd->req_smps;
 
 	assoc_data->capability = req->bss->capability;
-	assoc_data->wmm = bss->wmm_used &&
-			  (local->hw.queues >= IEEE80211_NUM_ACS);
 	assoc_data->supp_rates = bss->supp_rates;
 	assoc_data->supp_rates_len = bss->supp_rates_len;
 
@@ -4289,6 +4278,8 @@
 
 	sdata->control_port_protocol = req->crypto.control_port_ethertype;
 	sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt;
+	sdata->encrypt_headroom = ieee80211_cs_headroom(local, &req->crypto,
+							sdata->vif.type);
 
 	/* kick off associate process */
 
diff -ruw linux-3.11.10/net/mac80211/offchannel.c linux-3.11.10-fbx/net/mac80211/offchannel.c
--- linux-3.11.10/net/mac80211/offchannel.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/offchannel.c	2015-09-29 11:15:09.069757025 +0200
@@ -333,7 +333,7 @@
 		container_of(work, struct ieee80211_roc_work, work.work);
 	struct ieee80211_sub_if_data *sdata = roc->sdata;
 	struct ieee80211_local *local = sdata->local;
-	bool started;
+	bool started, on_channel;
 
 	mutex_lock(&local->mtx);
 
@@ -354,13 +354,26 @@
 	if (!roc->started) {
 		struct ieee80211_roc_work *dep;
 
-		/* start this ROC */
+		WARN_ON(local->use_chanctx);
+
+		/* If actually operating on the desired channel (with at least
+		 * 20 MHz channel width) don't stop all the operations but still
+		 * treat it as though the ROC operation started properly, so
+		 * other ROC operations won't interfere with this one.
+		 */
+		roc->on_channel = roc->chan == local->_oper_chandef.chan &&
+				  local->_oper_chandef.width != NL80211_CHAN_WIDTH_5 &&
+				  local->_oper_chandef.width != NL80211_CHAN_WIDTH_10;
 
-		/* switch channel etc */
+		/* start this ROC */
 		ieee80211_recalc_idle(local);
 
+		if (!roc->on_channel) {
+			ieee80211_offchannel_stop_vifs(local);
+
 		local->tmp_channel = roc->chan;
 		ieee80211_hw_config(local, 0);
+		}
 
 		/* tell userspace or send frame */
 		ieee80211_handle_roc_started(roc);
@@ -379,9 +392,10 @@
  finish:
 		list_del(&roc->list);
 		started = roc->started;
+		on_channel = roc->on_channel;
 		ieee80211_roc_notify_destroy(roc, !roc->abort);
 
-		if (started) {
+		if (started && !on_channel) {
 			ieee80211_flush_queues(local, NULL);
 
 			local->tmp_channel = NULL;
@@ -394,6 +408,8 @@
 
 		if (started)
 			ieee80211_start_next_roc(local);
+		else if (list_empty(&local->roc_list))
+			ieee80211_run_deferred_scan(local);
 	}
 
  out_unlock:
diff -ruw linux-3.11.10/net/mac80211/rate.c linux-3.11.10-fbx/net/mac80211/rate.c
--- linux-3.11.10/net/mac80211/rate.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/rate.c	2015-09-29 11:15:09.069757025 +0200
@@ -210,7 +210,7 @@
 		!ieee80211_is_data(fc);
 }
 
-static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,
+static void rc_send_low_basicrate(s8 *idx, u32 basic_rates,
 				  struct ieee80211_supported_band *sband)
 {
 	u8 i;
@@ -232,37 +232,35 @@
 	/* could not find a basic rate; use original selection */
 }
 
-static inline s8
-rate_lowest_non_cck_index(struct ieee80211_supported_band *sband,
-			  struct ieee80211_sta *sta)
+static void __rate_control_send_low(struct ieee80211_hw *hw,
+				    struct ieee80211_supported_band *sband,
+				    struct ieee80211_sta *sta,
+				    struct ieee80211_tx_info *info,
+				    u32 rate_mask)
 {
 	int i;
+	u32 rate_flags =
+		ieee80211_chandef_rate_flags(&hw->conf.chandef);
+
+	if ((sband->band == IEEE80211_BAND_2GHZ) &&
+	    (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
+		rate_flags |= IEEE80211_RATE_ERP_G;
 
+	info->control.rates[0].idx = 0;
 	for (i = 0; i < sband->n_bitrates; i++) {
-		struct ieee80211_rate *srate = &sband->bitrates[i];
-		if ((srate->bitrate == 10) || (srate->bitrate == 20) ||
-		    (srate->bitrate == 55) || (srate->bitrate == 110))
+		if (!(rate_mask & BIT(i)))
 			continue;
 
-		if (rate_supported(sta, sband->band, i))
-			return i;
-	}
+		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+			continue;
 
-	/* No matching rate found */
-	return 0;
-}
+		if (!rate_supported(sta, sband->band, i))
+			continue;
 
-static void __rate_control_send_low(struct ieee80211_hw *hw,
-				    struct ieee80211_supported_band *sband,
-				    struct ieee80211_sta *sta,
-				    struct ieee80211_tx_info *info)
-{
-	if ((sband->band != IEEE80211_BAND_2GHZ) ||
-	    !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
-		info->control.rates[0].idx = rate_lowest_index(sband, sta);
-	else
-		info->control.rates[0].idx =
-			rate_lowest_non_cck_index(sband, sta);
+		info->control.rates[0].idx = i;
+		break;
+	}
+	WARN_ON_ONCE(i == sband->n_bitrates);
 
 	info->control.rates[0].count =
 		(info->flags & IEEE80211_TX_CTL_NO_ACK) ?
@@ -272,28 +270,38 @@
 }
 
 
-bool rate_control_send_low(struct ieee80211_sta *sta,
+bool rate_control_send_low(struct ieee80211_sta *pubsta,
 			   void *priv_sta,
 			   struct ieee80211_tx_rate_control *txrc)
 {
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
 	struct ieee80211_supported_band *sband = txrc->sband;
+	struct sta_info *sta;
 	int mcast_rate;
+	bool use_basicrate = false;
 
-	if (!sta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) {
-		__rate_control_send_low(txrc->hw, sband, sta, info);
+	if (!pubsta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) {
+		__rate_control_send_low(txrc->hw, sband, pubsta, info,
+					txrc->rate_idx_mask);
 
-		if (!sta && txrc->bss) {
+		if (!pubsta && txrc->bss) {
 			mcast_rate = txrc->bss_conf->mcast_rate[sband->band];
 			if (mcast_rate > 0) {
 				info->control.rates[0].idx = mcast_rate - 1;
 				return true;
 			}
+			use_basicrate = true;
+		} else if (pubsta) {
+			sta = container_of(pubsta, struct sta_info, sta);
+			if (ieee80211_vif_is_mesh(&sta->sdata->vif))
+				use_basicrate = true;
+		}
 
-			rc_send_low_broadcast(&info->control.rates[0].idx,
+		if (use_basicrate)
+			rc_send_low_basicrate(&info->control.rates[0].idx,
 					      txrc->bss_conf->basic_rates,
 					      sband);
-		}
+
 		return true;
 	}
 	return false;
@@ -585,6 +593,7 @@
 	u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
 	bool has_mcs_mask;
 	u32 mask;
+	u32 rate_flags;
 	int i;
 
 	/*
@@ -594,6 +603,12 @@
 	 */
 	mask = sdata->rc_rateidx_mask[info->band];
 	has_mcs_mask = sdata->rc_has_mcs_mask[info->band];
+	rate_flags =
+		ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+	for (i = 0; i < sband->n_bitrates; i++)
+		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+			mask &= ~BIT(i);
+
 	if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask)
 		return;
 
@@ -649,7 +664,8 @@
 		rate_control_apply_mask(sdata, sta, sband, info, dest, max_rates);
 
 	if (dest[0].idx < 0)
-		__rate_control_send_low(&sdata->local->hw, sband, sta, info);
+		__rate_control_send_low(&sdata->local->hw, sband, sta, info,
+					sdata->rc_rateidx_mask[info->band]);
 
 	if (sta)
 		rate_fixup_ratelist(vif, sband, info, dest, max_rates);
diff -ruw linux-3.11.10/net/mac80211/rate.h linux-3.11.10-fbx/net/mac80211/rate.h
--- linux-3.11.10/net/mac80211/rate.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/rate.h	2015-09-29 11:15:09.069757025 +0200
@@ -54,6 +54,8 @@
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_chanctx_conf *chanctx_conf;
 
+	ieee80211_sta_set_rx_nss(sta);
+
 	if (!ref)
 		return;
 
@@ -66,11 +68,10 @@
 	}
 
 	sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
-	rcu_read_unlock();
-
-	ieee80211_sta_set_rx_nss(sta);
 
-	ref->ops->rate_init(ref->priv, sband, ista, priv_sta);
+	ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
+			    priv_sta);
+	rcu_read_unlock();
 	set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
 }
 
@@ -81,10 +82,21 @@
 	struct rate_control_ref *ref = local->rate_ctrl;
 	struct ieee80211_sta *ista = &sta->sta;
 	void *priv_sta = sta->rate_ctrl_priv;
+	struct ieee80211_chanctx_conf *chanctx_conf;
+
+	if (ref && ref->ops->rate_update) {
+		rcu_read_lock();
 
-	if (ref && ref->ops->rate_update)
-		ref->ops->rate_update(ref->priv, sband, ista,
-				      priv_sta, changed);
+		chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
+		if (WARN_ON(!chanctx_conf)) {
+			rcu_read_unlock();
+			return;
+		}
+
+		ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
+				      ista, priv_sta, changed);
+		rcu_read_unlock();
+	}
 	drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
 }
 
diff -ruw linux-3.11.10/net/mac80211/rc80211_minstrel.c linux-3.11.10-fbx/net/mac80211/rc80211_minstrel.c
--- linux-3.11.10/net/mac80211/rc80211_minstrel.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/rc80211_minstrel.c	2015-09-29 11:15:09.069757025 +0200
@@ -203,6 +203,15 @@
 	memcpy(mi->max_tp_rate, tmp_tp_rate, sizeof(mi->max_tp_rate));
 	mi->max_prob_rate = tmp_prob_rate;
 
+#ifdef CONFIG_MAC80211_DEBUGFS
+	/* use fixed index if set */
+	if (mp->fixed_rate_idx != -1) {
+		mi->max_tp_rate[0] = mp->fixed_rate_idx;
+		mi->max_tp_rate[1] = mp->fixed_rate_idx;
+		mi->max_prob_rate = mp->fixed_rate_idx;
+	}
+#endif
+
 	/* Reset update timer */
 	mi->stats_update = jiffies;
 
@@ -310,6 +319,11 @@
 	/* increase sum packet counter */
 	mi->packet_count++;
 
+#ifdef CONFIG_MAC80211_DEBUGFS
+	if (mp->fixed_rate_idx != -1)
+		return;
+#endif
+
 	delta = (mi->packet_count * sampling_ratio / 100) -
 			(mi->sample_count + mi->sample_deferred / 2);
 
@@ -383,14 +397,18 @@
 static void
 calc_rate_durations(enum ieee80211_band band,
 		    struct minstrel_rate *d,
-		    struct ieee80211_rate *rate)
+		    struct ieee80211_rate *rate,
+		    struct cfg80211_chan_def *chandef)
 {
 	int erp = !!(rate->flags & IEEE80211_RATE_ERP_G);
+	int shift = ieee80211_chandef_get_shift(chandef);
 
 	d->perfect_tx_time = ieee80211_frame_duration(band, 1200,
-			rate->bitrate, erp, 1);
+			DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
+			shift);
 	d->ack_time = ieee80211_frame_duration(band, 10,
-			rate->bitrate, erp, 1);
+			DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
+			shift);
 }
 
 static void
@@ -418,6 +436,7 @@
 
 static void
 minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
+		   struct cfg80211_chan_def *chandef,
                struct ieee80211_sta *sta, void *priv_sta)
 {
 	struct minstrel_sta_info *mi = priv_sta;
@@ -425,14 +444,17 @@
 	struct ieee80211_rate *ctl_rate;
 	unsigned int i, n = 0;
 	unsigned int t_slot = 9; /* FIXME: get real slot time */
+	u32 rate_flags;
 
 	mi->sta = sta;
 	mi->lowest_rix = rate_lowest_index(sband, sta);
 	ctl_rate = &sband->bitrates[mi->lowest_rix];
 	mi->sp_ack_dur = ieee80211_frame_duration(sband->band, 10,
 				ctl_rate->bitrate,
-				!!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1);
+				!!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1,
+				ieee80211_chandef_get_shift(chandef));
 
+	rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
 	memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate));
 	mi->max_prob_rate = 0;
 
@@ -441,15 +463,22 @@
 		unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0;
 		unsigned int tx_time_single;
 		unsigned int cw = mp->cw_min;
+		int shift;
 
 		if (!rate_supported(sta, sband->band, i))
 			continue;
+		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+			continue;
+
 		n++;
 		memset(mr, 0, sizeof(*mr));
 
 		mr->rix = i;
-		mr->bitrate = sband->bitrates[i].bitrate / 5;
-		calc_rate_durations(sband->band, mr, &sband->bitrates[i]);
+		shift = ieee80211_chandef_get_shift(chandef);
+		mr->bitrate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+					   (1 << shift) * 5);
+		calc_rate_durations(sband->band, mr, &sband->bitrates[i],
+				    chandef);
 
 		/* calculate maximum number of retransmissions before
 		 * fallback (based on maximum segment size) */
@@ -547,6 +576,7 @@
 {
 	static const int bitrates[4] = { 10, 20, 55, 110 };
 	struct ieee80211_supported_band *sband;
+	u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
 	int i, j;
 
 	sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ];
@@ -559,6 +589,9 @@
 		if (rate->flags & IEEE80211_RATE_ERP_G)
 			continue;
 
+		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+			continue;
+
 		for (j = 0; j < ARRAY_SIZE(bitrates); j++) {
 			if (rate->bitrate != bitrates[j])
 				continue;
diff -ruw linux-3.11.10/net/mac80211/rc80211_minstrel_ht.c linux-3.11.10-fbx/net/mac80211/rc80211_minstrel_ht.c
--- linux-3.11.10/net/mac80211/rc80211_minstrel_ht.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/rc80211_minstrel_ht.c	2015-09-29 11:15:09.073757054 +0200
@@ -365,6 +365,14 @@
 		}
 	}
 
+#ifdef CONFIG_MAC80211_DEBUGFS
+	/* use fixed index if set */
+	if (mp->fixed_rate_idx != -1) {
+		mi->max_tp_rate = mp->fixed_rate_idx;
+		mi->max_tp_rate2 = mp->fixed_rate_idx;
+		mi->max_prob_rate = mp->fixed_rate_idx;
+	}
+#endif
 
 	mi->stats_update = jiffies;
 }
@@ -774,23 +782,18 @@
 	info->flags |= mi->tx_flags;
 	minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);
 
+#ifdef CONFIG_MAC80211_DEBUGFS
+	if (mp->fixed_rate_idx != -1)
+		return;
+#endif
+
 	/* Don't use EAPOL frames for sampling on non-mrr hw */
 	if (mp->hw->max_rates == 1 &&
-	    txrc->skb->protocol == cpu_to_be16(ETH_P_PAE))
+	    (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
 		sample_idx = -1;
 	else
 		sample_idx = minstrel_get_sample_rate(mp, mi);
 
-#ifdef CONFIG_MAC80211_DEBUGFS
-	/* use fixed index if set */
-	if (mp->fixed_rate_idx != -1) {
-		mi->max_tp_rate = mp->fixed_rate_idx;
-		mi->max_tp_rate2 = mp->fixed_rate_idx;
-		mi->max_prob_rate = mp->fixed_rate_idx;
-		sample_idx = -1;
-	}
-#endif
-
 	mi->total_packets++;
 
 	/* wraparound */
@@ -847,6 +850,7 @@
 
 static void
 minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
+			struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *priv_sta)
 {
 	struct minstrel_priv *mp = priv;
@@ -872,8 +876,9 @@
 	mi->sta = sta;
 	mi->stats_update = jiffies;
 
-	ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1);
-	mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1) + ack_dur;
+	ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0);
+	mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0);
+	mi->overhead += ack_dur;
 	mi->overhead_rtscts = mi->overhead + 2 * ack_dur;
 
 	mi->avg_ampdu_len = MINSTREL_FRAC(1, 1);
@@ -942,22 +947,25 @@
 	memset(&msp->legacy, 0, sizeof(msp->legacy));
 	msp->legacy.r = msp->ratelist;
 	msp->legacy.sample_table = msp->sample_table;
-	return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy);
+	return mac80211_minstrel.rate_init(priv, sband, chandef, sta,
+					   &msp->legacy);
 }
 
 static void
 minstrel_ht_rate_init(void *priv, struct ieee80211_supported_band *sband,
+		      struct cfg80211_chan_def *chandef,
                       struct ieee80211_sta *sta, void *priv_sta)
 {
-	minstrel_ht_update_caps(priv, sband, sta, priv_sta);
+	minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);
 }
 
 static void
 minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband,
+			struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *priv_sta,
                         u32 changed)
 {
-	minstrel_ht_update_caps(priv, sband, sta, priv_sta);
+	minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);
 }
 
 static void *
diff -ruw linux-3.11.10/net/mac80211/rx.c linux-3.11.10-fbx/net/mac80211/rx.c
--- linux-3.11.10/net/mac80211/rx.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/rx.c	2015-09-29 11:15:09.073757054 +0200
@@ -87,11 +87,13 @@
 	int len;
 
 	/* always present fields */
-	len = sizeof(struct ieee80211_radiotap_header) + 9;
+	len = sizeof(struct ieee80211_radiotap_header) + 8;
 
-	/* allocate extra bitmap */
+	/* allocate extra bitmaps */
 	if (status->vendor_radiotap_len)
 		len += 4;
+	if (status->chains)
+		len += 4 * hweight8(status->chains);
 
 	if (ieee80211_have_rx_timestamp(status)) {
 		len = ALIGN(len, 8);
@@ -100,6 +102,10 @@
 	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
 		len += 1;
 
+	/* antenna field, if we don't have per-chain info */
+	if (!status->chains)
+		len += 1;
+
 	/* padding for RX_FLAGS if necessary */
 	len = ALIGN(len, 2);
 
@@ -116,6 +122,11 @@
 		len += 12;
 	}
 
+	if (status->chains) {
+		/* antenna and antenna signal fields */
+		len += 2 * hweight8(status->chains);
+	}
+
 	if (status->vendor_radiotap_len) {
 		if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
 			status->vendor_radiotap_align = 1;
@@ -145,8 +156,12 @@
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_radiotap_header *rthdr;
 	unsigned char *pos;
+	__le32 *it_present;
+	u32 it_present_val;
 	u16 rx_flags = 0;
-	int mpdulen;
+	u16 channel_flags = 0;
+	int mpdulen, chain;
+	unsigned long chains = status->chains;
 
 	mpdulen = skb->len;
 	if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
@@ -154,25 +169,39 @@
 
 	rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
 	memset(rthdr, 0, rtap_len);
+	it_present = &rthdr->it_present;
 
 	/* radiotap header, set always present flags */
-	rthdr->it_present =
-		cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
-			    (1 << IEEE80211_RADIOTAP_CHANNEL) |
-			    (1 << IEEE80211_RADIOTAP_ANTENNA) |
-			    (1 << IEEE80211_RADIOTAP_RX_FLAGS));
 	rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len);
-
-	pos = (unsigned char *)(rthdr + 1);
+	it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) |
+			 BIT(IEEE80211_RADIOTAP_CHANNEL) |
+			 BIT(IEEE80211_RADIOTAP_RX_FLAGS);
+
+	if (!status->chains)
+		it_present_val |= BIT(IEEE80211_RADIOTAP_ANTENNA);
+
+	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);
+	}
 
 	if (status->vendor_radiotap_len) {
-		rthdr->it_present |=
-			cpu_to_le32(BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE)) |
-			cpu_to_le32(BIT(IEEE80211_RADIOTAP_EXT));
-		put_unaligned_le32(status->vendor_radiotap_bitmap, pos);
-		pos += 4;
+		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 = status->vendor_radiotap_bitmap;
 	}
 
+	put_unaligned_le32(it_present_val, it_present);
+
+	pos = (void *)(it_present + 1);
+
 	/* the order of the following fields is important */
 
 	/* IEEE80211_RADIOTAP_TSFT */
@@ -207,28 +236,35 @@
 		 */
 		*pos = 0;
 	} else {
+		int shift = 0;
 		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
-		*pos = rate->bitrate / 5;
+		if (status->flag & RX_FLAG_10MHZ)
+			shift = 1;
+		else if (status->flag & RX_FLAG_5MHZ)
+			shift = 2;
+		*pos = DIV_ROUND_UP(rate->bitrate, 5 * (1 << shift));
 	}
 	pos++;
 
 	/* IEEE80211_RADIOTAP_CHANNEL */
 	put_unaligned_le16(status->freq, pos);
 	pos += 2;
+	if (status->flag & RX_FLAG_10MHZ)
+		channel_flags |= IEEE80211_CHAN_HALF;
+	else if (status->flag & RX_FLAG_5MHZ)
+		channel_flags |= IEEE80211_CHAN_QUARTER;
+
 	if (status->band == IEEE80211_BAND_5GHZ)
-		put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ,
-				   pos);
+		channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ;
 	else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
-		put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ,
-				   pos);
+		channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
 	else if (rate && rate->flags & IEEE80211_RATE_ERP_G)
-		put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ,
-				   pos);
+		channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
 	else if (rate)
-		put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ,
-				   pos);
+		channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
 	else
-		put_unaligned_le16(IEEE80211_CHAN_2GHZ, pos);
+		channel_flags |= IEEE80211_CHAN_2GHZ;
+	put_unaligned_le16(channel_flags, pos);
 	pos += 2;
 
 	/* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
@@ -242,9 +278,11 @@
 
 	/* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */
 
+	if (!status->chains) {
 	/* IEEE80211_RADIOTAP_ANTENNA */
 	*pos = status->antenna;
 	pos++;
+	}
 
 	/* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */
 
@@ -341,6 +379,11 @@
 		pos += 2;
 	}
 
+	for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+		*pos++ = status->chain_signal[chain];
+		*pos++ = chain;
+	}
+
 	if (status->vendor_radiotap_len) {
 		/* ensure 2 byte alignment for the vendor field as required */
 		if ((pos - (u8 *)rthdr) & 1)
@@ -595,6 +638,27 @@
 	return le16_to_cpu(mmie->key_id);
 }
 
+static int iwl80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs,
+				 struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	__le16 fc;
+	int hdrlen;
+	u8 keyid;
+
+	fc = hdr->frame_control;
+	hdrlen = ieee80211_hdrlen(fc);
+
+	if (skb->len < hdrlen + cs->hdr_len)
+		return -EINVAL;
+
+	skb_copy_bits(skb, hdrlen + cs->key_idx_off, &keyid, 1);
+	keyid &= cs->key_idx_mask;
+	keyid >>= cs->key_idx_shift;
+
+	return keyid;
+}
+
 static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
@@ -868,7 +932,8 @@
 	u16 sc;
 	u8 tid, ack_policy;
 
-	if (!ieee80211_is_data_qos(hdr->frame_control))
+	if (!ieee80211_is_data_qos(hdr->frame_control) ||
+	    is_multicast_ether_addr(hdr->addr1))
 		goto dont_reorder;
 
 	/*
@@ -952,9 +1017,10 @@
 				rx->sta->num_duplicates++;
 			}
 			return RX_DROP_UNUSABLE;
-		} else
+		} else if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
 			rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl;
 	}
+	}
 
 	if (unlikely(rx->skb->len < 16)) {
 		I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
@@ -1012,207 +1078,6 @@
 
 
 static ieee80211_rx_result debug_noinline
-ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
-{
-	struct sk_buff *skb = rx->skb;
-	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-	int keyidx;
-	int hdrlen;
-	ieee80211_rx_result result = RX_DROP_UNUSABLE;
-	struct ieee80211_key *sta_ptk = NULL;
-	int mmie_keyidx = -1;
-	__le16 fc;
-
-	/*
-	 * Key selection 101
-	 *
-	 * There are four types of keys:
-	 *  - GTK (group keys)
-	 *  - IGTK (group keys for management frames)
-	 *  - PTK (pairwise keys)
-	 *  - STK (station-to-station pairwise keys)
-	 *
-	 * When selecting a key, we have to distinguish between multicast
-	 * (including broadcast) and unicast frames, the latter can only
-	 * use PTKs and STKs while the former always use GTKs and IGTKs.
-	 * Unless, of course, actual WEP keys ("pre-RSNA") are used, then
-	 * unicast frames can also use key indices like GTKs. Hence, if we
-	 * don't have a PTK/STK we check the key index for a WEP key.
-	 *
-	 * Note that in a regular BSS, multicast frames are sent by the
-	 * AP only, associated stations unicast the frame to the AP first
-	 * which then multicasts it on their behalf.
-	 *
-	 * There is also a slight problem in IBSS mode: GTKs are negotiated
-	 * with each station, that is something we don't currently handle.
-	 * The spec seems to expect that one negotiates the same key with
-	 * every station but there's no such requirement; VLANs could be
-	 * possible.
-	 */
-
-	/*
-	 * No point in finding a key and decrypting if the frame is neither
-	 * addressed to us nor a multicast frame.
-	 */
-	if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
-		return RX_CONTINUE;
-
-	/* start without a key */
-	rx->key = NULL;
-
-	if (rx->sta)
-		sta_ptk = rcu_dereference(rx->sta->ptk);
-
-	fc = hdr->frame_control;
-
-	if (!ieee80211_has_protected(fc))
-		mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
-
-	if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) {
-		rx->key = sta_ptk;
-		if ((status->flag & RX_FLAG_DECRYPTED) &&
-		    (status->flag & RX_FLAG_IV_STRIPPED))
-			return RX_CONTINUE;
-		/* Skip decryption if the frame is not protected. */
-		if (!ieee80211_has_protected(fc))
-			return RX_CONTINUE;
-	} else if (mmie_keyidx >= 0) {
-		/* Broadcast/multicast robust management frame / BIP */
-		if ((status->flag & RX_FLAG_DECRYPTED) &&
-		    (status->flag & RX_FLAG_IV_STRIPPED))
-			return RX_CONTINUE;
-
-		if (mmie_keyidx < NUM_DEFAULT_KEYS ||
-		    mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
-			return RX_DROP_MONITOR; /* unexpected BIP keyidx */
-		if (rx->sta)
-			rx->key = rcu_dereference(rx->sta->gtk[mmie_keyidx]);
-		if (!rx->key)
-			rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]);
-	} else if (!ieee80211_has_protected(fc)) {
-		/*
-		 * The frame was not protected, so skip decryption. However, we
-		 * need to set rx->key if there is a key that could have been
-		 * used so that the frame may be dropped if encryption would
-		 * have been expected.
-		 */
-		struct ieee80211_key *key = NULL;
-		struct ieee80211_sub_if_data *sdata = rx->sdata;
-		int i;
-
-		if (ieee80211_is_mgmt(fc) &&
-		    is_multicast_ether_addr(hdr->addr1) &&
-		    (key = rcu_dereference(rx->sdata->default_mgmt_key)))
-			rx->key = key;
-		else {
-			if (rx->sta) {
-				for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
-					key = rcu_dereference(rx->sta->gtk[i]);
-					if (key)
-						break;
-				}
-			}
-			if (!key) {
-				for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
-					key = rcu_dereference(sdata->keys[i]);
-					if (key)
-						break;
-				}
-			}
-			if (key)
-				rx->key = key;
-		}
-		return RX_CONTINUE;
-	} else {
-		u8 keyid;
-		/*
-		 * The device doesn't give us the IV so we won't be
-		 * able to look up the key. That's ok though, we
-		 * don't need to decrypt the frame, we just won't
-		 * be able to keep statistics accurate.
-		 * Except for key threshold notifications, should
-		 * we somehow allow the driver to tell us which key
-		 * the hardware used if this flag is set?
-		 */
-		if ((status->flag & RX_FLAG_DECRYPTED) &&
-		    (status->flag & RX_FLAG_IV_STRIPPED))
-			return RX_CONTINUE;
-
-		hdrlen = ieee80211_hdrlen(fc);
-
-		if (rx->skb->len < 8 + hdrlen)
-			return RX_DROP_UNUSABLE; /* TODO: count this? */
-
-		/*
-		 * no need to call ieee80211_wep_get_keyidx,
-		 * it verifies a bunch of things we've done already
-		 */
-		skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
-		keyidx = keyid >> 6;
-
-		/* check per-station GTK first, if multicast packet */
-		if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
-			rx->key = rcu_dereference(rx->sta->gtk[keyidx]);
-
-		/* if not found, try default key */
-		if (!rx->key) {
-			rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
-
-			/*
-			 * RSNA-protected unicast frames should always be
-			 * sent with pairwise or station-to-station keys,
-			 * but for WEP we allow using a key index as well.
-			 */
-			if (rx->key &&
-			    rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 &&
-			    rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 &&
-			    !is_multicast_ether_addr(hdr->addr1))
-				rx->key = NULL;
-		}
-	}
-
-	if (rx->key) {
-		if (unlikely(rx->key->flags & KEY_FLAG_TAINTED))
-			return RX_DROP_MONITOR;
-
-		rx->key->tx_rx_count++;
-		/* TODO: add threshold stuff again */
-	} else {
-		return RX_DROP_MONITOR;
-	}
-
-	switch (rx->key->conf.cipher) {
-	case WLAN_CIPHER_SUITE_WEP40:
-	case WLAN_CIPHER_SUITE_WEP104:
-		result = ieee80211_crypto_wep_decrypt(rx);
-		break;
-	case WLAN_CIPHER_SUITE_TKIP:
-		result = ieee80211_crypto_tkip_decrypt(rx);
-		break;
-	case WLAN_CIPHER_SUITE_CCMP:
-		result = ieee80211_crypto_ccmp_decrypt(rx);
-		break;
-	case WLAN_CIPHER_SUITE_AES_CMAC:
-		result = ieee80211_crypto_aes_cmac_decrypt(rx);
-		break;
-	default:
-		/*
-		 * We can reach here only with HW-only algorithms
-		 * but why didn't it decrypt the frame?!
-		 */
-		return RX_DROP_UNUSABLE;
-	}
-
-	/* the hdr variable is invalid after the decrypt handlers */
-
-	/* either the frame has been decrypted or will be dropped */
-	status->flag |= RX_FLAG_DECRYPTED;
-
-	return result;
-}
-
-static ieee80211_rx_result debug_noinline
 ieee80211_rx_h_check_more_data(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_local *local;
@@ -1513,6 +1378,219 @@
 	return RX_CONTINUE;
 } /* ieee80211_rx_h_sta_process */
 
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
+{
+	struct sk_buff *skb = rx->skb;
+	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	int keyidx;
+	int hdrlen;
+	ieee80211_rx_result result = RX_DROP_UNUSABLE;
+	struct ieee80211_key *sta_ptk = NULL;
+	int mmie_keyidx = -1;
+	__le16 fc;
+	const struct ieee80211_cipher_scheme *cs = NULL;
+
+	/*
+	 * Key selection 101
+	 *
+	 * There are four types of keys:
+	 *  - GTK (group keys)
+	 *  - IGTK (group keys for management frames)
+	 *  - PTK (pairwise keys)
+	 *  - STK (station-to-station pairwise keys)
+	 *
+	 * When selecting a key, we have to distinguish between multicast
+	 * (including broadcast) and unicast frames, the latter can only
+	 * use PTKs and STKs while the former always use GTKs and IGTKs.
+	 * Unless, of course, actual WEP keys ("pre-RSNA") are used, then
+	 * unicast frames can also use key indices like GTKs. Hence, if we
+	 * don't have a PTK/STK we check the key index for a WEP key.
+	 *
+	 * Note that in a regular BSS, multicast frames are sent by the
+	 * AP only, associated stations unicast the frame to the AP first
+	 * which then multicasts it on their behalf.
+	 *
+	 * There is also a slight problem in IBSS mode: GTKs are negotiated
+	 * with each station, that is something we don't currently handle.
+	 * The spec seems to expect that one negotiates the same key with
+	 * every station but there's no such requirement; VLANs could be
+	 * possible.
+	 */
+
+	/*
+	 * No point in finding a key and decrypting if the frame is neither
+	 * addressed to us nor a multicast frame.
+	 */
+	if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
+		return RX_CONTINUE;
+
+	/* start without a key */
+	rx->key = NULL;
+	fc = hdr->frame_control;
+
+	if (rx->sta) {
+		int keyid = rx->sta->ptk_idx;
+
+		if (ieee80211_has_protected(fc) && rx->sta->cipher_scheme) {
+			cs = rx->sta->cipher_scheme;
+			keyid = iwl80211_get_cs_keyid(cs, rx->skb);
+			if (unlikely(keyid < 0))
+				return RX_DROP_UNUSABLE;
+		}
+		sta_ptk = rcu_dereference(rx->sta->ptk[keyid]);
+	}
+
+	if (!ieee80211_has_protected(fc))
+		mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
+
+	if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) {
+		rx->key = sta_ptk;
+		if ((status->flag & RX_FLAG_DECRYPTED) &&
+		    (status->flag & RX_FLAG_IV_STRIPPED))
+			return RX_CONTINUE;
+		/* Skip decryption if the frame is not protected. */
+		if (!ieee80211_has_protected(fc))
+			return RX_CONTINUE;
+	} else if (mmie_keyidx >= 0) {
+		/* Broadcast/multicast robust management frame / BIP */
+		if ((status->flag & RX_FLAG_DECRYPTED) &&
+		    (status->flag & RX_FLAG_IV_STRIPPED))
+			return RX_CONTINUE;
+
+		if (mmie_keyidx < NUM_DEFAULT_KEYS ||
+		    mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
+			return RX_DROP_MONITOR; /* unexpected BIP keyidx */
+		if (rx->sta)
+			rx->key = rcu_dereference(rx->sta->gtk[mmie_keyidx]);
+		if (!rx->key)
+			rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]);
+	} else if (!ieee80211_has_protected(fc)) {
+		/*
+		 * The frame was not protected, so skip decryption. However, we
+		 * need to set rx->key if there is a key that could have been
+		 * used so that the frame may be dropped if encryption would
+		 * have been expected.
+		 */
+		struct ieee80211_key *key = NULL;
+		struct ieee80211_sub_if_data *sdata = rx->sdata;
+		int i;
+
+		if (ieee80211_is_mgmt(fc) &&
+		    is_multicast_ether_addr(hdr->addr1) &&
+		    (key = rcu_dereference(rx->sdata->default_mgmt_key)))
+			rx->key = key;
+		else {
+			if (rx->sta) {
+				for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+					key = rcu_dereference(rx->sta->gtk[i]);
+					if (key)
+						break;
+				}
+			}
+			if (!key) {
+				for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+					key = rcu_dereference(sdata->keys[i]);
+					if (key)
+						break;
+				}
+			}
+			if (key)
+				rx->key = key;
+		}
+		return RX_CONTINUE;
+	} else {
+		u8 keyid;
+
+		/*
+		 * The device doesn't give us the IV so we won't be
+		 * able to look up the key. That's ok though, we
+		 * don't need to decrypt the frame, we just won't
+		 * be able to keep statistics accurate.
+		 * Except for key threshold notifications, should
+		 * we somehow allow the driver to tell us which key
+		 * the hardware used if this flag is set?
+		 */
+		if ((status->flag & RX_FLAG_DECRYPTED) &&
+		    (status->flag & RX_FLAG_IV_STRIPPED))
+			return RX_CONTINUE;
+
+		hdrlen = ieee80211_hdrlen(fc);
+
+		if (cs) {
+			keyidx = iwl80211_get_cs_keyid(cs, rx->skb);
+
+			if (unlikely(keyidx < 0))
+				return RX_DROP_UNUSABLE;
+		} else {
+			if (rx->skb->len < 8 + hdrlen)
+				return RX_DROP_UNUSABLE; /* TODO: count this? */
+			/*
+			 * no need to call ieee80211_wep_get_keyidx,
+			 * it verifies a bunch of things we've done already
+			 */
+			skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
+			keyidx = keyid >> 6;
+		}
+
+		/* check per-station GTK first, if multicast packet */
+		if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
+			rx->key = rcu_dereference(rx->sta->gtk[keyidx]);
+
+		/* if not found, try default key */
+		if (!rx->key) {
+			rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
+
+			/*
+			 * RSNA-protected unicast frames should always be
+			 * sent with pairwise or station-to-station keys,
+			 * but for WEP we allow using a key index as well.
+			 */
+			if (rx->key &&
+			    rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 &&
+			    rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 &&
+			    !is_multicast_ether_addr(hdr->addr1))
+				rx->key = NULL;
+		}
+	}
+
+	if (rx->key) {
+		if (unlikely(rx->key->flags & KEY_FLAG_TAINTED))
+			return RX_DROP_MONITOR;
+
+		rx->key->tx_rx_count++;
+		/* TODO: add threshold stuff again */
+	} else {
+		return RX_DROP_MONITOR;
+	}
+
+	switch (rx->key->conf.cipher) {
+	case WLAN_CIPHER_SUITE_WEP40:
+	case WLAN_CIPHER_SUITE_WEP104:
+		result = ieee80211_crypto_wep_decrypt(rx);
+		break;
+	case WLAN_CIPHER_SUITE_TKIP:
+		result = ieee80211_crypto_tkip_decrypt(rx);
+		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+		result = ieee80211_crypto_ccmp_decrypt(rx);
+		break;
+	case WLAN_CIPHER_SUITE_AES_CMAC:
+		result = ieee80211_crypto_aes_cmac_decrypt(rx);
+		break;
+	default:
+		result = ieee80211_crypto_hw_decrypt(rx);
+	}
+
+	/* the hdr variable is invalid after the decrypt handlers */
+
+	/* either the frame has been decrypted or will be dropped */
+	status->flag |= RX_FLAG_DECRYPTED;
+
+	return result;
+}
+
 static inline struct ieee80211_fragment_entry *
 ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
 			 unsigned int frag, unsigned int seq, int rx_queue,
@@ -2359,7 +2437,8 @@
 		return RX_DROP_UNUSABLE;
 
 	if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC &&
-	    mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED)
+	    mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED &&
+	    mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT)
 		return RX_DROP_UNUSABLE;
 
 	if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
@@ -2523,32 +2602,50 @@
 
 		goto queue;
 	case WLAN_CATEGORY_SPECTRUM_MGMT:
-		if (status->band != IEEE80211_BAND_5GHZ)
-			break;
-
-		if (sdata->vif.type != NL80211_IFTYPE_STATION)
-			break;
-
 		/* verify action_code is present */
 		if (len < IEEE80211_MIN_ACTION_SIZE + 1)
 			break;
 
 		switch (mgmt->u.action.u.measurement.action_code) {
 		case WLAN_ACTION_SPCT_MSR_REQ:
+			if (status->band != IEEE80211_BAND_5GHZ)
+				break;
+
 			if (len < (IEEE80211_MIN_ACTION_SIZE +
 				   sizeof(mgmt->u.action.u.measurement)))
 				break;
+
+			if (sdata->vif.type != NL80211_IFTYPE_STATION)
+				break;
+
 			ieee80211_process_measurement_req(sdata, mgmt, len);
 			goto handled;
-		case WLAN_ACTION_SPCT_CHL_SWITCH:
-			if (sdata->vif.type != NL80211_IFTYPE_STATION)
+		case WLAN_ACTION_SPCT_CHL_SWITCH: {
+			u8 *bssid;
+			if (len < (IEEE80211_MIN_ACTION_SIZE +
+				   sizeof(mgmt->u.action.u.chan_switch)))
 				break;
 
-			if (!ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid))
+			if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+			    sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+			    sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+				break;
+
+			if (sdata->vif.type == NL80211_IFTYPE_STATION)
+				bssid = sdata->u.mgd.bssid;
+			else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+				bssid = sdata->u.ibss.bssid;
+			else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+				bssid = mgmt->sa;
+			else
+				break;
+
+			if (!ether_addr_equal(mgmt->bssid, bssid))
 				break;
 
 			goto queue;
 		}
+		}
 		break;
 	case WLAN_CATEGORY_SA_QUERY:
 		if (len < (IEEE80211_MIN_ACTION_SIZE +
@@ -2641,8 +2738,7 @@
 		sig = status->signal;
 
 	if (cfg80211_rx_mgmt(&rx->sdata->wdev, status->freq, sig,
-			     rx->skb->data, rx->skb->len,
-			     GFP_ATOMIC)) {
+			     rx->skb->data, rx->skb->len, 0, GFP_ATOMIC)) {
 		if (rx->sta)
 			rx->sta->rx_packets++;
 		dev_kfree_skb(rx->skb);
@@ -2896,10 +2992,10 @@
 		 */
 		rx->skb = skb;
 
-		CALL_RXH(ieee80211_rx_h_decrypt)
 		CALL_RXH(ieee80211_rx_h_check_more_data)
 		CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll)
 		CALL_RXH(ieee80211_rx_h_sta_process)
+		CALL_RXH(ieee80211_rx_h_decrypt)
 		CALL_RXH(ieee80211_rx_h_defragment)
 		CALL_RXH(ieee80211_rx_h_michael_mic_verify)
 		/* must be after MMIC verify so header is counted in MPDU mic */
diff -ruw linux-3.11.10/net/mac80211/scan.c linux-3.11.10-fbx/net/mac80211/scan.c
--- linux-3.11.10/net/mac80211/scan.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/scan.c	2015-09-29 11:15:09.073757054 +0200
@@ -66,6 +66,7 @@
 	struct cfg80211_bss *cbss;
 	struct ieee80211_bss *bss;
 	int clen, srlen;
+	enum nl80211_bss_scan_width scan_width;
 	s32 signal = 0;
 
 	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
@@ -73,8 +74,15 @@
 	else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
 		signal = (rx_status->signal * 100) / local->hw.max_signal;
 
-	cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel,
-					 mgmt, len, signal, GFP_ATOMIC);
+	scan_width = NL80211_BSS_CHAN_WIDTH_20;
+	if (rx_status->flag & RX_FLAG_5MHZ)
+		scan_width = NL80211_BSS_CHAN_WIDTH_5;
+	if (rx_status->flag & RX_FLAG_10MHZ)
+		scan_width = NL80211_BSS_CHAN_WIDTH_10;
+
+	cbss = cfg80211_inform_bss_width_frame(local->hw.wiphy, channel,
+					       scan_width, mgmt, len, signal,
+					       GFP_ATOMIC);
 	if (!cbss)
 		return NULL;
 
@@ -204,10 +212,29 @@
 		ieee80211_rx_bss_put(local, bss);
 }
 
+static void
+ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef,
+			       enum nl80211_bss_scan_width scan_width)
+{
+	memset(chandef, 0, sizeof(*chandef));
+	switch (scan_width) {
+	case NL80211_BSS_CHAN_WIDTH_5:
+		chandef->width = NL80211_CHAN_WIDTH_5;
+		break;
+	case NL80211_BSS_CHAN_WIDTH_10:
+		chandef->width = NL80211_CHAN_WIDTH_10;
+		break;
+	default:
+		chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+		break;
+	}
+}
+
 /* return false if no more work */
 static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
 {
 	struct cfg80211_scan_request *req = local->scan_req;
+	struct cfg80211_chan_def chandef;
 	enum ieee80211_band band;
 	int i, ielen, n_chans;
 
@@ -232,11 +259,12 @@
 	} while (!n_chans);
 
 	local->hw_scan_req->n_channels = n_chans;
+	ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
 
 	ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
 					 local->hw_scan_ies_bufsize,
 					 req->ie, req->ie_len, band,
-					 req->rates[band], 0);
+					 req->rates[band], &chandef);
 	local->hw_scan_req->ie_len = ielen;
 	local->hw_scan_req->no_cck = req->no_cck;
 
@@ -283,7 +311,7 @@
 	rcu_assign_pointer(local->scan_sdata, NULL);
 
 	local->scanning = 0;
-	local->scan_channel = NULL;
+	local->scan_chandef.chan = NULL;
 
 	/* Set power back to normal operating levels. */
 	ieee80211_hw_config(local, 0);
@@ -366,8 +394,7 @@
 		return false;
 
 	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-	    sdata->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
-				  IEEE80211_STA_CONNECTION_POLL))
+	    sdata->u.mgd.flags & IEEE80211_STA_CONNECTION_POLL)
 		return false;
 
 	return true;
@@ -499,7 +526,7 @@
 		ieee80211_hw_config(local, 0);
 
 		if ((req->channels[0]->flags &
-		     IEEE80211_CHAN_PASSIVE_SCAN) ||
+		     IEEE80211_CHAN_NO_IR) ||
 		    !local->scan_req->n_ssids) {
 			next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
 		} else {
@@ -545,7 +572,7 @@
 	 * TODO: channel switching also consumes quite some time,
 	 * add that delay as well to get a better estimation
 	 */
-	if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+	if (chan->flags & IEEE80211_CHAN_NO_IR)
 		return IEEE80211_PASSIVE_CHANNEL_TIME;
 	return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;
 }
@@ -618,11 +645,34 @@
 {
 	int skip;
 	struct ieee80211_channel *chan;
+	enum nl80211_bss_scan_width oper_scan_width;
 
 	skip = 0;
 	chan = local->scan_req->channels[local->scan_channel_idx];
 
-	local->scan_channel = chan;
+	local->scan_chandef.chan = chan;
+	local->scan_chandef.center_freq1 = chan->center_freq;
+	local->scan_chandef.center_freq2 = 0;
+	switch (local->scan_req->scan_width) {
+	case NL80211_BSS_CHAN_WIDTH_5:
+		local->scan_chandef.width = NL80211_CHAN_WIDTH_5;
+		break;
+	case NL80211_BSS_CHAN_WIDTH_10:
+		local->scan_chandef.width = NL80211_CHAN_WIDTH_10;
+		break;
+	case NL80211_BSS_CHAN_WIDTH_20:
+		/* If scanning on oper channel, use whatever channel-type
+		 * is currently in use.
+		 */
+		oper_scan_width = cfg80211_chandef_to_scan_width(
+					&local->_oper_chandef);
+		if (chan == local->_oper_chandef.chan &&
+		    oper_scan_width == local->scan_req->scan_width)
+			local->scan_chandef = local->_oper_chandef;
+		else
+			local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+		break;
+	}
 
 	if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
 		skip = 1;
@@ -646,7 +696,7 @@
 	 *
 	 * In any case, it is not necessary for a passive scan.
 	 */
-	if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN ||
+	if (chan->flags & IEEE80211_CHAN_NO_IR ||
 	    !local->scan_req->n_ssids) {
 		*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
 		local->next_scan_state = SCAN_DECISION;
@@ -662,7 +712,7 @@
 					 unsigned long *next_delay)
 {
 	/* switch back to the operating channel */
-	local->scan_channel = NULL;
+	local->scan_chandef.chan = NULL;
 	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 
 	/* disable PS */
@@ -804,7 +854,8 @@
 
 int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
 				const u8 *ssid, u8 ssid_len,
-				struct ieee80211_channel *chan)
+				struct ieee80211_channel *chan,
+				enum nl80211_bss_scan_width scan_width)
 {
 	struct ieee80211_local *local = sdata->local;
 	int ret = -EBUSY;
@@ -830,7 +881,7 @@
 				struct ieee80211_channel *tmp_ch =
 				    &local->hw.wiphy->bands[band]->channels[i];
 
-				if (tmp_ch->flags & (IEEE80211_CHAN_NO_IBSS |
+				if (tmp_ch->flags & (IEEE80211_CHAN_NO_IR |
 						     IEEE80211_CHAN_DISABLED))
 					continue;
 
@@ -844,7 +895,7 @@
 
 		local->int_scan_req->n_channels = n_ch;
 	} else {
-		if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IBSS |
+		if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IR |
 						IEEE80211_CHAN_DISABLED)))
 			goto unlock;
 
@@ -854,6 +905,7 @@
 
 	local->int_scan_req->ssids = &local->scan_ssid;
 	local->int_scan_req->n_ssids = 1;
+	local->int_scan_req->scan_width = scan_width;
 	memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
 	local->int_scan_req->ssids[0].ssid_len = ssid_len;
 
@@ -931,6 +983,7 @@
 {
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_sched_scan_ies sched_scan_ies = {};
+	struct cfg80211_chan_def chandef;
 	int ret, i, iebufsz;
 
 	iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
@@ -958,10 +1011,12 @@
 			goto out_free;
 		}
 
+		ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
+
 		sched_scan_ies.len[i] =
 			ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
 						 iebufsz, req->ie, req->ie_len,
-						 i, (u32) -1, 0);
+						 i, (u32) -1, &chandef);
 	}
 
 	ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
@@ -1033,6 +1088,6 @@
 
 	trace_api_sched_scan_stopped(local);
 
-	ieee80211_queue_work(&local->hw, &local->sched_scan_stopped_work);
+	schedule_work(&local->sched_scan_stopped_work);
 }
 EXPORT_SYMBOL(ieee80211_sched_scan_stopped);
diff -ruw linux-3.11.10/net/mac80211/spectmgmt.c linux-3.11.10-fbx/net/mac80211/spectmgmt.c
--- linux-3.11.10/net/mac80211/spectmgmt.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/spectmgmt.c	2015-09-29 11:15:09.073757054 +0200
@@ -21,6 +21,177 @@
 #include "sta_info.h"
 #include "wme.h"
 
+int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
+				 struct ieee802_11_elems *elems, bool beacon,
+				 enum ieee80211_band current_band,
+				 u32 sta_flags, u8 *bssid,
+				 struct ieee80211_csa_ie *csa_ie)
+{
+	enum ieee80211_band new_band;
+	int new_freq;
+	u8 new_chan_no;
+	struct ieee80211_channel *new_chan;
+	struct cfg80211_chan_def new_vht_chandef = {};
+	const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
+	const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
+	const struct ieee80211_ht_operation *ht_oper;
+	int secondary_channel_offset = -1;
+
+	sec_chan_offs = elems->sec_chan_offs;
+	wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
+	ht_oper = elems->ht_operation;
+
+	if (sta_flags & (IEEE80211_STA_DISABLE_HT |
+			 IEEE80211_STA_DISABLE_40MHZ)) {
+		sec_chan_offs = NULL;
+		wide_bw_chansw_ie = NULL;
+		/* only used for bandwidth here */
+		ht_oper = NULL;
+	}
+
+	if (sta_flags & IEEE80211_STA_DISABLE_VHT)
+		wide_bw_chansw_ie = NULL;
+
+	if (elems->ext_chansw_ie) {
+		if (!ieee80211_operating_class_to_band(
+				elems->ext_chansw_ie->new_operating_class,
+				&new_band)) {
+			sdata_info(sdata,
+				   "cannot understand ECSA IE operating class %d, disconnecting\n",
+				   elems->ext_chansw_ie->new_operating_class);
+			return -EINVAL;
+		}
+		new_chan_no = elems->ext_chansw_ie->new_ch_num;
+		csa_ie->count = elems->ext_chansw_ie->count;
+		csa_ie->mode = elems->ext_chansw_ie->mode;
+	} else if (elems->ch_switch_ie) {
+		new_band = current_band;
+		new_chan_no = elems->ch_switch_ie->new_ch_num;
+		csa_ie->count = elems->ch_switch_ie->count;
+		csa_ie->mode = elems->ch_switch_ie->mode;
+	} else {
+		/* nothing here we understand */
+		return 1;
+	}
+
+	/* Mesh Channel Switch Parameters Element */
+	if (elems->mesh_chansw_params_ie) {
+		csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl;
+		csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags;
+		csa_ie->pre_value = le16_to_cpu(
+				elems->mesh_chansw_params_ie->mesh_pre_value);
+	}
+
+	new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
+	new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
+	if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
+		sdata_info(sdata,
+			   "BSS %pM switches to unsupported channel (%d MHz), disconnecting\n",
+			   bssid, new_freq);
+		return -EINVAL;
+	}
+
+	if (!beacon && sec_chan_offs) {
+		secondary_channel_offset = sec_chan_offs->sec_chan_offs;
+	} else if (beacon && ht_oper) {
+		secondary_channel_offset =
+			ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
+	} else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
+		/* If it's not a beacon, HT is enabled and the IE not present,
+		 * it's 20 MHz, 802.11-2012 8.5.2.6:
+		 *	This element [the Secondary Channel Offset Element] is
+		 *	present when switching to a 40 MHz channel. It may be
+		 *	present when switching to a 20 MHz channel (in which
+		 *	case the secondary channel offset is set to SCN).
+		 */
+		secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+	}
+
+	switch (secondary_channel_offset) {
+	default:
+		/* secondary_channel_offset was present but is invalid */
+	case IEEE80211_HT_PARAM_CHA_SEC_NONE:
+		cfg80211_chandef_create(&csa_ie->chandef, new_chan,
+					NL80211_CHAN_HT20);
+		break;
+	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+		cfg80211_chandef_create(&csa_ie->chandef, new_chan,
+					NL80211_CHAN_HT40PLUS);
+		break;
+	case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+		cfg80211_chandef_create(&csa_ie->chandef, new_chan,
+					NL80211_CHAN_HT40MINUS);
+		break;
+	case -1:
+		cfg80211_chandef_create(&csa_ie->chandef, new_chan,
+					NL80211_CHAN_NO_HT);
+		/* keep width for 5/10 MHz channels */
+		switch (sdata->vif.bss_conf.chandef.width) {
+		case NL80211_CHAN_WIDTH_5:
+		case NL80211_CHAN_WIDTH_10:
+			csa_ie->chandef.width =
+				sdata->vif.bss_conf.chandef.width;
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+
+	if (wide_bw_chansw_ie) {
+		new_vht_chandef.chan = new_chan;
+		new_vht_chandef.center_freq1 =
+			ieee80211_channel_to_frequency(
+				wide_bw_chansw_ie->new_center_freq_seg0,
+				new_band);
+
+		switch (wide_bw_chansw_ie->new_channel_width) {
+		default:
+			/* hmmm, ignore VHT and use HT if present */
+		case IEEE80211_VHT_CHANWIDTH_USE_HT:
+			new_vht_chandef.chan = NULL;
+			break;
+		case IEEE80211_VHT_CHANWIDTH_80MHZ:
+			new_vht_chandef.width = NL80211_CHAN_WIDTH_80;
+			break;
+		case IEEE80211_VHT_CHANWIDTH_160MHZ:
+			new_vht_chandef.width = NL80211_CHAN_WIDTH_160;
+			break;
+		case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+			/* field is otherwise reserved */
+			new_vht_chandef.center_freq2 =
+				ieee80211_channel_to_frequency(
+					wide_bw_chansw_ie->new_center_freq_seg1,
+					new_band);
+			new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
+			break;
+		}
+		if (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ &&
+		    new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
+			ieee80211_chandef_downgrade(&new_vht_chandef);
+		if (sta_flags & IEEE80211_STA_DISABLE_160MHZ &&
+		    new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
+			ieee80211_chandef_downgrade(&new_vht_chandef);
+		if (sta_flags & IEEE80211_STA_DISABLE_40MHZ &&
+		    new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
+			ieee80211_chandef_downgrade(&new_vht_chandef);
+	}
+
+	/* if VHT data is there validate & use it */
+	if (new_vht_chandef.chan) {
+		if (!cfg80211_chandef_compatible(&new_vht_chandef,
+						 &csa_ie->chandef)) {
+			sdata_info(sdata,
+				   "BSS %pM: CSA has inconsistent channel data, disconnecting\n",
+				   bssid);
+			return -EINVAL;
+		}
+		csa_ie->chandef = new_vht_chandef;
+	}
+
+	return 0;
+}
+
 static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,
 					struct ieee80211_msrment_ie *request_ie,
 					const u8 *da, const u8 *bssid,
diff -ruw linux-3.11.10/net/mac80211/sta_info.c linux-3.11.10-fbx/net/mac80211/sta_info.c
--- linux-3.11.10/net/mac80211/sta_info.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/sta_info.c	2015-09-29 11:15:09.073757054 +0200
@@ -271,6 +271,7 @@
 
 	sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
 
+	kfree(rcu_dereference_raw(sta->sta.rates));
 	kfree(sta);
 }
 
@@ -340,6 +341,7 @@
 		return NULL;
 
 	spin_lock_init(&sta->lock);
+	spin_lock_init(&sta->ps_lock);
 	INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
 	INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
 	mutex_init(&sta->ampdu_mlme.mtx);
@@ -385,6 +387,30 @@
 		sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
 
 	sta->sta.smps_mode = IEEE80211_SMPS_OFF;
+	if (sdata->vif.type == NL80211_IFTYPE_AP ||
+	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+		struct ieee80211_supported_band *sband =
+			local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
+		u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
+				IEEE80211_HT_CAP_SM_PS_SHIFT;
+		/*
+		 * Assume that hostapd advertises our caps in the beacon and
+		 * this is the known_smps_mode for a station that just assciated
+		 */
+		switch (smps) {
+		case WLAN_HT_SMPS_CONTROL_DISABLED:
+			sta->known_smps_mode = IEEE80211_SMPS_OFF;
+			break;
+		case WLAN_HT_SMPS_CONTROL_STATIC:
+			sta->known_smps_mode = IEEE80211_SMPS_STATIC;
+			break;
+		case WLAN_HT_SMPS_CONTROL_DYNAMIC:
+			sta->known_smps_mode = IEEE80211_SMPS_DYNAMIC;
+			break;
+		default:
+			WARN_ON(1);
+		}
+	}
 
 	sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
 
@@ -483,6 +509,7 @@
 
 	set_sta_flag(sta, WLAN_STA_INSERTED);
 
+	ieee80211_recalc_min_chandef(sdata);
 	ieee80211_sta_debugfs_add(sta);
 	rate_control_add_sta_debugfs(sta);
 
@@ -845,6 +872,7 @@
 
 	rate_control_remove_sta_debugfs(sta);
 	ieee80211_sta_debugfs_remove(sta);
+	ieee80211_recalc_min_chandef(sdata);
 
 	call_rcu(&sta->rcu_head, free_sta_rcu);
 
@@ -1049,6 +1077,8 @@
 
 	skb_queue_head_init(&pending);
 
+	/* sync with ieee80211_tx_h_unicast_ps_buf */
+	spin_lock(&sta->ps_lock);
 	/* Send all buffered frames to the station */
 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
 		int count = skb_queue_len(&pending), tmp;
@@ -1068,6 +1098,20 @@
 	}
 
 	ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta);
+	spin_unlock(&sta->ps_lock);
+
+	/* This station just woke up and isn't aware of our SMPS state */
+	if (!ieee80211_smps_is_restrictive(sta->known_smps_mode,
+					   sdata->smps_mode) &&
+	    sta->known_smps_mode != sdata->bss->req_smps &&
+	    sta_info_tx_streams(sta) != 1) {
+		ht_dbg(sdata,
+		       "%pM just woke up and MIMO capable - update SMPS\n",
+		       sta->sta.addr);
+		ieee80211_send_smps_action(sdata, sdata->bss->req_smps,
+					   sta->sta.addr,
+					   sdata->vif.bss_conf.bssid);
+	}
 
 	local->total_ps_buffered -= buffered;
 
@@ -1114,6 +1158,7 @@
 	memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
 	memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
 	memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN);
+	nullfunc->seq_ctrl = 0;
 
 	skb->priority = tid;
 	skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]);
@@ -1520,3 +1565,38 @@
 
 	return 0;
 }
+
+u8 sta_info_tx_streams(struct sta_info *sta)
+{
+	struct ieee80211_sta_ht_cap *ht_cap = &sta->sta.ht_cap;
+	u8 rx_streams;
+
+	if (!sta->sta.ht_cap.ht_supported)
+		return 1;
+
+	if (sta->sta.vht_cap.vht_supported) {
+		int i;
+		u16 tx_mcs_map =
+			le16_to_cpu(sta->sta.vht_cap.vht_mcs.tx_mcs_map);
+
+		for (i = 7; i >= 0; i--)
+			if ((tx_mcs_map & (0x3 << (i * 2))) !=
+			    IEEE80211_VHT_MCS_NOT_SUPPORTED)
+				return i + 1;
+	}
+
+	if (ht_cap->mcs.rx_mask[3])
+		rx_streams = 4;
+	else if (ht_cap->mcs.rx_mask[2])
+		rx_streams = 3;
+	else if (ht_cap->mcs.rx_mask[1])
+		rx_streams = 2;
+	else
+		rx_streams = 1;
+
+	if (!(ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF))
+		return rx_streams;
+
+	return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
+			>> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
+}
diff -ruw linux-3.11.10/net/mac80211/sta_info.h linux-3.11.10-fbx/net/mac80211/sta_info.h
--- linux-3.11.10/net/mac80211/sta_info.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/sta_info.h	2015-09-29 11:15:09.073757054 +0200
@@ -231,8 +231,10 @@
  * @hnext: hash table linked list pointer
  * @local: pointer to the global information
  * @sdata: virtual interface this station belongs to
- * @ptk: peer key negotiated with this station, if any
+ * @ptk: peer keys negotiated with this station, if any
+ * @ptk_idx: last installed peer key index
  * @gtk: group keys negotiated with this station, if any
+ * @gtk_idx: last installed group key index
  * @rate_ctrl: rate control algorithm reference
  * @rate_ctrl_priv: rate control private per-STA pointer
  * @last_tx_rate: rate used for last transmit, to report to userspace as
@@ -245,6 +247,7 @@
  * @drv_unblock_wk: used for driver PS unblocking
  * @listen_interval: listen interval of this station, when we're acting as AP
  * @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly
+ * @ps_lock: used for powersave (when mac80211 is the AP) related locking
  * @ps_tx_buf: buffers (per AC) of frames to transmit to this station
  *	when it leaves power saving state or polls
  * @tx_filtered: buffers (per AC) of frames we already tried to
@@ -301,6 +304,9 @@
  * @chains: chains ever used for RX from this station
  * @chain_signal_last: last signal (per chain)
  * @chain_signal_avg: signal average (per chain)
+ * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
+ *	AP only.
+ * @cipher_scheme: optional cipher scheme for this station
  */
 struct sta_info {
 	/* General information, mostly static */
@@ -310,7 +316,9 @@
 	struct ieee80211_local *local;
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
-	struct ieee80211_key __rcu *ptk;
+	struct ieee80211_key __rcu *ptk[NUM_DEFAULT_KEYS];
+	u8 gtk_idx;
+	u8 ptk_idx;
 	struct rate_control_ref *rate_ctrl;
 	void *rate_ctrl_priv;
 	spinlock_t lock;
@@ -328,10 +336,8 @@
 	/* use the accessors defined below */
 	unsigned long _flags;
 
-	/*
-	 * STA powersave frame queues, no more than the internal
-	 * locking required.
-	 */
+	/* STA powersave lock and frame queues */
+	spinlock_t ps_lock;
 	struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS];
 	struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS];
 	unsigned long driver_buffered_tids;
@@ -411,6 +417,9 @@
 	unsigned int lost_packets;
 	unsigned int beacon_loss_count;
 
+	enum ieee80211_smps_mode known_smps_mode;
+	const struct ieee80211_cipher_scheme *cipher_scheme;
+
 	/* keep last! */
 	struct ieee80211_sta sta;
 };
@@ -613,6 +622,7 @@
 			  struct rate_info *rinfo);
 void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
 			  unsigned long exp_time);
+u8 sta_info_tx_streams(struct sta_info *sta);
 
 void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
 void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
diff -ruw linux-3.11.10/net/mac80211/status.c linux-3.11.10-fbx/net/mac80211/status.c
--- linux-3.11.10/net/mac80211/status.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/status.c	2015-09-29 11:15:09.073757054 +0200
@@ -194,29 +194,36 @@
 	if (ieee80211_is_action(mgmt->frame_control) &&
 	    mgmt->u.action.category == WLAN_CATEGORY_HT &&
 	    mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS &&
-	    sdata->vif.type == NL80211_IFTYPE_STATION &&
 	    ieee80211_sdata_running(sdata)) {
-		/*
-		 * This update looks racy, but isn't -- if we come
-		 * here we've definitely got a station that we're
-		 * talking to, and on a managed interface that can
-		 * only be the AP. And the only other place updating
-		 * this variable in managed mode is before association.
-		 */
+		enum ieee80211_smps_mode smps_mode;
+
 		switch (mgmt->u.action.u.ht_smps.smps_control) {
 		case WLAN_HT_SMPS_CONTROL_DYNAMIC:
-			sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
+			smps_mode = IEEE80211_SMPS_DYNAMIC;
 			break;
 		case WLAN_HT_SMPS_CONTROL_STATIC:
-			sdata->smps_mode = IEEE80211_SMPS_STATIC;
+			smps_mode = IEEE80211_SMPS_STATIC;
 			break;
 		case WLAN_HT_SMPS_CONTROL_DISABLED:
 		default: /* shouldn't happen since we don't send that */
-			sdata->smps_mode = IEEE80211_SMPS_OFF;
+			smps_mode = IEEE80211_SMPS_OFF;
 			break;
 		}
 
+		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+			/*
+			 * This update looks racy, but isn't -- if we come
+			 * here we've definitely got a station that we're
+			 * talking to, and on a managed interface that can
+			 * only be the AP. And the only other place updating
+			 * this variable in managed mode is before association.
+			 */
+			sdata->smps_mode = smps_mode;
 		ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
+		} else if (sdata->vif.type == NL80211_IFTYPE_AP ||
+			   sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+			sta->known_smps_mode = smps_mode;
+		}
 	}
 }
 
@@ -238,7 +245,8 @@
 
 	/* IEEE80211_RADIOTAP_RATE rate */
 	if (info->status.rates[0].idx >= 0 &&
-	    !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS))
+	    !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
+					     IEEE80211_TX_RC_VHT_MCS)))
 		len += 2;
 
 	/* IEEE80211_RADIOTAP_TX_FLAGS */
@@ -247,17 +255,23 @@
 	/* IEEE80211_RADIOTAP_DATA_RETRIES */
 	len += 1;
 
-	/* IEEE80211_TX_RC_MCS */
-	if (info->status.rates[0].idx >= 0 &&
-	    info->status.rates[0].flags & IEEE80211_TX_RC_MCS)
+	/* IEEE80211_RADIOTAP_MCS
+	 * IEEE80211_RADIOTAP_VHT */
+	if (info->status.rates[0].idx >= 0) {
+		if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS)
 		len += 3;
+		else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS)
+			len = ALIGN(len, 2) + 12;
+	}
 
 	return len;
 }
 
-static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
-					     *sband, struct sk_buff *skb,
-					     int retry_count, int rtap_len)
+static void
+ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
+				 struct ieee80211_supported_band *sband,
+				 struct sk_buff *skb, int retry_count,
+				 int rtap_len, int shift)
 {
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -282,9 +296,13 @@
 
 	/* IEEE80211_RADIOTAP_RATE */
 	if (info->status.rates[0].idx >= 0 &&
-	    !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) {
+	    !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
+					     IEEE80211_TX_RC_VHT_MCS))) {
+		u16 rate;
+
 		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
-		*pos = sband->bitrates[info->status.rates[0].idx].bitrate / 5;
+		rate = sband->bitrates[info->status.rates[0].idx].bitrate;
+		*pos = DIV_ROUND_UP(rate, 5 * (1 << shift));
 		/* padding for tx flags */
 		pos += 2;
 	}
@@ -309,9 +327,12 @@
 	*pos = retry_count;
 	pos++;
 
-	/* IEEE80211_TX_RC_MCS */
-	if (info->status.rates[0].idx >= 0 &&
-	    info->status.rates[0].flags & IEEE80211_TX_RC_MCS) {
+	if (info->status.rates[0].idx < 0)
+		return;
+
+	/* IEEE80211_RADIOTAP_MCS
+	 * IEEE80211_RADIOTAP_VHT */
+	if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS) {
 		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
 		pos[0] = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
 			 IEEE80211_RADIOTAP_MCS_HAVE_GI |
@@ -324,8 +345,48 @@
 			pos[1] |= IEEE80211_RADIOTAP_MCS_FMT_GF;
 		pos[2] = info->status.rates[0].idx;
 		pos += 3;
-	}
+	} else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) {
+		u16 known = local->hw.radiotap_vht_details &
+			(IEEE80211_RADIOTAP_VHT_KNOWN_GI |
+			 IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH);
+
+		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
+
+		/* required alignment from rthdr */
+		pos = (u8 *)rthdr + ALIGN(pos - (u8 *)rthdr, 2);
+
+		/* u16 known - IEEE80211_RADIOTAP_VHT_KNOWN_* */
+		put_unaligned_le16(known, pos);
+		pos += 2;
+
+		/* u8 flags - IEEE80211_RADIOTAP_VHT_FLAG_* */
+		if (info->status.rates[0].flags & IEEE80211_TX_RC_SHORT_GI)
+			*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+		pos++;
 
+		/* u8 bandwidth */
+		if (info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+			*pos = 1;
+		else if (info->status.rates[0].flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+			*pos = 4;
+		else if (info->status.rates[0].flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+			*pos = 11;
+		else /* IEEE80211_TX_RC_{20_MHZ_WIDTH,FIXME:DUP_DATA} */
+			*pos = 0;
+		pos++;
+
+		/* u8 mcs_nss[4] */
+		*pos = (ieee80211_rate_get_vht_mcs(&info->status.rates[0]) << 4) |
+			ieee80211_rate_get_vht_nss(&info->status.rates[0]);
+		pos += 4;
+
+		/* u8 coding */
+		pos++;
+		/* u8 group_id */
+		pos++;
+		/* u16 partial_aid */
+		pos += 2;
+	}
 }
 
 static void ieee80211_report_used_skb(struct ieee80211_local *local,
@@ -427,6 +488,7 @@
 	bool acked;
 	struct ieee80211_bar *bar;
 	int rtap_len;
+	int shift = 0;
 
 	for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
 		if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
@@ -461,6 +523,8 @@
 		if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
 			continue;
 
+		shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+
 		if (info->flags & IEEE80211_TX_STATUS_EOSP)
 			clear_sta_flag(sta, WLAN_STA_SP);
 
@@ -560,7 +624,7 @@
 
 	rcu_read_unlock();
 
-	ieee80211_led_tx(local, 0);
+	ieee80211_led_tx(local);
 
 	/* SNMP counters
 	 * Fragments are passed to low-level drivers as separate skbs, so these
@@ -627,7 +691,8 @@
 		dev_kfree_skb(skb);
 		return;
 	}
-	ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len);
+	ieee80211_add_tx_radiotap_header(local, sband, skb, retry_count,
+					 rtap_len, shift);
 
 	/* XXX: is this sufficient for BPF? */
 	skb_set_mac_header(skb, 0);
diff -ruw linux-3.11.10/net/mac80211/trace.h linux-3.11.10-fbx/net/mac80211/trace.h
--- linux-3.11.10/net/mac80211/trace.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/trace.h	2015-09-29 11:15:09.073757054 +0200
@@ -41,14 +41,31 @@
 #define CHANDEF_PR_ARG	__entry->control_freq, __entry->chan_width,			\
 			__entry->center_freq1, __entry->center_freq2
 
+#define MIN_CHANDEF_ENTRY								\
+			__field(u32, min_control_freq)					\
+			__field(u32, min_chan_width)					\
+			__field(u32, min_center_freq1)					\
+			__field(u32, min_center_freq2)
+
+#define MIN_CHANDEF_ASSIGN(c)								\
+			__entry->min_control_freq = (c)->chan ? (c)->chan->center_freq : 0;	\
+			__entry->min_chan_width = (c)->width;				\
+			__entry->min_center_freq1 = (c)->center_freq1;			\
+			__entry->min_center_freq2 = (c)->center_freq2;
+#define MIN_CHANDEF_PR_FMT	" min_control:%d MHz min_width:%d min_center: %d/%d MHz"
+#define MIN_CHANDEF_PR_ARG	__entry->min_control_freq, __entry->min_chan_width,	\
+			__entry->min_center_freq1, __entry->min_center_freq2
+
 #define CHANCTX_ENTRY	CHANDEF_ENTRY							\
+			MIN_CHANDEF_ENTRY						\
 			__field(u8, rx_chains_static)					\
 			__field(u8, rx_chains_dynamic)
 #define CHANCTX_ASSIGN	CHANDEF_ASSIGN(&ctx->conf.def)					\
+			MIN_CHANDEF_ASSIGN(&ctx->conf.min_def)				\
 			__entry->rx_chains_static = ctx->conf.rx_chains_static;		\
 			__entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic
-#define CHANCTX_PR_FMT	CHANDEF_PR_FMT " chains:%d/%d"
-#define CHANCTX_PR_ARG	CHANDEF_PR_ARG,							\
+#define CHANCTX_PR_FMT	CHANDEF_PR_FMT MIN_CHANDEF_PR_FMT " chains:%d/%d"
+#define CHANCTX_PR_ARG	CHANDEF_PR_ARG,	MIN_CHANDEF_PR_ARG,				\
 			__entry->rx_chains_static, __entry->rx_chains_dynamic
 
 
@@ -1475,6 +1492,41 @@
 );
 #endif
 
+TRACE_EVENT(drv_join_ibss,
+	TP_PROTO(struct ieee80211_local *local,
+		 struct ieee80211_sub_if_data *sdata,
+		 struct ieee80211_bss_conf *info),
+
+	TP_ARGS(local, sdata, info),
+
+	TP_STRUCT__entry(
+		LOCAL_ENTRY
+		VIF_ENTRY
+		__field(u8, dtimper)
+		__field(u16, bcnint)
+		__dynamic_array(u8, ssid, info->ssid_len);
+	),
+
+	TP_fast_assign(
+		LOCAL_ASSIGN;
+		VIF_ASSIGN;
+		__entry->dtimper = info->dtim_period;
+		__entry->bcnint = info->beacon_int;
+		memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
+	),
+
+	TP_printk(
+		LOCAL_PR_FMT  VIF_PR_FMT,
+		LOCAL_PR_ARG, VIF_PR_ARG
+	)
+);
+
+DEFINE_EVENT(local_sdata_evt, drv_leave_ibss,
+	TP_PROTO(struct ieee80211_local *local,
+		 struct ieee80211_sub_if_data *sdata),
+	TP_ARGS(local, sdata)
+);
+
 /*
  * Tracing for API calls that drivers call.
  */
@@ -1906,6 +1958,32 @@
 	)
 );
 
+TRACE_EVENT(drv_channel_switch_beacon,
+	TP_PROTO(struct ieee80211_local *local,
+		 struct ieee80211_sub_if_data *sdata,
+		 struct cfg80211_chan_def *chandef),
+
+	TP_ARGS(local, sdata, chandef),
+
+	TP_STRUCT__entry(
+		LOCAL_ENTRY
+		VIF_ENTRY
+		CHANDEF_ENTRY
+	),
+
+	TP_fast_assign(
+		LOCAL_ASSIGN;
+		VIF_ASSIGN;
+		CHANDEF_ASSIGN(chandef);
+	),
+
+	TP_printk(
+		LOCAL_PR_FMT VIF_PR_FMT " channel switch to " CHANDEF_PR_FMT,
+		LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG
+	)
+);
+
+
 #ifdef CONFIG_MAC80211_MESSAGE_TRACING
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM mac80211_msg
diff -ruw linux-3.11.10/net/mac80211/tx.c linux-3.11.10-fbx/net/mac80211/tx.c
--- linux-3.11.10/net/mac80211/tx.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/tx.c	2015-09-29 11:15:09.077757091 +0200
@@ -40,12 +40,22 @@
 				 struct sk_buff *skb, int group_addr,
 				 int next_frag_len)
 {
-	int rate, mrate, erp, dur, i;
+	int rate, mrate, erp, dur, i, shift = 0;
 	struct ieee80211_rate *txrate;
 	struct ieee80211_local *local = tx->local;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_hdr *hdr;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_chanctx_conf *chanctx_conf;
+	u32 rate_flags = 0;
+
+	rcu_read_lock();
+	chanctx_conf = rcu_dereference(tx->sdata->vif.chanctx_conf);
+	if (chanctx_conf) {
+		shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
+		rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
+	}
+	rcu_read_unlock();
 
 	/* assume HW handles this */
 	if (tx->rate.flags & IEEE80211_TX_RC_MCS)
@@ -122,8 +132,11 @@
 		if (r->bitrate > txrate->bitrate)
 			break;
 
+		if ((rate_flags & r->flags) != rate_flags)
+			continue;
+
 		if (tx->sdata->vif.bss_conf.basic_rates & BIT(i))
-			rate = r->bitrate;
+			rate = DIV_ROUND_UP(r->bitrate, 1 << shift);
 
 		switch (sband->band) {
 		case IEEE80211_BAND_2GHZ: {
@@ -150,7 +163,7 @@
 	if (rate == -1) {
 		/* No matching basic rate found; use highest suitable mandatory
 		 * PHY rate */
-		rate = mrate;
+		rate = DIV_ROUND_UP(mrate, 1 << shift);
 	}
 
 	/* Don't calculate ACKs for QoS Frames with NoAck Policy set */
@@ -162,7 +175,8 @@
 		 * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up
 		 * to closest integer */
 		dur = ieee80211_frame_duration(sband->band, 10, rate, erp,
-				tx->sdata->vif.bss_conf.use_short_preamble);
+				tx->sdata->vif.bss_conf.use_short_preamble,
+				shift);
 
 	if (next_frag_len) {
 		/* Frame is fragmented: duration increases with time needed to
@@ -171,7 +185,8 @@
 		/* next fragment */
 		dur += ieee80211_frame_duration(sband->band, next_frag_len,
 				txrate->bitrate, erp,
-				tx->sdata->vif.bss_conf.use_short_preamble);
+				tx->sdata->vif.bss_conf.use_short_preamble,
+				shift);
 	}
 
 	return cpu_to_le16(dur);
@@ -448,7 +463,6 @@
 {
 	struct sta_info *sta = tx->sta;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
 	struct ieee80211_local *local = tx->local;
 
 	if (unlikely(!sta))
@@ -459,19 +473,24 @@
 		     !(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) {
 		int ac = skb_get_queue_mapping(tx->skb);
 
-		/* only deauth, disassoc and action are bufferable MMPDUs */
-		if (ieee80211_is_mgmt(hdr->frame_control) &&
-		    !ieee80211_is_deauth(hdr->frame_control) &&
-		    !ieee80211_is_disassoc(hdr->frame_control) &&
-		    !ieee80211_is_action(hdr->frame_control)) {
-			info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
-			return TX_CONTINUE;
-		}
-
 		ps_dbg(sta->sdata, "STA %pM aid %d: PS buffer for AC %d\n",
 		       sta->sta.addr, sta->sta.aid, ac);
 		if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
 			purge_old_ps_buffers(tx->local);
+
+		/* sync with ieee80211_sta_ps_deliver_wakeup */
+		spin_lock(&sta->ps_lock);
+		/*
+		 * STA woke up the meantime and all the frames on ps_tx_buf have
+		 * been queued to pending queue. No reordering can happen, go
+		 * ahead and Tx the packet.
+		 */
+		if (!test_sta_flag(sta, WLAN_STA_PS_STA) &&
+		    !test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
+			spin_unlock(&sta->ps_lock);
+			return TX_CONTINUE;
+		}
+
 		if (skb_queue_len(&sta->ps_tx_buf[ac]) >= STA_MAX_TX_BUFFER) {
 			struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf[ac]);
 			ps_dbg(tx->sdata,
@@ -485,6 +504,7 @@
 		info->control.vif = &tx->sdata->vif;
 		info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
 		skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb);
+		spin_unlock(&sta->ps_lock);
 
 		if (!timer_pending(&local->sta_cleanup))
 			mod_timer(&local->sta_cleanup,
@@ -510,9 +530,22 @@
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx)
 {
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
+
 	if (unlikely(tx->flags & IEEE80211_TX_PS_BUFFERED))
 		return TX_CONTINUE;
 
+	/* only deauth, disassoc and action are bufferable MMPDUs */
+	if (ieee80211_is_mgmt(hdr->frame_control) &&
+	    !ieee80211_is_deauth(hdr->frame_control) &&
+	    !ieee80211_is_disassoc(hdr->frame_control) &&
+	    !ieee80211_is_action(hdr->frame_control)) {
+		if (tx->flags & IEEE80211_TX_UNICAST)
+			info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
+		return TX_CONTINUE;
+	}
+
 	if (tx->flags & IEEE80211_TX_UNICAST)
 		return ieee80211_tx_h_unicast_ps_buf(tx);
 	else
@@ -524,9 +557,11 @@
 {
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
 
-	if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol &&
-		     tx->sdata->control_port_no_encrypt))
+	if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol)) {
+		if (tx->sdata->control_port_no_encrypt)
 		info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+		info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
+	}
 
 	return TX_CONTINUE;
 }
@@ -540,7 +575,8 @@
 
 	if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT))
 		tx->key = NULL;
-	else if (tx->sta && (key = rcu_dereference(tx->sta->ptk)))
+	else if (tx->sta &&
+		 (key = rcu_dereference(tx->sta->ptk[tx->sta->ptk_idx])))
 		tx->key = key;
 	else if (ieee80211_is_mgmt(hdr->frame_control) &&
 		 is_multicast_ether_addr(hdr->addr1) &&
@@ -764,9 +800,11 @@
 	/*
 	 * Anything but QoS data that has a sequence number field
 	 * (is long enough) gets a sequence number from the global
-	 * counter.
+	 * counter.  QoS data frames with a multicast destination
+	 * also use the global counter (802.11-2012 9.3.2.10).
 	 */
-	if (!ieee80211_is_data_qos(hdr->frame_control)) {
+	if (!ieee80211_is_data_qos(hdr->frame_control) ||
+	    is_multicast_ether_addr(hdr->addr1)) {
 		/* driver should assign sequence number */
 		info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
 		/* for pure STA mode without beacons, we can do it */
@@ -821,15 +859,16 @@
 		rem -= fraglen;
 		tmp = dev_alloc_skb(local->tx_headroom +
 				    frag_threshold +
-				    IEEE80211_ENCRYPT_HEADROOM +
+				    tx->sdata->encrypt_headroom +
 				    IEEE80211_ENCRYPT_TAILROOM);
 		if (!tmp)
 			return -ENOMEM;
 
 		__skb_queue_tail(&tx->skbs, tmp);
 
-		skb_reserve(tmp, local->tx_headroom +
-				 IEEE80211_ENCRYPT_HEADROOM);
+		skb_reserve(tmp,
+			    local->tx_headroom + tx->sdata->encrypt_headroom);
+
 		/* copy control information */
 		memcpy(tmp->cb, skb->cb, sizeof(tmp->cb));
 
@@ -852,7 +891,7 @@
 	}
 
 	/* adjust first fragment's length */
-	skb->len = hdrlen + per_fragm;
+	skb_trim(skb, hdrlen + per_fragm);
 	return 0;
 }
 
@@ -1258,6 +1297,10 @@
 
 	switch (sdata->vif.type) {
 	case NL80211_IFTYPE_MONITOR:
+		if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) {
+			vif = &sdata->vif;
+			break;
+		}
 		sdata = rcu_dereference(local->monitor_sdata);
 		if (sdata) {
 			vif = &sdata->vif;
@@ -1282,7 +1325,6 @@
 				    txpending);
 
 	ieee80211_tpt_led_trig_tx(local, fc, led_len);
-	ieee80211_led_tx(local, 1);
 
 	WARN_ON_ONCE(!skb_queue_empty(skbs));
 
@@ -1345,6 +1387,35 @@
 	return 0;
 }
 
+bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif, struct sk_buff *skb,
+			      int band, struct ieee80211_sta **sta)
+{
+	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_tx_data tx;
+
+	if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP)
+		return false;
+
+	info->band = band;
+	info->control.vif = vif;
+	info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)];
+
+	if (invoke_tx_handlers(&tx))
+		return false;
+
+	if (sta) {
+		if (tx.sta)
+			*sta = &tx.sta->sta;
+		else
+			*sta = NULL;
+	}
+
+	return true;
+}
+EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
+
 /*
  * Returns false if the frame couldn't be transmitted but was queued instead.
  */
@@ -1434,7 +1505,7 @@
 
 	headroom = local->tx_headroom;
 	if (may_encrypt)
-		headroom += IEEE80211_ENCRYPT_HEADROOM;
+		headroom += sdata->encrypt_headroom;
 	headroom -= skb_headroom(skb);
 	headroom = max_t(int, 0, headroom);
 
@@ -1673,8 +1744,7 @@
 	 * radar detection by itself. We can do that later by adding a
 	 * monitor flag interfaces used for AP support.
 	 */
-	if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR |
-			    IEEE80211_CHAN_PASSIVE_SCAN)))
+	if ((chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)))
 		goto fail_rcu;
 
 	ieee80211_xmit(sdata, skb, chan->band);
@@ -1960,7 +2030,7 @@
 	 * EAPOL frames from the local station.
 	 */
 	if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
-		     !is_multicast_ether_addr(hdr.addr1) && !authorized &&
+		     !multicast && !authorized &&
 		     (cpu_to_be16(ethertype) != sdata->control_port_protocol ||
 		      !ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@@ -2058,7 +2128,7 @@
 	 */
 
 	if (head_need > 0 || skb_cloned(skb)) {
-		head_need += IEEE80211_ENCRYPT_HEADROOM;
+		head_need += sdata->encrypt_headroom;
 		head_need += local->tx_headroom;
 		head_need = max_t(int, 0, head_need);
 		if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
@@ -2321,6 +2391,117 @@
 	return 0;
 }
 
+static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
+				 struct beacon_data *beacon)
+{
+	struct probe_resp *resp;
+	int counter_offset_beacon = sdata->csa_counter_offset_beacon;
+	int counter_offset_presp = sdata->csa_counter_offset_presp;
+	u8 *beacon_data;
+	size_t beacon_data_len;
+
+	switch (sdata->vif.type) {
+	case NL80211_IFTYPE_AP:
+		beacon_data = beacon->tail;
+		beacon_data_len = beacon->tail_len;
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		beacon_data = beacon->head;
+		beacon_data_len = beacon->head_len;
+		break;
+	case NL80211_IFTYPE_MESH_POINT:
+		beacon_data = beacon->head;
+		beacon_data_len = beacon->head_len;
+		break;
+	default:
+		return;
+	}
+	if (WARN_ON(counter_offset_beacon >= beacon_data_len))
+		return;
+
+	if (beacon_data[counter_offset_beacon] == 1) {
+		/* Warn if the driver did not check for/react to csa
+		 * completeness. A beacon with CSA counter set to 0 should
+		 * never occur, because a counter of 1 means switch just before
+		 * the next beacon. Multi-interface CSA may need to wait for
+		 * other interfaces to complete their counter so don't warn
+		 * unless driver actually didn't notify us. */
+		WARN_ON(!sdata->csa_complete);
+		return;
+	}
+
+	beacon_data[counter_offset_beacon]--;
+
+	if (sdata->vif.type == NL80211_IFTYPE_AP && counter_offset_presp) {
+		rcu_read_lock();
+		resp = rcu_dereference(sdata->u.ap.probe_resp);
+
+		/* if nl80211 accepted the offset, this should not happen. */
+		if (WARN_ON(!resp)) {
+			rcu_read_unlock();
+			return;
+		}
+		resp->data[counter_offset_presp]--;
+		rcu_read_unlock();
+	}
+}
+
+bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
+{
+	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+	struct beacon_data *beacon = NULL;
+	u8 *beacon_data;
+	size_t beacon_data_len;
+	int counter_beacon = sdata->csa_counter_offset_beacon;
+	int ret = false;
+
+	if (!ieee80211_sdata_running(sdata))
+		return false;
+
+	rcu_read_lock();
+	if (vif->type == NL80211_IFTYPE_AP) {
+		struct ieee80211_if_ap *ap = &sdata->u.ap;
+
+		beacon = rcu_dereference(ap->beacon);
+		if (WARN_ON(!beacon || !beacon->tail))
+			goto out;
+		beacon_data = beacon->tail;
+		beacon_data_len = beacon->tail_len;
+	} else if (vif->type == NL80211_IFTYPE_ADHOC) {
+		struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+		beacon = rcu_dereference(ifibss->presp);
+		if (!beacon)
+			goto out;
+
+		beacon_data = beacon->head;
+		beacon_data_len = beacon->head_len;
+	} else if (vif->type == NL80211_IFTYPE_MESH_POINT) {
+		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+		beacon = rcu_dereference(ifmsh->beacon);
+		if (!beacon)
+			goto out;
+
+		beacon_data = beacon->head;
+		beacon_data_len = beacon->head_len;
+	} else {
+		WARN_ON(1);
+		goto out;
+	}
+
+	if (WARN_ON(counter_beacon > beacon_data_len))
+		goto out;
+
+	if (beacon_data[counter_beacon] == 1)
+		ret = true;
+ out:
+	rcu_read_unlock();
+
+	return ret;
+}
+EXPORT_SYMBOL(ieee80211_csa_is_complete);
+
 struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
 					 struct ieee80211_vif *vif,
 					 u16 *tim_offset, u16 *tim_length)
@@ -2351,6 +2532,9 @@
 		struct beacon_data *beacon = rcu_dereference(ap->beacon);
 
 		if (beacon) {
+			if (sdata->vif.csa_active)
+				ieee80211_update_csa(sdata, beacon);
+
 			/*
 			 * headroom, head length,
 			 * tail length and maximum TIM length
@@ -2385,6 +2569,10 @@
 		if (!presp)
 			goto out;
 
+		if (sdata->vif.csa_active)
+			ieee80211_update_csa(sdata, presp);
+
+
 		skb = dev_alloc_skb(local->tx_headroom + presp->head_len);
 		if (!skb)
 			goto out;
@@ -2402,9 +2590,11 @@
 		if (!bcn)
 			goto out;
 
+		if (sdata->vif.csa_active)
+			ieee80211_update_csa(sdata, bcn);
+
 		if (ifmsh->sync_ops)
-			ifmsh->sync_ops->adjust_tbtt(
-						sdata);
+			ifmsh->sync_ops->adjust_tbtt(sdata, bcn);
 
 		skb = dev_alloc_skb(local->tx_headroom +
 				    bcn->head_len +
@@ -2688,7 +2878,7 @@
 				cpu_to_le16(IEEE80211_FCTL_MOREDATA);
 		}
 
-		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+		if (sdata->vif.type == NL80211_IFTYPE_AP)
 			sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
 		if (!ieee80211_tx_prepare(sdata, &tx, skb))
 			break;
diff -ruw linux-3.11.10/net/mac80211/util.c linux-3.11.10-fbx/net/mac80211/util.c
--- linux-3.11.10/net/mac80211/util.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/util.c	2015-09-29 11:15:09.077757091 +0200
@@ -107,7 +107,8 @@
 }
 
 int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
-			     int rate, int erp, int short_preamble)
+			     int rate, int erp, int short_preamble,
+			     int shift)
 {
 	int dur;
 
@@ -118,6 +119,9 @@
 	 *
 	 * rate is in 100 kbps, so divident is multiplied by 10 in the
 	 * DIV_ROUND_UP() operations.
+	 *
+	 * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and
+	 * is assumed to be 0 otherwise.
 	 */
 
 	if (band == IEEE80211_BAND_5GHZ || erp) {
@@ -130,13 +134,23 @@
 		 * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
 		 *
 		 * T_SYM = 4 usec
-		 * 802.11a - 17.5.2: aSIFSTime = 16 usec
+		 * 802.11a - 18.5.2: aSIFSTime = 16 usec
 		 * 802.11g - 19.8.4: aSIFSTime = 10 usec +
 		 *	signal ext = 6 usec
 		 */
 		dur = 16; /* SIFS + signal ext */
-		dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */
-		dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */
+		dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */
+		dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */
+
+		/* IEEE 802.11-2012 18.3.2.4: all values above are:
+		 *  * times 4 for 5 MHz
+		 *  * times 2 for 10 MHz
+		 */
+		dur *= 1 << shift;
+
+		/* rates should already consider the channel bandwidth,
+		 * don't apply divisor again.
+		 */
 		dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
 					4 * rate); /* T_SYM x N_SYM */
 	} else {
@@ -168,7 +182,7 @@
 {
 	struct ieee80211_sub_if_data *sdata;
 	u16 dur;
-	int erp;
+	int erp, shift = 0;
 	bool short_preamble = false;
 
 	erp = 0;
@@ -177,10 +191,11 @@
 		short_preamble = sdata->vif.bss_conf.use_short_preamble;
 		if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
 			erp = rate->flags & IEEE80211_RATE_ERP_G;
+		shift = ieee80211_vif_get_shift(vif);
 	}
 
 	dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp,
-				       short_preamble);
+				       short_preamble, shift);
 
 	return cpu_to_le16(dur);
 }
@@ -194,7 +209,7 @@
 	struct ieee80211_rate *rate;
 	struct ieee80211_sub_if_data *sdata;
 	bool short_preamble;
-	int erp;
+	int erp, shift = 0, bitrate;
 	u16 dur;
 	struct ieee80211_supported_band *sband;
 
@@ -210,17 +225,20 @@
 		short_preamble = sdata->vif.bss_conf.use_short_preamble;
 		if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
 			erp = rate->flags & IEEE80211_RATE_ERP_G;
+		shift = ieee80211_vif_get_shift(vif);
 	}
 
+	bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
+
 	/* CTS duration */
-	dur = ieee80211_frame_duration(sband->band, 10, rate->bitrate,
-				       erp, short_preamble);
+	dur = ieee80211_frame_duration(sband->band, 10, bitrate,
+				       erp, short_preamble, shift);
 	/* Data frame duration */
-	dur += ieee80211_frame_duration(sband->band, frame_len, rate->bitrate,
-					erp, short_preamble);
+	dur += ieee80211_frame_duration(sband->band, frame_len, bitrate,
+					erp, short_preamble, shift);
 	/* ACK duration */
-	dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate,
-					erp, short_preamble);
+	dur += ieee80211_frame_duration(sband->band, 10, bitrate,
+					erp, short_preamble, shift);
 
 	return cpu_to_le16(dur);
 }
@@ -235,7 +253,7 @@
 	struct ieee80211_rate *rate;
 	struct ieee80211_sub_if_data *sdata;
 	bool short_preamble;
-	int erp;
+	int erp, shift = 0, bitrate;
 	u16 dur;
 	struct ieee80211_supported_band *sband;
 
@@ -250,15 +268,18 @@
 		short_preamble = sdata->vif.bss_conf.use_short_preamble;
 		if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
 			erp = rate->flags & IEEE80211_RATE_ERP_G;
+		shift = ieee80211_vif_get_shift(vif);
 	}
 
+	bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
+
 	/* Data frame duration */
-	dur = ieee80211_frame_duration(sband->band, frame_len, rate->bitrate,
-				       erp, short_preamble);
+	dur = ieee80211_frame_duration(sband->band, frame_len, bitrate,
+				       erp, short_preamble, shift);
 	if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {
 		/* ACK duration */
-		dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate,
-						erp, short_preamble);
+		dur += ieee80211_frame_duration(sband->band, 10, bitrate,
+						erp, short_preamble, shift);
 	}
 
 	return cpu_to_le16(dur);
@@ -279,9 +300,6 @@
 		if (!sdata->dev)
 			continue;
 
-		if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
-			continue;
-
 		if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&
 		    local->queue_stop_reasons[sdata->vif.cab_queue] != 0)
 			continue;
@@ -546,18 +564,15 @@
 					IEEE80211_QUEUE_STOP_REASON_FLUSH);
 }
 
-void ieee80211_iterate_active_interfaces(
-	struct ieee80211_hw *hw, u32 iter_flags,
+static void __iterate_active_interfaces(struct ieee80211_local *local,
+					u32 iter_flags,
 	void (*iterator)(void *data, u8 *mac,
 			 struct ieee80211_vif *vif),
 	void *data)
 {
-	struct ieee80211_local *local = hw_to_local(hw);
 	struct ieee80211_sub_if_data *sdata;
 
-	mutex_lock(&local->iflist_mtx);
-
-	list_for_each_entry(sdata, &local->interfaces, list) {
+	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 		switch (sdata->vif.type) {
 		case NL80211_IFTYPE_MONITOR:
 			if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
@@ -576,13 +591,25 @@
 				 &sdata->vif);
 	}
 
-	sdata = rcu_dereference_protected(local->monitor_sdata,
-					  lockdep_is_held(&local->iflist_mtx));
+	sdata = rcu_dereference_check(local->monitor_sdata,
+				      lockdep_is_held(&local->iflist_mtx) ||
+				      lockdep_rtnl_is_held());
 	if (sdata &&
 	    (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
 	     sdata->flags & IEEE80211_SDATA_IN_DRIVER))
 		iterator(data, sdata->vif.addr, &sdata->vif);
+}
+
+void ieee80211_iterate_active_interfaces(
+	struct ieee80211_hw *hw, u32 iter_flags,
+	void (*iterator)(void *data, u8 *mac,
+			 struct ieee80211_vif *vif),
+	void *data)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
 
+	mutex_lock(&local->iflist_mtx);
+	__iterate_active_interfaces(local, iter_flags, iterator, data);
 	mutex_unlock(&local->iflist_mtx);
 }
 EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
@@ -594,38 +621,26 @@
 	void *data)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
-	struct ieee80211_sub_if_data *sdata;
 
 	rcu_read_lock();
-
-	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-		switch (sdata->vif.type) {
-		case NL80211_IFTYPE_MONITOR:
-			if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
-				continue;
-			break;
-		case NL80211_IFTYPE_AP_VLAN:
-			continue;
-		default:
-			break;
-		}
-		if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
-		    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
-			continue;
-		if (ieee80211_sdata_running(sdata))
-			iterator(data, sdata->vif.addr,
-				 &sdata->vif);
+	__iterate_active_interfaces(local, iter_flags, iterator, data);
+	rcu_read_unlock();
 	}
+EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
 
-	sdata = rcu_dereference(local->monitor_sdata);
-	if (sdata &&
-	    (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
-	     sdata->flags & IEEE80211_SDATA_IN_DRIVER))
-		iterator(data, sdata->vif.addr, &sdata->vif);
+void ieee80211_iterate_active_interfaces_rtnl(
+	struct ieee80211_hw *hw, u32 iter_flags,
+	void (*iterator)(void *data, u8 *mac,
+			 struct ieee80211_vif *vif),
+	void *data)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
 
-	rcu_read_unlock();
+	ASSERT_RTNL();
+
+	__iterate_active_interfaces(local, iter_flags, iterator, data);
 }
-EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
+EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
 
 /*
  * Nothing should have been stuffed into the workqueue during
@@ -725,6 +740,7 @@
 		case WLAN_EID_TIMEOUT_INTERVAL:
 		case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
 		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
+		case WLAN_EID_CHAN_SWITCH_PARAM:
 		/*
 		 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
 		 * that if the content gets bigger it might be needed more than once
@@ -890,6 +906,14 @@
 			}
 			elems->sec_chan_offs = (void *)pos;
 			break;
+		case WLAN_EID_CHAN_SWITCH_PARAM:
+			if (elen !=
+			    sizeof(*elems->mesh_chansw_params_ie)) {
+				elem_parse_failed = true;
+				break;
+			}
+			elems->mesh_chansw_params_ie = (void *)pos;
+			break;
 		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
 			if (!action ||
 			    elen != sizeof(*elems->wide_bw_chansw_ie)) {
@@ -986,7 +1010,6 @@
 	 */
 	enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
 
-	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
 		/* Set defaults according to 802.11-2007 Table 7-37 */
 		aCWmax = 1023;
 		if (use_11b)
@@ -994,6 +1017,14 @@
 		else
 			aCWmin = 15;
 
+	/* Confiure old 802.11b/g medium access rules. */
+	qparam.cw_max = aCWmax;
+	qparam.cw_min = aCWmin;
+	qparam.txop = 0;
+	qparam.aifs = 2;
+
+	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+		/* Update if QoS is enabled. */
 		if (enable_qos) {
 			switch (ac) {
 			case IEEE80211_AC_BK:
@@ -1029,12 +1060,6 @@
 				qparam.aifs = 2;
 				break;
 			}
-		} else {
-			/* Confiure old 802.11b/g medium access rules. */
-			qparam.cw_max = aCWmax;
-			qparam.cw_min = aCWmin;
-			qparam.txop = 0;
-			qparam.aifs = 2;
 		}
 
 		qparam.uapsd = false;
@@ -1052,32 +1077,6 @@
 	}
 }
 
-void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
-				  const size_t supp_rates_len,
-				  const u8 *supp_rates)
-{
-	struct ieee80211_chanctx_conf *chanctx_conf;
-	int i, have_higher_than_11mbit = 0;
-
-	/* cf. IEEE 802.11 9.2.12 */
-	for (i = 0; i < supp_rates_len; i++)
-		if ((supp_rates[i] & 0x7f) * 5 > 110)
-			have_higher_than_11mbit = 1;
-
-	rcu_read_lock();
-	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-
-	if (chanctx_conf &&
-	    chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ &&
-	    have_higher_than_11mbit)
-		sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
-	else
-		sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
-	rcu_read_unlock();
-
-	ieee80211_set_wmm_default(sdata, true);
-}
-
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
 			 u16 transaction, u16 auth_alg, u16 status,
 			 const u8 *extra, size_t extra_len, const u8 *da,
@@ -1089,8 +1088,8 @@
 	struct ieee80211_mgmt *mgmt;
 	int err;
 
-	skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-			    sizeof(*mgmt) + 6 + extra_len);
+	/* 24 + 6 = header + auth_algo + auth_transaction + status_code */
+	skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 6 + extra_len);
 	if (!skb)
 		return;
 
@@ -1162,7 +1161,7 @@
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
 			     size_t buffer_len, const u8 *ie, size_t ie_len,
 			     enum ieee80211_band band, u32 rate_mask,
-			     u8 channel)
+			     struct cfg80211_chan_def *chandef)
 {
 	struct ieee80211_supported_band *sband;
 	u8 *pos = buffer, *end = buffer + buffer_len;
@@ -1171,16 +1170,26 @@
 	u8 rates[32];
 	int num_rates;
 	int ext_rates_len;
+	int shift;
+	u32 rate_flags;
 
 	sband = local->hw.wiphy->bands[band];
 	if (WARN_ON_ONCE(!sband))
 		return 0;
 
+	rate_flags = ieee80211_chandef_rate_flags(chandef);
+	shift = ieee80211_chandef_get_shift(chandef);
+
 	num_rates = 0;
 	for (i = 0; i < sband->n_bitrates; i++) {
 		if ((BIT(i) & rate_mask) == 0)
 			continue; /* skip rate */
-		rates[num_rates++] = (u8) (sband->bitrates[i].bitrate / 5);
+		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+			continue;
+
+		rates[num_rates++] =
+			(u8) DIV_ROUND_UP(sband->bitrates[i].bitrate,
+					  (1 << shift) * 5);
 	}
 
 	supp_rates_len = min_t(int, num_rates, 8);
@@ -1220,12 +1229,13 @@
 		pos += ext_rates_len;
 	}
 
-	if (channel && sband->band == IEEE80211_BAND_2GHZ) {
+	if (chandef->chan && sband->band == IEEE80211_BAND_2GHZ) {
 		if (end - pos < 3)
 			goto out_err;
 		*pos++ = WLAN_EID_DS_PARAMS;
 		*pos++ = 1;
-		*pos++ = channel;
+		*pos++ = ieee80211_frequency_to_channel(
+				chandef->chan->center_freq);
 	}
 
 	/* insert custom IEs that go before HT */
@@ -1290,9 +1300,9 @@
 					  bool directed)
 {
 	struct ieee80211_local *local = sdata->local;
+	struct cfg80211_chan_def chandef;
 	struct sk_buff *skb;
 	struct ieee80211_mgmt *mgmt;
-	u8 chan_no;
 	int ies_len;
 
 	/*
@@ -1300,10 +1310,11 @@
 	 * in order to maximize the chance that we get a response.  Some
 	 * badly-behaved APs don't respond when this parameter is included.
 	 */
+	chandef.width = sdata->vif.bss_conf.chandef.width;
 	if (directed)
-		chan_no = 0;
+		chandef.chan = NULL;
 	else
-		chan_no = ieee80211_frequency_to_channel(chan->center_freq);
+		chandef.chan = chan;
 
 	skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
 				     ssid, ssid_len, 100 + ie_len);
@@ -1313,7 +1324,7 @@
 	ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
 					   skb_tailroom(skb),
 					   ie, ie_len, chan->band,
-					   ratemask, chan_no);
+					   ratemask, &chandef);
 	skb_put(skb, ies_len);
 
 	if (dst) {
@@ -1347,16 +1358,19 @@
 	}
 }
 
-u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
+u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
 			    struct ieee802_11_elems *elems,
 			    enum ieee80211_band band, u32 *basic_rates)
 {
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_rate *bitrates;
 	size_t num_rates;
-	u32 supp_rates;
-	int i, j;
-	sband = local->hw.wiphy->bands[band];
+	u32 supp_rates, rate_flags;
+	int i, j, shift;
+	sband = sdata->local->hw.wiphy->bands[band];
+
+	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+	shift = ieee80211_vif_get_shift(&sdata->vif);
 
 	if (WARN_ON(!sband))
 		return 1;
@@ -1381,7 +1395,15 @@
 			continue;
 
 		for (j = 0; j < num_rates; j++) {
-			if (bitrates[j].bitrate == own_rate) {
+			int brate;
+			if ((rate_flags & sband->bitrates[j].flags)
+			    != rate_flags)
+				continue;
+
+			brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
+					     1 << shift);
+
+			if (brate == own_rate) {
 				supp_rates |= BIT(j);
 				if (basic_rates && is_basic)
 					*basic_rates |= BIT(j);
@@ -1435,8 +1457,8 @@
 		local->resuming = true;
 
 	if (local->wowlan) {
-		local->wowlan = false;
 		res = drv_resume(local);
+		local->wowlan = false;
 		if (res < 0) {
 			local->resuming = false;
 			return res;
@@ -1782,6 +1804,26 @@
 	mutex_unlock(&local->chanctx_mtx);
 }
 
+void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_chanctx_conf *chanctx_conf;
+	struct ieee80211_chanctx *chanctx;
+
+	mutex_lock(&local->chanctx_mtx);
+
+	chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+					lockdep_is_held(&local->chanctx_mtx));
+
+	if (WARN_ON_ONCE(!chanctx_conf))
+		goto unlock;
+
+	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+	ieee80211_recalc_chanctx_min_def(local, chanctx);
+ unlock:
+	mutex_unlock(&local->chanctx_mtx);
+}
+
 static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
 {
 	int i;
@@ -2004,18 +2046,56 @@
 	cfg80211_chandef_create(chandef, control_chan, channel_type);
 }
 
+int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
+			     const struct ieee80211_supported_band *sband,
+			     const u8 *srates, int srates_len, u32 *rates)
+{
+	u32 rate_flags = ieee80211_chandef_rate_flags(chandef);
+	int shift = ieee80211_chandef_get_shift(chandef);
+	struct ieee80211_rate *br;
+	int brate, rate, i, j, count = 0;
+
+	*rates = 0;
+
+	for (i = 0; i < srates_len; i++) {
+		rate = srates[i] & 0x7f;
+
+		for (j = 0; j < sband->n_bitrates; j++) {
+			br = &sband->bitrates[j];
+			if ((rate_flags & br->flags) != rate_flags)
+				continue;
+
+			brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
+			if (brate == rate) {
+				*rates |= BIT(j);
+				count++;
+				break;
+			}
+		}
+	}
+	return count;
+}
+
 int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
 			    struct sk_buff *skb, bool need_basic,
 			    enum ieee80211_band band)
 {
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_supported_band *sband;
-	int rate;
+	int rate, shift;
 	u8 i, rates, *pos;
 	u32 basic_rates = sdata->vif.bss_conf.basic_rates;
+	u32 rate_flags;
 
+	shift = ieee80211_vif_get_shift(&sdata->vif);
+	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
 	sband = local->hw.wiphy->bands[band];
-	rates = sband->n_bitrates;
+	rates = 0;
+	for (i = 0; i < sband->n_bitrates; i++) {
+		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+			continue;
+		rates++;
+	}
 	if (rates > 8)
 		rates = 8;
 
@@ -2027,10 +2107,15 @@
 	*pos++ = rates;
 	for (i = 0; i < rates; i++) {
 		u8 basic = 0;
+		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+			continue;
+
 		if (need_basic && basic_rates & BIT(i))
 			basic = 0x80;
 		rate = sband->bitrates[i].bitrate;
-		*pos++ = basic | (u8) (rate / 5);
+		rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+				    5 * (1 << shift));
+		*pos++ = basic | (u8) rate;
 	}
 
 	return 0;
@@ -2042,12 +2127,22 @@
 {
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_supported_band *sband;
-	int rate;
+	int rate, shift;
 	u8 i, exrates, *pos;
 	u32 basic_rates = sdata->vif.bss_conf.basic_rates;
+	u32 rate_flags;
+
+	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+	shift = ieee80211_vif_get_shift(&sdata->vif);
 
 	sband = local->hw.wiphy->bands[band];
-	exrates = sband->n_bitrates;
+	exrates = 0;
+	for (i = 0; i < sband->n_bitrates; i++) {
+		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+			continue;
+		exrates++;
+	}
+
 	if (exrates > 8)
 		exrates -= 8;
 	else
@@ -2062,10 +2157,14 @@
 		*pos++ = exrates;
 		for (i = 8; i < sband->n_bitrates; i++) {
 			u8 basic = 0;
+			if ((rate_flags & sband->bitrates[i].flags)
+			    != rate_flags)
+				continue;
 			if (need_basic && basic_rates & BIT(i))
 				basic = 0x80;
-			rate = sband->bitrates[i].bitrate;
-			*pos++ = basic | (u8) (rate / 5);
+			rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+					    5 * (1 << shift));
+			*pos++ = basic | (u8) rate;
 		}
 	}
 	return 0;
@@ -2149,9 +2248,17 @@
 			ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
 	} else {
 		struct ieee80211_supported_band *sband;
+		int shift = 0;
+		int bitrate;
+
+		if (status->flag & RX_FLAG_10MHZ)
+			shift = 1;
+		if (status->flag & RX_FLAG_5MHZ)
+			shift = 2;
 
 		sband = local->hw.wiphy->bands[status->band];
-		ri.legacy = sband->bitrates[status->rate_idx].bitrate;
+		bitrate = sband->bitrates[status->rate_idx].bitrate;
+		ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift));
 	}
 
 	rate = cfg80211_calculate_bitrate(&ri);
@@ -2172,37 +2279,44 @@
 void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
 {
 	struct ieee80211_sub_if_data *sdata;
+	struct cfg80211_chan_def chandef;
 
+	mutex_lock(&local->mtx);
 	mutex_lock(&local->iflist_mtx);
 	list_for_each_entry(sdata, &local->interfaces, list) {
-		cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
+		/* it might be waiting for the local->mtx, but then
+		 * by the time it gets it, sdata->wdev.cac_started
+		 * will no longer be true
+		 */
+		cancel_delayed_work(&sdata->dfs_cac_timer_work);
 
 		if (sdata->wdev.cac_started) {
+			chandef = sdata->vif.bss_conf.chandef;
 			ieee80211_vif_release_channel(sdata);
 			cfg80211_cac_event(sdata->dev,
+					   &chandef,
 					   NL80211_RADAR_CAC_ABORTED,
 					   GFP_KERNEL);
 		}
 	}
 	mutex_unlock(&local->iflist_mtx);
+	mutex_unlock(&local->mtx);
 }
 
 void ieee80211_dfs_radar_detected_work(struct work_struct *work)
 {
 	struct ieee80211_local *local =
 		container_of(work, struct ieee80211_local, radar_detected_work);
-	struct cfg80211_chan_def chandef;
+	struct cfg80211_chan_def chandef = local->hw.conf.chandef;
 
 	ieee80211_dfs_cac_cancel(local);
 
 	if (local->use_chanctx)
 		/* currently not handled */
 		WARN_ON(1);
-	else {
-		chandef = local->hw.conf.chandef;
+	else
 		cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
 	}
-}
 
 void ieee80211_radar_detected(struct ieee80211_hw *hw)
 {
@@ -2213,3 +2327,256 @@
 	ieee80211_queue_work(hw, &local->radar_detected_work);
 }
 EXPORT_SYMBOL(ieee80211_radar_detected);
+
+u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
+{
+	u32 ret;
+	int tmp;
+
+	switch (c->width) {
+	case NL80211_CHAN_WIDTH_20:
+		c->width = NL80211_CHAN_WIDTH_20_NOHT;
+		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+		break;
+	case NL80211_CHAN_WIDTH_40:
+		c->width = NL80211_CHAN_WIDTH_20;
+		c->center_freq1 = c->chan->center_freq;
+		ret = IEEE80211_STA_DISABLE_40MHZ |
+		      IEEE80211_STA_DISABLE_VHT;
+		break;
+	case NL80211_CHAN_WIDTH_80:
+		tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
+		/* n_P40 */
+		tmp /= 2;
+		/* freq_P40 */
+		c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
+		c->width = NL80211_CHAN_WIDTH_40;
+		ret = IEEE80211_STA_DISABLE_VHT;
+		break;
+	case NL80211_CHAN_WIDTH_80P80:
+		c->center_freq2 = 0;
+		c->width = NL80211_CHAN_WIDTH_80;
+		ret = IEEE80211_STA_DISABLE_80P80MHZ |
+		      IEEE80211_STA_DISABLE_160MHZ;
+		break;
+	case NL80211_CHAN_WIDTH_160:
+		/* n_P20 */
+		tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
+		/* n_P80 */
+		tmp /= 4;
+		c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
+		c->width = NL80211_CHAN_WIDTH_80;
+		ret = IEEE80211_STA_DISABLE_80P80MHZ |
+		      IEEE80211_STA_DISABLE_160MHZ;
+		break;
+	default:
+	case NL80211_CHAN_WIDTH_20_NOHT:
+		WARN_ON_ONCE(1);
+		c->width = NL80211_CHAN_WIDTH_20_NOHT;
+		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+		break;
+	case NL80211_CHAN_WIDTH_5:
+	case NL80211_CHAN_WIDTH_10:
+		WARN_ON_ONCE(1);
+		/* keep c->width */
+		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+		break;
+	}
+
+	WARN_ON_ONCE(!cfg80211_chandef_valid(c));
+
+	return ret;
+}
+
+/*
+ * Returns true if smps_mode_new is strictly more restrictive than
+ * smps_mode_old.
+ */
+bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
+				   enum ieee80211_smps_mode smps_mode_new)
+{
+	if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC ||
+			 smps_mode_new == IEEE80211_SMPS_AUTOMATIC))
+		return false;
+
+	switch (smps_mode_old) {
+	case IEEE80211_SMPS_STATIC:
+		return false;
+	case IEEE80211_SMPS_DYNAMIC:
+		return smps_mode_new == IEEE80211_SMPS_STATIC;
+	case IEEE80211_SMPS_OFF:
+		return smps_mode_new != IEEE80211_SMPS_OFF;
+	default:
+		WARN_ON(1);
+	}
+
+	return false;
+}
+
+int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
+			      struct cfg80211_csa_settings *csa_settings)
+{
+	struct sk_buff *skb;
+	struct ieee80211_mgmt *mgmt;
+	struct ieee80211_local *local = sdata->local;
+	int freq;
+	int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
+			       sizeof(mgmt->u.action.u.chan_switch);
+	u8 *pos;
+
+	if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+		return -EOPNOTSUPP;
+
+	skb = dev_alloc_skb(local->tx_headroom + hdr_len +
+			    5 + /* channel switch announcement element */
+			    3 + /* secondary channel offset element */
+			    8); /* mesh channel switch parameters element */
+	if (!skb)
+		return -ENOMEM;
+
+	skb_reserve(skb, local->tx_headroom);
+	mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
+	memset(mgmt, 0, hdr_len);
+	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+					  IEEE80211_STYPE_ACTION);
+
+	eth_broadcast_addr(mgmt->da);
+	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+	if (ieee80211_vif_is_mesh(&sdata->vif)) {
+		memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
+	} else {
+		struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+		memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
+	}
+	mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
+	mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
+	pos = skb_put(skb, 5);
+	*pos++ = WLAN_EID_CHANNEL_SWITCH;			/* EID */
+	*pos++ = 3;						/* IE length */
+	*pos++ = csa_settings->block_tx ? 1 : 0;		/* CSA mode */
+	freq = csa_settings->chandef.chan->center_freq;
+	*pos++ = ieee80211_frequency_to_channel(freq);		/* channel */
+	*pos++ = csa_settings->count;				/* count */
+
+	if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
+		enum nl80211_channel_type ch_type;
+
+		skb_put(skb, 3);
+		*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;	/* EID */
+		*pos++ = 1;					/* IE length */
+		ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
+		if (ch_type == NL80211_CHAN_HT40PLUS)
+			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+		else
+			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+	}
+
+	if (ieee80211_vif_is_mesh(&sdata->vif)) {
+		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+		__le16 pre_value;
+
+		skb_put(skb, 8);
+		*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;		/* EID */
+		*pos++ = 6;					/* IE length */
+		*pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL;	/* Mesh TTL */
+		*pos = 0x00;	/* Mesh Flag: Tx Restrict, Initiator, Reason */
+		*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
+		*pos++ |= csa_settings->block_tx ?
+			  WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
+		put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */
+		pos += 2;
+		pre_value = cpu_to_le16(ifmsh->pre_value);
+		memcpy(pos, &pre_value, 2);		/* Precedence Value */
+		pos += 2;
+	}
+
+	ieee80211_tx_skb(sdata, skb);
+	return 0;
+}
+
+bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs)
+{
+	return !(cs == NULL || cs->cipher == 0 ||
+		 cs->hdr_len < cs->pn_len + cs->pn_off ||
+		 cs->hdr_len <= cs->key_idx_off ||
+		 cs->key_idx_shift > 7 ||
+		 cs->key_idx_mask == 0);
+}
+
+bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n)
+{
+	int i;
+
+	/* Ensure we have enough iftype bitmap space for all iftype values */
+	WARN_ON((NUM_NL80211_IFTYPES / 8 + 1) > sizeof(cs[0].iftype));
+
+	for (i = 0; i < n; i++)
+		if (!ieee80211_cs_valid(&cs[i]))
+			return false;
+
+	return true;
+}
+
+const struct ieee80211_cipher_scheme *
+ieee80211_cs_get(struct ieee80211_local *local, u32 cipher,
+		 enum nl80211_iftype iftype)
+{
+	const struct ieee80211_cipher_scheme *l = local->hw.cipher_schemes;
+	int n = local->hw.n_cipher_schemes;
+	int i;
+	const struct ieee80211_cipher_scheme *cs = NULL;
+
+	for (i = 0; i < n; i++) {
+		if (l[i].cipher == cipher) {
+			cs = &l[i];
+			break;
+		}
+	}
+
+	if (!cs || !(cs->iftype & BIT(iftype)))
+		return NULL;
+
+	return cs;
+}
+
+int ieee80211_cs_headroom(struct ieee80211_local *local,
+			  struct cfg80211_crypto_settings *crypto,
+			  enum nl80211_iftype iftype)
+{
+	const struct ieee80211_cipher_scheme *cs;
+	int headroom = IEEE80211_ENCRYPT_HEADROOM;
+	int i;
+
+	for (i = 0; i < crypto->n_ciphers_pairwise; i++) {
+		cs = ieee80211_cs_get(local, crypto->ciphers_pairwise[i],
+				      iftype);
+
+		if (cs && headroom < cs->hdr_len)
+			headroom = cs->hdr_len;
+	}
+
+	cs = ieee80211_cs_get(local, crypto->cipher_group, iftype);
+	if (cs && headroom < cs->hdr_len)
+		headroom = cs->hdr_len;
+
+	return headroom;
+}
+
+bool ieee80211_is_csa_active(struct ieee80211_local *local)
+{
+	struct ieee80211_sub_if_data *sdata;
+
+	lockdep_assert_held(&local->mtx);
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+		if (sdata->vif.csa_active) {
+			rcu_read_unlock();
+			return true;
+		}
+	}
+	rcu_read_unlock();
+
+	return false;
+}
diff -ruw linux-3.11.10/net/mac80211/vht.c linux-3.11.10-fbx/net/mac80211/vht.c
--- linux-3.11.10/net/mac80211/vht.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/vht.c	2015-09-29 11:15:09.077757091 +0200
@@ -185,13 +185,13 @@
 	if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) {
 		vht_cap->cap |= cap_info &
 				(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
-				 IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX |
 				 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX);
 	}
 
 	if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
 		vht_cap->cap |= cap_info &
-				IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+				(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+				 IEEE80211_VHT_CAP_BEAMFORMEE_STS_MAX);
 
 	if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
 		vht_cap->cap |= cap_info &
@@ -350,7 +350,7 @@
 	sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss);
 }
 
-void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
+u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
 				 struct sta_info *sta, u8 opmode,
 				 enum ieee80211_band band, bool nss_only)
 {
@@ -364,7 +364,7 @@
 
 	/* ignore - no support for BF yet */
 	if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)
-		return;
+		return 0;
 
 	nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
 	nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
@@ -376,7 +376,7 @@
 	}
 
 	if (nss_only)
-		goto change;
+		return changed;
 
 	switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) {
 	case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ:
@@ -399,7 +399,19 @@
 		changed |= IEEE80211_RC_BW_CHANGED;
 	}
 
- change:
-	if (changed)
+	return changed;
+}
+
+void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
+				 struct sta_info *sta, u8 opmode,
+				 enum ieee80211_band band, bool nss_only)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
+
+	u32 changed = __ieee80211_vht_handle_opmode(sdata, sta, opmode,
+						    band, nss_only);
+
+	if (changed > 0)
 		rate_control_rate_update(local, sband, sta, changed);
 }
diff -ruw linux-3.11.10/net/mac80211/wme.c linux-3.11.10-fbx/net/mac80211/wme.c
--- linux-3.11.10/net/mac80211/wme.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/wme.c	2015-09-29 11:15:09.077757091 +0200
@@ -153,6 +153,11 @@
 		return IEEE80211_AC_BE;
 	}
 
+	if (skb->protocol == sdata->control_port_protocol) {
+		skb->priority = 7;
+		return ieee80211_downgrade_queue(sdata, skb);
+	}
+
 	/* use the data classifier to determine what 802.1d tag the
 	 * data frame has */
 	skb->priority = cfg80211_classify8021d(skb);
diff -ruw linux-3.11.10/net/mac80211/wpa.c linux-3.11.10-fbx/net/mac80211/wpa.c
--- linux-3.11.10/net/mac80211/wpa.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/wpa.c	2015-09-29 11:15:09.077757091 +0200
@@ -301,22 +301,16 @@
 }
 
 
-static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
+static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
 				int encrypted)
 {
 	__le16 mask_fc;
 	int a4_included, mgmt;
 	u8 qos_tid;
-	u8 *b_0, *aad;
-	u16 data_len, len_a;
+	u16 len_a;
 	unsigned int hdrlen;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 
-	memset(scratch, 0, 6 * AES_BLOCK_SIZE);
-
-	b_0 = scratch + 3 * AES_BLOCK_SIZE;
-	aad = scratch + 4 * AES_BLOCK_SIZE;
-
 	/*
 	 * Mask FC: zero subtype b4 b5 b6 (if not mgmt)
 	 * Retry, PwrMgt, MoreData; set Protected
@@ -338,20 +332,21 @@
 	else
 		qos_tid = 0;
 
-	data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN;
-	if (encrypted)
-		data_len -= IEEE80211_CCMP_MIC_LEN;
+	/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
+	 * mode authentication are not allowed to collide, yet both are derived
+	 * from this vector b_0. We only set L := 1 here to indicate that the
+	 * data size can be represented in (L+1) bytes. The CCM layer will take
+	 * care of storing the data length in the top (L+1) bytes and setting
+	 * and clearing the other bits as is required to derive the two IVs.
+	 */
+	b_0[0] = 0x1;
 
-	/* First block, b_0 */
-	b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */
 	/* Nonce: Nonce Flags | A2 | PN
 	 * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)
 	 */
 	b_0[1] = qos_tid | (mgmt << 4);
 	memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
 	memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
-	/* l(m) */
-	put_unaligned_be16(data_len, &b_0[14]);
 
 	/* AAD (extra authenticate-only data) / masked 802.11 header
 	 * FC | A1 | A2 | A3 | SC | [A4] | [QC] */
@@ -407,7 +402,8 @@
 	u8 *pos;
 	u8 pn[6];
 	u64 pn64;
-	u8 scratch[6 * AES_BLOCK_SIZE];
+	u8 aad[2 * AES_BLOCK_SIZE];
+	u8 b_0[AES_BLOCK_SIZE];
 
 	if (info->control.hw_key &&
 	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
@@ -460,9 +456,9 @@
 		return 0;
 
 	pos += IEEE80211_CCMP_HDR_LEN;
-	ccmp_special_blocks(skb, pn, scratch, 0);
-	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len,
-				  pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN));
+	ccmp_special_blocks(skb, pn, b_0, aad, 0);
+	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
+				  skb_put(skb, IEEE80211_CCMP_MIC_LEN));
 
 	return 0;
 }
@@ -525,16 +521,16 @@
 	}
 
 	if (!(status->flag & RX_FLAG_DECRYPTED)) {
-		u8 scratch[6 * AES_BLOCK_SIZE];
+		u8 aad[2 * AES_BLOCK_SIZE];
+		u8 b_0[AES_BLOCK_SIZE];
 		/* hardware didn't decrypt/verify MIC */
-		ccmp_special_blocks(skb, pn, scratch, 1);
+		ccmp_special_blocks(skb, pn, b_0, aad, 1);
 
 		if (ieee80211_aes_ccm_decrypt(
-			    key->u.ccmp.tfm, scratch,
+			    key->u.ccmp.tfm, b_0, aad,
 			    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
 			    data_len,
-			    skb->data + skb->len - IEEE80211_CCMP_MIC_LEN,
-			    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN))
+			    skb->data + skb->len - IEEE80211_CCMP_MIC_LEN))
 			return RX_DROP_UNUSABLE;
 	}
 
@@ -549,6 +545,106 @@
 	return RX_CONTINUE;
 }
 
+static ieee80211_tx_result
+ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
+			    struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct ieee80211_key *key = tx->key;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	const struct ieee80211_cipher_scheme *cs = key->sta->cipher_scheme;
+	int hdrlen;
+	u8 *pos;
+
+	if (info->control.hw_key &&
+	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
+		/* hwaccel has no need for preallocated head room */
+		return TX_CONTINUE;
+	}
+
+	if (unlikely(skb_headroom(skb) < cs->hdr_len &&
+		     pskb_expand_head(skb, cs->hdr_len, 0, GFP_ATOMIC)))
+		return TX_DROP;
+
+	hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+	pos = skb_push(skb, cs->hdr_len);
+	memmove(pos, pos + cs->hdr_len, hdrlen);
+	skb_set_network_header(skb, skb_network_offset(skb) + cs->hdr_len);
+
+	return TX_CONTINUE;
+}
+
+static inline int ieee80211_crypto_cs_pn_compare(u8 *pn1, u8 *pn2, int len)
+{
+	int i;
+
+	/* pn is little endian */
+	for (i = len - 1; i >= 0; i--) {
+		if (pn1[i] < pn2[i])
+			return -1;
+		else if (pn1[i] > pn2[i])
+			return 1;
+	}
+
+	return 0;
+}
+
+static ieee80211_rx_result
+ieee80211_crypto_cs_decrypt(struct ieee80211_rx_data *rx)
+{
+	struct ieee80211_key *key = rx->key;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+	const struct ieee80211_cipher_scheme *cs = NULL;
+	int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
+	int data_len;
+	u8 *rx_pn;
+	u8 *skb_pn;
+	u8 qos_tid;
+
+	if (!rx->sta || !rx->sta->cipher_scheme ||
+	    !(status->flag & RX_FLAG_DECRYPTED))
+		return RX_DROP_UNUSABLE;
+
+	if (!ieee80211_is_data(hdr->frame_control))
+		return RX_CONTINUE;
+
+	cs = rx->sta->cipher_scheme;
+
+	data_len = rx->skb->len - hdrlen - cs->hdr_len;
+
+	if (data_len < 0)
+		return RX_DROP_UNUSABLE;
+
+	if (ieee80211_is_data_qos(hdr->frame_control))
+		qos_tid = *ieee80211_get_qos_ctl(hdr) &
+				IEEE80211_QOS_CTL_TID_MASK;
+	else
+		qos_tid = 0;
+
+	if (skb_linearize(rx->skb))
+		return RX_DROP_UNUSABLE;
+
+	hdr = (struct ieee80211_hdr *)rx->skb->data;
+
+	rx_pn = key->u.gen.rx_pn[qos_tid];
+	skb_pn = rx->skb->data + hdrlen + cs->pn_off;
+
+	if (ieee80211_crypto_cs_pn_compare(skb_pn, rx_pn, cs->pn_len) <= 0)
+		return RX_DROP_UNUSABLE;
+
+	memcpy(rx_pn, skb_pn, cs->pn_len);
+
+	/* remove security header and MIC */
+	if (pskb_trim(rx->skb, rx->skb->len - cs->mic_len))
+		return RX_DROP_UNUSABLE;
+
+	memmove(rx->skb->data + cs->hdr_len, rx->skb->data, hdrlen);
+	skb_pull(rx->skb, cs->hdr_len);
+
+	return RX_CONTINUE;
+}
 
 static void bip_aad(struct sk_buff *skb, u8 *aad)
 {
@@ -689,6 +785,7 @@
 {
 	struct sk_buff *skb;
 	struct ieee80211_tx_info *info = NULL;
+	ieee80211_tx_result res;
 
 	skb_queue_walk(&tx->skbs, skb) {
 		info  = IEEE80211_SKB_CB(skb);
@@ -696,9 +793,24 @@
 		/* handle hw-only algorithm */
 		if (!info->control.hw_key)
 			return TX_DROP;
+
+		if (tx->key->sta->cipher_scheme) {
+			res = ieee80211_crypto_cs_encrypt(tx, skb);
+			if (res != TX_CONTINUE)
+				return res;
+		}
 	}
 
 	ieee80211_tx_set_protected(tx);
 
 	return TX_CONTINUE;
 }
+
+ieee80211_rx_result
+ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx)
+{
+	if (rx->sta->cipher_scheme)
+		return ieee80211_crypto_cs_decrypt(rx);
+
+	return RX_DROP_UNUSABLE;
+}
diff -ruw linux-3.11.10/net/mac80211/wpa.h linux-3.11.10-fbx/net/mac80211/wpa.h
--- linux-3.11.10/net/mac80211/wpa.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/mac80211/wpa.h	2015-09-29 11:15:09.077757091 +0200
@@ -34,5 +34,7 @@
 ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx);
 ieee80211_tx_result
 ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_rx_result
+ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx);
 
 #endif /* WPA_H */
diff -ruw linux-3.11.10/net/Makefile linux-3.11.10-fbx/net/Makefile
--- linux-3.11.10/net/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/Makefile	2015-09-29 11:15:08.949756111 +0200
@@ -38,6 +38,12 @@
 obj-$(CONFIG_AF_RXRPC)		+= rxrpc/
 obj-$(CONFIG_ATM)		+= atm/
 obj-$(CONFIG_L2TP)		+= l2tp/
+ifneq ($(CONFIG_FBXATM),)
+obj-y				+= fbxatm/
+endif
+ifneq ($(CONFIG_FBXBRIDGE),)
+obj-y				+= fbxbridge/
+endif
 obj-$(CONFIG_DECNET)		+= decnet/
 obj-$(CONFIG_PHONET)		+= phonet/
 ifneq ($(CONFIG_VLAN_8021Q),)
diff -ruw linux-3.11.10/net/netfilter/core.c linux-3.11.10-fbx/net/netfilter/core.c
--- linux-3.11.10/net/netfilter/core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/netfilter/core.c	2015-09-29 11:15:09.077757091 +0200
@@ -163,7 +163,7 @@
 	}
 	return NF_ACCEPT;
 }
-
+EXPORT_SYMBOL(nf_iterate);
 
 /* Returns 1 if okfn() needs to be executed by the caller,
  * -EPERM for NF_DROP, 0 otherwise. */
diff -ruw linux-3.11.10/net/netfilter/nf_conntrack_core.c linux-3.11.10-fbx/net/netfilter/nf_conntrack_core.c
--- linux-3.11.10/net/netfilter/nf_conntrack_core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/netfilter/nf_conntrack_core.c	2015-09-29 11:15:09.089757182 +0200
@@ -196,6 +196,10 @@
 	nf_ct_remove_expectations(ct);
 }
 
+#ifdef CONFIG_IP_FFN
+extern void ip_ffn_ct_destroy(struct nf_conn *ct);
+#endif
+
 static void
 destroy_conntrack(struct nf_conntrack *nfct)
 {
@@ -207,6 +211,10 @@
 	NF_CT_ASSERT(atomic_read(&nfct->use) == 0);
 	NF_CT_ASSERT(!timer_pending(&ct->timeout));
 
+#ifdef CONFIG_IP_FFN
+	ip_ffn_ct_destroy(ct);
+#endif
+
 	/* To make sure we don't get any weird locking issues here:
 	 * destroy_conntrack() MUST NOT be called with a write lock
 	 * to nf_conntrack_lock!!! -HW */
diff -ruw linux-3.11.10/net/netfilter/nf_conntrack_ftp.c linux-3.11.10-fbx/net/netfilter/nf_conntrack_ftp.c
--- linux-3.11.10/net/netfilter/nf_conntrack_ftp.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/netfilter/nf_conntrack_ftp.c	2015-09-29 11:15:09.089757182 +0200
@@ -27,6 +27,11 @@
 #include <net/netfilter/nf_conntrack_helper.h>
 #include <linux/netfilter/nf_conntrack_ftp.h>
 
+#if defined(CONFIG_FREEBOX_BRIDGE) || defined(CONFIG_FREEBOX_BRIDGE_MODULE)
+#include <net/netfilter/nf_nat_helper.h>
+#include <fbxbridge.h>
+#endif
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
 MODULE_DESCRIPTION("ftp connection tracking helper");
@@ -410,6 +415,17 @@
 		return NF_ACCEPT;
 	}
 
+#if defined(CONFIG_FREEBOX_BRIDGE) || defined(CONFIG_FREEBOX_BRIDGE_MODULE)
+	if (!ct_ftp_info->is_fbxbridge && skb->dev->fbx_bridge) {
+		struct fbxbridge *fbxbr;
+
+		fbxbr = skb->dev->fbx_bridge;
+		ct_ftp_info->is_fbxbridge = 1;
+		ct_ftp_info->fbxbridge_remote = ntohl(fbxbr->br_remote_ipaddr);
+		ct_ftp_info->fbxbridge_wan = fbxbr->wan_ipaddr;
+	}
+#endif
+
 	th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
 	if (th == NULL)
 		return NF_ACCEPT;
@@ -496,6 +512,50 @@
 	 * Doesn't matter unless NAT is happening.  */
 	daddr = &ct->tuplehash[!dir].tuple.dst.u3;
 
+#if defined(CONFIG_FREEBOX_BRIDGE) || defined(CONFIG_FREEBOX_BRIDGE_MODULE)
+	if (ct_ftp_info->is_fbxbridge &&
+	    search[dir][i].ftptype == NF_CT_FTP_PORT) {
+		unsigned long orig_ip_addr;
+		unsigned short orig_port;
+		char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")];
+		unsigned int len;
+		__be32 addr;
+
+		/* kludge: if  we are here,  then this is a  local pkt
+		 * that has  gone through internal  fbxbridge snat.
+		 *
+		 * If we see a port  command, then we mangle packet to
+		 * change  ip  address  given  to  the  remote  bridge
+		 * address */
+
+		/* check  address  is  packet  is  the  one  fbxbridge
+		 * changed */
+		orig_ip_addr = cmd.u3.ip;
+		if (orig_ip_addr != ct_ftp_info->fbxbridge_wan)
+			goto donttouch;
+
+		/* now mangle the remote address */
+		orig_port = cmd.u.tcp.port;
+		addr = ct_ftp_info->fbxbridge_remote;
+		len = sprintf(buffer, "%u,%u,%u,%u,%u,%u",
+			      ((unsigned char *)&addr)[0],
+			      ((unsigned char *)&addr)[1],
+			      ((unsigned char *)&addr)[2],
+			      ((unsigned char *)&addr)[3],
+			      orig_port >> 8 , orig_port & 0xFF);
+
+		nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff,
+					 matchlen, buffer, len);
+
+		/* then adjust as if nothing happened */
+		matchlen = len;
+		cmd.u3.ip = ct_ftp_info->fbxbridge_remote;
+	}
+donttouch:
+
+#endif
+
+
 	/* Update the ftp info */
 	if ((cmd.l3num == nf_ct_l3num(ct)) &&
 	    memcmp(&cmd.u3.all, &ct->tuplehash[dir].tuple.src.u3.all,
diff -ruw linux-3.11.10/net/netfilter/nf_conntrack_proto_tcp.c linux-3.11.10-fbx/net/netfilter/nf_conntrack_proto_tcp.c
--- linux-3.11.10/net/netfilter/nf_conntrack_proto_tcp.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/netfilter/nf_conntrack_proto_tcp.c	2015-09-29 11:15:09.089757182 +0200
@@ -1068,6 +1068,18 @@
 	return NF_ACCEPT;
 }
 
+#ifdef CONFIG_IP_FFN
+int external_tcpv4_packet(struct nf_conn *ct,
+			  const struct sk_buff *skb,
+			  unsigned int dataoff,
+			  enum ip_conntrack_info ctinfo)
+{
+	return tcp_packet(ct, skb, dataoff, ctinfo, AF_INET, 0,
+			  tcp_get_timeouts(nf_ct_net(ct)));
+}
+#endif
+
+
 /* Called when a new connection for this protocol found. */
 static bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb,
 		    unsigned int dataoff, unsigned int *timeouts)
diff -ruw linux-3.11.10/net/netfilter/nf_conntrack_proto_udp.c linux-3.11.10-fbx/net/netfilter/nf_conntrack_proto_udp.c
--- linux-3.11.10/net/netfilter/nf_conntrack_proto_udp.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/netfilter/nf_conntrack_proto_udp.c	2015-09-29 11:15:09.089757182 +0200
@@ -100,6 +100,17 @@
 	return NF_ACCEPT;
 }
 
+#ifdef CONFIG_IP_FFN
+int external_udpv4_packet(struct nf_conn *ct,
+			  const struct sk_buff *skb,
+			  unsigned int dataoff,
+			  enum ip_conntrack_info ctinfo)
+{
+	return udp_packet(ct, skb, dataoff, ctinfo, AF_INET, 0,
+			  udp_get_timeouts(nf_ct_net(ct)));
+}
+#endif
+
 /* Called when a new connection for this protocol found. */
 static bool udp_new(struct nf_conn *ct, const struct sk_buff *skb,
 		    unsigned int dataoff, unsigned int *timeouts)
diff -ruw linux-3.11.10/net/netfilter/nf_nat_core.c linux-3.11.10-fbx/net/netfilter/nf_nat_core.c
--- linux-3.11.10/net/netfilter/nf_nat_core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/netfilter/nf_nat_core.c	2015-09-29 11:15:09.093757208 +0200
@@ -487,6 +487,39 @@
 	return i->status & IPS_NAT_MASK ? 1 : 0;
 }
 
+static int nf_nat_proto_clean(struct nf_conn *ct, void *data)
+{
+	struct nf_conn_nat *nat = nfct_nat(ct);
+
+	if (nf_nat_proto_remove(ct, data))
+		return 1;
+
+	if (!nat || !nat->ct)
+		return 0;
+
+	/* This netns is being destroyed, and conntrack has nat null binding.
+	 * Remove it from bysource hash, as the table will be freed soon.
+	 *
+	 * Else, when the conntrack is destoyed, nf_nat_cleanup_conntrack()
+	 * will delete entry from already-freed table.
+	 */
+	if (!del_timer(&ct->timeout))
+		return 1;
+
+	spin_lock_bh(&nf_nat_lock);
+	hlist_del_rcu(&nat->bysource);
+	ct->status &= ~IPS_NAT_DONE_MASK;
+	nat->ct = NULL;
+	spin_unlock_bh(&nf_nat_lock);
+
+	add_timer(&ct->timeout);
+
+	/* don't delete conntrack.  Although that would make things a lot
+	 * simpler, we'd end up flushing all conntracks on nat rmmod.
+	 */
+	return 0;
+}
+
 static void nf_nat_l4proto_clean(u8 l3proto, u8 l4proto)
 {
 	struct nf_nat_proto_clean clean = {
@@ -749,7 +782,7 @@
 {
 	struct nf_nat_proto_clean clean = {};
 
-	nf_ct_iterate_cleanup(net, &nf_nat_proto_remove, &clean);
+	nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean);
 	synchronize_rcu();
 	nf_ct_free_hashtable(net->ct.nat_bysource, net->ct.nat_htable_size);
 }
diff -ruw linux-3.11.10/net/netfilter/nf_nat_irc.c linux-3.11.10-fbx/net/netfilter/nf_nat_irc.c
--- linux-3.11.10/net/netfilter/nf_nat_irc.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/netfilter/nf_nat_irc.c	2014-07-29 21:29:45.609870539 +0200
@@ -34,10 +34,14 @@
 			 struct nf_conntrack_expect *exp)
 {
 	char buffer[sizeof("4294967296 65635")];
+	struct nf_conn *ct = exp->master;
+	union nf_inet_addr newaddr;
 	u_int16_t port;
 	unsigned int ret;
 
 	/* Reply comes from server. */
+	newaddr = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3;
+
 	exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
 	exp->dir = IP_CT_DIR_REPLY;
 	exp->expectfn = nf_nat_follow_master;
@@ -57,17 +61,35 @@
 	}
 
 	if (port == 0) {
-		nf_ct_helper_log(skb, exp->master, "all ports in use");
+		nf_ct_helper_log(skb, ct, "all ports in use");
 		return NF_DROP;
 	}
 
-	ret = nf_nat_mangle_tcp_packet(skb, exp->master, ctinfo,
-				       protoff, matchoff, matchlen, buffer,
-				       strlen(buffer));
+	/* strlen("\1DCC CHAT chat AAAAAAAA P\1\n")=27
+	 * strlen("\1DCC SCHAT chat AAAAAAAA P\1\n")=28
+	 * strlen("\1DCC SEND F AAAAAAAA P S\1\n")=26
+	 * strlen("\1DCC MOVE F AAAAAAAA P S\1\n")=26
+	 * strlen("\1DCC TSEND F AAAAAAAA P S\1\n")=27
+	 *
+	 * AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits,
+	 *                        255.255.255.255==4294967296, 10 digits)
+	 * P:         bound port (min 1 d, max 5d (65635))
+	 * F:         filename   (min 1 d )
+	 * S:         size       (min 1 d )
+	 * 0x01, \n:  terminators
+	 */
+	/* AAA = "us", ie. where server normally talks to. */
+	snprintf(buffer, sizeof(buffer), "%u %u", ntohl(newaddr.ip), port);
+	pr_debug("nf_nat_irc: inserting '%s' == %pI4, port %u\n",
+		 buffer, &newaddr.ip, port);
+
+	ret = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, matchoff,
+				       matchlen, buffer, strlen(buffer));
 	if (ret != NF_ACCEPT) {
-		nf_ct_helper_log(skb, exp->master, "cannot mangle packet");
+		nf_ct_helper_log(skb, ct, "cannot mangle packet");
 		nf_ct_unexpect_related(exp);
 	}
+
 	return ret;
 }
 
diff -ruw linux-3.11.10/net/netfilter/xt_owner.c linux-3.11.10-fbx/net/netfilter/xt_owner.c
--- linux-3.11.10/net/netfilter/xt_owner.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/netfilter/xt_owner.c	2015-09-29 11:15:09.097757237 +0200
@@ -28,6 +28,63 @@
 	return 0;
 }
 
+static int __owner_match_simple_gid(gid_t gid, kgid_t gid_min, kgid_t gid_max)
+{
+	if (gid_gte(gid, gid_min) && gid_lte(gid, gid_max))
+		return 1;
+	return 0;
+}
+
+/*
+ * see kernel/groups.c:groups_to_user() function, which inspired the
+ * content of this function.
+ */
+static int __owner_match_gid_groupinfo(const struct group_info *group_info,
+				       kgid_t gid_min, kgid_t gid_max)
+{
+	unsigned int count = group_info->ngroups;
+	unsigned int block;
+
+	for (block = 0; block < group_info->nblocks; ++block) {
+		unsigned int cp_count = min(NGROUPS_PER_BLOCK, count);
+		unsigned int i;
+
+		for (i = 0; i < cp_count; ++i) {
+			if (__owner_match_simple_gid(
+					     group_info->blocks[block][i],
+					     gid_min, gid_max)) {
+				return 1;
+			}
+			count -= cp_count;
+		}
+	}
+	return 0;
+}
+
+static int owner_match_gid(const struct file *filp,
+			   const struct xt_owner_match_info *info)
+{
+	kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min);
+	kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max);
+
+	/*
+	 * direct match, this is the simple and only case handled by
+	 * the old code, file fsgid matches info gid range.
+	 */
+	if (__owner_match_simple_gid(filp->f_cred->fsgid, gid_min, gid_max))
+		return 1;
+
+	/*
+	 * otherwise we need to have a look to the group list available
+	 * in f_cred->group_info.
+	 */
+	if (__owner_match_gid_groupinfo(filp->f_cred->group_info,
+					gid_min, gid_max))
+		return 1;
+
+	return 0;
+}
+
 static bool
 owner_mt(const struct sk_buff *skb, struct xt_action_param *par)
 {
@@ -58,10 +115,7 @@
 	}
 
 	if (info->match & XT_OWNER_GID) {
-		kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min);
-		kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max);
-		if ((gid_gte(filp->f_cred->fsgid, gid_min) &&
-		     gid_lte(filp->f_cred->fsgid, gid_max)) ^
+		if (owner_match_gid(filp, info) ^
 		    !(info->invert & XT_OWNER_GID))
 			return false;
 	}
diff -ruw linux-3.11.10/net/netlink/af_netlink.c linux-3.11.10-fbx/net/netlink/af_netlink.c
--- linux-3.11.10/net/netlink/af_netlink.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/netlink/af_netlink.c	2015-09-29 11:15:09.101757274 +0200
@@ -1334,7 +1334,74 @@
 	return err;
 }
 
-static inline int netlink_capable(const struct socket *sock, unsigned int flag)
+/**
+ * __netlink_ns_capable - General netlink message capability test
+ * @nsp: NETLINK_CB of the socket buffer holding a netlink command from userspace.
+ * @user_ns: The user namespace of the capability to use
+ * @cap: The capability to use
+ *
+ * Test to see if the opener of the socket we received the message
+ * from had when the netlink socket was created and the sender of the
+ * message has has the capability @cap in the user namespace @user_ns.
+ */
+bool __netlink_ns_capable(const struct netlink_skb_parms *nsp,
+			struct user_namespace *user_ns, int cap)
+{
+	return ((nsp->flags & NETLINK_SKB_DST) ||
+		file_ns_capable(nsp->sk->sk_socket->file, user_ns, cap)) &&
+		ns_capable(user_ns, cap);
+}
+EXPORT_SYMBOL(__netlink_ns_capable);
+
+/**
+ * netlink_ns_capable - General netlink message capability test
+ * @skb: socket buffer holding a netlink command from userspace
+ * @user_ns: The user namespace of the capability to use
+ * @cap: The capability to use
+ *
+ * Test to see if the opener of the socket we received the message
+ * from had when the netlink socket was created and the sender of the
+ * message has has the capability @cap in the user namespace @user_ns.
+ */
+bool netlink_ns_capable(const struct sk_buff *skb,
+			struct user_namespace *user_ns, int cap)
+{
+	return __netlink_ns_capable(&NETLINK_CB(skb), user_ns, cap);
+}
+EXPORT_SYMBOL(netlink_ns_capable);
+
+/**
+ * netlink_capable - Netlink global message capability test
+ * @skb: socket buffer holding a netlink command from userspace
+ * @cap: The capability to use
+ *
+ * Test to see if the opener of the socket we received the message
+ * from had when the netlink socket was created and the sender of the
+ * message has has the capability @cap in all user namespaces.
+ */
+bool netlink_capable(const struct sk_buff *skb, int cap)
+{
+	return netlink_ns_capable(skb, &init_user_ns, cap);
+}
+EXPORT_SYMBOL(netlink_capable);
+
+/**
+ * netlink_net_capable - Netlink network namespace message capability test
+ * @skb: socket buffer holding a netlink command from userspace
+ * @cap: The capability to use
+ *
+ * Test to see if the opener of the socket we received the message
+ * from had when the netlink socket was created and the sender of the
+ * message has has the capability @cap over the network namespace of
+ * the socket we received the message from.
+ */
+bool netlink_net_capable(const struct sk_buff *skb, int cap)
+{
+	return netlink_ns_capable(skb, sock_net(skb->sk)->user_ns, cap);
+}
+EXPORT_SYMBOL(netlink_net_capable);
+
+static inline int netlink_allowed(const struct socket *sock, unsigned int flag)
 {
 	return (nl_table[sock->sk->sk_protocol].flags & flag) ||
 		ns_capable(sock_net(sock->sk)->user_ns, CAP_NET_ADMIN);
@@ -1402,7 +1469,7 @@
 
 	/* Only superuser is allowed to listen multicasts */
 	if (nladdr->nl_groups) {
-		if (!netlink_capable(sock, NL_CFG_F_NONROOT_RECV))
+		if (!netlink_allowed(sock, NL_CFG_F_NONROOT_RECV))
 			return -EPERM;
 		err = netlink_realloc_groups(sk);
 		if (err)
@@ -1464,7 +1531,7 @@
 		return -EINVAL;
 
 	/* Only superuser is allowed to send multicasts */
-	if (nladdr->nl_groups && !netlink_capable(sock, NL_CFG_F_NONROOT_SEND))
+	if (nladdr->nl_groups && !netlink_allowed(sock, NL_CFG_F_NONROOT_SEND))
 		return -EPERM;
 
 	if (!nlk->portid)
@@ -2070,7 +2137,7 @@
 		break;
 	case NETLINK_ADD_MEMBERSHIP:
 	case NETLINK_DROP_MEMBERSHIP: {
-		if (!netlink_capable(sock, NL_CFG_F_NONROOT_RECV))
+		if (!netlink_allowed(sock, NL_CFG_F_NONROOT_RECV))
 			return -EPERM;
 		err = netlink_realloc_groups(sk);
 		if (err)
@@ -2202,6 +2269,7 @@
 	struct sk_buff *skb;
 	int err;
 	struct scm_cookie scm;
+	u32 netlink_skb_flags = 0;
 
 	if (msg->msg_flags&MSG_OOB)
 		return -EOPNOTSUPP;
@@ -2221,8 +2289,9 @@
 		dst_group = ffs(addr->nl_groups);
 		err =  -EPERM;
 		if ((dst_group || dst_portid) &&
-		    !netlink_capable(sock, NL_CFG_F_NONROOT_SEND))
+		    !netlink_allowed(sock, NL_CFG_F_NONROOT_SEND))
 			goto out;
+		netlink_skb_flags |= NETLINK_SKB_DST;
 	} else {
 		dst_portid = nlk->dst_portid;
 		dst_group = nlk->dst_group;
@@ -2252,6 +2321,7 @@
 	NETLINK_CB(skb).portid	= nlk->portid;
 	NETLINK_CB(skb).dst_group = dst_group;
 	NETLINK_CB(skb).creds	= siocb->scm->creds;
+	NETLINK_CB(skb).flags	= netlink_skb_flags;
 
 	err = -EFAULT;
 	if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
@@ -2317,8 +2387,6 @@
 	}
 #endif
 
-	msg->msg_namelen = 0;
-
 	copied = data_skb->len;
 	if (len < copied) {
 		msg->msg_flags |= MSG_TRUNC;
diff -ruw linux-3.11.10/net/netlink/genetlink.c linux-3.11.10-fbx/net/netlink/genetlink.c
--- linux-3.11.10/net/netlink/genetlink.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/netlink/genetlink.c	2015-09-29 11:15:09.101757274 +0200
@@ -592,7 +592,7 @@
 		return -EOPNOTSUPP;
 
 	if ((ops->flags & GENL_ADMIN_PERM) &&
-	    !capable(CAP_NET_ADMIN))
+	    !netlink_capable(skb, CAP_NET_ADMIN))
 		return -EPERM;
 
 	if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) {
diff -ruw linux-3.11.10/net/packet/af_packet.c linux-3.11.10-fbx/net/packet/af_packet.c
--- linux-3.11.10/net/packet/af_packet.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/packet/af_packet.c	2015-09-29 11:15:09.109757328 +0200
@@ -237,6 +237,30 @@
 static void __fanout_unlink(struct sock *sk, struct packet_sock *po);
 static void __fanout_link(struct sock *sk, struct packet_sock *po);
 
+static struct net_device *packet_cached_dev_get(struct packet_sock *po)
+{
+	struct net_device *dev;
+
+	rcu_read_lock();
+	dev = rcu_dereference(po->cached_dev);
+	if (likely(dev))
+		dev_hold(dev);
+	rcu_read_unlock();
+
+	return dev;
+}
+
+static void packet_cached_dev_assign(struct packet_sock *po,
+				     struct net_device *dev)
+{
+	rcu_assign_pointer(po->cached_dev, dev);
+}
+
+static void packet_cached_dev_reset(struct packet_sock *po)
+{
+	RCU_INIT_POINTER(po->cached_dev, NULL);
+}
+
 /* register_prot_hook must be invoked with the po->bind_lock held,
  * or from a context in which asynchronous accesses to the packet
  * socket is not possible (packet_create()).
@@ -244,11 +268,13 @@
 static void register_prot_hook(struct sock *sk)
 {
 	struct packet_sock *po = pkt_sk(sk);
+
 	if (!po->running) {
 		if (po->fanout)
 			__fanout_link(sk, po);
 		else
 			dev_add_pack(&po->prot_hook);
+
 		sock_hold(sk);
 		po->running = 1;
 	}
@@ -266,10 +292,12 @@
 	struct packet_sock *po = pkt_sk(sk);
 
 	po->running = 0;
+
 	if (po->fanout)
 		__fanout_unlink(sk, po);
 	else
 		__dev_remove_pack(&po->prot_hook);
+
 	__sock_put(sk);
 
 	if (sync) {
@@ -432,9 +460,9 @@
 
 	pkc = tx_ring ? &po->tx_ring.prb_bdqc : &po->rx_ring.prb_bdqc;
 
-	spin_lock(&rb_queue->lock);
+	spin_lock_bh(&rb_queue->lock);
 	pkc->delete_blk_timer = 1;
-	spin_unlock(&rb_queue->lock);
+	spin_unlock_bh(&rb_queue->lock);
 
 	prb_del_retire_blk_timer(pkc);
 }
@@ -2046,7 +2074,6 @@
 	struct sk_buff *skb;
 	struct net_device *dev;
 	__be16 proto;
-	bool need_rls_dev = false;
 	int err, reserve = 0;
 	void *ph;
 	struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name;
@@ -2058,8 +2085,8 @@
 
 	mutex_lock(&po->pg_vec_lock);
 
-	if (saddr == NULL) {
-		dev = po->prot_hook.dev;
+	if (likely(saddr == NULL)) {
+		dev	= packet_cached_dev_get(po);
 		proto	= po->num;
 		addr	= NULL;
 	} else {
@@ -2073,19 +2100,17 @@
 		proto	= saddr->sll_protocol;
 		addr	= saddr->sll_addr;
 		dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex);
-		need_rls_dev = true;
 	}
 
 	err = -ENXIO;
 	if (unlikely(dev == NULL))
 		goto out;
-
-	reserve = dev->hard_header_len;
-
 	err = -ENETDOWN;
 	if (unlikely(!(dev->flags & IFF_UP)))
 		goto out_put;
 
+	reserve = dev->hard_header_len;
+
 	size_max = po->tx_ring.frame_size
 		- (po->tp_hdrlen - sizeof(struct sockaddr_ll));
 
@@ -2162,7 +2187,6 @@
 	__packet_set_status(po, ph, status);
 	kfree_skb(skb);
 out_put:
-	if (need_rls_dev)
 		dev_put(dev);
 out:
 	mutex_unlock(&po->pg_vec_lock);
@@ -2201,7 +2225,6 @@
 	struct sk_buff *skb;
 	struct net_device *dev;
 	__be16 proto;
-	bool need_rls_dev = false;
 	unsigned char *addr;
 	int err, reserve = 0;
 	struct virtio_net_hdr vnet_hdr = { 0 };
@@ -2216,8 +2239,8 @@
 	 *	Get and verify the address.
 	 */
 
-	if (saddr == NULL) {
-		dev = po->prot_hook.dev;
+	if (likely(saddr == NULL)) {
+		dev	= packet_cached_dev_get(po);
 		proto	= po->num;
 		addr	= NULL;
 	} else {
@@ -2229,19 +2252,17 @@
 		proto	= saddr->sll_protocol;
 		addr	= saddr->sll_addr;
 		dev = dev_get_by_index(sock_net(sk), saddr->sll_ifindex);
-		need_rls_dev = true;
 	}
 
 	err = -ENXIO;
-	if (dev == NULL)
+	if (unlikely(dev == NULL))
 		goto out_unlock;
-	if (sock->type == SOCK_RAW)
-		reserve = dev->hard_header_len;
-
 	err = -ENETDOWN;
-	if (!(dev->flags & IFF_UP))
+	if (unlikely(!(dev->flags & IFF_UP)))
 		goto out_unlock;
 
+	if (sock->type == SOCK_RAW)
+		reserve = dev->hard_header_len;
 	if (po->has_vnet_hdr) {
 		vnet_hdr_len = sizeof(vnet_hdr);
 
@@ -2375,7 +2396,6 @@
 	if (err > 0 && (err = net_xmit_errno(err)) != 0)
 		goto out_unlock;
 
-	if (need_rls_dev)
 		dev_put(dev);
 
 	return len;
@@ -2383,7 +2403,7 @@
 out_free:
 	kfree_skb(skb);
 out_unlock:
-	if (dev && need_rls_dev)
+	if (dev)
 		dev_put(dev);
 out:
 	return err;
@@ -2428,6 +2448,8 @@
 
 	spin_lock(&po->bind_lock);
 	unregister_prot_hook(sk, false);
+	packet_cached_dev_reset(po);
+
 	if (po->prot_hook.dev) {
 		dev_put(po->prot_hook.dev);
 		po->prot_hook.dev = NULL;
@@ -2483,14 +2505,17 @@
 
 	spin_lock(&po->bind_lock);
 	unregister_prot_hook(sk, true);
+
 	po->num = protocol;
 	po->prot_hook.type = protocol;
 	if (po->prot_hook.dev)
 		dev_put(po->prot_hook.dev);
-	po->prot_hook.dev = dev;
 
+	po->prot_hook.dev = dev;
 	po->ifindex = dev ? dev->ifindex : 0;
 
+	packet_cached_dev_assign(po, dev);
+
 	if (protocol == 0)
 		goto out_unlock;
 
@@ -2604,6 +2629,8 @@
 	sk->sk_family = PF_PACKET;
 	po->num = proto;
 
+	packet_cached_dev_reset(po);
+
 	sk->sk_destruct = packet_sock_destruct;
 	sk_refcnt_debug_inc(sk);
 
@@ -2694,7 +2721,6 @@
 	struct sock *sk = sock->sk;
 	struct sk_buff *skb;
 	int copied, err;
-	struct sockaddr_ll *sll;
 	int vnet_hdr_len = 0;
 
 	err = -EINVAL;
@@ -2777,22 +2803,10 @@
 			goto out_free;
 	}
 
-	/*
-	 *	If the address length field is there to be filled in, we fill
-	 *	it in now.
+	/* You lose any data beyond the buffer you gave. If it worries
+	 * a user program they can ask the device for its MTU
+	 * anyway.
 	 */
-
-	sll = &PACKET_SKB_CB(skb)->sa.ll;
-	if (sock->type == SOCK_PACKET)
-		msg->msg_namelen = sizeof(struct sockaddr_pkt);
-	else
-		msg->msg_namelen = sll->sll_halen + offsetof(struct sockaddr_ll, sll_addr);
-
-	/*
-	 *	You lose any data beyond the buffer you gave. If it worries a
-	 *	user program they can ask the device for its MTU anyway.
-	 */
-
 	copied = skb->len;
 	if (copied > len) {
 		copied = len;
@@ -2805,9 +2819,20 @@
 
 	sock_recv_ts_and_drops(msg, sk, skb);
 
-	if (msg->msg_name)
+	if (msg->msg_name) {
+		/* If the address length field is there to be filled
+		 * in, we fill it in now.
+		 */
+		if (sock->type == SOCK_PACKET) {
+			msg->msg_namelen = sizeof(struct sockaddr_pkt);
+		} else {
+			struct sockaddr_ll *sll = &PACKET_SKB_CB(skb)->sa.ll;
+			msg->msg_namelen = sll->sll_halen +
+				offsetof(struct sockaddr_ll, sll_addr);
+		}
 		memcpy(msg->msg_name, &PACKET_SKB_CB(skb)->sa,
 		       msg->msg_namelen);
+	}
 
 	if (pkt_sk(sk)->auxdata) {
 		struct tpacket_auxdata aux;
@@ -3359,6 +3384,7 @@
 						sk->sk_error_report(sk);
 				}
 				if (msg == NETDEV_UNREGISTER) {
+					packet_cached_dev_reset(po);
 					po->ifindex = -1;
 					if (po->prot_hook.dev)
 						dev_put(po->prot_hook.dev);
diff -ruw linux-3.11.10/net/packet/internal.h linux-3.11.10-fbx/net/packet/internal.h
--- linux-3.11.10/net/packet/internal.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/packet/internal.h	2015-09-29 11:15:09.109757328 +0200
@@ -113,6 +113,7 @@
 	unsigned int		tp_loss:1;
 	unsigned int		tp_tx_has_off:1;
 	unsigned int		tp_tstamp;
+	struct net_device __rcu	*cached_dev;
 	struct packet_type	prot_hook ____cacheline_aligned_in_smp;
 };
 
diff -ruw linux-3.11.10/net/sched/act_api.c linux-3.11.10-fbx/net/sched/act_api.c
--- linux-3.11.10/net/sched/act_api.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/sched/act_api.c	2015-09-29 11:15:09.121757420 +0200
@@ -989,7 +989,7 @@
 	u32 portid = skb ? NETLINK_CB(skb).portid : 0;
 	int ret = 0, ovr = 0;
 
-	if ((n->nlmsg_type != RTM_GETACTION) && !capable(CAP_NET_ADMIN))
+	if ((n->nlmsg_type != RTM_GETACTION) && !netlink_capable(skb, CAP_NET_ADMIN))
 		return -EPERM;
 
 	ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL);
diff -ruw linux-3.11.10/net/sched/cls_api.c linux-3.11.10-fbx/net/sched/cls_api.c
--- linux-3.11.10/net/sched/cls_api.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/sched/cls_api.c	2015-09-29 11:15:09.121757420 +0200
@@ -138,7 +138,7 @@
 	int err;
 	int tp_created = 0;
 
-	if ((n->nlmsg_type != RTM_GETTFILTER) && !capable(CAP_NET_ADMIN))
+	if ((n->nlmsg_type != RTM_GETTFILTER) && !netlink_capable(skb, CAP_NET_ADMIN))
 		return -EPERM;
 
 replay:
diff -ruw linux-3.11.10/net/sched/sch_api.c linux-3.11.10-fbx/net/sched/sch_api.c
--- linux-3.11.10/net/sched/sch_api.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/sched/sch_api.c	2015-09-29 11:15:09.125757456 +0200
@@ -1024,7 +1024,7 @@
 	struct Qdisc *p = NULL;
 	int err;
 
-	if ((n->nlmsg_type != RTM_GETQDISC) && !capable(CAP_NET_ADMIN))
+	if ((n->nlmsg_type != RTM_GETQDISC) && !netlink_capable(skb, CAP_NET_ADMIN))
 		return -EPERM;
 
 	err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
@@ -1091,7 +1091,7 @@
 	struct Qdisc *q, *p;
 	int err;
 
-	if (!capable(CAP_NET_ADMIN))
+	if (!netlink_capable(skb, CAP_NET_ADMIN))
 		return -EPERM;
 
 replay:
@@ -1431,7 +1431,7 @@
 	u32 qid;
 	int err;
 
-	if ((n->nlmsg_type != RTM_GETTCLASS) && !capable(CAP_NET_ADMIN))
+	if ((n->nlmsg_type != RTM_GETTCLASS) && !netlink_capable(skb, CAP_NET_ADMIN))
 		return -EPERM;
 
 	err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
diff -ruw linux-3.11.10/net/sched/sch_drr.c linux-3.11.10-fbx/net/sched/sch_drr.c
--- linux-3.11.10/net/sched/sch_drr.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/sched/sch_drr.c	2015-09-29 11:15:09.125757456 +0200
@@ -344,7 +344,9 @@
 			cl = drr_find_class(sch, res.classid);
 		return cl;
 	}
-	return NULL;
+
+	/* default to first minor if it exists, or drop */
+	return drr_find_class(sch, TC_H_MAKE(TC_H_MAJ(sch->handle), 1));
 }
 
 static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch)
diff -ruw linux-3.11.10/net/socket.c linux-3.11.10-fbx/net/socket.c
--- linux-3.11.10/net/socket.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/socket.c	2015-09-29 11:15:09.141757573 +0200
@@ -221,12 +221,13 @@
 	int err;
 	int len;
 
+	BUG_ON(klen > sizeof(struct sockaddr_storage));
 	err = get_user(len, ulen);
 	if (err)
 		return err;
 	if (len > klen)
 		len = klen;
-	if (len < 0 || len > sizeof(struct sockaddr_storage))
+	if (len < 0)
 		return -EINVAL;
 	if (len) {
 		if (audit_sockaddr(klen, kaddr))
@@ -1036,6 +1037,31 @@
 	return err;
 }
 
+static DEFINE_MUTEX(fbxdiverter_ioctl_mutex);
+static int (*fbxdiverter_ioctl_hook) (struct net *, unsigned int cmd, void __user *arg) = NULL;
+
+void fbxdiverter_ioctl_set(int (*hook) (struct net *, unsigned int,
+					void __user *))
+{
+	mutex_lock(&fbxdiverter_ioctl_mutex);
+	fbxdiverter_ioctl_hook = hook;
+	mutex_unlock(&fbxdiverter_ioctl_mutex);
+}
+
+EXPORT_SYMBOL(fbxdiverter_ioctl_set);
+
+static DEFINE_MUTEX(fbxbridge_ioctl_mutex);
+static int (*fbxbridge_ioctl_hook)(struct net *, unsigned int cmd, void __user *arg) = NULL;
+
+void fbxbridge_set(int (*hook)(struct net *, unsigned int, void __user *))
+{
+	mutex_lock(&fbxbridge_ioctl_mutex);
+	fbxbridge_ioctl_hook = hook;
+	mutex_unlock(&fbxbridge_ioctl_mutex);
+}
+
+EXPORT_SYMBOL(fbxbridge_set);
+
 /*
  *	With an ioctl, arg may well be a user mode pointer, but we don't know
  *	what to do with it - that's up to the protocol still.
@@ -1108,6 +1134,28 @@
 				err = dlci_ioctl_hook(cmd, argp);
 			mutex_unlock(&dlci_ioctl_mutex);
 			break;
+		case SIOCGFBXDIVERT:
+		case SIOCSFBXDIVERT:
+			err = -ENOPKG;
+			if (!fbxdiverter_ioctl_hook)
+				request_module("fbxdiverter");
+
+			mutex_lock(&fbxdiverter_ioctl_mutex);
+			if (fbxdiverter_ioctl_hook)
+				err = fbxdiverter_ioctl_hook(net, cmd, argp);
+			mutex_unlock(&fbxdiverter_ioctl_mutex);
+			break;
+		case SIOCGFBXBRIDGE:
+		case SIOCSFBXBRIDGE:
+			err = -ENOPKG;
+			if (!fbxbridge_ioctl_hook)
+				request_module("fbxbridge");
+
+			mutex_lock(&fbxbridge_ioctl_mutex);
+			if (fbxbridge_ioctl_hook)
+				err = fbxbridge_ioctl_hook(net, cmd, argp);
+			mutex_unlock(&fbxbridge_ioctl_mutex);
+			break;
 		default:
 			err = sock_do_ioctl(net, sock, cmd, arg);
 			break;
@@ -1849,8 +1897,10 @@
 	msg.msg_iov = &iov;
 	iov.iov_len = size;
 	iov.iov_base = ubuf;
-	msg.msg_name = (struct sockaddr *)&address;
-	msg.msg_namelen = sizeof(address);
+	/* Save some cycles and don't copy the address if not needed */
+	msg.msg_name = addr ? (struct sockaddr *)&address : NULL;
+	/* We assume all kernel code knows the size of sockaddr_storage */
+	msg.msg_namelen = 0;
 	if (sock->file->f_flags & O_NONBLOCK)
 		flags |= MSG_DONTWAIT;
 	err = sock_recvmsg(sock, &msg, size, flags);
@@ -1978,8 +2028,12 @@
 {
 	if (copy_from_user(kmsg, umsg, sizeof(struct msghdr)))
 		return -EFAULT;
-	if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
+
+	if (kmsg->msg_namelen < 0)
 		return -EINVAL;
+
+	if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
+		kmsg->msg_namelen = sizeof(struct sockaddr_storage);
 	return 0;
 }
 
@@ -2230,16 +2284,14 @@
 			goto out;
 	}
 
-	/*
-	 *      Save the user-mode address (verify_iovec will change the
+	/* Save the user-mode address (verify_iovec will change the
 	 *      kernel msghdr to use the kernel address space)
 	 */
-
 	uaddr = (__force void __user *)msg_sys->msg_name;
 	uaddr_len = COMPAT_NAMELEN(msg);
-	if (MSG_CMSG_COMPAT & flags) {
+	if (MSG_CMSG_COMPAT & flags)
 		err = verify_compat_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
-	} else
+	else
 		err = verify_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
 	if (err < 0)
 		goto out_freeiov;
@@ -2248,6 +2300,9 @@
 	cmsg_ptr = (unsigned long)msg_sys->msg_control;
 	msg_sys->msg_flags = flags & (MSG_CMSG_CLOEXEC|MSG_CMSG_COMPAT);
 
+	/* We assume all kernel code knows the size of sockaddr_storage */
+	msg_sys->msg_namelen = 0;
+
 	if (sock->file->f_flags & O_NONBLOCK)
 		flags |= MSG_DONTWAIT;
 	err = (nosec ? sock_recvmsg_nosec : sock_recvmsg)(sock, msg_sys,
diff -ruw linux-3.11.10/net/sunrpc/auth_gss/gss_rpc_upcall.c linux-3.11.10-fbx/net/sunrpc/auth_gss/gss_rpc_upcall.c
--- linux-3.11.10/net/sunrpc/auth_gss/gss_rpc_upcall.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/sunrpc/auth_gss/gss_rpc_upcall.c	2015-09-29 11:15:09.145757602 +0200
@@ -137,7 +137,6 @@
 {
 	mutex_init(&sn->gssp_lock);
 	sn->gssp_clnt = NULL;
-	init_waitqueue_head(&sn->gssp_wq);
 }
 
 int set_gssp_clnt(struct net *net)
@@ -154,7 +153,6 @@
 		sn->gssp_clnt = clnt;
 	}
 	mutex_unlock(&sn->gssp_lock);
-	wake_up(&sn->gssp_wq);
 	return ret;
 }
 
diff -ruw linux-3.11.10/net/sunrpc/auth_gss/svcauth_gss.c linux-3.11.10-fbx/net/sunrpc/auth_gss/svcauth_gss.c
--- linux-3.11.10/net/sunrpc/auth_gss/svcauth_gss.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/sunrpc/auth_gss/svcauth_gss.c	2015-09-29 11:15:09.145757602 +0200
@@ -1295,34 +1295,9 @@
 	else
 		ret = -EBUSY;
 	spin_unlock(&use_gssp_lock);
-	wake_up(&sn->gssp_wq);
 	return ret;
 }
 
-static inline bool gssp_ready(struct sunrpc_net *sn)
-{
-	switch (sn->use_gss_proxy) {
-		case -1:
-			return false;
-		case 0:
-			return true;
-		case 1:
-			return sn->gssp_clnt;
-	}
-	WARN_ON_ONCE(1);
-	return false;
-}
-
-static int wait_for_gss_proxy(struct net *net, struct file *file)
-{
-	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
-
-	if (file->f_flags & O_NONBLOCK && !gssp_ready(sn))
-		return -EAGAIN;
-	return wait_event_interruptible(sn->gssp_wq, gssp_ready(sn));
-}
-
-
 static ssize_t write_gssp(struct file *file, const char __user *buf,
 			 size_t count, loff_t *ppos)
 {
@@ -1355,16 +1330,12 @@
 			 size_t count, loff_t *ppos)
 {
 	struct net *net = PDE_DATA(file_inode(file));
+	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
 	unsigned long p = *ppos;
 	char tbuf[10];
 	size_t len;
-	int ret;
-
-	ret = wait_for_gss_proxy(net, file);
-	if (ret)
-		return ret;
 
-	snprintf(tbuf, sizeof(tbuf), "%d\n", use_gss_proxy(net));
+	snprintf(tbuf, sizeof(tbuf), "%d\n", sn->use_gss_proxy);
 	len = strlen(tbuf);
 	if (p >= len)
 		return 0;
diff -ruw linux-3.11.10/net/sunrpc/clnt.c linux-3.11.10-fbx/net/sunrpc/clnt.c
--- linux-3.11.10/net/sunrpc/clnt.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/sunrpc/clnt.c	2015-09-29 11:15:09.145757602 +0200
@@ -645,14 +645,16 @@
 /*
  * Free an RPC client
  */
-static void
+static struct rpc_clnt *
 rpc_free_client(struct rpc_clnt *clnt)
 {
+	struct rpc_clnt *parent = NULL;
+
 	dprintk_rcu("RPC:       destroying %s client for %s\n",
 			clnt->cl_protname,
 			rcu_dereference(clnt->cl_xprt)->servername);
 	if (clnt->cl_parent != clnt)
-		rpc_release_client(clnt->cl_parent);
+		parent = clnt->cl_parent;
 	rpc_clnt_remove_pipedir(clnt);
 	rpc_unregister_client(clnt);
 	rpc_free_iostats(clnt->cl_metrics);
@@ -661,18 +663,17 @@
 	xprt_put(rcu_dereference_raw(clnt->cl_xprt));
 	rpciod_down();
 	kfree(clnt);
+	return parent;
 }
 
 /*
  * Free an RPC client
  */
-static void
+static struct rpc_clnt * 
 rpc_free_auth(struct rpc_clnt *clnt)
 {
-	if (clnt->cl_auth == NULL) {
-		rpc_free_client(clnt);
-		return;
-	}
+	if (clnt->cl_auth == NULL)
+		return rpc_free_client(clnt);
 
 	/*
 	 * Note: RPCSEC_GSS may need to send NULL RPC calls in order to
@@ -683,7 +684,8 @@
 	rpcauth_release(clnt->cl_auth);
 	clnt->cl_auth = NULL;
 	if (atomic_dec_and_test(&clnt->cl_count))
-		rpc_free_client(clnt);
+		return rpc_free_client(clnt);
+	return NULL;
 }
 
 /*
@@ -694,10 +696,13 @@
 {
 	dprintk("RPC:       rpc_release_client(%p)\n", clnt);
 
+	do {
 	if (list_empty(&clnt->cl_tasks))
 		wake_up(&destroy_wait);
-	if (atomic_dec_and_test(&clnt->cl_count))
-		rpc_free_auth(clnt);
+		if (!atomic_dec_and_test(&clnt->cl_count))
+			break;
+		clnt = rpc_free_auth(clnt);
+	} while (clnt != NULL);
 }
 EXPORT_SYMBOL_GPL(rpc_release_client);
 
@@ -1418,9 +1423,13 @@
 	task->tk_action = call_refresh;
 	switch (status) {
 	case 0:
-		if (rpcauth_uptodatecred(task))
+		if (rpcauth_uptodatecred(task)) {
 			task->tk_action = call_allocate;
 		return;
+		}
+		/* Use rate-limiting and a max number of retries if refresh
+		 * had status 0 but failed to update the cred.
+		 */
 	case -ETIMEDOUT:
 		rpc_delay(task, 3*HZ);
 	case -EAGAIN:
diff -ruw linux-3.11.10/net/sunrpc/netns.h linux-3.11.10-fbx/net/sunrpc/netns.h
--- linux-3.11.10/net/sunrpc/netns.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/sunrpc/netns.h	2015-09-29 11:15:09.145757602 +0200
@@ -26,7 +26,6 @@
 	unsigned int rpcb_is_af_local : 1;
 
 	struct mutex gssp_lock;
-	wait_queue_head_t gssp_wq;
 	struct rpc_clnt *gssp_clnt;
 	int use_gss_proxy;
 	int pipe_version;
diff -ruw linux-3.11.10/net/sunrpc/svcsock.c linux-3.11.10-fbx/net/sunrpc/svcsock.c
--- linux-3.11.10/net/sunrpc/svcsock.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/sunrpc/svcsock.c	2015-09-29 11:15:09.149757639 +0200
@@ -1395,6 +1395,22 @@
 	return svsk;
 }
 
+bool svc_alien_sock(struct net *net, int fd)
+{
+	int err;
+	struct socket *sock = sockfd_lookup(fd, &err);
+	bool ret = false;
+
+	if (!sock)
+		goto out;
+	if (sock_net(sock->sk) != net)
+		ret = true;
+	sockfd_put(sock);
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(svc_alien_sock);
+
 /**
  * svc_addsock - add a listener socket to an RPC service
  * @serv: pointer to RPC service to which to add a new listener
diff -ruw linux-3.11.10/net/sunrpc/svc_xprt.c linux-3.11.10-fbx/net/sunrpc/svc_xprt.c
--- linux-3.11.10/net/sunrpc/svc_xprt.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/sunrpc/svc_xprt.c	2015-09-29 11:15:09.149757639 +0200
@@ -730,6 +730,8 @@
 		newxpt = xprt->xpt_ops->xpo_accept(xprt);
 		if (newxpt)
 			svc_add_new_temp_xprt(serv, newxpt);
+		else
+			module_put(xprt->xpt_class->xcl_owner);
 	} else if (xprt->xpt_ops->xpo_has_wspace(xprt)) {
 		/* XPT_DATA|XPT_DEFERRED case: */
 		dprintk("svc: server %p, pool %u, transport %p, inuse=%d\n",
diff -ruw linux-3.11.10/net/sunrpc/xprtsock.c linux-3.11.10-fbx/net/sunrpc/xprtsock.c
--- linux-3.11.10/net/sunrpc/xprtsock.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/sunrpc/xprtsock.c	2015-09-29 11:15:09.153757665 +0200
@@ -391,8 +391,10 @@
 	return kernel_sendmsg(sock, &msg, NULL, 0, 0);
 }
 
-static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned int base, int more)
+static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned int base, int more, bool zerocopy)
 {
+	ssize_t (*do_sendpage)(struct socket *sock, struct page *page,
+			int offset, size_t size, int flags);
 	struct page **ppage;
 	unsigned int remainder;
 	int err, sent = 0;
@@ -401,6 +403,9 @@
 	base += xdr->page_base;
 	ppage = xdr->pages + (base >> PAGE_SHIFT);
 	base &= ~PAGE_MASK;
+	do_sendpage = sock->ops->sendpage;
+	if (!zerocopy)
+		do_sendpage = sock_no_sendpage;
 	for(;;) {
 		unsigned int len = min_t(unsigned int, PAGE_SIZE - base, remainder);
 		int flags = XS_SENDMSG_FLAGS;
@@ -408,7 +413,7 @@
 		remainder -= len;
 		if (remainder != 0 || more)
 			flags |= MSG_MORE;
-		err = sock->ops->sendpage(sock, *ppage, base, len, flags);
+		err = do_sendpage(sock, *ppage, base, len, flags);
 		if (remainder == 0 || err != len)
 			break;
 		sent += err;
@@ -429,9 +434,10 @@
  * @addrlen: UDP only -- length of destination address
  * @xdr: buffer containing this request
  * @base: starting position in the buffer
+ * @zerocopy: true if it is safe to use sendpage()
  *
  */
-static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base)
+static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base, bool zerocopy)
 {
 	unsigned int remainder = xdr->len - base;
 	int err, sent = 0;
@@ -459,7 +465,7 @@
 	if (base < xdr->page_len) {
 		unsigned int len = xdr->page_len - base;
 		remainder -= len;
-		err = xs_send_pagedata(sock, xdr, base, remainder != 0);
+		err = xs_send_pagedata(sock, xdr, base, remainder != 0, zerocopy);
 		if (remainder == 0 || err != len)
 			goto out;
 		sent += err;
@@ -496,6 +502,7 @@
 	struct rpc_rqst *req = task->tk_rqstp;
 	struct rpc_xprt *xprt = req->rq_xprt;
 	struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
+	struct sock *sk = transport->inet;
 	int ret = -EAGAIN;
 
 	dprintk("RPC: %5u xmit incomplete (%u left of %u)\n",
@@ -513,7 +520,7 @@
 			 * window size
 			 */
 			set_bit(SOCK_NOSPACE, &transport->sock->flags);
-			transport->inet->sk_write_pending++;
+			sk->sk_write_pending++;
 			/* ...and wait for more buffer space */
 			xprt_wait_for_buffer_space(task, xs_nospace_callback);
 		}
@@ -523,6 +530,9 @@
 	}
 
 	spin_unlock_bh(&xprt->transport_lock);
+
+	/* Race breaker in case memory is freed before above code is called */
+	sk->sk_write_space(sk);
 	return ret;
 }
 
@@ -562,7 +572,7 @@
 			req->rq_svec->iov_base, req->rq_svec->iov_len);
 
 	status = xs_sendpages(transport->sock, NULL, 0,
-						xdr, req->rq_bytes_sent);
+						xdr, req->rq_bytes_sent, true);
 	dprintk("RPC:       %s(%u) = %d\n",
 			__func__, xdr->len - req->rq_bytes_sent, status);
 	if (likely(status >= 0)) {
@@ -618,7 +628,7 @@
 	status = xs_sendpages(transport->sock,
 			      xs_addr(xprt),
 			      xprt->addrlen, xdr,
-			      req->rq_bytes_sent);
+			      req->rq_bytes_sent, true);
 
 	dprintk("RPC:       xs_udp_send_request(%u) = %d\n",
 			xdr->len - req->rq_bytes_sent, status);
@@ -689,6 +699,7 @@
 	struct rpc_xprt *xprt = req->rq_xprt;
 	struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
 	struct xdr_buf *xdr = &req->rq_snd_buf;
+	bool zerocopy = true;
 	int status;
 
 	xs_encode_stream_record_marker(&req->rq_snd_buf);
@@ -696,13 +707,20 @@
 	xs_pktdump("packet data:",
 				req->rq_svec->iov_base,
 				req->rq_svec->iov_len);
+	/* Don't use zero copy if this is a resend. If the RPC call
+	 * completes while the socket holds a reference to the pages,
+	 * then we may end up resending corrupted data.
+	 */
+	if (task->tk_flags & RPC_TASK_SENT)
+		zerocopy = false;
 
 	/* Continue transmitting the packet/record. We must be careful
 	 * to cope with writespace callbacks arriving _after_ we have
 	 * called sendmsg(). */
 	while (1) {
 		status = xs_sendpages(transport->sock,
-					NULL, 0, xdr, req->rq_bytes_sent);
+					NULL, 0, xdr, req->rq_bytes_sent,
+					zerocopy);
 
 		dprintk("RPC:       xs_tcp_send_request(%u) = %d\n",
 				xdr->len - req->rq_bytes_sent, status);
diff -ruw linux-3.11.10/net/unix/af_unix.c linux-3.11.10-fbx/net/unix/af_unix.c
--- linux-3.11.10/net/unix/af_unix.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/unix/af_unix.c	2015-09-29 11:15:09.161757731 +0200
@@ -161,9 +161,8 @@
 
 static inline unsigned int unix_hash_fold(__wsum n)
 {
-	unsigned int hash = (__force unsigned int)n;
+	unsigned int hash = (__force unsigned int)csum_fold(n);
 
-	hash ^= hash>>16;
 	hash ^= hash>>8;
 	return hash&(UNIX_HASH_SIZE-1);
 }
@@ -268,8 +267,10 @@
 	sk_for_each(s, &unix_socket_table[hash ^ type]) {
 		struct unix_sock *u = unix_sk(s);
 
+#ifdef UNIX_ABSTRACT_IGNORE_NETNS
 		if (!net_eq(sock_net(s), net))
 			continue;
+#endif
 
 		if (u->addr->len == len &&
 		    !memcmp(u->addr->name, sunname, len))
@@ -530,13 +531,17 @@
 static int unix_seqpacket_recvmsg(struct kiocb *, struct socket *,
 				  struct msghdr *, size_t, int);
 
-static void unix_set_peek_off(struct sock *sk, int val)
+static int unix_set_peek_off(struct sock *sk, int val)
 {
 	struct unix_sock *u = unix_sk(sk);
 
-	mutex_lock(&u->readlock);
+	if (mutex_lock_interruptible(&u->readlock))
+		return -EINTR;
+
 	sk->sk_peek_off = val;
 	mutex_unlock(&u->readlock);
+
+	return 0;
 }
 
 
@@ -714,7 +719,9 @@
 	int err;
 	unsigned int retries = 0;
 
-	mutex_lock(&u->readlock);
+	err = mutex_lock_interruptible(&u->readlock);
+	if (err)
+		return err;
 
 	err = 0;
 	if (u->addr)
@@ -873,7 +880,9 @@
 		goto out;
 	addr_len = err;
 
-	mutex_lock(&u->readlock);
+	err = mutex_lock_interruptible(&u->readlock);
+	if (err)
+		goto out;
 
 	err = -EINVAL;
 	if (u->addr)
@@ -1762,7 +1771,6 @@
 {
 	struct unix_sock *u = unix_sk(sk);
 
-	msg->msg_namelen = 0;
 	if (u->addr) {
 		msg->msg_namelen = u->addr->len;
 		memcpy(msg->msg_name, u->addr->name, u->addr->len);
@@ -1786,11 +1794,12 @@
 	if (flags&MSG_OOB)
 		goto out;
 
-	msg->msg_namelen = 0;
-
 	err = mutex_lock_interruptible(&u->readlock);
-	if (err) {
-		err = sock_intr_errno(sock_rcvtimeo(sk, noblock));
+	if (unlikely(err)) {
+		/* recvmsg() in non blocking mode is supposed to return -EAGAIN
+		 * sk_rcvtimeo is not honored by mutex_lock_interruptible()
+		 */
+		err = noblock ? -EAGAIN : -ERESTARTSYS;
 		goto out;
 	}
 
@@ -1910,6 +1919,7 @@
 	struct unix_sock *u = unix_sk(sk);
 	struct sockaddr_un *sunaddr = msg->msg_name;
 	int copied = 0;
+	int noblock = flags & MSG_DONTWAIT;
 	int check_creds = 0;
 	int target;
 	int err = 0;
@@ -1925,9 +1935,7 @@
 		goto out;
 
 	target = sock_rcvlowat(sk, flags&MSG_WAITALL, size);
-	timeo = sock_rcvtimeo(sk, flags&MSG_DONTWAIT);
-
-	msg->msg_namelen = 0;
+	timeo = sock_rcvtimeo(sk, noblock);
 
 	/* Lock the socket to prevent queue disordering
 	 * while sleeps in memcpy_tomsg
@@ -1939,8 +1947,11 @@
 	}
 
 	err = mutex_lock_interruptible(&u->readlock);
-	if (err) {
-		err = sock_intr_errno(timeo);
+	if (unlikely(err)) {
+		/* recvmsg() in non blocking mode is supposed to return -EAGAIN
+		 * sk_rcvtimeo is not honored by mutex_lock_interruptible()
+		 */
+		err = noblock ? -EAGAIN : -ERESTARTSYS;
 		goto out;
 	}
 
diff -ruw linux-3.11.10/net/unix/Kconfig linux-3.11.10-fbx/net/unix/Kconfig
--- linux-3.11.10/net/unix/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/unix/Kconfig	2015-06-12 14:02:28.239000472 +0200
@@ -19,6 +19,9 @@
 
 	  Say Y unless you know what you are doing.
 
+config UNIX_ABSTRACT_IGNORE_NETNS
+	bool "make abstract namespace global to all network namespaces"
+
 config UNIX_DIAG
 	tristate "UNIX: socket monitoring interface"
 	depends on UNIX
diff -ruw linux-3.11.10/net/wireless/chan.c linux-3.11.10-fbx/net/wireless/chan.c
--- linux-3.11.10/net/wireless/chan.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/chan.c	2015-09-29 11:15:09.161757731 +0200
@@ -277,21 +277,42 @@
 				     width, dfs_state);
 }
 
-static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
-					    u32 center_freq,
+static u32 cfg80211_get_start_freq(u32 center_freq,
 					    u32 bandwidth)
 {
-	struct ieee80211_channel *c;
-	u32 freq, start_freq, end_freq;
+	u32 start_freq;
 
-	if (bandwidth <= 20) {
+	if (bandwidth <= 20)
 		start_freq = center_freq;
-		end_freq = center_freq;
-	} else {
+	else
 		start_freq = center_freq - bandwidth/2 + 10;
+
+	return start_freq;
+}
+
+static u32 cfg80211_get_end_freq(u32 center_freq,
+				 u32 bandwidth)
+{
+	u32 end_freq;
+
+	if (bandwidth <= 20)
+		end_freq = center_freq;
+	else
 		end_freq = center_freq + bandwidth/2 - 10;
+
+	return end_freq;
 	}
 
+static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
+					    u32 center_freq,
+					    u32 bandwidth)
+{
+	struct ieee80211_channel *c;
+	u32 freq, start_freq, end_freq;
+
+	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
 	for (freq = start_freq; freq <= end_freq; freq += 20) {
 		c = ieee80211_get_channel(wiphy, freq);
 		if (!c)
@@ -328,6 +349,156 @@
 	return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
 					       width);
 }
+EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
+
+static int cfg80211_get_chans_dfs_usable(struct wiphy *wiphy,
+					 u32 center_freq,
+					 u32 bandwidth)
+{
+	struct ieee80211_channel *c;
+	u32 freq, start_freq, end_freq;
+	int count = 0;
+
+	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+	/*
+	 * Check entire range of channels for the bandwidth.
+	 * Check all channels are DFS channels (DFS_USABLE or
+	 * DFS_AVAILABLE). Return number of usable channels
+	 * (require CAC).
+	 */
+	for (freq = start_freq; freq <= end_freq; freq += 20) {
+		c = ieee80211_get_channel(wiphy, freq);
+		if (!c)
+			return -EINVAL;
+
+		if (c->flags & IEEE80211_CHAN_DISABLED)
+			return -EINVAL;
+
+		if (!(c->flags & IEEE80211_CHAN_RADAR))
+			return -EINVAL;
+
+		if (c->dfs_state == NL80211_DFS_UNAVAILABLE)
+			return -EINVAL;
+
+		if (c->dfs_state == NL80211_DFS_USABLE)
+			count++;
+	}
+
+	return count;
+}
+
+bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
+				 const struct cfg80211_chan_def *chandef)
+{
+	int width;
+	int r1, r2 = 0;
+
+	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+		return false;
+
+	width = cfg80211_chandef_get_width(chandef);
+	if (width < 0)
+		return false;
+
+	r1 = cfg80211_get_chans_dfs_usable(wiphy, chandef->center_freq1,
+					  width);
+
+	if (r1 < 0)
+		return false;
+
+	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_80P80:
+		WARN_ON(!chandef->center_freq2);
+		r2 = cfg80211_get_chans_dfs_usable(wiphy,
+						   chandef->center_freq2,
+						   width);
+		if (r2 < 0)
+			return false;
+		break;
+	default:
+		WARN_ON(chandef->center_freq2);
+		break;
+	}
+
+	return (r1 + r2 > 0);
+}
+
+static int cfg80211_get_chans_dfs_is_weather(struct wiphy *wiphy,
+					     u32 center_freq,
+					     u32 bandwidth)
+{
+	struct ieee80211_channel *c;
+	u32 start_freq, end_freq, freq;
+
+	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+	/*
+	 * ETSI EN 301 893 V1.7.0 Table D.1
+	 * 5600 - 5650MHz - weather radar channels
+	 */
+	for (freq = start_freq; freq <= end_freq; freq += 20) {
+		c = ieee80211_get_channel(wiphy, freq);
+		if (!c)
+			return -EINVAL;
+		if (c->center_freq >= 5600 && c->center_freq <= 5650)
+			return 1;
+	}
+	return 0;
+}
+
+static int
+cfg80211_chandef_dfs_is_weather(struct wiphy *wiphy,
+				const struct cfg80211_chan_def *chandef)
+{
+	int width;
+	int r;
+
+	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+		return -EINVAL;
+
+	width = cfg80211_chandef_get_width(chandef);
+	if (width < 0)
+		return -EINVAL;
+
+	r = cfg80211_get_chans_dfs_is_weather(wiphy, chandef->center_freq1,
+					      width);
+
+	if (r)
+		return r;
+
+	if (!chandef->center_freq2)
+		return 0;
+
+	return cfg80211_get_chans_dfs_is_weather(wiphy, chandef->center_freq2,
+						 width);
+}
+
+unsigned int
+cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
+			      const struct cfg80211_chan_def *chandef,
+			      enum nl80211_dfs_regions dfs_region)
+{
+	unsigned int timeout_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+
+	switch (dfs_region) {
+	case NL80211_DFS_ETSI:
+		if (cfg80211_chandef_dfs_is_weather(wiphy, chandef) > 0)
+			timeout_ms = IEEE80211_DFS_WEATHER_MIN_CAC_TIME_MS;
+		break;
+	/* TODO check JP CAC time */
+	case NL80211_DFS_JP:
+		break;
+	/* FCC don't allow weather channels */
+	case NL80211_DFS_FCC:
+	default:
+		break;
+	}
+
+	return timeout_ms;
+}
 
 static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
 					u32 center_freq, u32 bandwidth,
@@ -335,27 +506,36 @@
 {
 	struct ieee80211_channel *c;
 	u32 freq, start_freq, end_freq;
+	u32 ignore_flags;
 
-	if (bandwidth <= 20) {
-		start_freq = center_freq;
-		end_freq = center_freq;
-	} else {
-		start_freq = center_freq - bandwidth/2 + 10;
-		end_freq = center_freq + bandwidth/2 - 10;
-	}
+	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
 
 	for (freq = start_freq; freq <= end_freq; freq += 20) {
 		c = ieee80211_get_channel(wiphy, freq);
 		if (!c)
 			return false;
 
+		ignore_flags = IEEE80211_CHAN_RADAR;
+
 		/* check for radar flags */
-		if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
-		    (c->dfs_state != NL80211_DFS_AVAILABLE))
+		if (prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) {
+			if (c->dfs_state != NL80211_DFS_AVAILABLE)
 			return false;
+			/*
+			 * If DFS is required we should check only
+			 * c->dfs_state == NL80211_DFS_AVAILABLE and
+			 * ignore IEEE80211_CHAN_NO_IBSS and
+			 * IEEE80211_CHAN_PASSIVE_SCAN flags
+			 */
+			ignore_flags |= IEEE80211_CHAN_NO_IR;
+		}
+
+		WARN_ON(ignore_flags & ~(IEEE80211_CHAN_RADAR |
+					 IEEE80211_CHAN_NO_IR));
 
 		/* check for the other flags */
-		if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
+		if (c->flags & prohibited_flags & ~ignore_flags)
 			return false;
 	}
 
@@ -466,8 +646,7 @@
 
 	res = cfg80211_chandef_usable(wiphy, chandef,
 				      IEEE80211_CHAN_DISABLED |
-				      IEEE80211_CHAN_PASSIVE_SCAN |
-				      IEEE80211_CHAN_NO_IBSS |
+				      IEEE80211_CHAN_NO_IR |
 				      IEEE80211_CHAN_RADAR);
 
 	trace_cfg80211_return_bool(res);
@@ -503,7 +682,8 @@
 	case NL80211_IFTYPE_ADHOC:
 		if (wdev->current_bss) {
 			*chan = wdev->current_bss->pub.channel;
-			*chanmode = wdev->ibss_fixed
+			*chanmode = (wdev->ibss_fixed &&
+				     !wdev->ibss_dfs_possible)
 				  ? CHAN_MODE_SHARED
 				  : CHAN_MODE_EXCLUSIVE;
 			return;
diff -ruw linux-3.11.10/net/wireless/core.c linux-3.11.10-fbx/net/wireless/core.c
--- linux-3.11.10/net/wireless/core.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/core.c	2015-09-29 11:15:09.161757731 +0200
@@ -462,6 +462,14 @@
 		return -EINVAL;
 #endif
 
+	if (WARN_ON(wiphy->coalesce &&
+		    (!wiphy->coalesce->n_rules ||
+		     !wiphy->coalesce->n_patterns) &&
+		    (!wiphy->coalesce->pattern_min_len ||
+		     wiphy->coalesce->pattern_min_len >
+			wiphy->coalesce->pattern_max_len)))
+		return -EINVAL;
+
 	if (WARN_ON(wiphy->ap_sme_capa &&
 		    !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME)))
 		return -EINVAL;
@@ -558,18 +566,13 @@
 	/* check and set up bitrates */
 	ieee80211_set_bitrate_flags(wiphy);
 
-
+	rtnl_lock();
 	res = device_add(&rdev->wiphy.dev);
-	if (res)
-		return res;
-
-	res = rfkill_register(rdev->rfkill);
 	if (res) {
-		device_del(&rdev->wiphy.dev);
+		rtnl_unlock();
 		return res;
 	}
 
-	rtnl_lock();
 	/* set up regulatory info */
 	wiphy_regulatory_register(wiphy);
 
@@ -598,6 +601,15 @@
 
 	rdev->wiphy.registered = true;
 	rtnl_unlock();
+
+	res = rfkill_register(rdev->rfkill);
+	if (res) {
+		rfkill_destroy(rdev->rfkill);
+		rdev->rfkill = NULL;
+		wiphy_unregister(&rdev->wiphy);
+		return res;
+	}
+
 	return 0;
 }
 EXPORT_SYMBOL(wiphy_register);
@@ -632,6 +644,7 @@
 		rtnl_unlock();
 		__count == 0; }));
 
+	if (rdev->rfkill)
 	rfkill_unregister(rdev->rfkill);
 
 	rtnl_lock();
@@ -668,6 +681,7 @@
 		rdev_set_wakeup(rdev, false);
 #endif
 	cfg80211_rdev_free_wowlan(rdev);
+	cfg80211_rdev_free_coalesce(rdev);
 }
 EXPORT_SYMBOL(wiphy_unregister);
 
@@ -944,8 +958,6 @@
 	case NETDEV_PRE_UP:
 		if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
 			return notifier_from_errno(-EOPNOTSUPP);
-		if (rfkill_blocked(rdev->rfkill))
-			return notifier_from_errno(-ERFKILL);
 		ret = cfg80211_can_add_interface(rdev, wdev->iftype);
 		if (ret)
 			return notifier_from_errno(ret);
diff -ruw linux-3.11.10/net/wireless/core.h linux-3.11.10-fbx/net/wireless/core.h
--- linux-3.11.10/net/wireless/core.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/core.h	2015-09-29 11:15:09.165757756 +0200
@@ -79,6 +79,8 @@
 	/* netlink port which started critical protocol (0 means not started) */
 	u32 crit_proto_nlportid;
 
+	struct cfg80211_coalesce *coalesce;
+
 	/* must be last because of the way we do wiphy_priv(),
 	 * and it should at least be aligned to NETDEV_ALIGN */
 	struct wiphy wiphy __aligned(NETDEV_ALIGN);
@@ -226,6 +228,14 @@
 	CHAN_MODE_EXCLUSIVE,
 };
 
+struct cfg80211_iftype_chan_param {
+	struct wireless_dev *wdev;
+	enum nl80211_iftype iftype;
+	struct ieee80211_channel *chan;
+	enum cfg80211_chan_mode chanmode;
+	u8 radar_detect_width;
+};
+
 struct cfg80211_beacon_registration {
 	struct list_head list;
 	u32 nlportid;
@@ -373,21 +383,20 @@
 void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
 void cfg80211_process_wdev_events(struct wireless_dev *wdev);
 
-int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
-				 struct wireless_dev *wdev,
-				 enum nl80211_iftype iftype,
-				 struct ieee80211_channel *chan,
-				 enum cfg80211_chan_mode chanmode,
-				 u8 radar_detect);
+int cfg80211_can_use_iftype_chan_params(struct cfg80211_registered_device *rdev,
+				const struct cfg80211_iftype_chan_param *params,
+				int num_params);
 
 /**
- * cfg80211_chandef_dfs_required - checks if radar detection is required
+ * cfg80211_chandef_dfs_usable - checks if chandef is usable and we can
+ * start CAC on such chandef.
  * @wiphy: the wiphy to validate against
  * @chandef: the channel definition to check
- * Return: 1 if radar detection is required, 0 if it is not, < 0 on error
+ * Return: Return true if all channels available and at least
+ * one channel required CAC (NL80211_DFS_USABLE)
  */
-int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
-				  const struct cfg80211_chan_def *c);
+bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
+				 const struct cfg80211_chan_def *chandef);
 
 void cfg80211_set_dfs_state(struct wiphy *wiphy,
 			    const struct cfg80211_chan_def *chandef,
@@ -395,6 +404,29 @@
 
 void cfg80211_dfs_channels_update_work(struct work_struct *work);
 
+unsigned int
+cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
+			      const struct cfg80211_chan_def *chandef,
+			      enum nl80211_dfs_regions dfs_region);
+
+static inline int
+cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
+			     struct wireless_dev *wdev,
+			     enum nl80211_iftype iftype,
+			     struct ieee80211_channel *chan,
+			     enum cfg80211_chan_mode chanmode,
+			     u8 radar_detect_width)
+{
+	struct cfg80211_iftype_chan_param param = {
+		.wdev = wdev,
+		.iftype = iftype,
+		.chan = chan,
+		.chanmode = chanmode,
+		.radar_detect_width = radar_detect_width,
+	};
+
+	return cfg80211_can_use_iftype_chan_params(rdev, &param, 1);
+}
 
 static inline int
 cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
@@ -409,6 +441,9 @@
 cfg80211_can_add_interface(struct cfg80211_registered_device *rdev,
 			   enum nl80211_iftype iftype)
 {
+	if (rfkill_blocked(rdev->rfkill))
+		return -ERFKILL;
+
 	return cfg80211_can_change_interface(rdev, NULL, iftype);
 }
 
diff -ruw linux-3.11.10/net/wireless/db.txt linux-3.11.10-fbx/net/wireless/db.txt
--- linux-3.11.10/net/wireless/db.txt	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/db.txt	2015-06-12 14:02:28.247000530 +0200
@@ -1,17 +1,823 @@
-#
-# This file is a placeholder to prevent accidental build breakage if someone
-# enables CONFIG_CFG80211_INTERNAL_REGDB.  Almost no one actually needs to
-# enable that build option.
-#
-# You should be using CRDA instead.  It is even better if you use the CRDA
-# package provided by your distribution, since they will probably keep it
-# up-to-date on your behalf.
-#
-# If you _really_ intend to use CONFIG_CFG80211_INTERNAL_REGDB then you will
-# need to replace this file with one containing appropriately formatted
-# regulatory rules that cover the regulatory domains you will be using.  Your
-# best option is to extract the db.txt file from the wireless-regdb git
-# repository:
-#
-#   git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git
-#
+# This is the world regulatory domain
+country 00:
+	(2402 - 2472 @ 40), (3, 20)
+	# Channel 12 - 13.
+	(2457 - 2482 @ 40), (3, 20), PASSIVE-SCAN, NO-IBSS
+	# Channel 14. Only JP enables this and for 802.11b only
+	(2474 - 2494 @ 20), (3, 20), PASSIVE-SCAN, NO-IBSS, NO-OFDM
+	# Channel 36 - 48
+	(5170 - 5250 @ 80), (3, 20), PASSIVE-SCAN, NO-IBSS
+	# NB: 5260 MHz - 5700 MHz requies DFS
+	# Channel 149 - 165
+	(5735 - 5835 @ 80), (3, 20), PASSIVE-SCAN, NO-IBSS
+	# IEEE 802.11ad (60GHz), channels 1..3
+	(57240 - 63720 @ 2160), (N/A, 0)
+
+
+country AD:
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+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 AL:
+	(2402 - 2482 @ 20), (N/A, 20)
+
+country AM:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (N/A, 18)
+	(5250 - 5330 @ 20), (N/A, 18), DFS
+
+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 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 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
+
+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)
+
+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 AZ:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 18)
+	(5250 - 5330 @ 40), (N/A, 18), DFS
+
+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
+
+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 BD:
+	(2402 - 2482 @ 40), (N/A, 20)
+
+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
+
+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
+
+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 BL:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 18)
+	(5250 - 5330 @ 40), (N/A, 18), 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 BO:
+	(2402 - 2482 @ 40), (N/A, 30)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+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 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 BZ:
+	(2402 - 2482 @ 40), (N/A, 30)
+	(5735 - 5835 @ 40), (N/A, 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)
+
+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
+
+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 CN:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 80), (N/A, 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)
+
+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 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 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
+
+# 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.
+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
+
+# 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.
+
+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
+
+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
+
+country DO:
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country DZ:
+	(2402 - 2482 @ 40), (N/A, 20)
+
+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 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
+
+country EG:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (N/A, 20)
+	(5250 - 5330 @ 20), (N/A, 20), DFS
+
+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
+
+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
+
+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
+
+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
+
+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
+
+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 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 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
+
+country GT:
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+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)
+
+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 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 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
+
+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 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
+
+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 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
+
+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 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)
+
+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
+
+country IR:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+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
+
+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 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:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 18)
+
+country KE:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+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 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)
+
+country KR:
+	(2402 - 2482 @ 20), (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 KW:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 80), (N/A, 20)
+	(5250 - 5330 @ 80), (N/A, 20), DFS
+
+country KZ:
+	(2402 - 2482 @ 40), (N/A, 20)
+
+country LB:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+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
+
+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 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
+
+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
+
+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
+
+country MC: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 18)
+	(5250 - 5330 @ 40), (N/A, 18), DFS
+
+country MA:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 80), (N/A, 23)
+	(5735 - 5835 @ 80), (N/A, 23)
+
+country MO:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 23)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+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
+
+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
+
+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 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)
+
+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
+
+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
+
+country NP:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+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 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 PA:
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+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 PG:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+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 PK:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+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
+
+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
+
+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 QA:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+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
+
+
+# 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
+
+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)
+
+country RW:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 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 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
+
+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)
+
+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
+
+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
+
+country SV:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (3, 17)
+	(5250 - 5330 @ 20), (3, 23), DFS
+	(5735 - 5835 @ 20), (3, 30)
+
+country SY:
+	(2402 - 2482 @ 40), (N/A, 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)
+
+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 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 TN:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (N/A, 20)
+	(5250 - 5330 @ 20), (N/A, 20), DFS
+
+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
+ 
+# 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
+# (appendix 8)
+# 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 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)
+	# 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)
+
+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 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)
+
+country VE:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5815 @ 40), (N/A, 23)
+
+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)
+
+country YE:
+	(2402 - 2482 @ 40), (N/A, 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 ZW:
+	(2402 - 2482 @ 40), (N/A, 20)
+
diff -ruw linux-3.11.10/net/wireless/genregdb.awk linux-3.11.10-fbx/net/wireless/genregdb.awk
--- linux-3.11.10/net/wireless/genregdb.awk	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/genregdb.awk	2015-09-29 11:15:09.165757756 +0200
@@ -46,6 +46,12 @@
 	sub(/:/, "", country)
 	printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
 	printf "\t.alpha2 = \"%s\",\n", country
+	if ($NF ~ /DFS-ETSI/)
+		printf "\t.dfs_region = NL80211_DFS_ETSI,\n"
+	else if ($NF ~ /DFS-FCC/)
+		printf "\t.dfs_region = NL80211_DFS_FCC,\n"
+	else if ($NF ~ /DFS-JP/)
+		printf "\t.dfs_region = NL80211_DFS_JP,\n"
 	printf "\t.reg_rules = {\n"
 	active = 1
 	regdb = regdb "\t&regdom_" country ",\n"
@@ -101,10 +107,13 @@
 		} else if (flagarray[arg] == "PTMP-ONLY") {
 			flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | "
 		} else if (flagarray[arg] == "PASSIVE-SCAN") {
-			flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | "
+			flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
 		} else if (flagarray[arg] == "NO-IBSS") {
-			flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | "
+			flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
+		} else if (flagarray[arg] == "NO-IR") {
+			flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
 		}
+
 	}
 	flags = flags "0"
 	printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags
diff -ruw linux-3.11.10/net/wireless/ibss.c linux-3.11.10-fbx/net/wireless/ibss.c
--- linux-3.11.10/net/wireless/ibss.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/ibss.c	2015-09-29 11:15:09.165757756 +0200
@@ -83,6 +83,8 @@
 			 struct cfg80211_cached_keys *connkeys)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct ieee80211_channel *check_chan;
+	u8 radar_detect_width = 0;
 	int err;
 
 	ASSERT_WDEV_LOCK(wdev);
@@ -114,14 +116,28 @@
 	wdev->connect_keys = connkeys;
 
 	wdev->ibss_fixed = params->channel_fixed;
+	wdev->ibss_dfs_possible = params->userspace_handles_dfs;
 #ifdef CONFIG_CFG80211_WEXT
 	wdev->wext.ibss.chandef = params->chandef;
 #endif
+	check_chan = params->chandef.chan;
+	if (params->userspace_handles_dfs) {
+		/* use channel NULL to check for radar even if the current
+		 * channel is not a radar channel - it might decide to change
+		 * to DFS channel later.
+		 */
+		radar_detect_width = BIT(params->chandef.width);
+		check_chan = NULL;
+	}
 
-	err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
-				    params->channel_fixed
+	err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+					   check_chan,
+					   (params->channel_fixed &&
+					    !radar_detect_width)
 				    ? CHAN_MODE_SHARED
-				    : CHAN_MODE_EXCLUSIVE);
+					   : CHAN_MODE_EXCLUSIVE,
+					   radar_detect_width);
+
 	if (err) {
 		wdev->connect_keys = NULL;
 		return err;
@@ -258,7 +274,7 @@
 
 			for (i = 0; i < sband->n_channels; i++) {
 				chan = &sband->channels[i];
-				if (chan->flags & IEEE80211_CHAN_NO_IBSS)
+				if (chan->flags & IEEE80211_CHAN_NO_IR)
 					continue;
 				if (chan->flags & IEEE80211_CHAN_DISABLED)
 					continue;
@@ -329,7 +345,7 @@
 		chan = ieee80211_get_channel(wdev->wiphy, freq);
 		if (!chan)
 			return -EINVAL;
-		if (chan->flags & IEEE80211_CHAN_NO_IBSS ||
+		if (chan->flags & IEEE80211_CHAN_NO_IR ||
 		    chan->flags & IEEE80211_CHAN_DISABLED)
 			return -EINVAL;
 	}
diff -ruw linux-3.11.10/net/wireless/mesh.c linux-3.11.10-fbx/net/wireless/mesh.c
--- linux-3.11.10/net/wireless/mesh.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/mesh.c	2015-09-29 11:15:09.165757756 +0200
@@ -141,8 +141,7 @@
 
 			for (i = 0; i < sband->n_channels; i++) {
 				chan = &sband->channels[i];
-				if (chan->flags & (IEEE80211_CHAN_NO_IBSS |
-						   IEEE80211_CHAN_PASSIVE_SCAN |
+				if (chan->flags & (IEEE80211_CHAN_NO_IR |
 						   IEEE80211_CHAN_DISABLED |
 						   IEEE80211_CHAN_RADAR))
 					continue;
@@ -167,9 +166,12 @@
 	 * basic rates
 	 */
 	if (!setup->basic_rates) {
+		enum nl80211_bss_scan_width scan_width;
 		struct ieee80211_supported_band *sband =
 				rdev->wiphy.bands[setup->chandef.chan->band];
-		setup->basic_rates = ieee80211_mandatory_rates(sband);
+		scan_width = cfg80211_chandef_to_scan_width(&setup->chandef);
+		setup->basic_rates = ieee80211_mandatory_rates(sband,
+							       scan_width);
 	}
 
 	if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
diff -ruw linux-3.11.10/net/wireless/mlme.c linux-3.11.10-fbx/net/wireless/mlme.c
--- linux-3.11.10/net/wireless/mlme.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/mlme.c	2015-09-29 11:15:09.165757756 +0200
@@ -621,7 +621,7 @@
 }
 
 bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
-		      const u8 *buf, size_t len, gfp_t gfp)
+		      const u8 *buf, size_t len, u32 flags, gfp_t gfp)
 {
 	struct wiphy *wiphy = wdev->wiphy;
 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
@@ -664,7 +664,7 @@
 		/* Indicate the received Action frame to user space */
 		if (nl80211_send_mgmt(rdev, wdev, reg->nlportid,
 				      freq, sig_mbm,
-				      buf, len, gfp))
+				      buf, len, flags, gfp))
 			continue;
 
 		result = true;
@@ -707,11 +707,13 @@
 			if (c->dfs_state != NL80211_DFS_UNAVAILABLE)
 				continue;
 
-			timeout = c->dfs_state_entered +
-				  IEEE80211_DFS_MIN_NOP_TIME_MS;
+			timeout = c->dfs_state_entered + msecs_to_jiffies(
+					IEEE80211_DFS_MIN_NOP_TIME_MS);
 
 			if (time_after_eq(jiffies, timeout)) {
 				c->dfs_state = NL80211_DFS_USABLE;
+				c->dfs_state_entered = jiffies;
+
 				cfg80211_chandef_create(&chandef, c,
 							NL80211_CHAN_NO_HT);
 
@@ -761,12 +763,12 @@
 EXPORT_SYMBOL(cfg80211_radar_event);
 
 void cfg80211_cac_event(struct net_device *netdev,
+			struct cfg80211_chan_def *chandef,
 			enum nl80211_radar_event event, gfp_t gfp)
 {
 	struct wireless_dev *wdev = netdev->ieee80211_ptr;
 	struct wiphy *wiphy = wdev->wiphy;
 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-	struct cfg80211_chan_def chandef;
 	unsigned long timeout;
 
 	trace_cfg80211_cac_event(netdev, event);
@@ -777,14 +779,12 @@
 	if (WARN_ON(!wdev->channel))
 		return;
 
-	cfg80211_chandef_create(&chandef, wdev->channel, NL80211_CHAN_NO_HT);
-
 	switch (event) {
 	case NL80211_RADAR_CAC_FINISHED:
 		timeout = wdev->cac_start_time +
-			  msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
+			  msecs_to_jiffies(wdev->cac_time_ms);
 		WARN_ON(!time_after_eq(jiffies, timeout));
-		cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_AVAILABLE);
+		cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
 		break;
 	case NL80211_RADAR_CAC_ABORTED:
 		break;
@@ -794,6 +794,6 @@
 	}
 	wdev->cac_started = false;
 
-	nl80211_radar_notify(rdev, &chandef, event, netdev, gfp);
+	nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
 }
 EXPORT_SYMBOL(cfg80211_cac_event);
diff -ruw linux-3.11.10/net/wireless/nl80211.c linux-3.11.10-fbx/net/wireless/nl80211.c
--- linux-3.11.10/net/wireless/nl80211.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/nl80211.c	2015-09-29 11:15:09.165757756 +0200
@@ -349,6 +349,16 @@
 	[NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
 				  .len = IEEE80211_MAX_DATA_LEN },
 	[NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
+	[NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },
+	[NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },
+	[NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
+	[NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
+	[NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
+	[NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
+	[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
+	[NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
+	[NL80211_ATTR_CH_SWITCH_IFACES] = { .type = NLA_NESTED },
+	[NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 },
 };
 
 /* policy for the key attributes */
@@ -403,6 +413,14 @@
 	[NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },
 };
 
+/* policy for coalesce rule attributes */
+static const struct nla_policy
+nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = {
+	[NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 },
+	[NL80211_ATTR_COALESCE_RULE_CONDITION] = { .type = NLA_U32 },
+	[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED },
+};
+
 /* policy for GTK rekey offload attributes */
 static const struct nla_policy
 nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
@@ -529,12 +547,12 @@
 	if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
 	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
 		goto nla_put_failure;
-	if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
-	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN))
+	if (chan->flags & IEEE80211_CHAN_NO_IR) {
+		if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IR))
 		goto nla_put_failure;
-	if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
-	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
+		if (nla_put_flag(msg, __NL80211_FREQUENCY_ATTR_NO_IBSS))
 		goto nla_put_failure;
+	}
 	if (chan->flags & IEEE80211_CHAN_RADAR) {
 		if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
 			goto nla_put_failure;
@@ -976,7 +994,7 @@
 		return -ENOBUFS;
 
 	if (dev->wiphy.wowlan->n_patterns) {
-		struct nl80211_wowlan_pattern_support pat = {
+		struct nl80211_pattern_support pat = {
 			.max_patterns = dev->wiphy.wowlan->n_patterns,
 			.min_pattern_len = dev->wiphy.wowlan->pattern_min_len,
 			.max_pattern_len = dev->wiphy.wowlan->pattern_max_len,
@@ -997,6 +1015,27 @@
 }
 #endif
 
+static int nl80211_send_coalesce(struct sk_buff *msg,
+				 struct cfg80211_registered_device *dev)
+{
+	struct nl80211_coalesce_rule_support rule;
+
+	if (!dev->wiphy.coalesce)
+		return 0;
+
+	rule.max_rules = dev->wiphy.coalesce->n_rules;
+	rule.max_delay = dev->wiphy.coalesce->max_delay;
+	rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns;
+	rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len;
+	rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len;
+	rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset;
+
+	if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))
+		return -ENOBUFS;
+
+	return 0;
+}
+
 static int nl80211_send_band_rateinfo(struct sk_buff *msg,
 				      struct ieee80211_supported_band *sband)
 {
@@ -1191,10 +1230,9 @@
 		if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
 		    nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
 			goto nla_put_failure;
-		if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
-		    nla_put_flag(msg, WIPHY_FLAG_SUPPORTS_5_10_MHZ))
+		if ((dev->wiphy.flags & WIPHY_FLAG_HAS_MULTI_IF_CHSWITCH) &&
+		     nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_IFACES))
 			goto nla_put_failure;
-
 		state->split_start++;
 		if (state->split)
 			break;
@@ -1395,6 +1433,8 @@
 		if (state->split) {
 			CMD(crit_proto_start, CRIT_PROTOCOL_START);
 			CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
+			if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
+				CMD(channel_switch, CHANNEL_SWITCH);
 		}
 
 #ifdef CONFIG_NL80211_TESTMODE
@@ -1515,6 +1555,12 @@
 			    dev->wiphy.vht_capa_mod_mask))
 			goto nla_put_failure;
 
+		state->split_start++;
+		break;
+	case 10:
+		if (nl80211_send_coalesce(msg, dev))
+			goto nla_put_failure;
+
 		/* done */
 		state->split_start = 0;
 		break;
@@ -1613,9 +1659,10 @@
 				 * We can then retry with the larger buffer.
 				 */
 				if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
-				    !skb->len &&
+				    !skb->len && !state->split &&
 				    cb->min_dump_alloc < 4096) {
 					cb->min_dump_alloc = 4096;
+					state->split_start = 0;
 					rtnl_unlock();
 					return 1;
 				}
@@ -1710,15 +1757,15 @@
 }
 
 static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
-				 struct genl_info *info,
+				 struct nlattr **attrs,
 				 struct cfg80211_chan_def *chandef)
 {
 	u32 control_freq;
 
-	if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+	if (!attrs[NL80211_ATTR_WIPHY_FREQ])
 		return -EINVAL;
 
-	control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+	control_freq = nla_get_u32(attrs[NL80211_ATTR_WIPHY_FREQ]);
 
 	chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq);
 	chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
@@ -1729,11 +1776,10 @@
 	if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED)
 		return -EINVAL;
 
-	if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+	if (attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
 		enum nl80211_channel_type chantype;
 
-		chantype = nla_get_u32(
-				info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+		chantype = nla_get_u32(attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
 
 		switch (chantype) {
 		case NL80211_CHAN_NO_HT:
@@ -1746,17 +1792,15 @@
 		default:
 			return -EINVAL;
 		}
-	} else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) {
+	} else if (attrs[NL80211_ATTR_CHANNEL_WIDTH]) {
 		chandef->width =
-			nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]);
-		if (info->attrs[NL80211_ATTR_CENTER_FREQ1])
+			nla_get_u32(attrs[NL80211_ATTR_CHANNEL_WIDTH]);
+		if (attrs[NL80211_ATTR_CENTER_FREQ1])
 			chandef->center_freq1 =
-				nla_get_u32(
-					info->attrs[NL80211_ATTR_CENTER_FREQ1]);
-		if (info->attrs[NL80211_ATTR_CENTER_FREQ2])
+				nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ1]);
+		if (attrs[NL80211_ATTR_CENTER_FREQ2])
 			chandef->center_freq2 =
-				nla_get_u32(
-					info->attrs[NL80211_ATTR_CENTER_FREQ2]);
+				nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ2]);
 	}
 
 	if (!cfg80211_chandef_valid(chandef))
@@ -1788,7 +1832,7 @@
 	if (!nl80211_can_set_dev_channel(wdev))
 		return -EOPNOTSUPP;
 
-	result = nl80211_parse_chandef(rdev, info, &chandef);
+	result = nl80211_parse_chandef(rdev, info->attrs, &chandef);
 	if (result)
 		return result;
 
@@ -3140,7 +3184,7 @@
 	}
 
 	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
-		err = nl80211_parse_chandef(rdev, info, &params.chandef);
+		err = nl80211_parse_chandef(rdev, info->attrs, &params.chandef);
 		if (err)
 			return err;
 	} else if (wdev->preset_chandef.chan) {
@@ -3172,6 +3216,7 @@
 			return PTR_ERR(params.acl);
 	}
 
+	wdev_lock(wdev);
 	err = rdev_start_ap(rdev, dev, &params);
 	if (!err) {
 		wdev->preset_chandef = params.chandef;
@@ -3180,6 +3225,7 @@
 		wdev->ssid_len = params.ssid_len;
 		memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
 	}
+	wdev_unlock(wdev);
 
 	kfree(params.acl);
 
@@ -3208,7 +3254,11 @@
 	if (err)
 		return err;
 
-	return rdev_change_beacon(rdev, dev, &params);
+	wdev_lock(wdev);
+	err = rdev_change_beacon(rdev, dev, &params);
+	wdev_unlock(wdev);
+
+	return err;
 }
 
 static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
@@ -3854,9 +3904,45 @@
 	return 0;
 }
 
+static int nl80211_parse_sta_channel_info(struct genl_info *info,
+				      struct station_parameters *params)
+{
+	if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) {
+		params->supported_channels =
+		     nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+		params->supported_channels_len =
+		     nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+		/*
+		 * Need to include at least one (first channel, number of
+		 * channels) tuple for each subband, and must have proper
+		 * tuples for the rest of the data as well.
+		 */
+		if (params->supported_channels_len < 2)
+			return -EINVAL;
+		if (params->supported_channels_len % 2)
+			return -EINVAL;
+	}
+
+	if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) {
+		params->supported_oper_classes =
+		 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+		params->supported_oper_classes_len =
+		  nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+		/*
+		 * The value of the Length field of the Supported Operating
+		 * Classes element is between 2 and 253.
+		 */
+		if (params->supported_oper_classes_len < 2 ||
+		    params->supported_oper_classes_len > 253)
+			return -EINVAL;
+	}
+	return 0;
+}
+
 static int nl80211_set_station_tdls(struct genl_info *info,
 				    struct station_parameters *params)
 {
+	int err;
 	/* Dummy STA entry gets updated once the peer capabilities are known */
 	if (info->attrs[NL80211_ATTR_PEER_AID])
 		params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
@@ -3867,6 +3953,10 @@
 		params->vht_capa =
 			nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
 
+	err = nl80211_parse_sta_channel_info(info, params);
+	if (err)
+		return err;
+
 	return nl80211_parse_sta_wme(info, params);
 }
 
@@ -4040,6 +4130,12 @@
 		params.vht_capa =
 			nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
 
+	if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) {
+		params.opmode_notif_used = true;
+		params.opmode_notif =
+			nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]);
+	}
+
 	if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
 		params.plink_action =
 			nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
@@ -4047,6 +4143,10 @@
 			return -EINVAL;
 	}
 
+	err = nl80211_parse_sta_channel_info(info, &params);
+	if (err)
+		return err;
+
 	err = nl80211_parse_sta_wme(info, &params);
 	if (err)
 		return err;
@@ -4370,7 +4470,9 @@
 {
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
 	struct net_device *dev = info->user_ptr[1];
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
 	struct bss_parameters params;
+	int err;
 
 	memset(&params, 0, sizeof(params));
 	/* default to not changing parameters */
@@ -4436,7 +4538,11 @@
 	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
 		return -EOPNOTSUPP;
 
-	return rdev_change_bss(rdev, dev, &params);
+	wdev_lock(wdev);
+	err = rdev_change_bss(rdev, dev, &params);
+	wdev_unlock(wdev);
+
+	return err;
 }
 
 static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
@@ -4990,7 +5096,7 @@
 	char *alpha2 = NULL;
 	int rem_reg_rules = 0, r = 0;
 	u32 num_rules = 0, rule_idx = 0, size_of_regd;
-	u8 dfs_region = 0;
+	enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;
 	struct ieee80211_regdomain *rd = NULL;
 
 	if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
@@ -5543,12 +5649,21 @@
 	struct net_device *dev = info->user_ptr[1];
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
 	struct cfg80211_chan_def chandef;
+	enum nl80211_dfs_regions dfs_region;
+	unsigned int cac_time_ms;
 	int err;
 
-	err = nl80211_parse_chandef(rdev, info, &chandef);
+	dfs_region = reg_get_dfs_region(wdev->wiphy);
+	if (dfs_region == NL80211_DFS_UNSET)
+		return -EINVAL;
+
+	err = nl80211_parse_chandef(rdev, info->attrs, &chandef);
 	if (err)
 		return err;
 
+	if (netif_carrier_ok(dev))
+		return -EBUSY;
+
 	if (wdev->cac_started)
 		return -EBUSY;
 
@@ -5559,7 +5674,7 @@
 	if (err == 0)
 		return -EINVAL;
 
-	if (chandef.chan->dfs_state != NL80211_DFS_USABLE)
+	if (!cfg80211_chandef_dfs_usable(wdev->wiphy, &chandef))
 		return -EINVAL;
 
 	if (!rdev->ops->start_radar_detection)
@@ -5571,15 +5686,255 @@
 	if (err)
 		return err;
 
-	err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
+	cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef,
+						    dfs_region);
+
+	err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef,
+					       cac_time_ms);
 	if (!err) {
 		wdev->channel = chandef.chan;
 		wdev->cac_started = true;
 		wdev->cac_start_time = jiffies;
+		wdev->cac_time_ms = cac_time_ms;
 	}
 	return err;
 }
 
+static int
+nl80211_parse_csa_settings(struct cfg80211_registered_device *rdev,
+			   struct nlattr **attrs,
+			   struct cfg80211_csa_settings *params,
+			   struct cfg80211_iftype_chan_param *ifch_params)
+{
+	/* static variables avoid waste of stack size - this
+	 * function is called under RTNL lock, so this should not be a problem.
+	 */
+	static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
+	struct net_device *dev = NULL;
+	struct wireless_dev *wdev;
+	int err;
+	bool need_new_beacon = false;
+
+	ASSERT_RTNL();
+
+	if (!attrs[NL80211_ATTR_IFINDEX])
+		return -EINVAL;
+
+	dev = dev_get_by_index(wiphy_net(&rdev->wiphy),
+			       nla_get_u32(attrs[NL80211_ATTR_IFINDEX]));
+	if (!dev)
+		return -ENOENT;
+
+	params->dev = dev;
+	wdev = dev->ieee80211_ptr;
+	if (!wdev) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (!netif_running(dev)) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	switch (dev->ieee80211_ptr->iftype) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_P2P_GO:
+		need_new_beacon = true;
+
+		/* useless if AP is not running */
+		if (!wdev->beacon_interval) {
+			err = -EINVAL;
+			goto out;
+		}
+		break;
+	case NL80211_IFTYPE_ADHOC:
+	case NL80211_IFTYPE_MESH_POINT:
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	if (!attrs[NL80211_ATTR_WIPHY_FREQ] ||
+	    !attrs[NL80211_ATTR_CH_SWITCH_COUNT]) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	/* only important for AP, IBSS and mesh create IEs internally */
+	if (need_new_beacon && !attrs[NL80211_ATTR_CSA_IES]) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	params->count = nla_get_u32(attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
+
+	if (!need_new_beacon)
+		goto skip_beacons;
+
+	err = nl80211_parse_beacon(attrs, &params->beacon_after);
+	if (err)
+		goto out;
+
+	err = nla_parse_nested(csa_attrs, NL80211_ATTR_MAX,
+			       attrs[NL80211_ATTR_CSA_IES],
+			       nl80211_policy);
+	if (err)
+		goto out;
+
+	err = nl80211_parse_beacon(csa_attrs, &params->beacon_csa);
+	if (err)
+		goto out;
+
+	if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	params->counter_offset_beacon =
+		nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+	if (params->counter_offset_beacon >= params->beacon_csa.tail_len) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	/* sanity check - counters should be the same */
+	if (params->beacon_csa.tail[params->counter_offset_beacon] !=
+	    params->count) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
+		params->counter_offset_presp =
+			nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+		if (params->counter_offset_presp >=
+		    params->beacon_csa.probe_resp_len) {
+			err = -EINVAL;
+			goto out;
+		}
+
+		if (params->beacon_csa.probe_resp[params->counter_offset_presp] !=
+		    params->count) {
+			err = -EINVAL;
+			goto out;
+		}
+	}
+
+skip_beacons:
+	err = nl80211_parse_chandef(rdev, attrs, &params->chandef);
+	if (err)
+		goto out;
+
+	if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params->chandef)) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
+	    dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO ||
+	    dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC) {
+		err = cfg80211_chandef_dfs_required(wdev->wiphy,
+						    &params->chandef);
+		if (err < 0) {
+			err = -EINVAL;
+			goto out;
+		} else if (err) {
+			params->radar_required = true;
+		}
+	}
+
+	if (attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
+		params->block_tx = true;
+
+	ifch_params->wdev = wdev;
+	ifch_params->iftype = wdev->iftype;
+	ifch_params->chan = params->chandef.chan;
+	ifch_params->chanmode = CHAN_MODE_SHARED;
+	ifch_params->radar_detect_width = params->radar_required
+					? BIT(params->chandef.width)
+					: 0;
+
+out:
+	dev_put(dev);
+	return 0;
+}
+
+static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_device *rdev = info->user_ptr[0];
+	struct cfg80211_csa_settings *csa_params;
+	struct cfg80211_iftype_chan_param *ifch_params;
+	struct nlattr *attrs;
+	/* static variables avoid waste of stack size - this function is called
+	 * under RTNL lock, so this should not be a problem. */
+	static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
+	int err, num_params = 0, tmp;
+
+	if (!rdev->ops->channel_switch ||
+	    !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
+		return -EOPNOTSUPP;
+
+	if (info->attrs[NL80211_ATTR_CH_SWITCH_IFACES]) {
+		if (!(rdev->wiphy.flags & NL80211_ATTR_CH_SWITCH_IFACES))
+			return -EOPNOTSUPP;
+
+		nla_for_each_nested(attrs,
+				    info->attrs[NL80211_ATTR_CH_SWITCH_IFACES],
+				    tmp)
+			num_params++;
+	} else {
+		num_params = 1;
+	}
+
+	csa_params = kzalloc(sizeof(*csa_params) * num_params, GFP_KERNEL);
+	if (!csa_params)
+		return -ENOMEM;
+
+	ifch_params = kzalloc(sizeof(*ifch_params) * num_params, GFP_KERNEL);
+	if (!ifch_params) {
+		kfree(csa_params);
+		return -ENOMEM;
+	}
+
+	if (info->attrs[NL80211_ATTR_CH_SWITCH_IFACES]) {
+		int i = 0;
+
+		nla_for_each_nested(attrs,
+				    info->attrs[NL80211_ATTR_CH_SWITCH_IFACES],
+				    tmp) {
+			nla_parse(csa_attrs, NL80211_ATTR_MAX, nla_data(attrs),
+				  nla_len(attrs), nl80211_policy);
+
+			err = nl80211_parse_csa_settings(rdev, csa_attrs,
+							 &csa_params[i],
+							 &ifch_params[i]);
+			if (err)
+				goto out;
+			i++;
+		}
+	} else {
+		err = nl80211_parse_csa_settings(rdev, info->attrs,
+						 &csa_params[0],
+						 &ifch_params[0]);
+		if (err)
+			goto out;
+	}
+
+	err = cfg80211_can_use_iftype_chan_params(rdev, ifch_params,
+						  num_params);
+	if (err)
+		goto out;
+
+	err = rdev_channel_switch(rdev, csa_params, num_params);
+
+out:
+	kfree(csa_params);
+	kfree(ifch_params);
+	return err;
+}
+
 static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
 			    u32 seq, int flags,
 			    struct cfg80211_registered_device *rdev,
@@ -5641,6 +5996,7 @@
 		goto nla_put_failure;
 	if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) ||
 	    nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
+	    nla_put_u32(msg, NL80211_BSS_CHAN_WIDTH, res->scan_width) ||
 	    nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
 			jiffies_to_msecs(jiffies - intbss->ts)))
 		goto nla_put_failure;
@@ -6313,7 +6669,7 @@
 		ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
 	}
 
-	err = nl80211_parse_chandef(rdev, info, &ibss.chandef);
+	err = nl80211_parse_chandef(rdev, info->attrs, &ibss.chandef);
 	if (err)
 		return err;
 
@@ -6321,6 +6677,8 @@
 		return -EINVAL;
 
 	switch (ibss.chandef.width) {
+	case NL80211_CHAN_WIDTH_5:
+	case NL80211_CHAN_WIDTH_10:
 	case NL80211_CHAN_WIDTH_20_NOHT:
 		break;
 	case NL80211_CHAN_WIDTH_20:
@@ -6348,6 +6706,19 @@
 			return err;
 	}
 
+	if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+		memcpy(&ibss.ht_capa_mask,
+		       nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+		       sizeof(ibss.ht_capa_mask));
+
+	if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
+		if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+			return -EINVAL;
+		memcpy(&ibss.ht_capa,
+		       nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+		       sizeof(ibss.ht_capa));
+	}
+
 	if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
 	    !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
 			nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
@@ -6372,6 +6743,9 @@
 	ibss.control_port =
 		nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
 
+	ibss.userspace_handles_dfs =
+		nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
+
 	err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
 	if (err)
 		kfree(connkeys);
@@ -6430,19 +6804,30 @@
 static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
 {
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
+	struct wireless_dev *wdev =
+		__cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
 	int err;
 
+	if (!rdev->ops->testmode_cmd)
+		return -EOPNOTSUPP;
+
+	if (IS_ERR(wdev)) {
+		err = PTR_ERR(wdev);
+		if (err != -EINVAL)
+			return err;
+		wdev = NULL;
+	} else if (wdev->wiphy != &rdev->wiphy) {
+		return -EINVAL;
+	}
+
 	if (!info->attrs[NL80211_ATTR_TESTDATA])
 		return -EINVAL;
 
-	err = -EOPNOTSUPP;
-	if (rdev->ops->testmode_cmd) {
 		rdev->testmode_info = info;
-		err = rdev_testmode_cmd(rdev,
+	err = rdev_testmode_cmd(rdev, wdev,
 				nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
 				nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
 		rdev->testmode_info = NULL;
-	}
 
 	return err;
 }
@@ -6944,7 +7329,7 @@
 	    duration > rdev->wiphy.max_remain_on_channel_duration)
 		return -EINVAL;
 
-	err = nl80211_parse_chandef(rdev, info, &chandef);
+	err = nl80211_parse_chandef(rdev, info->attrs, &chandef);
 	if (err)
 		return err;
 
@@ -7049,11 +7434,73 @@
 	return true;
 }
 
+static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map)
+{
+	u16 mcs_mask = 0;
+
+	switch (vht_mcs_map) {
+	case IEEE80211_VHT_MCS_NOT_SUPPORTED:
+		break;
+	case IEEE80211_VHT_MCS_SUPPORT_0_7:
+		mcs_mask = 0x00FF;
+		break;
+	case IEEE80211_VHT_MCS_SUPPORT_0_8:
+		mcs_mask = 0x01FF;
+		break;
+	case IEEE80211_VHT_MCS_SUPPORT_0_9:
+		mcs_mask = 0x03FF;
+		break;
+	default:
+		break;
+	}
+
+	return mcs_mask;
+}
+
+static void vht_build_mcs_mask(u16 vht_mcs_map,
+			       u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
+{
+	u8 nss;
+
+	for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
+		vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
+		vht_mcs_map >>= 2;
+	}
+}
+
+static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
+			     struct nl80211_txrate_vht *txrate,
+			     u16 mcs[NL80211_VHT_NSS_MAX])
+{
+	u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+	u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
+	u8 i;
+
+	if (!sband->vht_cap.vht_supported)
+		return false;
+
+	memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
+
+	/* Build vht_mcs_mask from VHT capabilities */
+	vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
+
+	for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+		if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
+			mcs[i] = txrate->mcs[i];
+		else
+			return false;
+	}
+
+	return true;
+}
+
 static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
 	[NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
 				    .len = NL80211_MAX_SUPP_RATES },
-	[NL80211_TXRATE_MCS] = { .type = NLA_BINARY,
+	[NL80211_TXRATE_HT] = { .type = NLA_BINARY,
 				 .len = NL80211_MAX_SUPP_HT_RATES },
+	[NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
+	[NL80211_TXRATE_GI] = { .type = NLA_U8 },
 };
 
 static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
@@ -7066,6 +7513,7 @@
 	struct net_device *dev = info->user_ptr[1];
 	struct nlattr *tx_rates;
 	struct ieee80211_supported_band *sband;
+	u16 vht_tx_mcs_map;
 
 	if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
 		return -EINVAL;
@@ -7077,15 +7525,20 @@
 	/* Default to all rates enabled */
 	for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
 		sband = rdev->wiphy.bands[i];
-		mask.control[i].legacy =
-			sband ? (1 << sband->n_bitrates) - 1 : 0;
-		if (sband)
-			memcpy(mask.control[i].mcs,
+
+		if (!sband)
+			continue;
+
+		mask.control[i].legacy = (1 << sband->n_bitrates) - 1;
+		memcpy(mask.control[i].ht_mcs,
 			       sband->ht_cap.mcs.rx_mask,
-			       sizeof(mask.control[i].mcs));
-		else
-			memset(mask.control[i].mcs, 0,
-			       sizeof(mask.control[i].mcs));
+		       sizeof(mask.control[i].ht_mcs));
+
+		if (!sband->vht_cap.vht_supported)
+			continue;
+
+		vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+		vht_build_mcs_mask(vht_tx_mcs_map, mask.control[i].vht_mcs);
 	}
 
 	/*
@@ -7112,31 +7565,50 @@
 			    nla_len(tb[NL80211_TXRATE_LEGACY]))
 				return -EINVAL;
 		}
-		if (tb[NL80211_TXRATE_MCS]) {
+		if (tb[NL80211_TXRATE_HT]) {
 			if (!ht_rateset_to_mask(
 					sband,
-					nla_data(tb[NL80211_TXRATE_MCS]),
-					nla_len(tb[NL80211_TXRATE_MCS]),
-					mask.control[band].mcs))
+					nla_data(tb[NL80211_TXRATE_HT]),
+					nla_len(tb[NL80211_TXRATE_HT]),
+					mask.control[band].ht_mcs))
+				return -EINVAL;
+		}
+		if (tb[NL80211_TXRATE_VHT]) {
+			if (!vht_set_mcs_mask(
+					sband,
+					nla_data(tb[NL80211_TXRATE_VHT]),
+					mask.control[band].vht_mcs))
+				return -EINVAL;
+		}
+		if (tb[NL80211_TXRATE_GI]) {
+			mask.control[band].gi =
+				nla_get_u8(tb[NL80211_TXRATE_GI]);
+			if (mask.control[band].gi > NL80211_TXRATE_FORCE_LGI)
 				return -EINVAL;
 		}
 
 		if (mask.control[band].legacy == 0) {
-			/* don't allow empty legacy rates if HT
-			 * is not even supported. */
-			if (!rdev->wiphy.bands[band]->ht_cap.ht_supported)
+			/* don't allow empty legacy rates if HT or VHT
+			 * are not even supported.
+			 */
+			if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
+			      rdev->wiphy.bands[band]->vht_cap.vht_supported))
 				return -EINVAL;
 
 			for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
-				if (mask.control[band].mcs[i])
-					break;
+				if (mask.control[band].ht_mcs[i])
+					goto out;
+
+			for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+				if (mask.control[band].vht_mcs[i])
+					goto out;
 
 			/* legacy and mcs rates may not be both empty */
-			if (i == IEEE80211_HT_MCS_MASK_LEN)
 				return -EINVAL;
 		}
 	}
 
+out:
 	return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
 }
 
@@ -7238,7 +7710,7 @@
 	 */
 	chandef.chan = NULL;
 	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
-		err = nl80211_parse_chandef(rdev, info, &chandef);
+		err = nl80211_parse_chandef(rdev, info->attrs, &chandef);
 		if (err)
 			return err;
 	}
@@ -7404,14 +7876,12 @@
 			       u32 rate, u32 pkts, u32 intvl)
 {
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
-	struct wireless_dev *wdev;
 	struct net_device *dev = info->user_ptr[1];
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
 
 	if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)
 		return -EINVAL;
 
-	wdev = dev->ieee80211_ptr;
-
 	if (!rdev->ops->set_cqm_txe_config)
 		return -EOPNOTSUPP;
 
@@ -7426,13 +7896,15 @@
 				s32 threshold, u32 hysteresis)
 {
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
-	struct wireless_dev *wdev;
 	struct net_device *dev = info->user_ptr[1];
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
 
 	if (threshold > 0)
 		return -EINVAL;
 
-	wdev = dev->ieee80211_ptr;
+	/* disabling - hysteresis should also be zero then */
+	if (threshold == 0)
+		hysteresis = 0;
 
 	if (!rdev->ops->set_cqm_rssi_config)
 		return -EOPNOTSUPP;
@@ -7451,36 +7923,33 @@
 	int err;
 
 	cqm = info->attrs[NL80211_ATTR_CQM];
-	if (!cqm) {
-		err = -EINVAL;
-		goto out;
-	}
+	if (!cqm)
+		return -EINVAL;
 
 	err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
 			       nl80211_attr_cqm_policy);
 	if (err)
-		goto out;
+		return err;
 
 	if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
 	    attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
-		s32 threshold;
-		u32 hysteresis;
-		threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
-		hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
-		err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
-	} else if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
+		s32 threshold = nla_get_s32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
+		u32 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
+
+		return nl80211_set_cqm_rssi(info, threshold, hysteresis);
+	}
+
+	if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
 		   attrs[NL80211_ATTR_CQM_TXE_PKTS] &&
 		   attrs[NL80211_ATTR_CQM_TXE_INTVL]) {
-		u32 rate, pkts, intvl;
-		rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]);
-		pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]);
-		intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]);
-		err = nl80211_set_cqm_txe(info, rate, pkts, intvl);
-	} else
-		err = -EINVAL;
+		u32 rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]);
+		u32 pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]);
+		u32 intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]);
 
-out:
-	return err;
+		return nl80211_set_cqm_txe(info, rate, pkts, intvl);
+	}
+
+	return -EINVAL;
 }
 
 static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
@@ -7540,7 +8009,7 @@
 		cfg.auto_open_plinks = false;
 
 	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
-		err = nl80211_parse_chandef(rdev, info, &setup.chandef);
+		err = nl80211_parse_chandef(rdev, info->attrs, &setup.chandef);
 		if (err)
 			return err;
 	} else {
@@ -7596,12 +8065,11 @@
 		if (!nl_pat)
 			return -ENOBUFS;
 		pat_len = wowlan->patterns[i].pattern_len;
-		if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
-			    DIV_ROUND_UP(pat_len, 8),
+		if (nla_put(msg, NL80211_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8),
 			    wowlan->patterns[i].mask) ||
-		    nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
-			    pat_len, wowlan->patterns[i].pattern) ||
-		    nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET,
+		    nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
+			    wowlan->patterns[i].pattern) ||
+		    nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
 				wowlan->patterns[i].pkt_offset))
 			return -ENOBUFS;
 		nla_nest_end(msg, nl_pat);
@@ -7942,7 +8410,7 @@
 		struct nlattr *pat;
 		int n_patterns = 0;
 		int rem, pat_len, mask_len, pkt_offset;
-		struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
+		struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
 
 		nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
 				    rem)
@@ -7961,26 +8429,25 @@
 
 		nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
 				    rem) {
-			nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
-				  nla_data(pat), nla_len(pat), NULL);
+			nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
+				  nla_len(pat), NULL);
 			err = -EINVAL;
-			if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
-			    !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
+			if (!pat_tb[NL80211_PKTPAT_MASK] ||
+			    !pat_tb[NL80211_PKTPAT_PATTERN])
 				goto error;
-			pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
+			pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
 			mask_len = DIV_ROUND_UP(pat_len, 8);
-			if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
-			    mask_len)
+			if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
 				goto error;
 			if (pat_len > wowlan->pattern_max_len ||
 			    pat_len < wowlan->pattern_min_len)
 				goto error;
 
-			if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET])
+			if (!pat_tb[NL80211_PKTPAT_OFFSET])
 				pkt_offset = 0;
 			else
 				pkt_offset = nla_get_u32(
-					pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]);
+					pat_tb[NL80211_PKTPAT_OFFSET]);
 			if (pkt_offset > wowlan->max_pkt_offset)
 				goto error;
 			new_triggers.patterns[i].pkt_offset = pkt_offset;
@@ -7994,11 +8461,11 @@
 			new_triggers.patterns[i].pattern =
 				new_triggers.patterns[i].mask + mask_len;
 			memcpy(new_triggers.patterns[i].mask,
-			       nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
+			       nla_data(pat_tb[NL80211_PKTPAT_MASK]),
 			       mask_len);
 			new_triggers.patterns[i].pattern_len = pat_len;
 			memcpy(new_triggers.patterns[i].pattern,
-			       nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
+			       nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
 			       pat_len);
 			i++;
 		}
@@ -8037,6 +8504,264 @@
 }
 #endif
 
+static int nl80211_send_coalesce_rules(struct sk_buff *msg,
+				       struct cfg80211_registered_device *rdev)
+{
+	struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules;
+	int i, j, pat_len;
+	struct cfg80211_coalesce_rules *rule;
+
+	if (!rdev->coalesce->n_rules)
+		return 0;
+
+	nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE);
+	if (!nl_rules)
+		return -ENOBUFS;
+
+	for (i = 0; i < rdev->coalesce->n_rules; i++) {
+		nl_rule = nla_nest_start(msg, i + 1);
+		if (!nl_rule)
+			return -ENOBUFS;
+
+		rule = &rdev->coalesce->rules[i];
+		if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY,
+				rule->delay))
+			return -ENOBUFS;
+
+		if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION,
+				rule->condition))
+			return -ENOBUFS;
+
+		nl_pats = nla_nest_start(msg,
+				NL80211_ATTR_COALESCE_RULE_PKT_PATTERN);
+		if (!nl_pats)
+			return -ENOBUFS;
+
+		for (j = 0; j < rule->n_patterns; j++) {
+			nl_pat = nla_nest_start(msg, j + 1);
+			if (!nl_pat)
+				return -ENOBUFS;
+			pat_len = rule->patterns[j].pattern_len;
+			if (nla_put(msg, NL80211_PKTPAT_MASK,
+				    DIV_ROUND_UP(pat_len, 8),
+				    rule->patterns[j].mask) ||
+			    nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
+				    rule->patterns[j].pattern) ||
+			    nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
+					rule->patterns[j].pkt_offset))
+				return -ENOBUFS;
+			nla_nest_end(msg, nl_pat);
+		}
+		nla_nest_end(msg, nl_pats);
+		nla_nest_end(msg, nl_rule);
+	}
+	nla_nest_end(msg, nl_rules);
+
+	return 0;
+}
+
+static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_device *rdev = info->user_ptr[0];
+	struct sk_buff *msg;
+	void *hdr;
+
+	if (!rdev->wiphy.coalesce)
+		return -EOPNOTSUPP;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+			     NL80211_CMD_GET_COALESCE);
+	if (!hdr)
+		goto nla_put_failure;
+
+	if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev))
+		goto nla_put_failure;
+
+	genlmsg_end(msg, hdr);
+	return genlmsg_reply(msg, info);
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev)
+{
+	struct cfg80211_coalesce *coalesce = rdev->coalesce;
+	int i, j;
+	struct cfg80211_coalesce_rules *rule;
+
+	if (!coalesce)
+		return;
+
+	for (i = 0; i < coalesce->n_rules; i++) {
+		rule = &coalesce->rules[i];
+		for (j = 0; j < rule->n_patterns; j++)
+			kfree(rule->patterns[j].mask);
+		kfree(rule->patterns);
+	}
+	kfree(coalesce->rules);
+	kfree(coalesce);
+	rdev->coalesce = NULL;
+}
+
+static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
+				       struct nlattr *rule,
+				       struct cfg80211_coalesce_rules *new_rule)
+{
+	int err, i;
+	const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
+	struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat;
+	int rem, pat_len, mask_len, pkt_offset, n_patterns = 0;
+	struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
+
+	err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule),
+			nla_len(rule), nl80211_coalesce_policy);
+	if (err)
+		return err;
+
+	if (tb[NL80211_ATTR_COALESCE_RULE_DELAY])
+		new_rule->delay =
+			nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]);
+	if (new_rule->delay > coalesce->max_delay)
+		return -EINVAL;
+
+	if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION])
+		new_rule->condition =
+			nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]);
+	if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH &&
+	    new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH)
+		return -EINVAL;
+
+	if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN])
+		return -EINVAL;
+
+	nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
+			    rem)
+		n_patterns++;
+	if (n_patterns > coalesce->n_patterns)
+		return -EINVAL;
+
+	new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]),
+				     GFP_KERNEL);
+	if (!new_rule->patterns)
+		return -ENOMEM;
+
+	new_rule->n_patterns = n_patterns;
+	i = 0;
+
+	nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
+			    rem) {
+		nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
+			  nla_len(pat), NULL);
+		if (!pat_tb[NL80211_PKTPAT_MASK] ||
+		    !pat_tb[NL80211_PKTPAT_PATTERN])
+			return -EINVAL;
+		pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
+		mask_len = DIV_ROUND_UP(pat_len, 8);
+		if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
+			return -EINVAL;
+		if (pat_len > coalesce->pattern_max_len ||
+		    pat_len < coalesce->pattern_min_len)
+			return -EINVAL;
+
+		if (!pat_tb[NL80211_PKTPAT_OFFSET])
+			pkt_offset = 0;
+		else
+			pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]);
+		if (pkt_offset > coalesce->max_pkt_offset)
+			return -EINVAL;
+		new_rule->patterns[i].pkt_offset = pkt_offset;
+
+		new_rule->patterns[i].mask =
+			kmalloc(mask_len + pat_len, GFP_KERNEL);
+		if (!new_rule->patterns[i].mask)
+			return -ENOMEM;
+		new_rule->patterns[i].pattern =
+			new_rule->patterns[i].mask + mask_len;
+		memcpy(new_rule->patterns[i].mask,
+		       nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len);
+		new_rule->patterns[i].pattern_len = pat_len;
+		memcpy(new_rule->patterns[i].pattern,
+		       nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len);
+		i++;
+	}
+
+	return 0;
+}
+
+static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_device *rdev = info->user_ptr[0];
+	const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
+	struct cfg80211_coalesce new_coalesce = {};
+	struct cfg80211_coalesce *n_coalesce;
+	int err, rem_rule, n_rules = 0, i, j;
+	struct nlattr *rule;
+	struct cfg80211_coalesce_rules *tmp_rule;
+
+	if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce)
+		return -EOPNOTSUPP;
+
+	if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) {
+		cfg80211_rdev_free_coalesce(rdev);
+		rdev->ops->set_coalesce(&rdev->wiphy, NULL);
+		return 0;
+	}
+
+	nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
+			    rem_rule)
+		n_rules++;
+	if (n_rules > coalesce->n_rules)
+		return -EINVAL;
+
+	new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]),
+				     GFP_KERNEL);
+	if (!new_coalesce.rules)
+		return -ENOMEM;
+
+	new_coalesce.n_rules = n_rules;
+	i = 0;
+
+	nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
+			    rem_rule) {
+		err = nl80211_parse_coalesce_rule(rdev, rule,
+						  &new_coalesce.rules[i]);
+		if (err)
+			goto error;
+
+		i++;
+	}
+
+	err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce);
+	if (err)
+		goto error;
+
+	n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL);
+	if (!n_coalesce) {
+		err = -ENOMEM;
+		goto error;
+	}
+	cfg80211_rdev_free_coalesce(rdev);
+	rdev->coalesce = n_coalesce;
+
+	return 0;
+error:
+	for (i = 0; i < new_coalesce.n_rules; i++) {
+		tmp_rule = &new_coalesce.rules[i];
+		for (j = 0; j < tmp_rule->n_patterns; j++)
+			kfree(tmp_rule->patterns[j].mask);
+		kfree(tmp_rule->patterns);
+	}
+	kfree(new_coalesce.rules);
+
+	return err;
+}
+
 static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
 {
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -9043,7 +9768,30 @@
 		.flags = GENL_ADMIN_PERM,
 		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
 				  NL80211_FLAG_NEED_RTNL,
-	}
+	},
+	{
+		.cmd = NL80211_CMD_GET_COALESCE,
+		.doit = nl80211_get_coalesce,
+		.policy = nl80211_policy,
+		.internal_flags = NL80211_FLAG_NEED_WIPHY |
+				  NL80211_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL80211_CMD_SET_COALESCE,
+		.doit = nl80211_set_coalesce,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL80211_FLAG_NEED_WIPHY |
+				  NL80211_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL80211_CMD_CHANNEL_SWITCH,
+		.doit = nl80211_channel_switch,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL80211_FLAG_NEED_WIPHY |
+				  NL80211_FLAG_NEED_RTNL,
+	},
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -10000,7 +10748,7 @@
 int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
 		      struct wireless_dev *wdev, u32 nlportid,
 		      int freq, int sig_dbm,
-		      const u8 *buf, size_t len, gfp_t gfp)
+		      const u8 *buf, size_t len, u32 flags, gfp_t gfp)
 {
 	struct net_device *netdev = wdev->netdev;
 	struct sk_buff *msg;
@@ -10023,7 +10771,9 @@
 	    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, buf))
+	    nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
+	    (flags &&
+	     nla_put_u32(msg, NL80211_ATTR_RXMGMT_FLAGS, flags)))
 		goto nla_put_failure;
 
 	genlmsg_end(msg, hdr);
@@ -10283,19 +11033,18 @@
 	struct wiphy *wiphy = wdev->wiphy;
 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
 
-	trace_cfg80211_ch_switch_notify(dev, chandef);
+	ASSERT_WDEV_LOCK(wdev);
 
-	wdev_lock(wdev);
+	trace_cfg80211_ch_switch_notify(dev, chandef);
 
 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
-		    wdev->iftype != NL80211_IFTYPE_P2P_GO))
-		goto out;
+		    wdev->iftype != NL80211_IFTYPE_P2P_GO &&
+		    wdev->iftype != NL80211_IFTYPE_ADHOC &&
+		    wdev->iftype != NL80211_IFTYPE_MESH_POINT))
+		return;
 
 	wdev->channel = chandef->chan;
 	nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
-out:
-	wdev_unlock(wdev);
-	return;
 }
 EXPORT_SYMBOL(cfg80211_ch_switch_notify);
 
diff -ruw linux-3.11.10/net/wireless/nl80211.h linux-3.11.10-fbx/net/wireless/nl80211.h
--- linux-3.11.10/net/wireless/nl80211.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/nl80211.h	2015-09-29 11:15:09.165757756 +0200
@@ -66,7 +66,7 @@
 int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
 		      struct wireless_dev *wdev, u32 nlpid,
 		      int freq, int sig_dbm,
-		      const u8 *buf, size_t len, gfp_t gfp);
+		      const u8 *buf, size_t len, u32 flags, gfp_t gfp);
 
 void
 nl80211_radar_notify(struct cfg80211_registered_device *rdev,
@@ -74,4 +74,6 @@
 		     enum nl80211_radar_event event,
 		     struct net_device *netdev, gfp_t gfp);
 
+void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
+
 #endif /* __NET_WIRELESS_NL80211_H */
diff -ruw linux-3.11.10/net/wireless/radiotap.c linux-3.11.10-fbx/net/wireless/radiotap.c
--- linux-3.11.10/net/wireless/radiotap.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/radiotap.c	2014-07-29 21:29:45.673870856 +0200
@@ -124,6 +124,10 @@
 	/* find payload start allowing for extended bitmap(s) */
 
 	if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
+		if ((unsigned long)iterator->_arg -
+		    (unsigned long)iterator->_rtheader + sizeof(uint32_t) >
+		    (unsigned long)iterator->_max_length)
+			return -EINVAL;
 		while (get_unaligned_le32(iterator->_arg) &
 					(1 << IEEE80211_RADIOTAP_EXT)) {
 			iterator->_arg += sizeof(uint32_t);
diff -ruw linux-3.11.10/net/wireless/rdev-ops.h linux-3.11.10-fbx/net/wireless/rdev-ops.h
--- linux-3.11.10/net/wireless/rdev-ops.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/rdev-ops.h	2015-09-29 11:15:09.165757756 +0200
@@ -516,11 +516,12 @@
 
 #ifdef CONFIG_NL80211_TESTMODE
 static inline int rdev_testmode_cmd(struct cfg80211_registered_device *rdev,
+				    struct wireless_dev *wdev,
 				    void *data, int len)
 {
 	int ret;
-	trace_rdev_testmode_cmd(&rdev->wiphy);
-	ret = rdev->ops->testmode_cmd(&rdev->wiphy, data, len);
+	trace_rdev_testmode_cmd(&rdev->wiphy, wdev);
+	ret = rdev->ops->testmode_cmd(&rdev->wiphy, wdev, data, len);
 	trace_rdev_return_int(&rdev->wiphy, ret);
 	return ret;
 }
@@ -923,4 +924,16 @@
 	trace_rdev_return_void(&rdev->wiphy);
 }
 
+static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev,
+				      struct cfg80211_csa_settings *params,
+				      int num_params)
+{
+	int ret;
+
+	trace_rdev_channel_switch(&rdev->wiphy, params, num_params);
+	ret = rdev->ops->channel_switch(&rdev->wiphy, params, num_params);
+	trace_rdev_return_int(&rdev->wiphy, ret);
+	return ret;
+}
+
 #endif /* __CFG80211_RDEV_OPS */
diff -ruw linux-3.11.10/net/wireless/reg.c linux-3.11.10-fbx/net/wireless/reg.c
--- linux-3.11.10/net/wireless/reg.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/reg.c	2015-09-29 11:15:09.169757785 +0200
@@ -120,6 +120,48 @@
 	return rtnl_dereference(wiphy->regd);
 }
 
+static const char *reg_dfs_region_str(enum nl80211_dfs_regions dfs_region)
+{
+	switch (dfs_region) {
+	case NL80211_DFS_UNSET:
+		return "unset";
+	case NL80211_DFS_FCC:
+		return "FCC";
+	case NL80211_DFS_ETSI:
+		return "ETSI";
+	case NL80211_DFS_JP:
+		return "JP";
+	}
+	return "Unknown";
+}
+
+enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy)
+{
+	const struct ieee80211_regdomain *regd = NULL;
+	const struct ieee80211_regdomain *wiphy_regd = NULL;
+
+	regd = get_cfg80211_regdom();
+	if (!wiphy)
+		goto out;
+
+	wiphy_regd = get_wiphy_regdom(wiphy);
+	if (!wiphy_regd)
+		goto out;
+
+	if (wiphy_regd->dfs_region == regd->dfs_region)
+		goto out;
+
+	REG_DBG_PRINT("%s: device specific dfs_region "
+		      "(%s) disagrees with cfg80211's "
+		      "central dfs_region (%s)\n",
+		      dev_name(&wiphy->dev),
+		      reg_dfs_region_str(wiphy_regd->dfs_region),
+		      reg_dfs_region_str(regd->dfs_region));
+
+out:
+	return regd->dfs_region;
+}
+
 static void rcu_free_regdom(const struct ieee80211_regdomain *r)
 {
 	if (!r)
@@ -163,25 +205,29 @@
 		REG_RULE(2412-10, 2462+10, 40, 6, 20, 0),
 		/* IEEE 802.11b/g, channels 12..13. */
 		REG_RULE(2467-10, 2472+10, 40, 6, 20,
-			NL80211_RRF_PASSIVE_SCAN |
-			NL80211_RRF_NO_IBSS),
+			NL80211_RRF_NO_IR),
 		/* IEEE 802.11 channel 14 - Only JP enables
 		 * this and for 802.11b only */
 		REG_RULE(2484-10, 2484+10, 20, 6, 20,
-			NL80211_RRF_PASSIVE_SCAN |
-			NL80211_RRF_NO_IBSS |
+			NL80211_RRF_NO_IR |
 			NL80211_RRF_NO_OFDM),
 		/* IEEE 802.11a, channel 36..48 */
-		REG_RULE(5180-10, 5240+10, 80, 6, 20,
-                        NL80211_RRF_PASSIVE_SCAN |
-                        NL80211_RRF_NO_IBSS),
+		REG_RULE(5180-10, 5240+10, 160, 6, 20,
+                        NL80211_RRF_NO_IR),
 
-		/* NB: 5260 MHz - 5700 MHz requires DFS */
+		/* IEEE 802.11a, channel 52..64 - DFS required */
+		REG_RULE(5260-10, 5320+10, 160, 6, 20,
+			NL80211_RRF_NO_IR |
+			NL80211_RRF_DFS),
+
+		/* IEEE 802.11a, channel 100..144 - DFS required */
+		REG_RULE(5500-10, 5720+10, 160, 6, 20,
+			NL80211_RRF_NO_IR |
+			NL80211_RRF_DFS),
 
 		/* IEEE 802.11a, channel 149..165 */
 		REG_RULE(5745-10, 5825+10, 80, 6, 20,
-			NL80211_RRF_PASSIVE_SCAN |
-			NL80211_RRF_NO_IBSS),
+			NL80211_RRF_NO_IR),
 
 		/* IEEE 802.11ad (60gHz), channels 1..3 */
 		REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0),
@@ -688,10 +734,8 @@
 static u32 map_regdom_flags(u32 rd_flags)
 {
 	u32 channel_flags = 0;
-	if (rd_flags & NL80211_RRF_PASSIVE_SCAN)
-		channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN;
-	if (rd_flags & NL80211_RRF_NO_IBSS)
-		channel_flags |= IEEE80211_CHAN_NO_IBSS;
+	if (rd_flags & NL80211_RRF_NO_IR_ALL)
+		channel_flags |= IEEE80211_CHAN_NO_IR;
 	if (rd_flags & NL80211_RRF_DFS)
 		channel_flags |= IEEE80211_CHAN_RADAR;
 	if (rd_flags & NL80211_RRF_NO_OFDM)
@@ -758,23 +802,25 @@
 }
 EXPORT_SYMBOL(freq_reg_info);
 
-#ifdef CONFIG_CFG80211_REG_DEBUG
-static const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
+const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
 {
 	switch (initiator) {
 	case NL80211_REGDOM_SET_BY_CORE:
-		return "Set by core";
+		return "core";
 	case NL80211_REGDOM_SET_BY_USER:
-		return "Set by user";
+		return "user";
 	case NL80211_REGDOM_SET_BY_DRIVER:
-		return "Set by driver";
+		return "driver";
 	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
-		return "Set by country IE";
+		return "country IE";
 	default:
 		WARN_ON(1);
-		return "Set by bug";
+		return "bug";
 	}
 }
+EXPORT_SYMBOL(reg_initiator_name);
+
+#ifdef CONFIG_CFG80211_REG_DEBUG
 
 static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
 				    const struct ieee80211_reg_rule *reg_rule)
@@ -962,6 +1008,13 @@
 }
 #endif
 
+static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy)
+{
+	if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY &&
+	    !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY))
+		return true;
+	return false;
+}
 
 static bool ignore_reg_update(struct wiphy *wiphy,
 			      enum nl80211_reg_initiator initiator)
@@ -969,14 +1022,17 @@
 	struct regulatory_request *lr = get_last_request();
 
 	if (!lr) {
-		REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n",
+		REG_DBG_PRINT("Ignoring regulatory request set by %s "
+			      "since last_request is not set\n",
 			      reg_initiator_name(initiator));
 		return true;
 	}
 
 	if (initiator == NL80211_REGDOM_SET_BY_CORE &&
 	    wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) {
-		REG_DBG_PRINT("Ignoring regulatory request %s since the driver uses its own custom regulatory domain\n",
+		REG_DBG_PRINT("Ignoring regulatory request set by %s "
+			      "since the driver uses its own custom "
+			      "regulatory domain\n",
 			      reg_initiator_name(initiator));
 		return true;
 	}
@@ -985,10 +1041,12 @@
 	 * wiphy->regd will be set once the device has its own
 	 * desired regulatory domain set
 	 */
-	if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd &&
+	if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd &&
 	    initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
 	    !is_world_regdom(lr->alpha2)) {
-		REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n",
+		REG_DBG_PRINT("Ignoring regulatory request set by %s "
+			      "since the driver requires its own regulatory "
+			      "domain to be set first\n",
 			      reg_initiator_name(initiator));
 		return true;
 	}
@@ -1043,13 +1101,8 @@
 	chan_before.center_freq = chan->center_freq;
 	chan_before.flags = chan->flags;
 
-	if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
-		chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
-		channel_changed = true;
-	}
-
-	if (chan->flags & IEEE80211_CHAN_NO_IBSS) {
-		chan->flags &= ~IEEE80211_CHAN_NO_IBSS;
+	if (chan->flags & IEEE80211_CHAN_NO_IR) {
+		chan->flags &= ~IEEE80211_CHAN_NO_IR;
 		channel_changed = true;
 	}
 
@@ -1689,7 +1742,7 @@
 }
 EXPORT_SYMBOL(regulatory_hint);
 
-void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
+void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band,
 			 const u8 *country_ie, u8 country_ie_len)
 {
 	char alpha2[2];
@@ -1993,7 +2046,7 @@
 	}
 }
 
-bool reg_supported_dfs_region(u8 dfs_region)
+bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region)
 {
 	switch (dfs_region) {
 	case NL80211_DFS_UNSET:
@@ -2008,27 +2061,6 @@
 	}
 }
 
-static void print_dfs_region(u8 dfs_region)
-{
-	if (!dfs_region)
-		return;
-
-	switch (dfs_region) {
-	case NL80211_DFS_FCC:
-		pr_info(" DFS Master region FCC");
-		break;
-	case NL80211_DFS_ETSI:
-		pr_info(" DFS Master region ETSI");
-		break;
-	case NL80211_DFS_JP:
-		pr_info(" DFS Master region JP");
-		break;
-	default:
-		pr_info(" DFS Master region Unknown");
-		break;
-	}
-}
-
 static void print_regdomain(const struct ieee80211_regdomain *rd)
 {
 	struct regulatory_request *lr = get_last_request();
@@ -2060,7 +2092,7 @@
 		}
 	}
 
-	print_dfs_region(rd->dfs_region);
+	pr_info(" DFS Master region: %s", reg_dfs_region_str(rd->dfs_region));
 	print_rd_rules(rd);
 }
 
diff -ruw linux-3.11.10/net/wireless/reg.h linux-3.11.10-fbx/net/wireless/reg.h
--- linux-3.11.10/net/wireless/reg.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/reg.h	2015-09-29 11:15:09.169757785 +0200
@@ -19,7 +19,8 @@
 extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
 
 bool is_world_regdom(const char *alpha2);
-bool reg_supported_dfs_region(u8 dfs_region);
+bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region);
+enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy);
 
 int regulatory_hint_user(const char *alpha2,
 			 enum nl80211_user_reg_hint_type user_reg_hint_type);
@@ -58,7 +59,7 @@
 				 gfp_t gfp);
 
 /**
- * regulatory_hint_11d - hints a country IE as a regulatory domain
+ * regulatory_hint_country_ie - hints a country IE as a regulatory domain
  * @wiphy: the wireless device giving the hint (used only for reporting
  *	conflicts)
  * @band: the band on which the country IE was received on. This determines
@@ -78,7 +79,7 @@
  * not observed. For this reason if a triplet is seen with channel
  * information for a band the BSS is not present in it will be ignored.
  */
-void regulatory_hint_11d(struct wiphy *wiphy,
+void regulatory_hint_country_ie(struct wiphy *wiphy,
 			 enum ieee80211_band band,
 			 const u8 *country_ie,
 			 u8 country_ie_len);
diff -ruw linux-3.11.10/net/wireless/scan.c linux-3.11.10-fbx/net/wireless/scan.c
--- linux-3.11.10/net/wireless/scan.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/scan.c	2015-09-29 11:15:09.169757785 +0200
@@ -254,10 +254,10 @@
 	rdev = container_of(wk, struct cfg80211_registered_device,
 			    sched_scan_results_wk);
 
-	request = rdev->sched_scan_req;
-
 	rtnl_lock();
 
+	request = rdev->sched_scan_req;
+
 	/* we don't have sched_scan_req anymore if the scan is stopping */
 	if (request) {
 		if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
@@ -465,10 +465,6 @@
 		}
 	}
 
-	/*
-	 * we can't use compare_ether_addr here since we need a < > operator.
-	 * The binary return value of compare_ether_addr isn't enough
-	 */
 	r = memcmp(a->bssid, b->bssid, sizeof(a->bssid));
 	if (r)
 		return r;
@@ -651,6 +647,8 @@
 			continue;
 		if (bss->pub.channel != new->pub.channel)
 			continue;
+		if (bss->pub.scan_width != new->pub.scan_width)
+			continue;
 		if (rcu_access_pointer(bss->pub.beacon_ies))
 			continue;
 		ies = rcu_access_pointer(bss->pub.ies);
@@ -870,8 +868,9 @@
 
 /* Returned bss is reference counted and must be cleaned up appropriately. */
 struct cfg80211_bss*
-cfg80211_inform_bss(struct wiphy *wiphy,
+cfg80211_inform_bss_width(struct wiphy *wiphy,
 		    struct ieee80211_channel *channel,
+			  enum nl80211_bss_scan_width scan_width,
 		    const u8 *bssid, u64 tsf, u16 capability,
 		    u16 beacon_interval, const u8 *ie, size_t ielen,
 		    s32 signal, gfp_t gfp)
@@ -892,6 +891,7 @@
 
 	memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
 	tmp.pub.channel = channel;
+	tmp.pub.scan_width = scan_width;
 	tmp.pub.signal = signal;
 	tmp.pub.beacon_interval = beacon_interval;
 	tmp.pub.capability = capability;
@@ -924,12 +924,13 @@
 	/* cfg80211_bss_update gives us a referenced result */
 	return &res->pub;
 }
-EXPORT_SYMBOL(cfg80211_inform_bss);
+EXPORT_SYMBOL(cfg80211_inform_bss_width);
 
 /* Returned bss is reference counted and must be cleaned up appropriately. */
 struct cfg80211_bss *
-cfg80211_inform_bss_frame(struct wiphy *wiphy,
+cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
 			  struct ieee80211_channel *channel,
+				enum nl80211_bss_scan_width scan_width,
 			  struct ieee80211_mgmt *mgmt, size_t len,
 			  s32 signal, gfp_t gfp)
 {
@@ -941,7 +942,8 @@
 	BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
 			offsetof(struct ieee80211_mgmt, u.beacon.variable));
 
-	trace_cfg80211_inform_bss_frame(wiphy, channel, mgmt, len, signal);
+	trace_cfg80211_inform_bss_width_frame(wiphy, channel, scan_width, mgmt,
+					      len, signal);
 
 	if (WARN_ON(!mgmt))
 		return NULL;
@@ -976,6 +978,7 @@
 	
 	memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
 	tmp.pub.channel = channel;
+	tmp.pub.scan_width = scan_width;
 	tmp.pub.signal = signal;
 	tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
 	tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
@@ -991,7 +994,7 @@
 	/* cfg80211_bss_update gives us a referenced result */
 	return &res->pub;
 }
-EXPORT_SYMBOL(cfg80211_inform_bss_frame);
+EXPORT_SYMBOL(cfg80211_inform_bss_width_frame);
 
 void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 {
diff -ruw linux-3.11.10/net/wireless/sme.c linux-3.11.10-fbx/net/wireless/sme.c
--- linux-3.11.10/net/wireless/sme.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/sme.c	2015-09-29 11:15:09.169757785 +0200
@@ -242,7 +242,6 @@
 					NULL, 0, NULL, 0,
 					WLAN_STATUS_UNSPECIFIED_FAILURE,
 					false, NULL);
-			cfg80211_sme_free(wdev);
 		}
 		wdev_unlock(wdev);
 	}
@@ -646,6 +645,7 @@
 			cfg80211_unhold_bss(bss_from_pub(bss));
 			cfg80211_put_bss(wdev->wiphy, bss);
 		}
+		cfg80211_sme_free(wdev);
 		return;
 	}
 
@@ -682,7 +682,7 @@
 	 * - country_ie + 2, the start of the country ie data, and
 	 * - and country_ie[1] which is the IE length
 	 */
-	regulatory_hint_11d(wdev->wiphy, bss->channel->band,
+	regulatory_hint_country_ie(wdev->wiphy, bss->channel->band,
 			    country_ie + 2, country_ie[1]);
 	kfree(country_ie);
 }
diff -ruw linux-3.11.10/net/wireless/sysfs.c linux-3.11.10-fbx/net/wireless/sysfs.c
--- linux-3.11.10/net/wireless/sysfs.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/sysfs.c	2015-09-29 11:15:09.169757785 +0200
@@ -30,7 +30,8 @@
 			      char *buf)				\
 {									\
 	return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member);	\
-}
+}									\
+static DEVICE_ATTR_RO(name)
 
 SHOW_FMT(index, "%d", wiphy_idx);
 SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
@@ -42,7 +43,7 @@
 	struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy;
 	return sprintf(buf, "%s\n", dev_name(&wiphy->dev));
 }
-
+static DEVICE_ATTR_RO(name);
 
 static ssize_t addresses_show(struct device *dev,
 			      struct device_attribute *attr,
@@ -60,15 +61,17 @@
 
 	return buf - start;
 }
+static DEVICE_ATTR_RO(addresses);
 
-static struct device_attribute ieee80211_dev_attrs[] = {
-	__ATTR_RO(index),
-	__ATTR_RO(macaddress),
-	__ATTR_RO(address_mask),
-	__ATTR_RO(addresses),
-	__ATTR_RO(name),
-	{}
+static struct attribute *ieee80211_attrs[] = {
+	&dev_attr_index.attr,
+	&dev_attr_macaddress.attr,
+	&dev_attr_address_mask.attr,
+	&dev_attr_addresses.attr,
+	&dev_attr_name.attr,
+	NULL,
 };
+ATTRIBUTE_GROUPS(ieee80211);
 
 static void wiphy_dev_release(struct device *dev)
 {
@@ -146,7 +149,7 @@
 	.name = "ieee80211",
 	.owner = THIS_MODULE,
 	.dev_release = wiphy_dev_release,
-	.dev_attrs = ieee80211_dev_attrs,
+	.dev_groups = ieee80211_groups,
 	.dev_uevent = wiphy_uevent,
 #ifdef CONFIG_PM
 	.suspend = wiphy_suspend,
diff -ruw linux-3.11.10/net/wireless/trace.h linux-3.11.10-fbx/net/wireless/trace.h
--- linux-3.11.10/net/wireless/trace.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/trace.h	2015-09-29 11:15:09.169757785 +0200
@@ -1293,15 +1293,17 @@
 
 #ifdef CONFIG_NL80211_TESTMODE
 TRACE_EVENT(rdev_testmode_cmd,
-	TP_PROTO(struct wiphy *wiphy),
-	TP_ARGS(wiphy),
+	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+	TP_ARGS(wiphy, wdev),
 	TP_STRUCT__entry(
 		WIPHY_ENTRY
+		WDEV_ENTRY
 	),
 	TP_fast_assign(
 		WIPHY_ASSIGN;
+		WDEV_ASSIGN;
 	),
-	TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG)
+	TP_printk(WIPHY_PR_FMT WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
 );
 
 TRACE_EVENT(rdev_testmode_dump,
@@ -1841,6 +1843,26 @@
 		  WIPHY_PR_ARG, WDEV_PR_ARG)
 );
 
+TRACE_EVENT(rdev_channel_switch,
+	TP_PROTO(struct wiphy *wiphy,
+		 struct cfg80211_csa_settings *params,
+		 int num_params),
+	TP_ARGS(wiphy, params, num_params),
+	TP_STRUCT__entry(
+		WIPHY_ENTRY
+		__field(int, num_params)
+		__dynamic_array(u8, params, sizeof(*params) * num_params)
+	),
+	TP_fast_assign(
+		WIPHY_ASSIGN;
+		__entry->num_params = num_params;
+		memcpy(__get_dynamic_array(params), params,
+		       sizeof(*params) * num_params);
+	),
+	TP_printk(WIPHY_PR_FMT ", num_params=%d",
+		  WIPHY_PR_ARG, __entry->num_params)
+);
+
 /*************************************************************
  *	     cfg80211 exported functions traces		     *
  *************************************************************/
@@ -2391,26 +2413,30 @@
 		  __entry->capa_mask, __entry->capa_val)
 );
 
-TRACE_EVENT(cfg80211_inform_bss_frame,
+TRACE_EVENT(cfg80211_inform_bss_width_frame,
 	TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
+		 enum nl80211_bss_scan_width scan_width,
 		 struct ieee80211_mgmt *mgmt, size_t len,
 		 s32 signal),
-	TP_ARGS(wiphy, channel, mgmt, len, signal),
+	TP_ARGS(wiphy, channel, scan_width, mgmt, len, signal),
 	TP_STRUCT__entry(
 		WIPHY_ENTRY
 		CHAN_ENTRY
+		__field(enum nl80211_bss_scan_width, scan_width)
 		__dynamic_array(u8, mgmt, len)
 		__field(s32, signal)
 	),
 	TP_fast_assign(
 		WIPHY_ASSIGN;
 		CHAN_ASSIGN(channel);
+		__entry->scan_width = scan_width;
 		if (mgmt)
 			memcpy(__get_dynamic_array(mgmt), mgmt, len);
 		__entry->signal = signal;
 	),
-	TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "signal: %d",
-		  WIPHY_PR_ARG, CHAN_PR_ARG, __entry->signal)
+	TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d",
+		  WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width,
+		  __entry->signal)
 );
 
 DECLARE_EVENT_CLASS(cfg80211_bss_evt,
diff -ruw linux-3.11.10/net/wireless/util.c linux-3.11.10-fbx/net/wireless/util.c
--- linux-3.11.10/net/wireless/util.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/wireless/util.c	2015-09-29 11:15:09.169757785 +0200
@@ -10,6 +10,7 @@
 #include <net/cfg80211.h>
 #include <net/ip.h>
 #include <net/dsfield.h>
+#include <linux/if_vlan.h>
 #include "core.h"
 #include "rdev-ops.h"
 
@@ -33,7 +34,8 @@
 }
 EXPORT_SYMBOL(ieee80211_get_response_rate);
 
-u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband)
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
+			      enum nl80211_bss_scan_width scan_width)
 {
 	struct ieee80211_rate *bitrates;
 	u32 mandatory_rates = 0;
@@ -43,10 +45,15 @@
 	if (WARN_ON(!sband))
 		return 1;
 
-	if (sband->band == IEEE80211_BAND_2GHZ)
-		mandatory_flag = IEEE80211_RATE_MANDATORY_B;
+	if (sband->band == IEEE80211_BAND_2GHZ) {
+		if (scan_width == NL80211_BSS_CHAN_WIDTH_5 ||
+		    scan_width == NL80211_BSS_CHAN_WIDTH_10)
+			mandatory_flag = IEEE80211_RATE_MANDATORY_G;
 	else
+			mandatory_flag = IEEE80211_RATE_MANDATORY_B;
+	} else {
 		mandatory_flag = IEEE80211_RATE_MANDATORY_A;
+	}
 
 	bitrates = sband->bitrates;
 	for (i = 0; i < sband->n_bitrates; i++)
@@ -685,6 +692,7 @@
 unsigned int cfg80211_classify8021d(struct sk_buff *skb)
 {
 	unsigned int dscp;
+	unsigned char vlan_priority;
 
 	/* skb->priority values from 256->263 are magic values to
 	 * directly indicate a specific 802.1d priority.  This is used
@@ -694,6 +702,13 @@
 	if (skb->priority >= 256 && skb->priority <= 263)
 		return skb->priority - 256;
 
+	if (vlan_tx_tag_present(skb)) {
+		vlan_priority = (vlan_tx_tag_get(skb) & VLAN_PRIO_MASK)
+			>> VLAN_PRIO_SHIFT;
+		if (vlan_priority > 0)
+			return vlan_priority;
+	}
+
 	switch (skb->protocol) {
 	case htons(ETH_P_IP):
 		dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;
@@ -1218,30 +1233,11 @@
 	return res;
 }
 
-int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
-				 struct wireless_dev *wdev,
-				 enum nl80211_iftype iftype,
+static int cfg80211_is_radar_required(enum nl80211_iftype iftype,
 				 struct ieee80211_channel *chan,
 				 enum cfg80211_chan_mode chanmode,
-				 u8 radar_detect)
+				      u8 radar_detect_width)
 {
-	struct wireless_dev *wdev_iter;
-	u32 used_iftypes = BIT(iftype);
-	int num[NUM_NL80211_IFTYPES];
-	struct ieee80211_channel
-			*used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS];
-	struct ieee80211_channel *ch;
-	enum cfg80211_chan_mode chmode;
-	int num_different_channels = 0;
-	int total = 1;
-	bool radar_required;
-	int i, j;
-
-	ASSERT_RTNL();
-
-	if (WARN_ON(hweight32(radar_detect) > 1))
-		return -EINVAL;
-
 	switch (iftype) {
 	case NL80211_IFTYPE_ADHOC:
 	case NL80211_IFTYPE_AP:
@@ -1249,52 +1245,132 @@
 	case NL80211_IFTYPE_MESH_POINT:
 	case NL80211_IFTYPE_P2P_GO:
 	case NL80211_IFTYPE_WDS:
-		radar_required = !!(chan &&
-				    (chan->flags & IEEE80211_CHAN_RADAR));
-		break;
+		/* if the interface could potentially choose a DFS
+		 * channel, then mark DFS as required. */
+		if (!chan)
+			return (chanmode != CHAN_MODE_UNDEFINED &&
+				radar_detect_width) ? 1 : 0;
+
+		return (chan->flags & IEEE80211_CHAN_RADAR) ? 1 : 0;
 	case NL80211_IFTYPE_P2P_CLIENT:
 	case NL80211_IFTYPE_STATION:
 	case NL80211_IFTYPE_P2P_DEVICE:
 	case NL80211_IFTYPE_MONITOR:
-		radar_required = false;
-		break;
+		return 0;
 	case NUM_NL80211_IFTYPES:
 	case NL80211_IFTYPE_UNSPECIFIED:
 	default:
 		return -EINVAL;
 	}
-
-	if (radar_required && !radar_detect)
-		return -EINVAL;
-
-	/* Always allow software iftypes */
-	if (rdev->wiphy.software_iftypes & BIT(iftype)) {
-		if (radar_detect)
-			return -EINVAL;
-		return 0;
 	}
 
-	memset(num, 0, sizeof(num));
-	memset(used_channels, 0, sizeof(used_channels));
-
-	num[iftype] = 1;
+static int cfg80211_add_used_chan(struct ieee80211_channel **used_chans,
+				  int *num_diff_chans,
+				  int max_num_diff_chans,
+				  struct ieee80211_channel *chan,
+				  enum cfg80211_chan_mode chanmode)
+{
+	int i;
 
 	switch (chanmode) {
 	case CHAN_MODE_UNDEFINED:
 		break;
 	case CHAN_MODE_SHARED:
-		WARN_ON(!chan);
-		used_channels[0] = chan;
-		num_different_channels++;
+		if (WARN_ON(!chan))
+			return -EINVAL;
+
+		for (i = 0; i < max_num_diff_chans; i++)
+			if (!used_chans[i] || used_chans[i] == chan)
+				break;
+
+		if (i == max_num_diff_chans)
+			return -EBUSY;
+
+		if (used_chans[i] == NULL) {
+			used_chans[i] = chan;
+			(*num_diff_chans)++;
+		}
 		break;
 	case CHAN_MODE_EXCLUSIVE:
-		num_different_channels++;
+		(*num_diff_chans)++;
 		break;
 	}
 
+	return 0;
+}
+
+int cfg80211_can_use_iftype_chan_params(struct cfg80211_registered_device *rdev,
+				const struct cfg80211_iftype_chan_param *params,
+				int num_params)
+{
+	struct wireless_dev *wdev_iter;
+	u32 used_iftypes = 0;
+	int num[NUM_NL80211_IFTYPES];
+	struct ieee80211_channel
+			*used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS];
+	struct ieee80211_channel *ch;
+	enum cfg80211_chan_mode chmode;
+	int num_different_channels = 0;
+	int total = num_params;
+	bool num_software_iftypes = 0;
+	int err, i, j;
+
+	ASSERT_RTNL();
+
+	memset(num, 0, sizeof(num));
+	memset(used_channels, 0, sizeof(used_channels));
+
+	for (i = 0; i < num_params; i++) {
+		if (WARN_ON(hweight32(params[i].radar_detect_width) > 1))
+			return -EINVAL;
+
+		/* sanity check - make sure all wdevs in params[] are unique */
+		for (j = 0; j < num_params; j++)
+			if (WARN_ON(i != j && params[i].wdev == params[j].wdev))
+				return -EINVAL;
+
+		if (params[i].wdev && params[i].wdev->wiphy != &rdev->wiphy)
+			return -EINVAL;
+
+		used_iftypes |= BIT(params[i].iftype);
+		num[params[i].iftype]++;
+
+		err = cfg80211_is_radar_required(params[i].iftype,
+						 params[i].chan,
+						 params[i].chanmode,
+						 params[i].radar_detect_width);
+		if (err < 0)
+			return err;
+		else if (err && !params[i].radar_detect_width)
+			return -EINVAL;
+
+		if (rdev->wiphy.software_iftypes & BIT(params[i].iftype)) {
+			num_software_iftypes++;
+			if (params[i].radar_detect_width)
+				return -EINVAL;
+		}
+
+		err = cfg80211_add_used_chan(used_channels,
+					     &num_different_channels,
+					     ARRAY_SIZE(used_channels),
+					     params[i].chan,
+					     params[i].chanmode);
+		if (err)
+			return err;
+	}
+
+	/* Always allow software iftypes */
+	if (num_params == num_software_iftypes)
+		return 0;
+
 	list_for_each_entry(wdev_iter, &rdev->wdev_list, list) {
-		if (wdev_iter == wdev)
+		/* skip wdevs which are in params[] */
+		for (i = 0; i < num_params; i++)
+			if (wdev_iter == params[i].wdev)
+				break;
+		if (i < num_params)
 			continue;
+
 		if (wdev_iter->iftype == NL80211_IFTYPE_P2P_DEVICE) {
 			if (!wdev_iter->p2p_started)
 				continue;
@@ -1319,38 +1395,25 @@
 		cfg80211_get_chan_state(wdev_iter, &ch, &chmode);
 		wdev_unlock(wdev_iter);
 
-		switch (chmode) {
-		case CHAN_MODE_UNDEFINED:
-			break;
-		case CHAN_MODE_SHARED:
-			for (i = 0; i < CFG80211_MAX_NUM_DIFFERENT_CHANNELS; i++)
-				if (!used_channels[i] || used_channels[i] == ch)
-					break;
-
-			if (i == CFG80211_MAX_NUM_DIFFERENT_CHANNELS)
-				return -EBUSY;
-
-			if (used_channels[i] == NULL) {
-				used_channels[i] = ch;
-				num_different_channels++;
-			}
-			break;
-		case CHAN_MODE_EXCLUSIVE:
-			num_different_channels++;
-			break;
-		}
+		err = cfg80211_add_used_chan(used_channels,
+					     &num_different_channels,
+					     ARRAY_SIZE(used_channels),
+					     ch, chmode);
+		if (err)
+			return err;
 
 		num[wdev_iter->iftype]++;
 		total++;
 		used_iftypes |= BIT(wdev_iter->iftype);
 	}
 
-	if (total == 1 && !radar_detect)
+	if (total == 1 && num_params == 1 && !params[0].radar_detect_width)
 		return 0;
 
 	for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) {
 		const struct ieee80211_iface_combination *c;
 		struct ieee80211_iface_limit *limits;
+		enum nl80211_iftype iftype;
 		u32 all_iftypes = 0;
 
 		c = &rdev->wiphy.iface_combinations[i];
@@ -1378,8 +1441,12 @@
 			}
 		}
 
-		if (radar_detect && !(c->radar_detect_widths & radar_detect))
+		for (j = 0; j < num_params; j++) {
+			if (params[j].radar_detect_width &&
+			    !(c->radar_detect_widths &
+			      params[j].radar_detect_width))
 			goto cont;
+		}
 
 		/*
 		 * Finally check that all iftypes that we're currently
diff -ruw linux-3.11.10/net/xfrm/xfrm_user.c linux-3.11.10-fbx/net/xfrm/xfrm_user.c
--- linux-3.11.10/net/xfrm/xfrm_user.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/net/xfrm/xfrm_user.c	2015-09-29 11:15:09.173757822 +0200
@@ -2362,7 +2362,7 @@
 	link = &xfrm_dispatch[type];
 
 	/* All operations require privileges, even GET */
-	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
+	if (!netlink_net_capable(skb, CAP_NET_ADMIN))
 		return -EPERM;
 
 	if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) ||
diff -ruw linux-3.11.10/scripts/link-vmlinux.sh linux-3.11.10-fbx/scripts/link-vmlinux.sh
--- linux-3.11.10/scripts/link-vmlinux.sh	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/scripts/link-vmlinux.sh	2015-09-29 11:15:09.193757968 +0200
@@ -82,7 +82,9 @@
 		kallsymopt="${kallsymopt} --all-symbols"
 	fi
 
+	if [ -n "${CONFIG_ARM}" ] && [ -n "${CONFIG_PAGE_OFFSET}" ]; then
 	kallsymopt="${kallsymopt} --page-offset=$CONFIG_PAGE_OFFSET"
+	fi
 
 	local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL}               \
 		      ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS}"
diff -ruw linux-3.11.10/scripts/mod/file2alias.c linux-3.11.10-fbx/scripts/mod/file2alias.c
--- linux-3.11.10/scripts/mod/file2alias.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/scripts/mod/file2alias.c	2015-09-29 11:15:09.197758005 +0200
@@ -210,8 +210,8 @@
 				range_lo < 0x9 ? "[%X-9" : "[%X",
 				range_lo);
 			sprintf(alias + strlen(alias),
-				range_hi > 0xA ? "a-%X]" : "%X]",
-				range_lo);
+				range_hi > 0xA ? "A-%X]" : "%X]",
+				range_hi);
 		}
 	}
 	if (bcdDevice_initial_digits < (sizeof(bcdDevice_lo) * 2 - 1))
diff -ruw linux-3.11.10/scripts/mod/modpost.c linux-3.11.10-fbx/scripts/mod/modpost.c
--- linux-3.11.10/scripts/mod/modpost.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/scripts/mod/modpost.c	2015-09-29 11:15:09.197758005 +0200
@@ -573,12 +573,16 @@
 		if (strncmp(symname, "_restgpr_", sizeof("_restgpr_") - 1) == 0 ||
 		    strncmp(symname, "_savegpr_", sizeof("_savegpr_") - 1) == 0 ||
 		    strncmp(symname, "_rest32gpr_", sizeof("_rest32gpr_") - 1) == 0 ||
-		    strncmp(symname, "_save32gpr_", sizeof("_save32gpr_") - 1) == 0)
+		    strncmp(symname, "_save32gpr_", sizeof("_save32gpr_") - 1) == 0 ||
+		    strncmp(symname, "_restvr_", sizeof("_restvr_") - 1) == 0 ||
+		    strncmp(symname, "_savevr_", sizeof("_savevr_") - 1) == 0)
 			return 1;
 	if (info->hdr->e_machine == EM_PPC64)
 		/* Special register function linked on all modules during final link of .ko */
 		if (strncmp(symname, "_restgpr0_", sizeof("_restgpr0_") - 1) == 0 ||
-		    strncmp(symname, "_savegpr0_", sizeof("_savegpr0_") - 1) == 0)
+		    strncmp(symname, "_savegpr0_", sizeof("_savegpr0_") - 1) == 0 ||
+		    strncmp(symname, "_restvr_", sizeof("_restvr_") - 1) == 0 ||
+		    strncmp(symname, "_savevr_", sizeof("_savevr_") - 1) == 0)
 			return 1;
 	/* Do not ignore this symbol */
 	return 0;
diff -ruw linux-3.11.10/security/Kconfig linux-3.11.10-fbx/security/Kconfig
--- linux-3.11.10/security/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/security/Kconfig	2015-09-29 11:15:09.201758030 +0200
@@ -122,6 +122,7 @@
 source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
 source security/yama/Kconfig
+source security/fbxlsmjail/Kconfig
 
 source security/integrity/Kconfig
 
diff -ruw linux-3.11.10/security/Makefile linux-3.11.10-fbx/security/Makefile
--- linux-3.11.10/security/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/security/Makefile	2015-09-29 11:15:09.201758030 +0200
@@ -8,6 +8,7 @@
 subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
 subdir-$(CONFIG_SECURITY_YAMA)		+= yama
+subdir-$(CONFIG_SECURITY_FBXLSMJAIL)	+= fbxlsmjail
 
 # always enable default capabilities
 obj-y					+= commoncap.o
@@ -23,6 +24,7 @@
 obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/built-in.o
 obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/built-in.o
 obj-$(CONFIG_SECURITY_YAMA)		+= yama/built-in.o
+obj-$(CONFIG_SECURITY_FBXLSMJAIL)	+= fbxlsmjail/built-in.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 # Object integrity file lists
diff -ruw linux-3.11.10/sound/core/compress_offload.c linux-3.11.10-fbx/sound/core/compress_offload.c
--- linux-3.11.10/sound/core/compress_offload.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/core/compress_offload.c	2015-09-29 11:15:09.225758213 +0200
@@ -133,7 +133,7 @@
 		kfree(data);
 	}
 	snd_card_unref(compr->card);
-	return 0;
+	return ret;
 }
 
 static int snd_compr_free(struct inode *inode, struct file *f)
@@ -668,14 +668,48 @@
 		return -EPERM;
 	retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
 	if (!retval) {
-		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
-		wake_up(&stream->runtime->sleep);
+		snd_compr_drain_notify(stream);
 		stream->runtime->total_bytes_available = 0;
 		stream->runtime->total_bytes_transferred = 0;
 	}
 	return retval;
 }
 
+static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
+{
+	int ret;
+
+	/*
+	 * We are called with lock held. So drop the lock while we wait for
+	 * drain complete notfication from the driver
+	 *
+	 * It is expected that driver will notify the drain completion and then
+	 * stream will be moved to SETUP state, even if draining resulted in an
+	 * error. We can trigger next track after this.
+	 */
+	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+	mutex_unlock(&stream->device->lock);
+
+	/* we wait for drain to complete here, drain can return when
+	 * interruption occurred, wait returned error or success.
+	 * For the first two cases we don't do anything different here and
+	 * return after waking up
+	 */
+
+	ret = wait_event_interruptible(stream->runtime->sleep,
+			(stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
+	if (ret == -ERESTARTSYS)
+		pr_debug("wait aborted by a signal");
+	else if (ret)
+		pr_debug("wait for drain failed with %d\n", ret);
+
+
+	wake_up(&stream->runtime->sleep);
+	mutex_lock(&stream->device->lock);
+
+	return ret;
+}
+
 static int snd_compr_drain(struct snd_compr_stream *stream)
 {
 	int retval;
@@ -683,14 +717,17 @@
 	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
 			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
 		return -EPERM;
+
 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
-	if (!retval) {
-		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+	if (retval) {
+		pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
 		wake_up(&stream->runtime->sleep);
-	}
 	return retval;
 }
 
+	return snd_compress_wait_for_drain(stream);
+}
+
 static int snd_compr_next_track(struct snd_compr_stream *stream)
 {
 	int retval;
@@ -724,9 +761,14 @@
 		return -EPERM;
 
 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
+	if (retval) {
+		pr_debug("Partial drain returned failure\n");
+		wake_up(&stream->runtime->sleep);
+		return retval;
+	}
 
 	stream->next_track = false;
-	return retval;
+	return snd_compress_wait_for_drain(stream);
 }
 
 static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
diff -ruw linux-3.11.10/sound/core/control.c linux-3.11.10-fbx/sound/core/control.c
--- linux-3.11.10/sound/core/control.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/core/control.c	2015-09-29 11:15:09.225758213 +0200
@@ -289,6 +289,10 @@
 {
 	struct snd_kcontrol *kctl;
 
+	/* Make sure that the ids assigned to the control do not wrap around */
+	if (card->last_numid >= UINT_MAX - count)
+		card->last_numid = 0;
+
 	list_for_each_entry(kctl, &card->controls, list) {
 		if (kctl->id.numid < card->last_numid + 1 + count &&
 		    kctl->id.numid + kctl->count > card->last_numid + 1) {
@@ -331,6 +335,7 @@
 {
 	struct snd_ctl_elem_id id;
 	unsigned int idx;
+	unsigned int count;
 	int err = -EINVAL;
 
 	if (! kcontrol)
@@ -338,6 +343,9 @@
 	if (snd_BUG_ON(!card || !kcontrol->info))
 		goto error;
 	id = kcontrol->id;
+	if (id.index > UINT_MAX - kcontrol->count)
+		goto error;
+
 	down_write(&card->controls_rwsem);
 	if (snd_ctl_find_id(card, &id)) {
 		up_write(&card->controls_rwsem);
@@ -359,8 +367,9 @@
 	card->controls_count += kcontrol->count;
 	kcontrol->id.numid = card->last_numid + 1;
 	card->last_numid += kcontrol->count;
+	count = kcontrol->count;
 	up_write(&card->controls_rwsem);
-	for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
+	for (idx = 0; idx < count; idx++, id.index++, id.numid++)
 		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
 	return 0;
 
@@ -389,6 +398,7 @@
 		    bool add_on_replace)
 {
 	struct snd_ctl_elem_id id;
+	unsigned int count;
 	unsigned int idx;
 	struct snd_kcontrol *old;
 	int ret;
@@ -424,8 +434,9 @@
 	card->controls_count += kcontrol->count;
 	kcontrol->id.numid = card->last_numid + 1;
 	card->last_numid += kcontrol->count;
+	count = kcontrol->count;
 	up_write(&card->controls_rwsem);
-	for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
+	for (idx = 0; idx < count; idx++, id.index++, id.numid++)
 		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
 	return 0;
 
@@ -898,9 +909,9 @@
 			result = kctl->put(kctl, control);
 		}
 		if (result > 0) {
+			struct snd_ctl_elem_id id = control->id;
 			up_read(&card->controls_rwsem);
-			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-				       &control->id);
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id);
 			return 0;
 		}
 	}
@@ -992,6 +1003,7 @@
 
 struct user_element {
 	struct snd_ctl_elem_info info;
+	struct snd_card *card;
 	void *elem_data;		/* element data */
 	unsigned long elem_data_size;	/* size of element data in bytes */
 	void *tlv_data;			/* TLV data */
@@ -1035,7 +1047,9 @@
 {
 	struct user_element *ue = kcontrol->private_data;
 
+	mutex_lock(&ue->card->user_ctl_lock);
 	memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size);
+	mutex_unlock(&ue->card->user_ctl_lock);
 	return 0;
 }
 
@@ -1045,9 +1059,11 @@
 	int change;
 	struct user_element *ue = kcontrol->private_data;
 	
+	mutex_lock(&ue->card->user_ctl_lock);
 	change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0;
 	if (change)
 		memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size);
+	mutex_unlock(&ue->card->user_ctl_lock);
 	return change;
 }
 
@@ -1067,19 +1083,32 @@
 		new_data = memdup_user(tlv, size);
 		if (IS_ERR(new_data))
 			return PTR_ERR(new_data);
+		mutex_lock(&ue->card->user_ctl_lock);
 		change = ue->tlv_data_size != size;
 		if (!change)
 			change = memcmp(ue->tlv_data, new_data, size);
 		kfree(ue->tlv_data);
 		ue->tlv_data = new_data;
 		ue->tlv_data_size = size;
+		mutex_unlock(&ue->card->user_ctl_lock);
 	} else {
-		if (! ue->tlv_data_size || ! ue->tlv_data)
-			return -ENXIO;
-		if (size < ue->tlv_data_size)
-			return -ENOSPC;
+		int ret = 0;
+
+		mutex_lock(&ue->card->user_ctl_lock);
+		if (!ue->tlv_data_size || !ue->tlv_data) {
+			ret = -ENXIO;
+			goto err_unlock;
+		}
+		if (size < ue->tlv_data_size) {
+			ret = -ENOSPC;
+			goto err_unlock;
+		}
 		if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))
-			return -EFAULT;
+			ret = -EFAULT;
+err_unlock:
+		mutex_unlock(&ue->card->user_ctl_lock);
+		if (ret)
+			return ret;
 	}
 	return change;
 }
@@ -1137,8 +1166,6 @@
 	struct user_element *ue;
 	int idx, err;
 
-	if (!replace && card->user_ctl_count >= MAX_USER_CONTROLS)
-		return -ENOMEM;
 	if (info->count < 1)
 		return -EINVAL;
 	access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
@@ -1147,21 +1174,16 @@
 				 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
 	info->id.numid = 0;
 	memset(&kctl, 0, sizeof(kctl));
-	down_write(&card->controls_rwsem);
-	_kctl = snd_ctl_find_id(card, &info->id);
-	err = 0;
-	if (_kctl) {
-		if (replace)
-			err = snd_ctl_remove(card, _kctl);
-		else
-			err = -EBUSY;
-	} else {
-		if (replace)
-			err = -ENOENT;
-	}
-	up_write(&card->controls_rwsem);
-	if (err < 0)
+
+	if (replace) {
+		err = snd_ctl_remove_user_ctl(file, &info->id);
+		if (err)
 		return err;
+	}
+
+	if (card->user_ctl_count >= MAX_USER_CONTROLS)
+		return -ENOMEM;
+
 	memcpy(&kctl.id, &info->id, sizeof(info->id));
 	kctl.count = info->owner ? info->owner : 1;
 	access |= SNDRV_CTL_ELEM_ACCESS_USER;
@@ -1211,6 +1233,7 @@
 	ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL);
 	if (ue == NULL)
 		return -ENOMEM;
+	ue->card = card;
 	ue->info = *info;
 	ue->info.access = 0;
 	ue->elem_data = (char *)ue + sizeof(*ue);
@@ -1322,8 +1345,9 @@
 		}
 		err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv);
 		if (err > 0) {
+			struct snd_ctl_elem_id id = kctl->id;
 			up_read(&card->controls_rwsem);
-			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id);
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &id);
 			return 0;
 		}
 	} else {
diff -ruw linux-3.11.10/sound/core/init.c linux-3.11.10-fbx/sound/core/init.c
--- linux-3.11.10/sound/core/init.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/core/init.c	2015-09-29 11:15:09.225758213 +0200
@@ -170,7 +170,7 @@
 	if (idx < 0) {
 		for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) {
 			/* idx == -1 == 0xffff means: take any free slot */
-			if (idx2 < sizeof(int) && !(idx & (1U << idx2)))
+			if (idx2 < 32 && !(idx & (1U << idx2)))
 				continue;
 			if (!test_bit(idx2, snd_cards_lock)) {
 				if (module_slot_match(module, idx2)) {
@@ -183,7 +183,7 @@
 	if (idx < 0) {
 		for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) {
 			/* idx == -1 == 0xffff means: take any free slot */
-			if (idx2 < sizeof(int) && !(idx & (1U << idx2)))
+			if (idx2 < 32 && !(idx & (1U << idx2)))
 				continue;
 			if (!test_bit(idx2, snd_cards_lock)) {
 				if (!slots[idx2] || !*slots[idx2]) {
@@ -215,6 +215,7 @@
 	INIT_LIST_HEAD(&card->devices);
 	init_rwsem(&card->controls_rwsem);
 	rwlock_init(&card->ctl_files_rwlock);
+	mutex_init(&card->user_ctl_lock);
 	INIT_LIST_HEAD(&card->controls);
 	INIT_LIST_HEAD(&card->ctl_files);
 	spin_lock_init(&card->files_lock);
diff -ruw linux-3.11.10/sound/core/pcm_lib.c linux-3.11.10-fbx/sound/core/pcm_lib.c
--- linux-3.11.10/sound/core/pcm_lib.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/core/pcm_lib.c	2015-09-29 11:15:09.229758242 +0200
@@ -1937,6 +1937,8 @@
 		case SNDRV_PCM_STATE_DISCONNECTED:
 			err = -EBADFD;
 			goto _endloop;
+		case SNDRV_PCM_STATE_PAUSED:
+			continue;
 		}
 		if (!tout) {
 			snd_printd("%s write error (DMA or IRQ trouble?)\n",
diff -ruw linux-3.11.10/sound/pci/Kconfig linux-3.11.10-fbx/sound/pci/Kconfig
--- linux-3.11.10/sound/pci/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/pci/Kconfig	2015-09-29 11:15:09.261758487 +0200
@@ -30,6 +30,7 @@
 	select SND_PCM
 	select SND_AC97_CODEC
 	select SND_OPL3_LIB
+	select ZONE_DMA
 	help
 	  Say 'Y' or 'M' to include support for Avance Logic ALS300/ALS300+
 
@@ -54,6 +55,7 @@
 	tristate "ALi M5451 PCI Audio Controller"
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
+	select ZONE_DMA
 	help
 	  Say Y here to include support for the integrated AC97 sound
 	  device on motherboards using the ALi M5451 Audio Controller
@@ -158,6 +160,7 @@
 	select SND_PCM
 	select SND_RAWMIDI
 	select SND_AC97_CODEC
+	select ZONE_DMA
 	help
 	  Say Y here to include support for Aztech AZF3328 (PCI168)
 	  soundcards.
@@ -463,6 +466,7 @@
 	select SND_HWDEP
 	select SND_RAWMIDI
 	select SND_AC97_CODEC
+	select ZONE_DMA
 	help
 	  Say Y to include support for Sound Blaster PCI 512, Live!,
 	  Audigy and E-mu APS (partially supported) soundcards.
@@ -478,6 +482,7 @@
 	tristate "Emu10k1X (Dell OEM Version)"
 	select SND_AC97_CODEC
 	select SND_RAWMIDI
+	select ZONE_DMA
 	help
 	  Say Y here to include support for the Dell OEM version of the
 	  Sound Blaster Live!.
@@ -511,6 +516,7 @@
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
+	select ZONE_DMA
 	help
 	  Say Y here to include support for soundcards based on ESS Solo-1
 	  (ES1938, ES1946, ES1969) chips.
@@ -522,6 +528,7 @@
 	tristate "ESS ES1968/1978 (Maestro-1/2/2E)"
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
+	select ZONE_DMA
 	help
 	  Say Y here to include support for soundcards based on ESS Maestro
 	  1/2/2E chips.
@@ -603,6 +610,7 @@
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	select BITREVERSE
+	select ZONE_DMA
 	help
 	  Say Y here to include support for soundcards based on the
 	  ICE1712 (Envy24) chip.
@@ -690,6 +698,7 @@
 config SND_MAESTRO3
 	tristate "ESS Allegro/Maestro3"
 	select SND_AC97_CODEC
+	select ZONE_DMA
 	help
 	  Say Y here to include support for soundcards based on ESS Maestro 3
 	  (Allegro) chips.
@@ -786,6 +795,7 @@
 	tristate "SiS 7019 Audio Accelerator"
 	depends on X86 && !X86_64
 	select SND_AC97_CODEC
+	select ZONE_DMA
 	help
 	  Say Y here to include support for the SiS 7019 Audio Accelerator.
 
@@ -797,6 +807,7 @@
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
+	select ZONE_DMA
 	help
 	  Say Y here to include support for soundcards based on the S3
 	  SonicVibes chip.
@@ -808,6 +819,7 @@
 	tristate "Trident 4D-Wave DX/NX; SiS 7018"
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
+	select ZONE_DMA
 	help
 	  Say Y here to include support for soundcards based on Trident
 	  4D-Wave DX/NX or SiS 7018 chips.
diff -ruw linux-3.11.10/sound/soc/codecs/cs42l52.c linux-3.11.10-fbx/sound/soc/codecs/cs42l52.c
--- linux-3.11.10/sound/soc/codecs/cs42l52.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/soc/codecs/cs42l52.c	2015-09-29 11:15:09.349759154 +0200
@@ -183,6 +183,8 @@
 	}
 }
 
+static DECLARE_TLV_DB_SCALE(mst_tlv, -6000, 50, 0);
+
 static DECLARE_TLV_DB_SCALE(hl_tlv, -10200, 50, 0);
 
 static DECLARE_TLV_DB_SCALE(hpd_tlv, -9600, 50, 1);
@@ -352,7 +354,7 @@
 static const unsigned int swap_values[] = { 0, 1, 3 };
 
 static const struct soc_enum adca_swap_enum =
-	SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 2, 1,
+	SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 2, 3,
 			      ARRAY_SIZE(left_swap_text),
 			      left_swap_text,
 			      swap_values);
@@ -361,7 +363,7 @@
 	SOC_DAPM_ENUM("Route", adca_swap_enum);
 
 static const struct soc_enum pcma_swap_enum =
-	SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 6, 1,
+	SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 6, 3,
 			      ARRAY_SIZE(left_swap_text),
 			      left_swap_text,
 			      swap_values);
@@ -370,7 +372,7 @@
 	SOC_DAPM_ENUM("Route", pcma_swap_enum);
 
 static const struct soc_enum adcb_swap_enum =
-	SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 0, 1,
+	SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 0, 3,
 			      ARRAY_SIZE(right_swap_text),
 			      right_swap_text,
 			      swap_values);
@@ -379,7 +381,7 @@
 	SOC_DAPM_ENUM("Route", adcb_swap_enum);
 
 static const struct soc_enum pcmb_swap_enum =
-	SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 4, 1,
+	SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 4, 3,
 			      ARRAY_SIZE(right_swap_text),
 			      right_swap_text,
 			      swap_values);
@@ -409,7 +411,7 @@
 static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
 
 	SOC_DOUBLE_R_SX_TLV("Master Volume", CS42L52_MASTERA_VOL,
-			      CS42L52_MASTERB_VOL, 0, 0x34, 0xE4, hl_tlv),
+			      CS42L52_MASTERB_VOL, 0, 0x88, 0x90, mst_tlv),
 
 	SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L52_HPA_VOL,
 			      CS42L52_HPB_VOL, 0, 0x34, 0xCC, hpd_tlv),
diff -ruw linux-3.11.10/sound/soc/codecs/cs42l52.h linux-3.11.10-fbx/sound/soc/codecs/cs42l52.h
--- linux-3.11.10/sound/soc/codecs/cs42l52.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/soc/codecs/cs42l52.h	2015-09-29 11:15:09.349759154 +0200
@@ -179,7 +179,7 @@
 #define CS42L52_MICB_CTL			0x11
 #define	CS42L52_MIC_CTL_MIC_SEL_MASK		0xBF
 #define	CS42L52_MIC_CTL_MIC_SEL_SHIFT		6
-#define CS42L52_MIC_CTL_TYPE_MASK		0xDF
+#define CS42L52_MIC_CTL_TYPE_MASK		0x20
 #define CS42L52_MIC_CTL_TYPE_SHIFT		5
 
 
diff -ruw linux-3.11.10/sound/soc/kirkwood/Kconfig linux-3.11.10-fbx/sound/soc/kirkwood/Kconfig
--- linux-3.11.10/sound/soc/kirkwood/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/soc/kirkwood/Kconfig	2015-09-29 11:15:09.389759460 +0200
@@ -28,3 +28,15 @@
 	  Say Y if you want to add support for SoC audio on
 	  the HP t5325 thin client.
 
+
+config SND_KIRKWOOD_RD88F6282A
+	tristate "SoC Audio support for RD-88F6282-A"
+	depends on SND_KIRKWOOD_SOC && MACH_RD88F6282A
+	select SND_KIRKWOOD_SOC_I2S
+	select SND_SOC_CS42L51
+
+config SND_KIRKWOOD_SOC_FBXGW2R
+	tristate "Soc Audio support for fbxgw2r"
+	depends on SND_KIRKWOOD_SOC && MACH_FBXGW2R && I2C
+	select SND_KIRKWOOD_SOC_I2S
+	select SND_SOC_CS42L52
diff -ruw linux-3.11.10/sound/soc/kirkwood/kirkwood-dma.c linux-3.11.10-fbx/sound/soc/kirkwood/kirkwood-dma.c
--- linux-3.11.10/sound/soc/kirkwood/kirkwood-dma.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/soc/kirkwood/kirkwood-dma.c	2015-09-29 11:15:09.389759460 +0200
@@ -247,6 +247,7 @@
 	struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
 	struct kirkwood_dma_data *priv;
 	unsigned long size, count;
+	unsigned long val;
 
 	priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
 
@@ -255,16 +256,22 @@
 	size = (size>>2)-1;
 	count = snd_pcm_lib_period_bytes(substream);
 
+	val = readl(priv->io + KIRKWOOD_SCNTR_CTL);
+
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		writel(count, priv->io + KIRKWOOD_PLAY_BYTE_INT_COUNT);
 		writel(runtime->dma_addr, priv->io + KIRKWOOD_PLAY_BUF_ADDR);
 		writel(size, priv->io + KIRKWOOD_PLAY_BUF_SIZE);
+		val |= KIRKWOOD_SCNTR_CTL_PLAY_CLEAR |
+			KIRKWOOD_SCNTR_CTL_PLAY_EN;
 	} else {
 		writel(count, priv->io + KIRKWOOD_REC_BYTE_INT_COUNT);
 		writel(runtime->dma_addr, priv->io + KIRKWOOD_REC_BUF_ADDR);
 		writel(size, priv->io + KIRKWOOD_REC_BUF_SIZE);
+		val |= KIRKWOOD_SCNTR_CTL_REC_CLEAR |
+			KIRKWOOD_SCNTR_CTL_REC_EN;
 	}
-
+	writel(val, priv->io + KIRKWOOD_SCNTR_CTL);
 
 	return 0;
 }
@@ -289,6 +296,35 @@
 	return count;
 }
 
+static snd_pcm_sframes_t kirkwood_dma_delay(struct snd_pcm_substream *substream,
+					    struct snd_soc_dai *codec_dai)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
+	struct kirkwood_dma_data *priv;
+	snd_pcm_sframes_t cur_pointer;
+	u32 real;
+
+	priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+	/* it sucks that we have to call pointer again, would be
+	 * better if current one was given */
+	cur_pointer = kirkwood_dma_pointer(substream);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		real = readl(priv->io + KIRKWOOD_PLAY_SCNTR);
+	else
+		real = readl(priv->io + KIRKWOOD_REC_SCNTR);
+
+	real %= runtime->buffer_size;
+
+	if (cur_pointer >= real)
+		return cur_pointer - real;
+	return runtime->buffer_size - real + cur_pointer - 1;
+
+}
+
 static struct snd_pcm_ops kirkwood_dma_ops = {
 	.open =		kirkwood_dma_open,
 	.close =        kirkwood_dma_close,
@@ -368,6 +404,7 @@
 
 static struct snd_soc_platform_driver kirkwood_soc_platform = {
 	.ops		= &kirkwood_dma_ops,
+	.delay		= kirkwood_dma_delay,
 	.pcm_new	= kirkwood_dma_new,
 	.pcm_free	= kirkwood_dma_free_dma_buffers,
 };
diff -ruw linux-3.11.10/sound/soc/kirkwood/kirkwood.h linux-3.11.10-fbx/sound/soc/kirkwood/kirkwood.h
--- linux-3.11.10/sound/soc/kirkwood/kirkwood.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/soc/kirkwood/kirkwood.h	2015-09-29 11:15:09.389759460 +0200
@@ -74,6 +74,15 @@
 #define KIRKWOOD_DCO_CTL_FREQ_12		(1<<0)
 #define KIRKWOOD_DCO_CTL_FREQ_24		(2<<0)
 
+#define KIRKWOOD_SCNTR_CTL			0x1220
+#define KIRKWOOD_SCNTR_CTL_REC_EN		(1 << 0)
+#define KIRKWOOD_SCNTR_CTL_PLAY_EN		(1 << 1)
+#define KIRKWOOD_SCNTR_CTL_REC_CLEAR		(1 << 8)
+#define KIRKWOOD_SCNTR_CTL_PLAY_CLEAR		(1 << 9)
+
+#define KIRKWOOD_PLAY_SCNTR			0x1224
+#define KIRKWOOD_REC_SCNTR			0x1228
+
 #define KIRKWOOD_DCO_SPCR_STATUS		0x120c
 #define KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK	(1<<16)
 
diff -ruw linux-3.11.10/sound/soc/kirkwood/Makefile linux-3.11.10-fbx/sound/soc/kirkwood/Makefile
--- linux-3.11.10/sound/soc/kirkwood/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/soc/kirkwood/Makefile	2015-09-29 11:15:09.389759460 +0200
@@ -6,6 +6,11 @@
 
 snd-soc-openrd-objs := kirkwood-openrd.o
 snd-soc-t5325-objs := kirkwood-t5325.o
+snd-soc-rd88f6282a-objs := kirkwood-rd88f6282a.o
+snd-soc-fbxgw2r-objs := kirkwood-fbxgw2r.o
 
 obj-$(CONFIG_SND_KIRKWOOD_SOC_OPENRD) += snd-soc-openrd.o
 obj-$(CONFIG_SND_KIRKWOOD_SOC_T5325) += snd-soc-t5325.o
+
+obj-$(CONFIG_SND_KIRKWOOD_RD88F6282A) += snd-soc-rd88f6282a.o
+obj-$(CONFIG_SND_KIRKWOOD_SOC_FBXGW2R) += snd-soc-fbxgw2r.o
diff -ruw linux-3.11.10/sound/soc/soc-dapm.c linux-3.11.10-fbx/sound/soc/soc-dapm.c
--- linux-3.11.10/sound/soc/soc-dapm.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/soc/soc-dapm.c	2015-09-29 11:15:09.409759614 +0200
@@ -3046,15 +3046,11 @@
 	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 	const char *pin = (const char *)kcontrol->private_value;
 
-	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
-
 	if (ucontrol->value.integer.value[0])
 		snd_soc_dapm_enable_pin(&card->dapm, pin);
 	else
 		snd_soc_dapm_disable_pin(&card->dapm, pin);
 
-	mutex_unlock(&card->dapm_mutex);
-
 	snd_soc_dapm_sync(&card->dapm);
 	return 0;
 }
@@ -3554,23 +3550,52 @@
 }
 
 /**
+ * snd_soc_dapm_enable_pin_unlocked - enable pin.
+ * @dapm: DAPM context
+ * @pin: pin name
+ *
+ * Enables input/output pin and its parents or children widgets iff there is
+ * a valid audio route and active audio stream.
+ *
+ * Requires external locking.
+ *
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
+				   const char *pin)
+{
+	return snd_soc_dapm_set_pin(dapm, pin, 1);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin_unlocked);
+
+/**
  * snd_soc_dapm_enable_pin - enable pin.
  * @dapm: DAPM context
  * @pin: pin name
  *
  * Enables input/output pin and its parents or children widgets iff there is
  * a valid audio route and active audio stream.
+ *
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
  */
 int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin)
 {
-	return snd_soc_dapm_set_pin(dapm, pin, 1);
+	int ret;
+
+	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+	ret = snd_soc_dapm_set_pin(dapm, pin, 1);
+
+	mutex_unlock(&dapm->card->dapm_mutex);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
 
 /**
- * snd_soc_dapm_force_enable_pin - force a pin to be enabled
+ * snd_soc_dapm_force_enable_pin_unlocked - force a pin to be enabled
  * @dapm: DAPM context
  * @pin: pin name
  *
@@ -3578,10 +3603,12 @@
  * intended for use with microphone bias supplies used in microphone
  * jack detection.
  *
+ * Requires external locking.
+ *
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
  */
-int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,
+int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
 				  const char *pin)
 {
 	struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
@@ -3598,25 +3625,103 @@
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin_unlocked);
+
+/**
+ * snd_soc_dapm_force_enable_pin - force a pin to be enabled
+ * @dapm: DAPM context
+ * @pin: pin name
+ *
+ * Enables input/output pin regardless of any other state.  This is
+ * intended for use with microphone bias supplies used in microphone
+ * jack detection.
+ *
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,
+				  const char *pin)
+{
+	int ret;
+
+	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+	ret = snd_soc_dapm_force_enable_pin_unlocked(dapm, pin);
+
+	mutex_unlock(&dapm->card->dapm_mutex);
+
+	return ret;
+}
 EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin);
 
 /**
+ * snd_soc_dapm_disable_pin_unlocked - disable pin.
+ * @dapm: DAPM context
+ * @pin: pin name
+ *
+ * Disables input/output pin and its parents or children widgets.
+ *
+ * Requires external locking.
+ *
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm,
+				    const char *pin)
+{
+	return snd_soc_dapm_set_pin(dapm, pin, 0);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin_unlocked);
+
+/**
  * snd_soc_dapm_disable_pin - disable pin.
  * @dapm: DAPM context
  * @pin: pin name
  *
  * Disables input/output pin and its parents or children widgets.
+ *
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
  */
 int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm,
 			     const char *pin)
 {
-	return snd_soc_dapm_set_pin(dapm, pin, 0);
+	int ret;
+
+	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+	ret = snd_soc_dapm_set_pin(dapm, pin, 0);
+
+	mutex_unlock(&dapm->card->dapm_mutex);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin);
 
 /**
+ * snd_soc_dapm_nc_pin_unlocked - permanently disable pin.
+ * @dapm: DAPM context
+ * @pin: pin name
+ *
+ * Marks the specified pin as being not connected, disabling it along
+ * any parent or child widgets.  At present this is identical to
+ * snd_soc_dapm_disable_pin() but in future it will be extended to do
+ * additional things such as disabling controls which only affect
+ * paths through the pin.
+ *
+ * Requires external locking.
+ *
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm,
+			       const char *pin)
+{
+	return snd_soc_dapm_set_pin(dapm, pin, 0);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin_unlocked);
+
+/**
  * snd_soc_dapm_nc_pin - permanently disable pin.
  * @dapm: DAPM context
  * @pin: pin name
@@ -3632,7 +3737,15 @@
  */
 int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin)
 {
-	return snd_soc_dapm_set_pin(dapm, pin, 0);
+	int ret;
+
+	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+	ret = snd_soc_dapm_set_pin(dapm, pin, 0);
+
+	mutex_unlock(&dapm->card->dapm_mutex);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin);
 
diff -ruw linux-3.11.10/sound/usb/card.c linux-3.11.10-fbx/sound/usb/card.c
--- linux-3.11.10/sound/usb/card.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/usb/card.c	2015-09-29 11:15:09.417759680 +0200
@@ -307,6 +307,11 @@
 
 static int snd_usb_audio_free(struct snd_usb_audio *chip)
 {
+	struct list_head *p, *n;
+
+	list_for_each_safe(p, n, &chip->ep_list)
+		snd_usb_endpoint_free(p);
+
 	mutex_destroy(&chip->mutex);
 	kfree(chip);
 	return 0;
@@ -583,7 +588,7 @@
 				     struct snd_usb_audio *chip)
 {
 	struct snd_card *card;
-	struct list_head *p, *n;
+	struct list_head *p;
 
 	if (chip == (void *)-1L)
 		return;
@@ -596,14 +601,16 @@
 	mutex_lock(&register_mutex);
 	chip->num_interfaces--;
 	if (chip->num_interfaces <= 0) {
+		struct snd_usb_endpoint *ep;
+
 		snd_card_disconnect(card);
 		/* release the pcm resources */
 		list_for_each(p, &chip->pcm_list) {
 			snd_usb_stream_disconnect(p);
 		}
 		/* release the endpoint resources */
-		list_for_each_safe(p, n, &chip->ep_list) {
-			snd_usb_endpoint_free(p);
+		list_for_each_entry(ep, &chip->ep_list, list) {
+			snd_usb_endpoint_release(ep);
 		}
 		/* release the midi resources */
 		list_for_each(p, &chip->midi_list) {
diff -ruw linux-3.11.10/sound/usb/card.h linux-3.11.10-fbx/sound/usb/card.h
--- linux-3.11.10/sound/usb/card.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/usb/card.h	2015-09-29 11:15:09.417759680 +0200
@@ -91,6 +91,7 @@
 	unsigned int curframesize;      /* current packet size in frames (for capture) */
 	unsigned int syncmaxsize;	/* sync endpoint packet size */
 	unsigned int fill_max:1;	/* fill max packet size always */
+	unsigned int udh01_fb_quirk:1;	/* corrupted feedback data */
 	unsigned int datainterval;      /* log_2 of data packet interval */
 	unsigned int syncinterval;	/* P for adaptive mode, 0 otherwise */
 	unsigned char silence_value;
@@ -140,7 +141,7 @@
 	struct snd_pcm_hw_constraint_list rate_list;	/* limited rates */
 	spinlock_t lock;
 
-	int last_frame_number;          /* stored frame number */
+	ktime_t last_frame_time;        /* stored frame time */
 	int last_delay;                 /* stored delay */
 
 	struct {
diff -ruw linux-3.11.10/sound/usb/endpoint.c linux-3.11.10-fbx/sound/usb/endpoint.c
--- linux-3.11.10/sound/usb/endpoint.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/usb/endpoint.c	2015-09-29 11:15:09.417759680 +0200
@@ -467,6 +467,10 @@
 			ep->syncinterval = 3;
 
 		ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
+
+		if (chip->usb_id == USB_ID(0x0644, 0x8038) /* TEAC UD-H01 */ &&
+		    ep->syncmaxsize == 4)
+			ep->udh01_fb_quirk = 1;
 	}
 
 	list_add_tail(&ep->list, &chip->ep_list);
@@ -953,19 +957,30 @@
 }
 
 /**
+ * snd_usb_endpoint_release: Tear down an snd_usb_endpoint
+ *
+ * @ep: the endpoint to release
+ *
+ * This function does not care for the endpoint's use count but will tear
+ * down all the streaming URBs immediately.
+ */
+void snd_usb_endpoint_release(struct snd_usb_endpoint *ep)
+{
+	release_urbs(ep, 1);
+}
+
+/**
  * snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint
  *
  * @ep: the list header of the endpoint to free
  *
- * This function does not care for the endpoint's use count but will tear
- * down all the streaming URBs immediately and free all resources.
+ * This free all resources of the given ep.
  */
 void snd_usb_endpoint_free(struct list_head *head)
 {
 	struct snd_usb_endpoint *ep;
 
 	ep = list_entry(head, struct snd_usb_endpoint, list);
-	release_urbs(ep, 1);
 	kfree(ep);
 }
 
@@ -1075,7 +1090,16 @@
 	if (f == 0)
 		return;
 
-	if (unlikely(ep->freqshift == INT_MIN)) {
+	if (unlikely(sender->udh01_fb_quirk)) {
+		/*
+		 * The TEAC UD-H01 firmware sometimes changes the feedback value
+		 * by +/- 0x1.0000.
+		 */
+		if (f < ep->freqn - 0x8000)
+			f += 0x10000;
+		else if (f > ep->freqn + 0x8000)
+			f -= 0x10000;
+	} else if (unlikely(ep->freqshift == INT_MIN)) {
 		/*
 		 * The first time we see a feedback value, determine its format
 		 * by shifting it left or right until it matches the nominal
diff -ruw linux-3.11.10/sound/usb/endpoint.h linux-3.11.10-fbx/sound/usb/endpoint.h
--- linux-3.11.10/sound/usb/endpoint.h	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/usb/endpoint.h	2015-09-29 11:15:09.417759680 +0200
@@ -21,6 +21,7 @@
 void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
 int  snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
 int  snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_release(struct snd_usb_endpoint *ep);
 void snd_usb_endpoint_free(struct list_head *head);
 
 int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
diff -ruw linux-3.11.10/sound/usb/Kconfig linux-3.11.10-fbx/sound/usb/Kconfig
--- linux-3.11.10/sound/usb/Kconfig	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/usb/Kconfig	2015-09-29 11:15:09.417759680 +0200
@@ -14,6 +14,7 @@
 	select SND_HWDEP
 	select SND_RAWMIDI
 	select SND_PCM
+	select BITREVERSE
 	help
 	  Say Y here to include support for USB audio and USB MIDI
 	  devices.
diff -ruw linux-3.11.10/sound/usb/mixer.c linux-3.11.10-fbx/sound/usb/mixer.c
--- linux-3.11.10/sound/usb/mixer.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/usb/mixer.c	2015-09-29 11:15:09.421759705 +0200
@@ -883,6 +883,7 @@
 		}
 		break;
 
+	case USB_ID(0x046d, 0x0807): /* Logitech Webcam C500 */
 	case USB_ID(0x046d, 0x0808):
 	case USB_ID(0x046d, 0x0809):
 	case USB_ID(0x046d, 0x081b): /* HD Webcam c310 */
diff -ruw linux-3.11.10/sound/usb/mixer_maps.c linux-3.11.10-fbx/sound/usb/mixer_maps.c
--- linux-3.11.10/sound/usb/mixer_maps.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/usb/mixer_maps.c	2015-09-29 11:15:09.421759705 +0200
@@ -322,6 +322,11 @@
 	{ 0 }				/* terminator */
 };
 
+static const struct usbmix_name_map kef_x300a_map[] = {
+	{ 10, NULL }, /* firmware locks up (?) when we try to access this FU */
+	{ 0 }
+};
+
 /*
  * Control map entries
  */
@@ -409,6 +414,10 @@
 		.id = USB_ID(0x200c, 0x1018),
 		.map = ebox44_map,
 	},
+	{
+		.id = USB_ID(0x27ac, 0x1000),
+		.map = kef_x300a_map,
+	},
 	{ 0 } /* terminator */
 };
 
diff -ruw linux-3.11.10/sound/usb/pcm.c linux-3.11.10-fbx/sound/usb/pcm.c
--- linux-3.11.10/sound/usb/pcm.c	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/sound/usb/pcm.c	2015-09-29 11:15:09.421759705 +0200
@@ -43,28 +43,12 @@
 snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
 				    unsigned int rate)
 {
-	int current_frame_number;
-	int frame_diff;
+	unsigned int delta;
 	int est_delay;
 
-	if (!subs->last_delay)
-		return 0; /* short path */
-
-	current_frame_number = usb_get_current_frame_number(subs->dev);
-	/*
-	 * HCD implementations use different widths, use lower 8 bits.
-	 * The delay will be managed up to 256ms, which is more than
-	 * enough
-	 */
-	frame_diff = (current_frame_number - subs->last_frame_number) & 0xff;
+	delta = ktime_us_delta(ktime_get(), subs->last_frame_time);
 
-	/* Approximation based on number of samples per USB frame (ms),
-	   some truncation for 44.1 but the estimate is good enough */
-	est_delay =  frame_diff * rate / 1000;
-	if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
-		est_delay = subs->last_delay - est_delay;
-	else
-		est_delay = subs->last_delay + est_delay;
+	est_delay =  subs->last_delay - ((delta / 10 * rate) / 100000);
 
 	if (est_delay < 0)
 		est_delay = 0;
@@ -765,7 +749,7 @@
 	subs->hwptr_done = 0;
 	subs->transfer_done = 0;
 	subs->last_delay = 0;
-	subs->last_frame_number = 0;
+	subs->last_frame_time = ktime_get();
 	runtime->delay = 0;
 
 	/* for playback, submit the URBs now; otherwise, the first hwptr_done
@@ -1240,10 +1224,6 @@
 		 */
 		runtime->delay = subs->last_delay = 0;
 
-		/* realign last_frame_number */
-		subs->last_frame_number = current_frame_number;
-		subs->last_frame_number &= 0xFF; /* keep 8 LSBs */
-
 		spin_unlock_irqrestore(&subs->lock, flags);
 		/* copy a data chunk */
 		if (oldptr + bytes > runtime->buffer_size * stride) {
@@ -1412,9 +1392,8 @@
 	runtime->delay += frames;
 	subs->last_delay = runtime->delay;
 
-	/* realign last_frame_number */
-	subs->last_frame_number = usb_get_current_frame_number(subs->dev);
-	subs->last_frame_number &= 0xFF; /* keep 8 LSBs */
+	/* record time */
+	subs->last_frame_time = ktime_get();
 
 	spin_unlock_irqrestore(&subs->lock, flags);
 	urb->transfer_buffer_length = bytes;
@@ -1433,7 +1412,6 @@
 	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
 	struct snd_usb_endpoint *ep = subs->data_endpoint;
 	int processed = urb->transfer_buffer_length / ep->stride;
-	int est_delay;
 
 	/* ignore the delay accounting when procssed=0 is given, i.e.
 	 * silent payloads are procssed before handling the actual data
@@ -1442,10 +1420,6 @@
 		return;
 
 	spin_lock_irqsave(&subs->lock, flags);
-	if (!subs->last_delay)
-		goto out; /* short path */
-
-	est_delay = snd_usb_pcm_delay(subs, runtime->rate);
 	/* update delay with exact number of samples played */
 	if (processed > subs->last_delay)
 		subs->last_delay = 0;
@@ -1453,24 +1427,6 @@
 		subs->last_delay -= processed;
 	runtime->delay = subs->last_delay;
 
-	/*
-	 * Report when delay estimate is off by more than 2ms.
-	 * The error should be lower than 2ms since the estimate relies
-	 * on two reads of a counter updated every ms.
-	 */
-	if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2)
-		snd_printk(KERN_DEBUG "delay: estimated %d, actual %d\n",
-			est_delay, subs->last_delay);
-
-	if (!subs->running) {
-		/* update last_frame_number for delay counting here since
-		 * prepare_playback_urb won't be called during pause
-		 */
-		subs->last_frame_number =
-			usb_get_current_frame_number(subs->dev) & 0xff;
-	}
-
- out:
 	spin_unlock_irqrestore(&subs->lock, flags);
 }
 
diff -ruw linux-3.11.10/usr/Makefile linux-3.11.10-fbx/usr/Makefile
--- linux-3.11.10/usr/Makefile	2013-11-29 19:42:37.000000000 +0100
+++ linux-3.11.10-fbx/usr/Makefile	2015-09-29 11:15:09.473760100 +0200
@@ -46,8 +46,10 @@
 # in initramfs and to detect if any files are added/removed.
 # Removed files are identified by directory timestamp being updated
 # The dependency list is generated by gen_initramfs.sh -l
-ifneq ($(wildcard $(obj)/.initramfs_data.cpio.d),)
-	include $(obj)/.initramfs_data.cpio.d
+ifneq ($(wildcard $(obj)/.initramfs_data.cpio$(suffix_y).d),)
+	include $(obj)/.initramfs_data.cpio$(suffix_y).d
+else
+	deps_initramfs := FORCE
 endif
 
 quiet_cmd_initfs = GEN     $@
@@ -64,6 +66,6 @@
 # 3) If gen_init_cpio are newer than initramfs_data.cpio
 # 4) arguments to gen_initramfs.sh changes
 $(obj)/initramfs_data.cpio$(suffix_y): $(obj)/gen_init_cpio $(deps_initramfs) klibcdirs
-	$(Q)$(initramfs) -l $(ramfs-input) > $(obj)/.initramfs_data.cpio.d
+	$(Q)$(initramfs) -l $(ramfs-input) > $(obj)/.initramfs_data.cpio$(suffix_y).d
 	$(call if_changed,initfs)
 
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/fbxgw1r-setup.c	2015-09-29 11:15:04.881725130 +0200
@@ -0,0 +1,701 @@
+/*
+ * Freebox GW01r
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/irq.h>
+#include <linux/mtd/physmap.h>
+#include <linux/memblock.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/mv643xx_eth.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/flash.h>
+#include <linux/spi/ssd1327.h>
+#include <linux/i2c/at24.h>
+#include <linux/module.h>
+
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/pci.h>
+#include <mach/kirkwood.h>
+#include <mach/board_fbxgw1r.h>
+#include <linux/fbxgpio_core.h>
+#include <linux/smsc_cap1066.h>
+#include <linux/ethtool.h>
+#include <linux/input.h>
+#include "common.h"
+#include "mpp.h"
+
+#define PFX	"fbxgw1r: "
+
+#include "fbxgw-switch.h"
+#include "fbxgw-common.h"
+
+/*
+ * set shift registers output to given value
+ */
+static void set_shift_register(u8 val)
+{
+	int i;
+
+	gpio_set_value(GPIO_SR_CLK, 0);
+	gpio_set_value(GPIO_SR_LOAD, 0);
+
+	udelay(1);
+
+	for (i = 7; i >= 0; i--) {
+		gpio_set_value(GPIO_SR_DIN, (val & (1 << i)) ? 1 : 0);
+		udelay(1);
+		gpio_set_value(GPIO_SR_CLK, 1);
+		udelay(1);
+		gpio_set_value(GPIO_SR_CLK, 0);
+	}
+
+	udelay(1);
+	gpio_set_value(GPIO_SR_LOAD, 1);
+	udelay(1);
+}
+
+/*
+ * reset values can not be read back from shift registers, we have to
+ * keep them
+ */
+static DEFINE_SPINLOCK(sr_lock);
+static unsigned long sr_value;
+
+/*
+ * clear or set sr bit
+ */
+static void sr_set_bit(int bit, int value)
+{
+	unsigned long flags;
+
+	value = !!value;
+	spin_lock_irqsave(&sr_lock, flags);
+	sr_value &= ~(1 << bit);
+	sr_value |= (value << bit);
+	set_shift_register((u8)sr_value);
+	spin_unlock_irqrestore(&sr_lock, flags);
+}
+
+/*
+ * return cached bit value
+ */
+static int sr_get_bit(int bit)
+{
+	return test_bit(bit, &sr_value);
+}
+
+/*
+ * control PCIe bus reset
+ */
+static void fbxgw1r_pcie_reset(int value)
+{
+	sr_set_bit(SROUT_PCIE_RST, value);
+}
+
+#ifdef CONFIG_MACH_FBXGW1R_ETHSW_CONFIG
+/*
+ * control marvell swith reset
+ */
+static void fbxgw1r_marvell_switch_reset(int value)
+{
+	gpio_set_value(GPIO_SW_RESET, value);
+}
+
+static int fbxgw1r_do_vlan(void)
+{
+#ifdef CONFIG_MACH_FBXGW1R_ETHSW_CONFIG_VLAN
+	return 1;
+#else
+	return 0;
+#endif
+}
+
+#define NFS_VLAN_ID 41
+
+static int marvell_6161_config(struct net_device *dev, int probe,
+			       int (*mii_read)(struct net_device *dev,
+					       int phy_id, int reg),
+			       void (*mii_write)(struct net_device *dev,
+						 int phy_id, int reg, int val))
+{
+	u16 val;
+	struct mii_struct mii = {
+		.dev = dev,
+		.read = mii_read,
+		.write = mii_write,
+	};
+
+	if (!probe)
+		return 0;
+
+	/* switch needs more than 1 second (!) to go out of reset */
+	fbxgw1r_marvell_switch_reset(0);
+	mdelay(1);
+	fbxgw1r_marvell_switch_reset(1);
+	msleep(2000);
+
+	/* probe */
+	val = mii.read(mii.dev, SWPORT(0), PORTREG_SWITCH_IDENTIFIER);
+	if (PRODUCT_NUM(val) != 0x161) {
+		printk(KERN_ERR PFX "unknown switch id: 0x%08x\n",
+		       PRODUCT_NUM(val));
+		return 1;
+	}
+	mii.dev_id = PRODUCT_NUM(val);
+	mii.indirect_phy_access = false;
+
+	fbxgw_sw_config_cpu_port(&mii, SWPORT(5));
+	fbxgw_sw_config_phy_port(&mii, PHYPORT(1));
+
+
+	if (fbxgw1r_do_vlan()) {
+		const u8 config[6] = {
+			PDATA_NOT_MEMBER,
+			PDATA_MEMBER_UNTAGGED,
+			PDATA_NOT_MEMBER,
+			PDATA_NOT_MEMBER,
+			PDATA_NOT_MEMBER,
+			PDATA_MEMBER_TAGGED,
+		};
+
+		fbxgw_sw_vtu_load(&mii, NFS_VLAN_ID, config, sizeof (config));
+
+		fbxgw_sw_port_default_vid(&mii, SWPORT(1), NFS_VLAN_ID);
+		fbxgw_sw_port_dot1q_secure(&mii, SWPORT(1));
+		fbxgw_sw_port_dot1q_secure(&mii, SWPORT(5));
+	}
+
+	fbxgw_sw_port_forward_enable(&mii, SWPORT(1));
+	fbxgw_sw_port_forward_enable(&mii, SWPORT(5));
+
+	printk(KERN_INFO PFX "marvell 6161 initialized\n");
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_MACH_FBXGW1R_EXPANSION_BOARD_SETUP
+static int fbxgw1r_expansion_board_setup(void)
+{
+	int plugged;
+
+	plugged = !gpio_get_value(GPIO_EXP_PRESENCE);
+	if (!plugged) {
+		printk(KERN_INFO PFX "No expansion board plugged.\n");
+		return 0;
+	}
+
+	printk(KERN_INFO PFX "Expansion board plugged, powering up.\n");
+	sr_set_bit(SROUT_EXP_PWREN, 1);
+	gpio_set_value(GPIO_EXP_RST, 1);
+	mdelay(1000);
+
+#if 0
+	/*
+	 * catch early powerfault conditions (pwrgood to zero could
+	 * meand short circuits some where between the midplane and
+	 * the expansion board and could damage the
+	 * midplane/freebox/expansion board).
+	 */
+	if (gpio_get_value(GPIO_EXP_PWRGOOD) == 0) {
+		printk(KERN_WARNING PFX "Expansion power is not good, "
+		       "disabling");
+		sr_set_bit(SROUT_EXP_PWREN, 0);
+		gpio_set_value(GPIO_EXP_RST, 0);
+		return -ENXIO;
+	}
+#endif
+
+	printk(KERN_INFO PFX "Expansion powered up safely");
+	return 0;
+}
+#endif
+
+static struct mv643xx_eth_platform_data fbxgw1r_ge00_data = {
+	.phy_addr	= MV643XX_ETH_PHY_NONE,
+	.speed		= SPEED_1000,
+	.duplex		= DUPLEX_FULL,
+#ifdef CONFIG_MACH_FBXGW1R_ETHSW_CONFIG
+	.mii_config	= marvell_6161_config,
+#endif
+};
+
+static struct mv643xx_eth_platform_data fbxgw1r_ge01_data = {
+	.phy_addr	= MV643XX_ETH_PHY_NONE,
+	.speed		= SPEED_1000,
+	.duplex		= DUPLEX_FULL,
+};
+
+static const struct flash_platform_data	flash_info = {
+	.name		= "bcmflash",
+};
+
+static const struct ssd1327_platform_data ssd1327_pd = {
+	.data_select_gpio	= GPIO_OLED_DATA_SELECT,
+	.width			= 128,
+	.height			= 128,
+	.rotate			= 270,
+	.watchdog		= 300,
+};
+
+static struct spi_board_info spi_board_info[] __initdata = {
+
+	{
+		.modalias       = "ssd1327",
+		.platform_data	= &ssd1327_pd,
+		.mode		= SPI_MODE_0,
+		.max_speed_hz	= 10 * 1000 * 1000,
+		.bus_num	= 0,
+		.chip_select    = 0,
+	},
+
+	{
+		.modalias       = "m25p80",
+		.platform_data	= &flash_info,
+		.mode		= SPI_MODE_0,
+		.max_speed_hz	= 4 * 1000 * 1000,
+		.bus_num	= 0,
+		.chip_select    = 1,
+	},
+};
+
+/*
+ * fbxgpio
+ */
+static struct fbxgpio_operations fbxgw1r_gpio_ops = {
+	/* cast only for signed/unsigned */
+	.get_datain = (int (*)(int))gpio_get_value,
+	.get_dataout = (int (*)(int))gpio_get_value,
+	.set_dataout = (void (*)(int, int))gpio_set_value,
+};
+
+static struct fbxgpio_operations fbxgw1r_sr_ops = {
+	.get_dataout = sr_get_bit,
+	.set_dataout = sr_set_bit,
+};
+
+static struct fbxgpio_pin fbxgw1r_gpio_pins[] = {
+	/* marvell gpios */
+	{
+		.pin_name	 = "oled-data-select",
+		.direction	= GPIO_DIR_OUT,
+		.pin_num	= GPIO_OLED_DATA_SELECT,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "test-mode",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_TEST_MODE,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "sfp-txdis",
+		.direction	= GPIO_DIR_OUT,
+		.pin_num	= GPIO_SFP_TXDIS,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "sw-reset",
+		.direction	= GPIO_DIR_OUT,
+		.pin_num	= GPIO_SW_RESET,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "sw-int",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_SW_INT,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "sfp-pwrgood",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_SFP_PWRGOOD,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "sfp-txfault",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_SFP_TXFAULT,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "sfp-presence",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_SFP_PRESENCE,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "sfp-rxloss",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_SFP_RXLOSS,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "exp-rst",
+		.direction	= GPIO_DIR_OUT,
+		.pin_num	= GPIO_EXP_RST,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "pos-sense",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_POS_SENSE,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "exp-pwrgood",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_EXP_PWRGOOD,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "exp-presence",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_EXP_PRESENCE,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "kp-int",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_KP_INT,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "board-id-0",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_BOARD_ID_0,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+	{
+		.pin_name	= "board-id-1",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_BOARD_ID_1,
+		.ops		= &fbxgw1r_gpio_ops,
+	},
+
+	/* shift registers resets */
+	{
+		.pin_name       = "sfp-pwren",
+		.direction      = GPIO_DIR_OUT,
+		.pin_num	= SROUT_SFP_PWREN,
+		.ops		= &fbxgw1r_sr_ops,
+	},
+	{
+		.pin_name       = "usb-rst",
+		.direction      = GPIO_DIR_OUT,
+		.pin_num	= SROUT_USB_RST,
+		.ops		= &fbxgw1r_sr_ops,
+	},
+	{
+		.pin_name       = "audio-rst",
+		.direction      = GPIO_DIR_OUT,
+		.pin_num	= SROUT_AUDIO_RST,
+		.ops		= &fbxgw1r_sr_ops,
+	},
+	{
+		.pin_name       = "exp-pwren",
+		.direction      = GPIO_DIR_OUT,
+		.pin_num	= SROUT_EXP_PWREN,
+		.ops		= &fbxgw1r_sr_ops,
+	},
+	{
+		.pin_name       = "bcm-rst",
+		.direction      = GPIO_DIR_OUT,
+		.pin_num	= SROUT_BCM_RST,
+		.ops		= &fbxgw1r_sr_ops,
+	},
+	{
+		.pin_name       = "pcie-rst",
+		.direction      = GPIO_DIR_OUT,
+		.pin_num	= SROUT_PCIE_RST,
+		.ops		= &fbxgw1r_sr_ops,
+	},
+	{
+		.pin_name       = "keypad-oled-rst",
+		.direction      = GPIO_DIR_OUT,
+		.pin_num	= SROUT_KEYPAD_OLED_RST,
+		.ops		= &fbxgw1r_sr_ops,
+	},
+	{
+		.pin_name       = "keypad-pwren",
+		.direction      = GPIO_DIR_OUT,
+		.pin_num	= SROUT_OLED_PWREN,
+		.ops		= &fbxgw1r_sr_ops,
+	},
+
+
+	{  },
+};
+
+static struct platform_device fbxgw1r_gpio_device = {
+	.name   = "fbxgpio",
+	.id     = -1,
+	.dev    = {
+		.platform_data = &fbxgw1r_gpio_pins,
+	},
+};
+
+static unsigned int fbxgw1r_mpp_config[] __initdata = {
+	MPP0_NF_IO2,
+	MPP1_NF_IO3,
+	MPP2_NF_IO4,
+	MPP3_NF_IO5,
+	MPP4_NF_IO6,
+	MPP5_NF_IO7,
+	MPP6_SYSRST_OUTn,
+	MPP7_GPO,
+	MPP8_TW0_SDA,
+	MPP9_TW0_SCK,
+	MPP10_GPO,
+	MPP11_UART0_RXD,
+	MPP12_GPO,
+	MPP13_GPIO,
+	MPP14_GPIO,
+	MPP15_UART1_TXD,
+	MPP16_UART1_RXD,
+	MPP17_GPIO,
+	MPP18_NF_IO0,
+	MPP19_NF_IO1,
+	MPP20_GE1_TXD0,
+	MPP21_GE1_TXD1,
+	MPP22_GE1_TXD2,
+	MPP23_GE1_TXD3,
+	MPP24_GE1_RXD0,
+	MPP25_GE1_RXD1,
+	MPP26_GE1_RXD2,
+	MPP27_GE1_RXD3,
+	MPP28_GPIO,
+	MPP29_GPIO,
+	MPP30_GE1_RXCTL,
+	MPP31_GE1_RXCLK,
+	MPP32_GE1_TCLKOUT,
+	MPP33_GE1_TXCTL,
+	MPP34_GPIO,
+	MPP35_GPIO,
+	MPP36_GPIO,
+	MPP37_GPIO,
+	MPP38_GPIO,
+	MPP39_TDM_SPI_CS0,
+	MPP40_TDM_SPI_SCK,
+	MPP41_TDM_SPI_MISO,
+	MPP42_TDM_SPI_MOSI,
+	MPP43_GPIO,
+	MPP44_GPIO,
+	MPP45_GPIO,
+	MPP46_GPIO,
+	MPP47_GPIO,
+	MPP48_GPIO,
+	MPP49_GPIO,
+	0,
+};
+
+/*
+ * i2c midplane eeprom.
+ */
+static struct at24_platform_data midplane_eeprom_data = {
+	.byte_len	= 4096,
+	.page_size	= 8,
+	.flags		= AT24_FLAG_ADDR16,
+};
+
+/*
+ * expansion board eeprom.
+ */
+static struct at24_platform_data expansion_eeprom_data = {
+	.byte_len	= 32768,
+	.page_size	= 64,
+	.flags		= AT24_FLAG_ADDR16,
+};
+
+/*
+ * i2c smsc
+ */
+static struct smsc_cap1066_pdata cap1066_pdata = {
+	.key_map = {
+		KEY_DOWN,
+		KEY_LEFT,
+		KEY_UP,
+		0,
+		KEY_ENTER,
+		KEY_RIGHT,
+	},
+
+	.has_irq_gpio = true,
+	.irq_gpio = GPIO_KP_INT,
+};
+
+static struct i2c_board_info fbxgw1r_i2c_devs[] = {
+	{
+		.type		= "cap1066",
+		.addr		= 0x28,
+		.platform_data	= &cap1066_pdata,
+	},
+	{
+		.type		= "24c32",
+		.addr		= 0x57,
+		.platform_data	= &midplane_eeprom_data,
+	},
+	{
+		.type		= "24c256",
+		.addr		= 0x53,
+		.platform_data	= &expansion_eeprom_data,
+	},
+};
+
+void __init fbxgw1r_init(void)
+{
+	const struct fbx_serial *serial;
+	int tdm_cs_gpio[2];
+	extern int panic_timeout;
+	unsigned int n;
+
+	panic_timeout = 10;
+	panic_on_oops = 1;
+
+	fbxgw_common_fixup_i2c(0);
+
+	/* setup gpio */
+	kirkwood_mpp_conf(fbxgw1r_mpp_config);
+
+	gpio_request(GPIO_OLED_DATA_SELECT, "oled-data-select");
+	gpio_request(GPIO_SR_CLK, "sr-clk");
+	gpio_request(GPIO_SR_DIN, "sr-din");
+	gpio_request(GPIO_TEST_MODE, "test-mode");
+	gpio_request(GPIO_SFP_TXDIS, "sfp-txdis");
+	gpio_request(GPIO_SR_LOAD, "sr-load");
+	gpio_request(GPIO_SW_RESET, "sw-reset");
+	gpio_request(GPIO_SW_INT, "sw-int");
+	gpio_request(GPIO_SFP_PWRGOOD, "sfp-pwrgood");
+	gpio_request(GPIO_SFP_TXFAULT, "sfp-txfault");
+	gpio_request(GPIO_SPI_CS_BCM, "spi-cs-bcm");
+	gpio_request(GPIO_SFP_PRESENCE, "sfp-presence");
+	gpio_request(GPIO_SFP_RXLOSS, "sfp-rxloss");
+	gpio_request(GPIO_EXP_RST, "exp-rst");
+	gpio_request(GPIO_POS_SENSE, "pos-sense");
+	gpio_request(GPIO_EXP_PWRGOOD, "exp-pwrgood");
+	gpio_request(GPIO_EXP_PRESENCE, "exp-presence");
+	gpio_request(GPIO_KP_INT, "kp-int");
+	gpio_request(GPIO_BOARD_ID_0, "board-id-0");
+	gpio_request(GPIO_BOARD_ID_1, "board-id-1");
+
+	gpio_direction_output(GPIO_OLED_DATA_SELECT, 0);
+	gpio_direction_output(GPIO_SR_CLK, 0);
+	gpio_direction_output(GPIO_SR_DIN, 0);
+	gpio_direction_input(GPIO_TEST_MODE);
+	gpio_direction_output(GPIO_SFP_TXDIS, 1);
+	gpio_direction_output(GPIO_SR_LOAD, 0);
+	gpio_direction_output(GPIO_SW_RESET, 1);
+	gpio_direction_input(GPIO_SW_INT);
+	gpio_direction_input(GPIO_SFP_PWRGOOD);
+	gpio_direction_input(GPIO_SFP_TXFAULT);
+	gpio_direction_output(GPIO_SPI_CS_BCM, 1);
+	gpio_direction_input(GPIO_SFP_PRESENCE);
+	gpio_direction_input(GPIO_SFP_RXLOSS);
+	gpio_direction_output(GPIO_EXP_RST, 0);
+	gpio_direction_input(GPIO_POS_SENSE);
+	gpio_direction_input(GPIO_EXP_PWRGOOD);
+	gpio_direction_input(GPIO_EXP_PRESENCE);
+	gpio_direction_input(GPIO_KP_INT);
+	gpio_direction_input(GPIO_BOARD_ID_0);
+	gpio_direction_input(GPIO_BOARD_ID_1);
+
+	/* set shift register default value */
+	sr_value = (0 << SROUT_PCIE_RST) |
+		(0 << SROUT_BCM_RST) |
+		(1 << SROUT_KEYPAD_OLED_RST) |
+		(0 << SROUT_SFP_PWREN) |
+		(1 << SROUT_USB_RST) |
+		(1 << SROUT_AUDIO_RST) |
+		(0 << SROUT_EXP_PWREN) |
+		(1 << SROUT_OLED_PWREN);
+	set_shift_register((u8)sr_value);
+
+	fbxgw_common_pci_preinit();
+
+	kirkwood_init();
+
+#ifdef CONFIG_MACH_FBXGW1R_EXPANSION_BOARD_SETUP
+	/*
+	 * invoke expansion board setup early if needed, we want the
+	 * PCI device on the expansion board to be there 
+	 */
+	fbxgw1r_expansion_board_setup();
+#endif
+
+	/* make a good pci reset */
+	fbxgw1r_pcie_reset(0);
+	mdelay(100);
+	fbxgw1r_pcie_reset(1);
+	mdelay(100);
+
+	fbxgw_common_pcie_retrain_link();
+
+#ifdef CONFIG_FBXGW_COMMON_I2C_25K
+	n = 5;
+#endif
+#ifdef CONFIG_FBXGW_COMMON_I2C_50K
+	n = 4;
+#endif
+#ifdef CONFIG_FBXGW_COMMON_I2C_100K
+	n = 3;
+#endif
+	kirkwood_i2c_init(12, n);
+	i2c_register_board_info(0, fbxgw1r_i2c_devs,
+				ARRAY_SIZE(fbxgw1r_i2c_devs));
+
+	/* tdm spi gpio, only for CS1 */
+	tdm_cs_gpio[0] = -1;
+	tdm_cs_gpio[1] = GPIO_SPI_CS_BCM;
+	kirkwood_tdm_spi_init(tdm_cs_gpio);
+	spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
+
+	serial = arch_get_fbxserial();
+	if (serial) {
+		/*
+		 * use mac provided in serialinfo.
+		 */
+		memcpy(fbxgw1r_ge00_data.mac_addr, serial->mac_addr_base,
+		       ETH_ALEN);
+		memcpy(fbxgw1r_ge01_data.mac_addr, serial->mac_addr_base,
+		       ETH_ALEN);
+	}
+
+
+	kirkwood_ehci_init();
+	kirkwood_ge00_init(&fbxgw1r_ge00_data);
+	kirkwood_ge01_init(&fbxgw1r_ge01_data);
+	fbxgw_sata_init();
+	kirkwood_uart0_init();
+	kirkwood_uart1_init();
+
+	fbxgw_common_nand_init();
+
+	platform_device_register(&fbxgw1r_gpio_device);
+	fbxgw_fbxatm_init();
+	fbxgw_fbxwatchdog_init();
+}
+
+MACHINE_START(FBXGW1R, "Freebox Gateway version 1")
+	.atag_offset	= 0x100,
+	.init_machine	= fbxgw1r_init,
+#ifdef CONFIG_PSTORE_RAM
+	.reserve	= fbxgw_reserve_crash_zone,
+#endif
+	.map_io		= kirkwood_map_io,
+	.init_early	= kirkwood_init_early,
+	.init_irq	= kirkwood_init_irq,
+	.init_time	= kirkwood_timer_init,
+	.restart	= kirkwood_restart,
+MACHINE_END
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/fbxgw2r-setup.c	2015-09-29 11:15:04.881725130 +0200
@@ -0,0 +1,620 @@
+/*
+ * fbxgw2r-setup.c for fbxgw2r
+ * Created by <nschichan@freebox.fr> on Wed May 30 17:31:27 2012
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mv643xx_eth.h>
+#include <linux/gpio.h>
+#include <linux/kthread.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/flash.h>
+#include <linux/spi/ssd1327.h>
+#include <linux/i2c.h>
+#include <linux/i2c/at24.h>
+#include <linux/smsc_cap1066.h>
+#include <linux/fbxgpio_core.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include <mach/board_fbxgw2r.h>
+
+#include "mpp.h"
+#include "common.h"
+#include "fbxgw-common.h"
+#include "fbxgw-switch.h"
+
+#define PFX	"fbxgw2r: "
+
+/*
+ * reset whatever is behind the (inactive) mini-PCIe connector
+ */
+static void fbxgw2r_pcie_reset(int value)
+{
+	gpio_set_value(GPIO_PCIE_RST, value);
+}
+
+/*
+ * reset onboard WLAN chip.
+ */
+static void fbxgw2r_wlan_rst(int value)
+{
+	gpio_set_value(GPIO_WLAN_RST, value);
+}
+
+/*
+ * reset onboard audio codec
+ */
+static void fbxgw2r_audio_rst(int value)
+{
+	gpio_set_value(GPIO_AUDIO_RST, value);
+}
+
+static void fbxgw2r_marvell_switch_reset(int value)
+{
+	gpio_set_value(GPIO_SW_RESET, value);
+}
+
+static int fbxgw2r_do_vlan(void)
+{
+#ifdef CONFIG_MACH_FBXGW2R_ETHSW_CONFIG_VLAN
+	return 1;
+#else
+	return 0;
+#endif
+}
+
+#define NFS_VLAN_ID			41
+
+static int marvell_6176_config(struct net_device *dev, int probe,
+			       int (*mii_read)(struct net_device *dev,
+					       int phy_id, int reg),
+			       void (*mii_write)(struct net_device *dev,
+						 int phy_id, int reg, int val))
+{
+	u16 val;
+	struct mii_struct mii = {
+		.dev_id = 0,
+		.dev = dev,
+		.read = mii_read,
+		.write = mii_write,
+	};
+
+	if (!probe)
+		return 0;
+
+	fbxgw2r_marvell_switch_reset(0);
+	msleep(1);
+	fbxgw2r_marvell_switch_reset(1);
+	msleep(2000);
+
+	/* probe */
+	val = mii_read(dev, SWPORT(0), PORTREG_SWITCH_IDENTIFIER);
+	if (PRODUCT_NUM(val) != 0x176) {
+		printk(KERN_ERR PFX "unknown switch id: 0x%08x\n",
+		       PRODUCT_NUM(val));
+		return 1;
+	}
+	mii.dev_id = PRODUCT_NUM(val);
+	mii.indirect_phy_access = true;
+
+	fbxgw_sw_config_cpu_port(&mii, SWPORT(5));
+	fbxgw_sw_config_phy_port(&mii, PHYPORT(0));
+
+	mii.write(dev, SWPORT(0), 0x16, 0x8011);
+	mii.write(dev, SWPORT(1), 0x16, 0x8011);
+	mii.write(dev, SWPORT(2), 0x16, 0x8011);
+	mii.write(dev, SWPORT(3), 0x16, 0x8011);
+
+	if (fbxgw2r_do_vlan()) {
+		const u8 config[7] = {
+			PDATA_MEMBER_UNTAGGED,
+			PDATA_NOT_MEMBER,
+			PDATA_NOT_MEMBER,
+			PDATA_NOT_MEMBER,
+			PDATA_NOT_MEMBER,
+			PDATA_MEMBER_TAGGED,
+			PDATA_NOT_MEMBER,
+		};
+
+		fbxgw_sw_vtu_stu_init(&mii, PCR_PORTSTATE_FORWARDING,
+				      sizeof (config));
+		fbxgw_sw_vtu_load(&mii, NFS_VLAN_ID, config, sizeof (config));
+
+		fbxgw_sw_port_default_vid(&mii, SWPORT(0), NFS_VLAN_ID);
+		fbxgw_sw_port_dot1q_secure(&mii, SWPORT(0));
+		fbxgw_sw_port_dot1q_secure(&mii, SWPORT(5));
+	}
+
+	fbxgw_sw_port_forward_enable(&mii, SWPORT(5));
+	fbxgw_sw_port_forward_enable(&mii, SWPORT(0));
+
+	printk(KERN_INFO PFX "marvell 6176 initialized\n");
+
+	return 0;
+}
+
+static struct mv643xx_eth_platform_data fbxgw2r_ge00_data = {
+	.phy_addr	= MV643XX_ETH_PHY_NONE,
+	.speed		= SPEED_1000,
+	.duplex		= DUPLEX_FULL,
+#ifdef CONFIG_MACH_FBXGW2R_ETHSW_CONFIG
+	.mii_config	= marvell_6176_config,
+#endif
+};
+
+
+
+static const struct flash_platform_data	flash_info = {
+	.name		= "bcmflash",
+};
+
+static const struct ssd1327_platform_data ssd1327_pd = {
+	.data_select_gpio	= GPIO_OLED_DATA_SELECT,
+	.width			= 128,
+	.height			= 128,
+	.rotate			= 270,
+	.watchdog		= 300,
+};
+
+static struct spi_board_info spi_board_info[] __initdata = {
+
+	{
+		.modalias       = "ssd1327",
+		.platform_data	= &ssd1327_pd,
+		.mode		= SPI_MODE_0,
+		.max_speed_hz	= 10 * 1000 * 1000,
+		.bus_num	= 0,
+		.chip_select    = 0,
+	},
+
+	{
+		.modalias       = "m25p80",
+		.platform_data	= &flash_info,
+		.mode		= SPI_MODE_0,
+		.max_speed_hz	= 4 * 1000 * 1000,
+		.bus_num	= 0,
+		.chip_select    = 1,
+	},
+};
+
+/*
+ * fbxgpio
+ */
+static struct fbxgpio_operations fbxgw2r_gpio_ops = {
+	/* cast only for signed/unsigned */
+	.get_datain = (int (*)(int))gpio_get_value,
+	.get_dataout = (int (*)(int))gpio_get_value,
+	.set_dataout = (void (*)(int, int))gpio_set_value,
+};
+
+static struct fbxgpio_pin fbxgw2r_gpio_pins[] = {
+	/* marvell gpios */
+	{
+		.pin_name	= "bcm-rst",
+		.direction	= GPIO_DIR_OUT,
+		.pin_num	= GPIO_BCM_RST,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "pcie-rst",
+		.direction	= GPIO_DIR_OUT,
+		.pin_num	= GPIO_PCIE_RST,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "sw-int",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_SW_INT,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "test-mode",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_TEST_MODE,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "sfp-pwren",
+		.direction	= GPIO_DIR_OUT,
+		.pin_num	= GPIO_SFP_PWREN,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "sfp-txdis",
+		.direction	= GPIO_DIR_OUT,
+		.pin_num	= GPIO_SFP_TXDIS,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "sfp-pwrgood",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_SFP_PWRGOOD,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "sfp-presence",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_SFP_PRESENCE,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "sfp-rxloss",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_SFP_RXLOSS,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "sfp-txfault",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_SFP_TXFAULT,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "audio-rst",
+		.direction	= GPIO_DIR_OUT,
+		.pin_num	= GPIO_AUDIO_RST,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "pos-sense",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_POS_SENSE,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "exp-presence",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_EXP_PRESENCE,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "exp-pwrgood",
+		.direction	= GPIO_DIR_IN,
+		.pin_num	= GPIO_EXP_PWRGOOD,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "exp-pwren",
+		.direction	= GPIO_DIR_OUT,
+		.pin_num	= GPIO_EXP_PWREN,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "exp-rst",
+		.direction	= GPIO_DIR_OUT,
+		.pin_num	= GPIO_EXP_RST,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+	{
+		.pin_name	= "sw-reset",
+		.direction	= GPIO_DIR_OUT,
+		.pin_num	= GPIO_SW_RESET,
+		.ops		= &fbxgw2r_gpio_ops,
+	},
+
+	{ },
+};
+
+static struct platform_device fbxgw2r_gpio_device = {
+	.name   = "fbxgpio",
+	.id     = -1,
+	.dev    = {
+		.platform_data = &fbxgw2r_gpio_pins,
+	},
+};
+
+
+/*
+ * MPP pin muxing.
+ */
+static u32 fbxgw2r_mpp_config[] = {
+	MPP0_NF_IO2,
+	MPP1_NF_IO3,
+	MPP2_NF_IO4,
+	MPP3_NF_IO5,
+	MPP4_NF_IO6,
+	MPP5_NF_IO7,
+
+	MPP6_SYSRST_OUTn,
+	MPP7_GPO,		/* GPIO_OLED_DATA_SELECT */
+
+	MPP8_TW0_SDA,
+	MPP9_TW0_SCK,
+
+	MPP10_GPO,		/* USB_PWREN */
+
+	MPP11_UART0_RXD,	/* From Broadcom Uart */
+
+	MPP12_GPO,		/* SFP TX disable */
+	MPP13_GPIO,		/* BOOT_ETH */
+	MPP14_GPIO,		/* WLAN RST */
+
+	MPP15_UART1_TXD,	/* main uart tx */
+	MPP16_UART1_RXD,	/* main uart rx */
+
+	MPP17_GPIO,		/* GPIO_PCIE_RST */
+
+	MPP18_NF_IO0,
+	MPP19_NF_IO1,
+
+	MPP20_GPIO,		/* DISPLAY_PWREN */
+	MPP21_GPIO,		/* IHM_RST */
+	MPP22_GPIO,		/* EXP_PWREN*/
+	MPP23_GPIO,		/* USB_RSTn */
+
+	MPP24_TDM_SPI_CS0,
+	MPP25_TDM_SPI_SCK,
+	MPP26_TDM_SPI_MISO,
+	MPP27_TDM_SPI_MOSI,
+
+	MPP28_GPIO,		/* GPIO_SW_RESET */
+	MPP29_GPIO,		/* SW_INT */
+	MPP30_GPIO,		/* GPIO_BCM_RST */
+	MPP31_GPIO,		/* SFP_PRESn */
+	MPP32_GPIO,		/* SFP_PWRGOOD */
+	MPP33_GPO,		/* AUDIO_RSTn */
+
+	MPP34_GPIO,		/* GPIO_SPI_CS_BCM */
+
+	MPP35_GPIO,		/* SFP_TXFAULT */
+
+	MPP36_TW1_SDA,
+	MPP37_TW1_SCK,
+
+	MPP38_GPIO,		/* SFP_RXLOSS */
+
+	MPP39_AU_I2SBCLK,
+	MPP40_AU_I2SDO,
+	MPP41_AU_I2SLRCLK,
+	MPP42_AU_I2SMCLK,
+	MPP43_AU_I2SDI,
+
+	MPP44_GPIO,		/* EXP_RSTn */
+	MPP45_GPIO,		/* POS_SENSE  */
+	MPP46_GPIO,		/* EXP_PWRGOOD */
+	MPP47_GPIO,		/* EXP_PRESn */
+	MPP48_GPIO,		/* KEYPAD_IRQn */
+	MPP49_GPO,		/* SFP_PWREN */
+
+	0
+};
+
+
+/*
+ * i2c midplane eeprom.
+ */
+static struct at24_platform_data midplane_eeprom_data = {
+	.byte_len	= 4096,
+	.page_size	= 8,
+	.flags		= AT24_FLAG_ADDR16,
+};
+
+/*
+ * expansion board eeprom.
+ */
+static struct at24_platform_data expansion_eeprom_data = {
+	.byte_len	= 32768,
+	.page_size	= 64,
+	.flags		= AT24_FLAG_ADDR16,
+};
+
+/*
+ * i2c smsc
+ */
+static struct smsc_cap1066_pdata cap1066_pdata = {
+	.key_map = {
+		KEY_DOWN,
+		KEY_LEFT,
+		KEY_UP,
+		0,
+		KEY_ENTER,
+		KEY_RIGHT,
+	},
+
+	.has_irq_gpio = true,
+	.irq_gpio = GPIO_KP_INT,
+};
+
+static struct i2c_board_info fbxgw2r_i2c0_devs[] = {
+	{
+		.type		= "cap1066",
+		.addr		= 0x28,
+		.platform_data	= &cap1066_pdata,
+	},
+	{
+		.type		= "24c32",
+		.addr		= 0x57,
+		.platform_data	= &midplane_eeprom_data,
+	},
+	{
+		.type		= "24c256",
+		.addr		= 0x53,
+		.platform_data	= &expansion_eeprom_data,
+	},
+};
+
+static struct i2c_board_info fbxgw2r_i2c1_devs[] = {
+	{
+		I2C_BOARD_INFO("cs42l52", 0x4a),
+	},
+};
+
+#ifdef CONFIG_MACH_FBXGW2R_EXPANSION_BOARD_SETUP
+static int fbxgw2r_expansion_board_setup(void)
+{
+	int plugged;
+
+	plugged = !gpio_get_value(GPIO_EXP_PRESENCE);
+	if (!plugged) {
+		printk(KERN_INFO PFX "No expansion board plugged.\n");
+		return 0;
+	}
+
+	printk(KERN_INFO PFX "Expansion board plugged, powering up.\n");
+	gpio_set_value(GPIO_EXP_PWREN, 1);
+	gpio_set_value(GPIO_EXP_RST, 1);
+	mdelay(1000);
+
+	printk(KERN_INFO PFX "Expansion powered up safely");
+	return 0;
+}
+#endif
+
+struct platform_device fbxgw2r_audio = {
+	.name = "fbxgw2r-audio",
+	.id = -1,
+};
+
+void __init fbxgw2r_init(void)
+{
+	const struct fbx_serial *serial;
+	extern int panic_timeout;
+	int tdm_cs_gpio[2];
+	unsigned int n;
+
+	panic_timeout = 10;
+	panic_on_oops = 1;
+
+	fbxgw_common_fixup_i2c(0);
+	fbxgw_common_fixup_i2c(1);
+
+	kirkwood_mpp_conf(fbxgw2r_mpp_config);
+
+	gpio_request(GPIO_OLED_DATA_SELECT, "oled-data-select");
+	gpio_request(GPIO_WLAN_RST, "wlan-rst");
+	gpio_request(GPIO_PCIE_RST, "pcie-rst");
+	gpio_request(GPIO_SW_RESET, "sw-reset");
+	gpio_request(GPIO_SW_INT, "sw-int");
+	gpio_request(GPIO_TEST_MODE, "test-mode");
+	gpio_request(GPIO_SPI_CS_BCM, "spi-cs-bcm");
+	gpio_request(GPIO_BCM_RST, "bcm-rst");
+	gpio_request(GPIO_SFP_TXDIS, "sfp-txdis");
+	gpio_request(GPIO_SFP_PRESENCE, "sfp-presence");
+	gpio_request(GPIO_SFP_PWRGOOD, "sfp-pwrgood");
+	gpio_request(GPIO_SFP_TXFAULT, "sfp-txfault");
+	gpio_request(GPIO_SFP_RXLOSS, "sfp-rxloss");
+	gpio_request(GPIO_KP_INT, "kp-int");
+	gpio_request(GPIO_SFP_PWREN, "sfp-pwren");
+	gpio_request(GPIO_POS_SENSE, "pos-sense");
+	gpio_request(GPIO_AUDIO_RST, "audio-rst");
+
+	gpio_request(GPIO_EXP_PWREN, "exp-pwren");
+	gpio_request(GPIO_EXP_PWRGOOD, "exp-pwrgood");
+	gpio_request(GPIO_EXP_PRESENCE, "exp-presence");
+	gpio_request(GPIO_EXP_RST, "exp-rst");
+
+	gpio_direction_output(GPIO_OLED_DATA_SELECT, 0);
+	gpio_direction_output(GPIO_WLAN_RST, 0);
+	gpio_direction_input(GPIO_TEST_MODE);
+	gpio_direction_output(GPIO_PCIE_RST, 0);
+	gpio_direction_output(GPIO_SW_RESET, 0);
+	gpio_direction_input(GPIO_SW_INT);
+	gpio_direction_output(GPIO_SPI_CS_BCM, 1);
+	gpio_direction_output(GPIO_BCM_RST, 0);
+	gpio_direction_output(GPIO_AUDIO_RST, 0);
+	gpio_direction_input(GPIO_POS_SENSE);
+
+	gpio_direction_output(GPIO_SFP_PWREN, 0);
+	gpio_direction_output(GPIO_SFP_TXDIS, 1);
+	gpio_direction_input(GPIO_SFP_PRESENCE);
+	gpio_direction_input(GPIO_SFP_PWRGOOD);
+	gpio_direction_input(GPIO_SFP_TXFAULT);
+	gpio_direction_input(GPIO_SFP_RXLOSS);
+
+	gpio_direction_output(GPIO_EXP_PWREN, 0);
+	gpio_direction_output(GPIO_EXP_RST, 0);
+	gpio_direction_input(GPIO_EXP_PRESENCE);
+	gpio_direction_input(GPIO_EXP_PWRGOOD);
+
+	kirkwood_init();
+
+#ifdef CONFIG_MACH_FBXGW2R_EXPANSION_BOARD_SETUP
+	/*
+	 * invoke expansion board setup early if needed, we want the
+	 * PCI device on the expansion board to be there
+	 */
+	fbxgw2r_expansion_board_setup();
+#endif
+
+	fbxgw_common_pci_preinit();
+
+	/* make a good pci reset */
+	fbxgw2r_pcie_reset(0);
+	fbxgw2r_wlan_rst(0);
+	mdelay(100);
+	fbxgw2r_pcie_reset(1);
+	fbxgw2r_wlan_rst(1);
+	mdelay(100);
+	fbxgw_common_pcie_retrain_link();
+
+	/* reset audio codec */
+	fbxgw2r_audio_rst(0);
+	mdelay(100);
+	fbxgw2r_audio_rst(1);
+
+	/* 25khz I2C */
+#ifdef CONFIG_FBXGW_COMMON_I2C_25K
+	n = 5;
+#endif
+#ifdef CONFIG_FBXGW_COMMON_I2C_50K
+	n = 4;
+#endif
+#ifdef CONFIG_FBXGW_COMMON_I2C_100K
+	n = 3;
+#endif
+	kirkwood_i2c_init(12, n);
+ 	kirkwood_i2c1_init(12, n);
+	i2c_register_board_info(0, fbxgw2r_i2c0_devs,
+				ARRAY_SIZE(fbxgw2r_i2c0_devs));
+	i2c_register_board_info(1, fbxgw2r_i2c1_devs,
+				ARRAY_SIZE(fbxgw2r_i2c1_devs));
+
+	kirkwood_uart0_init();
+	kirkwood_uart1_init();
+
+	serial = arch_get_fbxserial();
+	if (serial) {
+		memcpy(fbxgw2r_ge00_data.mac_addr, serial->mac_addr_base,
+		       ETH_ALEN);
+	} else {
+		memcpy(fbxgw2r_ge00_data.mac_addr,
+		       "\x00\x07\xcb\0x00\x00\xfd", ETH_ALEN);
+	}
+
+	kirkwood_ge00_init(&fbxgw2r_ge00_data);
+	kirkwood_ehci_init();
+
+	fbxgw_common_nand_init();
+
+	/* tdm spi gpio, only for CS1 */
+	tdm_cs_gpio[0] = -1;
+	tdm_cs_gpio[1] = GPIO_SPI_CS_BCM;
+	kirkwood_tdm_spi_init(tdm_cs_gpio);
+	spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
+
+	platform_device_register(&fbxgw2r_gpio_device);
+	fbxgw_fbxatm_init();
+	fbxgw_sata_init();
+	fbxgw_fbxwatchdog_init();
+	kirkwood_audio_init();
+	platform_device_register(&fbxgw2r_audio);
+}
+
+MACHINE_START(FBXGW2R, "Freebox Gateway version 2")
+	.atag_offset	= 0x100,
+	.init_machine	= fbxgw2r_init,
+#ifdef CONFIG_PSTORE_RAM
+	.reserve	= fbxgw_reserve_crash_zone,
+#endif
+	.map_io		= kirkwood_map_io,
+	.init_early	= kirkwood_init_early,
+	.init_irq	= kirkwood_init_irq,
+	.init_time	= kirkwood_timer_init,
+	.restart	= kirkwood_restart,
+MACHINE_END
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/fbxgw-common.c	2015-09-29 11:15:04.881725130 +0200
@@ -0,0 +1,561 @@
+/*
+ * fbxgw-common.c for fbxgw-common
+ * Created by <nschichan@freebox.fr> on Wed May 30 17:53:10 2012
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/memblock.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/pstore_ram.h>
+#include <linux/mtd/physmap.h>
+#include <linux/delay.h>
+#include <linux/mbus.h>
+#include <linux/platform_device.h>
+#include <linux/fbxatm_remote.h>
+#include <linux/mtd/nand.h>
+#include <linux/ata_platform.h>
+
+#include <asm/mach-types.h>
+#include <asm/setup.h>
+#include <plat/pcie.h>
+#include <mach/bridge-regs.h>
+
+#include "mpp.h"
+#include "common.h"
+
+struct fbx_serial serial;
+
+#define PFX "fbxgw-common: "
+
+/*
+ * code that can be shared between fbxgw1r & fbxgw2r:
+ * - crash zone init
+ * - fbxserial stuff.
+ * - nand timings & nand partitions
+ * - ATAG_LOADER_VERSION and ATAG_BOOT_INFO entries in
+ *   tagged list.
+ */
+
+/*
+ * top of RAM - 32k, just below bootloader page table
+ */
+#define CRASH_ZONE_ADDR_FBXGW1R       0x1fff8000
+#define CRASH_ZONE_ADDR_FBXGW2R       0x3fff8000
+
+static phys_addr_t __init crash_zone_addr(void)
+{
+	if (machine_is_fbxgw1r())
+		return CRASH_ZONE_ADDR_FBXGW1R;
+	if (machine_is_fbxgw2r())
+		return CRASH_ZONE_ADDR_FBXGW2R;
+
+	return 0;
+}
+
+void __init fbxgw_reserve_crash_zone(void)
+{
+	phys_addr_t addr = crash_zone_addr();
+
+	if (!addr) {
+		pr_warn(PFX "invalid crash_zone_addr.\n");
+		return ;
+	}
+
+	memblock_reserve(addr, SZ_16K);
+}
+
+static struct ramoops_platform_data ramoops_data = {
+        .mem_size		= SZ_16K,
+        .mem_address		= ~0 /* changed at runtime */,
+        .record_size		= SZ_16K,
+        .dump_oops		= 1,
+        .ecc_info		= {
+		.ecc_size = 1,
+	},
+};
+
+static struct platform_device ramoops_dev = {
+        .name = "ramoops",
+        .dev = {
+                .platform_data = &ramoops_data,
+        },
+};
+
+/*
+ * cannot setup crash_zone earlier than an init call since we need a
+ * working ioremap, not provided earlier.
+ */
+static int __init fbxgw_setup_crash_zone(void)
+{
+	phys_addr_t addr = crash_zone_addr();
+
+	if (!addr) {
+		pr_warn(PFX "invalid crash_zone_addr.\n");
+		return -EINVAL;
+	}
+
+	ramoops_data.mem_address = addr;
+	return platform_device_register(&ramoops_dev);
+}
+
+arch_initcall(fbxgw_setup_crash_zone);
+
+
+/*
+ * fbxserialinfo stuff.
+ */
+static int got_serial;
+const struct fbx_serial *arch_get_fbxserial(void)
+{
+	if (got_serial)
+		return &serial;
+	return NULL;
+}
+EXPORT_SYMBOL(arch_get_fbxserial);
+
+static int __init fbxgw2r_parse_fbxserial_tag(const struct tag *tag)
+{
+	memcpy(&serial, &tag->u.fbxserial, sizeof (serial));
+	add_device_randomness(&serial, sizeof (serial));
+	got_serial = 1;
+	return 0;
+}
+__tagtable(ATAG_FBXSERIAL, fbxgw2r_parse_fbxserial_tag);
+
+
+/*
+ * NAND flash
+ */
+static struct mtd_partition fbxgw1r_nand_parts[] = {
+	{
+		.name = "all",
+		.offset = 0,
+		.size = MTDPART_SIZ_FULL,
+		.mask_flags = MTD_WRITEABLE,
+	}, {
+		.name = "u-boot",
+		.offset = 0,
+		.size = SZ_1M,
+		.mask_flags = MTD_WRITEABLE,
+	}, {
+		.name = "serial",
+		.offset = SZ_1M,
+		.size = SZ_1M,
+		.mask_flags = MTD_WRITEABLE,
+	}, {
+		.name = "calibration",
+		.offset = SZ_1M * 2,
+		.size = SZ_1M,
+		.mask_flags = MTD_WRITEABLE,
+	}, {
+		.name = "bank0",
+		.offset = SZ_1M * 3,
+		.size = SZ_1M * 18,
+		.mask_flags = MTD_WRITEABLE,
+	}, {
+		.name = "nvram",
+		.offset = SZ_1M * 21,
+		.size = SZ_1M * 3,
+	}, {
+		.name = "bank1",
+		.offset = SZ_1M * 24,
+		.size = SZ_1M * 62,
+	}, {
+		.name = "femto",
+		.offset = SZ_1M * 86,
+		.size = SZ_1M * 16,
+	}, {
+		.name = "config",
+		.offset = SZ_1M * 120,
+		.size = SZ_1M * 8,
+	}, {
+		.name = "new_bank0",
+		.offset = SZ_1M * 102,
+		.size = SZ_1M * 18,
+	},
+};
+
+
+/*
+ * Hardcoded timings for two known NAND parts:
+ * - NUMONYX NAND01GW3B2CZA6F
+ * - TOSHIBA TC58NVG0S3EBAI4
+ *
+ * Kirkwood to ONFI mapping (from Marvell)
+ * =======================================
+ * TurnOff => tRHW
+ * Acc2First => tCEA
+ * Acc2Next => tRC
+ * NFOEnW => tREH
+ * CEn2WEn => tCS – tWP
+ * WrLow => tWP
+ * WrHigh => tWH
+ *
+ * = Numonyx
+ * tRHW = 100ns | 20 Turnoff cycles (regvalue = 16)
+ * tCEA = 25ns | 5 Acc2first cycles (regvalue = 9)
+ * tRC = 25ns | 5 Acc2next cycles (regvalue = 5)
+ * tREH = 10ns | 2 NOFEnW cycles (regvalue = 1)
+ * tCS - tWP = 20ns - 12ns = 8ns | 2 CEn2WEn cycles (regvalue = 6)
+ * tWp = 12ns | 3 WrLow cycles (regvalue = 3)
+ * tWh = 10ns | 2 WrHigh cycles (regvalue = 2)
+ * command delay: 25ns
+ *
+ * = Toshiba
+ * tRHW = 30ns | 6 Turnoff cycles (regvalue 2)
+ * tCEA = 25ns | 5 Acc2first cycles (regvalue 9)
+ * tRC = 25ns | 5 Acc2next cycles (regvalue 5)
+ * tREH = 10ns | 5 NOFEnW cycles (regvalue 2)
+ * tCS - tWP = 20ns - 12ns = 8ns | 2 CEn2WEn cycles (regvalue = 6)
+ * tWp = 12ns | 3 WrLow cycles (regvalue = 3)
+ * tWh = 10ns | 2 WrHigh cycles (regvalue = 2)
+ * command delay: 30ns
+ *
+ * all values are the same besides Turnoff
+ * add one cycle for all values
+ *
+ * BEWARE: Acc2next & NFOEnW ARE LINKED ! Any additional cycles given
+ * to NOFEnW must be accounted for in Acc2next
+ */
+#define NAND_COMMAND_DELAY	35
+
+static void __init set_nand_timings(void)
+{
+	u32 val;
+	u32 turnoff, acc2first, acc2next, nofenw, cen2wen, wrlow, wrhigh;
+
+#ifdef CONFIG_FBXGW_COMMON_NAND_SAFE_READ_TIMINGS
+	turnoff = 0x1f;
+	acc2first = 0x1f;
+	acc2next = 0x1f;
+	nofenw = 0xc;
+#else
+	turnoff = 0x11;
+	acc2first = 0xa;
+	acc2next = 0x7;
+	nofenw = 0x2;
+#endif
+
+#ifdef CONFIG_FBXGW_COMMON_NAND_SAFE_WRITE_TIMINGS
+	cen2wen = 0xf;
+	wrlow = 0xf;
+	wrhigh = 0xf;
+#else
+	cen2wen = 0x7;
+	wrlow = 0x4;
+	wrhigh = 0x3;
+#endif
+
+
+	val = readl(NAND_VIRT_BASE + NAND_RD_PARAM_OFF);
+	/* turnoff */
+	val &= ~(0x1f << 0);
+	val |= (turnoff << 0);
+	/* acc2first */
+	val &= ~(0x1f << 6);
+	val |= (acc2first << 6);
+	/* acc2next */
+	val &= ~(0x1f << 17);
+	val |= (acc2next << 17);
+	writel(val, NAND_VIRT_BASE + NAND_RD_PARAM_OFF);
+
+	val = readl(NAND_VIRT_BASE + NAND_FLASH_CTL_OFF);
+	/* nfoenw */
+	val &= ~(0x1f << 9);
+	val |= (nofenw << 9);
+	writel(val, NAND_VIRT_BASE + NAND_FLASH_CTL_OFF);
+
+	val = readl(NAND_VIRT_BASE + NAND_WR_PARAM_OFF);
+	/* CEn2WEn */
+	val &= ~(0xf << 0);
+	val |= (cen2wen << 0);
+	/* WrLow */
+	val &= ~(0xf << 8);
+	val |= (wrlow << 8);
+	/* WrHigh */
+	val &= ~(0xf << 16);
+	val |= (wrhigh << 16);
+	writel(val, NAND_VIRT_BASE + NAND_WR_PARAM_OFF);
+}
+
+#ifdef CONFIG_FBXGW_COMMON_PARTS_WRITE_ALL
+static void __init set_parts_writeable(struct mtd_partition *parts, int count)
+{
+	int i;
+
+	for (i = 0; i < count; ++i) {
+		parts[i].mask_flags &= ~MTD_WRITEABLE;
+	}
+}
+#endif
+
+
+void __init fbxgw_common_nand_init(void)
+{
+	struct kirkwood_nand_ecc ecc;
+
+	set_nand_timings();
+#ifdef CONFIG_FBXGW_COMMON_PARTS_WRITE_ALL
+	set_parts_writeable(fbxgw1r_nand_parts, ARRAY_SIZE(fbxgw1r_nand_parts));
+#endif
+
+	if (machine_is_fbxgw1r())
+		ecc.ecc = NAND_ECC_SOFT;
+
+	if (machine_is_fbxgw2r()) {
+		ecc.ecc = NAND_ECC_SOFT_BCH;
+		/* default to 4 bits error correction per 512 bytes for now */
+		ecc.bch_ecc_size = 512;
+		ecc.bch_ecc_bytes = 7;
+	}
+
+	kirkwood_nand_init_ecc(ARRAY_AND_SIZE(fbxgw1r_nand_parts),
+			       NAND_COMMAND_DELAY, &ecc);
+}
+
+/*
+ * fbxhwinfo fields, retrieved from ATAG list.
+ */
+char loader_version_str[128];
+int loader_erase_nvram = 0;
+int loader_bank0_forced = 0;
+EXPORT_SYMBOL(loader_version_str);
+EXPORT_SYMBOL(loader_erase_nvram);
+EXPORT_SYMBOL(loader_bank0_forced);
+
+static int __init fbxgw_parse_loader_version(const struct tag *tag)
+{
+	const char *version;
+
+	version = tag->u.loader_version.version;
+
+	if (strncmp(version, "u-boot-", 7) ||
+	    strlen(version) > sizeof (loader_version_str) - 1) {
+		printk(KERN_ERR PFX "invalid loader version.\n");
+		return 0;
+	}
+
+	strcpy(loader_version_str, version);
+	printk(KERN_INFO PFX "loader version is '%s'\n", loader_version_str);
+	return 0;
+}
+__tagtable(ATAG_LOADER_VERSION, fbxgw_parse_loader_version);
+
+static int __init fbxgw_parse_boot_info(const struct tag *tag)
+{
+	if (tag->u.boot_info.erase_nvram) {
+		printk(KERN_INFO PFX "loader asked for nvram erase.\n");
+		loader_erase_nvram = 1;
+	}
+	if (tag->u.boot_info.bank0_forced) {
+		printk(KERN_INFO PFX "loader user forced a bank0 boot.\n");
+		loader_bank0_forced = 1;
+	}
+	return 0;
+}
+__tagtable(ATAG_BOOT_INFO, fbxgw_parse_boot_info);
+
+/*
+ * PCIe
+ */
+#define PCIE_BASE	((void __iomem *)PCIE_VIRT_BASE)
+#define PCIE1_BASE	((void __iomem *)PCIE1_VIRT_BASE)
+
+static void __init __fbxgw_pcie_preinit(int index, void __iomem *base)
+{
+	u32 val;
+	u32 pcie_enable_mask = index == 0 ? PCIE0_ENABLE : PCIE1_ENABLE;
+
+	/* we will reset PCIe bus, make sure it's correctly disabled
+	 * first */
+	val = readl(CPU_CONTROL);
+	if (!(val & pcie_enable_mask)) {
+		val |= pcie_enable_mask;
+		writel(val, CPU_CONTROL);
+		return;
+	}
+
+	if (!orion_pcie_link_up(base))
+		return;
+
+	orion_pcie_set_link_disable(base, 1);
+	mdelay(100);
+	orion_pcie_set_link_disable(base, 0);
+}
+
+void __init fbxgw_common_pci_preinit(void)
+{
+	__fbxgw_pcie_preinit(0, PCIE_BASE);
+	if (machine_is_fbxgw2r())
+		__fbxgw_pcie_preinit(1, PCIE1_BASE);
+}
+
+static void __init __fbxgw_pcie_retrain_link(int index, void __iomem *base)
+{
+	u32 cgc;
+
+	/*
+	 * be sure to enable corresponding PCIe clock. CGC_PEX0 might
+	 * already be set due to previous call to kirkwood_pcie_id()
+	 * though.
+	 */
+	cgc = readl(CLOCK_GATING_CTRL);
+	switch (index) {
+	case 0:
+		cgc |= CGC_PEX0;
+		break;
+	case 1:
+		cgc |= CGC_PEX1;
+		break;
+	}
+	writel(cgc, CLOCK_GATING_CTRL);
+
+	mdelay(100);
+	orion_pcie_set_retrain_link(base, 1);
+	mdelay(1);
+	/* check link, should be up */
+	if (!orion_pcie_link_up(base))
+		printk(KERN_ERR PFX "PCIe%d link is down\n", index);
+
+}
+
+void __init fbxgw_common_pcie_retrain_link(void)
+{
+	__fbxgw_pcie_retrain_link(0, PCIE_BASE);
+	if (machine_is_fbxgw2r())
+		__fbxgw_pcie_retrain_link(1, PCIE1_BASE);
+}
+
+static unsigned int i2c0_gpio_mpp_config[] __initdata = {
+	MPP8_GPIO,
+	MPP9_GPIO,
+	0,
+};
+
+static unsigned int i2c1_gpio_mpp_config[] __initdata = {
+	MPP36_GPIO,
+	MPP37_GPIO,
+	0,
+};
+
+void __init fbxgw_common_fixup_i2c(int bus_nr)
+{
+	int gpio_scl, gpio_sda;
+
+	if (bus_nr == 0) {
+		kirkwood_mpp_conf(i2c0_gpio_mpp_config);
+		gpio_sda = 8;
+		gpio_scl = 9;
+	} else if (bus_nr == 1) {
+		kirkwood_mpp_conf(i2c1_gpio_mpp_config);
+		gpio_sda = 36;
+		gpio_scl = 37;
+	} else
+		return;
+
+	gpio_request(gpio_sda, "sda");
+	gpio_request(gpio_scl, "scl");
+	gpio_direction_input(gpio_scl);
+	gpio_direction_input(gpio_sda);
+
+	if (!gpio_get_value(gpio_sda)) {
+		size_t i;
+
+		for (i = 0; i < 32; i++) {
+			gpio_direction_output(gpio_scl, 0);
+			udelay(100);
+			gpio_direction_input(gpio_scl);
+			udelay(100);
+		}
+
+		if (!gpio_get_value(gpio_sda))
+			printk(KERN_ERR "i2c%d seems locked\n", bus_nr);
+		else
+			printk(KERN_ERR "i2c%d unlocked manually\n", bus_nr);
+	}
+
+	gpio_free(gpio_sda);
+	gpio_free(gpio_scl);
+}
+
+/*
+ * be sure to add allow any new board here ...
+ */
+static int __init fbxgw_pci_init(void)
+{
+	unsigned int mask;
+	if (!machine_is_fbxgw1r() && !machine_is_fbxgw2r())
+		return -ENODEV;
+
+	mask = KW_PCIE0;
+	if (machine_is_fbxgw2r())
+		mask |= KW_PCIE1;
+	kirkwood_pcie_init(mask);
+	return 0;
+}
+subsys_initcall(fbxgw_pci_init);
+
+/*
+ * board name for fbxhwinfo
+ */
+char fbxhwinfo_model[32];
+EXPORT_SYMBOL(fbxhwinfo_model);
+static int __init fbxgw_setup_model(void)
+{
+	if (machine_is_fbxgw1r())
+		sprintf(fbxhwinfo_model, "fbxgw1r");
+	if (machine_is_fbxgw2r())
+		sprintf(fbxhwinfo_model, "fbxgw2r");
+	return 0;
+}
+arch_initcall(fbxgw_setup_model);
+
+/*
+ * broadcom 6358 remote atm device
+ */
+static struct fbxatm_remote_pdata bcm6358_remote_pdata = {
+	.remote_mac	= "\x00\x07\xcb\x00\x00\xfe",
+	.netdev_name	= "eth0.43",
+	.remote_name	= "bcm63xx_fbxxtm0",
+};
+
+static struct platform_device fbxatm_remote_device = {
+	.name	= "fbxatm_remote",
+	.id	= -1,
+	.dev	= {
+		.platform_data = &bcm6358_remote_pdata,
+	},
+};
+
+void __init fbxgw_fbxatm_init(void)
+{
+	platform_device_register(&fbxatm_remote_device);
+}
+
+/*
+ * internal & external SATA ports
+ */
+static struct mv_sata_platform_data fbxgw_sata_data = {
+	.n_ports	= 2,
+};
+
+void __init fbxgw_sata_init(void)
+{
+	kirkwood_sata_init(&fbxgw_sata_data);
+}
+
+/*
+ * fbxwatchdog
+ */
+static struct platform_device orion_fbxwdt_device = {
+	.name		= "orion_fbxwdt",
+	.id		= -1,
+	.num_resources	= 0,
+};
+
+void __init fbxgw_fbxwatchdog_init(void)
+{
+	platform_device_register(&orion_fbxwdt_device);
+}
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/fbxgw-common.h	2015-09-29 11:15:04.881725130 +0200
@@ -0,0 +1,18 @@
+/*
+ * fbxgw-common.h for fbxgw-common
+ * Created by <nschichan@freebox.fr> on Wed May 30 17:56:16 2012
+ */
+
+#ifndef __FBXGW_COMMON_H
+# define __FBXGW_COMMON_H
+
+void fbxgw_reserve_crash_zone(void);
+void fbxgw_common_nand_init(void);
+void fbxgw_common_pci_preinit(void);
+void fbxgw_common_pcie_retrain_link(void);
+void fbxgw_fbxatm_init(void);
+void fbxgw_sata_init(void);
+void fbxgw_fbxwatchdog_init(void);
+void fbxgw_common_fixup_i2c(int bus_nr);
+
+#endif /* !__FBXGW_COMMON_H */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/fbxgw-switch.c	2015-09-29 11:15:04.881725130 +0200
@@ -0,0 +1,309 @@
+/*
+ * fbxgw-switch.c for fbxgw-switch
+ * Created by <nschichan@freebox.fr> on Tue Jun  5 20:46:17 2012
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+
+#include "fbxgw-switch.h"
+
+#define PFX "fbxgw-switch: "
+
+
+#define MARVELL_PHYPORT0		0x00
+
+#define MARVELL_SWPORT0			0x10
+#define MARVELL_SWPORT4			0x14
+#define MARVELL_SWPORT5			0x15
+
+#define PORTREG_PCS			0x1
+
+#define PCS_RGMII_RX_DELAY		(1 << 15)
+#define PCS_RGMII_TX_DELAY		(1 << 14)
+#define PCS_FORCELINK_UP		(1 << 5)
+#define PCS_FORCELINK			(1 << 4)
+#define PCS_FORCEDUPLEX_FULL		(1 << 3)
+#define PCS_FORCEDUPLEX			(1 << 2)
+#define PCS_FORCESPEED_10		0x0
+#define PCS_FORCESPEED_100		0x1
+#define PCS_FORCESPEED_1000		0x2
+#define PCS_FORCESPEED_AUTO		0x3
+
+#define PORTREG_PCR			0x4
+#define PCR_PORTSTATE_DISABLED		0x0
+/* #define PCR_PORTSTATE_FORWARDING	0x3 */
+
+#define PORTREG_VLANID			0x7
+
+#define PORTREG_PCR2			0x8
+#define PCR2_MODE_SHIFT			10
+#define PCR2_MODE_SECURE		3
+
+#define MARVELL_GLOBAL1			0x1b
+#define MARVELL_GLOBAL2			0x1c
+
+#define GLOBREG_VTUFID			0x2
+#define GLOBREG_VTUSID			0x3
+
+#define GLOBREG_GCR			0x4
+#define GCR_PPUEN			(1 << 14)
+
+#define GLOBREG_VTUOP			0x5
+#define VTUOP_BUSY			(1 << 15)
+#define VTUOP_OP_LOAD			(0x3 << 12)
+#define VTUOP_OP_STU_LOAD		(0x5 << 12)
+#define VTUOP_OP_GETNEXT		(0x4 << 12)
+
+#define GLOBREG_VTUVID			0x6
+#define VTUVID_VALID			(1 << 12)
+
+#define GLOBREG_VTU_P03_DATA		0x7
+#define GLOBREG_VTU_P46_DATA		0x8
+
+#define GLOBREG_VTU_DATA		0x9
+
+#define GLOBREG_SMI_CMD			0x18
+# define SMI_CMD_BUSY			(1 << 15)
+# define SMI_CLAUSE_22			(1 << 12)
+# define SMI_CMD_READ			(2 << 10)
+# define SMI_CMD_WRITE			(1 << 10)
+# define SMI_DEVADDR(DevAddr)	(DevAddr << 5)
+# define SMI_REGADDR(RegAddr)	(RegAddr)
+#define GLOBREG_SMI_DATA		0x19
+
+#define GLOBREG_GSR			0x0
+# define GSR_PPU_POLLING		(1 << 15)
+
+
+/*
+ * voodo register content. P4_RGMII_FORCE is effective on revision A2
+ * of mv6161 chip. see revision A2 release notes for details.
+ */
+#define P4_RGMII_DELAY			0x03
+#define P5_RGMII_DELAY			0x18
+
+
+
+static int __mii_indirect_wait(struct mii_struct *mii, int tries)
+{
+	while (tries) {
+		u16 val = mii->read(mii->dev, MARVELL_GLOBAL2, GLOBREG_SMI_CMD);
+		if ((val & SMI_CMD_BUSY) == 0)
+			return 0;
+		udelay(1000);
+		--tries;
+	}
+	return -ETIMEDOUT;
+}
+
+static int __mii_indirect_read(struct mii_struct *mii, int phy_id, int reg)
+{
+	u16 smi_cmd = SMI_CMD_BUSY | SMI_CLAUSE_22 | SMI_CMD_READ |
+		SMI_DEVADDR(phy_id) | SMI_REGADDR(reg);
+
+	mii->write(mii->dev, MARVELL_GLOBAL2, GLOBREG_SMI_CMD, smi_cmd);
+	if (__mii_indirect_wait(mii, 1000) < 0) {
+		printk(KERN_WARNING PFX "indirect phy read did not "
+		       "complete.\n");
+		return 0xffff;
+	}
+	return mii->read(mii->dev, MARVELL_GLOBAL2, GLOBREG_SMI_DATA);
+}
+
+static int mii_phy_read(struct mii_struct *mii, int phy_id, int reg)
+{
+	if (mii->indirect_phy_access == false)
+		return mii->read(mii->dev, phy_id, reg);
+	else
+		return __mii_indirect_read(mii, phy_id, reg);
+}
+
+static void __mii_indirect_write(struct mii_struct *mii, int phy_id, int reg,
+				int val)
+{
+	u16 smi_cmd = SMI_CMD_BUSY | SMI_CLAUSE_22 | SMI_CMD_WRITE |
+		SMI_DEVADDR(phy_id) | SMI_REGADDR(reg);
+
+	mii->write(mii->dev, MARVELL_GLOBAL2, GLOBREG_SMI_DATA, val);
+	mii->write(mii->dev, MARVELL_GLOBAL2, GLOBREG_SMI_CMD, smi_cmd);
+	if (__mii_indirect_wait(mii, 1000) < 0) {
+		printk(KERN_WARNING PFX "indirect phy write did not "
+		       "complete.\n");
+	}
+}
+
+static void mii_phy_write(struct mii_struct *mii, int phy_id, int reg, int val)
+{
+
+	if (mii->indirect_phy_access == false)
+		return mii->write(mii->dev, phy_id, reg, val);
+	else
+		return __mii_indirect_write(mii, phy_id, reg, val);
+}
+
+static void __vtu_wait(struct mii_struct *mii)
+{
+	for (;;) {
+		u16 val = mii->read(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTUOP);
+		if ((val & (1 << 15)) == 0)
+			break;
+		msleep(10);
+	}
+}
+
+void fbxgw_sw_vtu_stu_init(struct mii_struct *mii, int port_state,
+			   size_t nr_ports)
+{
+	u16 vtu_op = VTUOP_OP_STU_LOAD;
+	u16 regs[2] = { 0 , 0 };
+	int i;
+
+	mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTUOP, vtu_op);
+
+	mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTUSID, 0);
+	mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTUVID, VTUVID_VALID);
+
+
+	for (i = 0; i < nr_ports; ++i) {
+		int off;
+		int shift;
+
+		off = i / 4;
+		shift = 4 * (i % 4) +  2;
+
+		regs[off] |= port_state << shift;
+	}
+
+	mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTU_P03_DATA, regs[0]);
+	mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTU_P46_DATA, regs[1]);
+
+	vtu_op |= VTUOP_BUSY;
+	mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTUOP, vtu_op);
+	__vtu_wait(mii);
+}
+
+void fbxgw_sw_vtu_load(struct mii_struct *mii, u16 vid, const u8 *ports,
+		       size_t nr_ports)
+{
+	u16 vtu_op;
+	u16 regs[2] = { 0, 0 };
+	int i;
+
+	vtu_op = VTUOP_OP_LOAD;
+	mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTUOP, vtu_op);
+
+	mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTUFID, 1);
+	mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTUSID, 0);
+
+	mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTUVID,
+		   vid | VTUVID_VALID);
+
+	for (i = 0; i < nr_ports; ++i) {
+		int off;
+		int shift;
+
+		off = (i / 4);
+		shift = (i % 4) * 4;
+
+		regs[off] |= ports[i] << shift;
+	}
+
+	mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTU_P03_DATA, regs[0]);
+	mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTU_P46_DATA, regs[1]);
+
+	vtu_op |= VTUOP_BUSY;
+	mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_VTUOP, vtu_op);
+
+	__vtu_wait(mii);
+}
+
+void fbxgw_sw_config_cpu_port(struct mii_struct *mii, int swport)
+{
+	u16 pcs = PCS_FORCEDUPLEX_FULL | PCS_FORCEDUPLEX |
+		PCS_FORCESPEED_1000 |
+		PCS_FORCELINK | PCS_FORCELINK_UP;
+
+	if (mii->dev_id == 0x176)
+		pcs |= PCS_RGMII_TX_DELAY | PCS_RGMII_RX_DELAY;
+	else {
+		static u16 delay;
+		/*
+		 * set rgmii delay for cpu port (5) and ftth port (4), also
+		 * force ftth port in RGMII mode
+		 */
+		if (swport == 5)
+			delay = P5_RGMII_DELAY;
+		else
+			delay = P4_RGMII_DELAY;
+		mii->write(mii->dev, MARVELL_SWPORT4, 0x1a, 0x81e7);
+		(void)mii->read(mii->dev, MARVELL_SWPORT5, 0x1a);
+		mii->write(mii->dev, MARVELL_SWPORT5, 0x1a, P5_RGMII_DELAY);
+		mii->write(mii->dev, MARVELL_SWPORT4, 0x1a, 0xc1e7);
+	}
+
+	mii->write(mii->dev, swport, PORTREG_PCS, pcs);
+}
+
+void fbxgw_sw_config_phy_port(struct mii_struct *mii, int phy_port)
+{
+	u16 val;
+
+	if (mii->indirect_phy_access == false) {
+		val = mii->read(mii->dev, MARVELL_GLOBAL1, GLOBREG_GCR);
+		val &= ~GCR_PPUEN;
+		mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_GCR, val);
+	}
+
+
+	/* power up phy for eth port 0 */
+	val = mii_phy_read(mii, phy_port, 0x00);
+	val &= ~0x0800;
+	mii_phy_write(mii, phy_port, 0x00, val);
+
+
+	/* restart autoneg */
+	val = mii_phy_read(mii, phy_port, 0x00);
+	val |= 0x0200;
+	mii_phy_write(mii, phy_port, 0x00, val);
+
+	mii_phy_write(mii, phy_port, 20, 0);
+
+	if (mii->indirect_phy_access == false) {
+		val = mii->read(mii->dev, MARVELL_GLOBAL1, GLOBREG_GCR);
+		val |= GCR_PPUEN;
+		mii->write(mii->dev, MARVELL_GLOBAL1, GLOBREG_GCR, val);
+	}
+}
+
+void fbxgw_sw_port_default_vid(struct mii_struct *mii, int swport, u16 vid)
+{
+	u16 val;
+
+	val = mii->read(mii->dev, swport, PORTREG_VLANID);
+	val &= ~0xfff;
+	val |= vid;
+	mii->write(mii->dev, swport, PORTREG_VLANID, val);
+}
+
+void fbxgw_sw_port_dot1q_secure(struct mii_struct *mii, int swport)
+{
+	u16 val;
+
+	val = mii->read(mii->dev, swport, PORTREG_PCR2);
+	val &= ~(3 << PCR2_MODE_SHIFT);
+	val |= (PCR2_MODE_SECURE << PCR2_MODE_SHIFT);
+	mii->write(mii->dev, swport, PORTREG_PCR2, val);
+}
+
+void fbxgw_sw_port_forward_enable(struct mii_struct *mii, int swport)
+{
+	u16 val;
+
+	/* enable forwarding */
+	val = mii->read(mii->dev, swport, PORTREG_PCR);
+	val |= PCR_PORTSTATE_FORWARDING;
+	mii->write(mii->dev, swport, PORTREG_PCR, val);
+}
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/fbxgw-switch.h	2015-09-29 11:15:04.881725130 +0200
@@ -0,0 +1,51 @@
+/*
+ * fbxgw-switch.h for fbxgw-switch.h
+ * Created by <nschichan@freebox.fr> on Tue Jun  5 20:46:37 2012
+ */
+
+#ifndef __FBXGW_SWITCH_H
+# define __FBXGW_SWITCH_H
+
+struct mii_struct {
+	u16 dev_id;
+	bool indirect_phy_access;
+	struct net_device *dev;
+	int (*read)(struct net_device *dev, int phy_id, int reg);
+	void (*write)(struct net_device *dev, int phy_id, int reg, int val);
+};
+
+
+
+
+void fbxgw_sw_vtu_stu_init(struct mii_struct *mii, int port_state,
+			   size_t nr_ports);
+
+void fbxgw_sw_vtu_load(struct mii_struct *mii, u16 vid, const u8 *ports,
+		       size_t nr_ports);
+
+void fbxgw_sw_config_cpu_port(struct mii_struct *mii, int swport);
+
+void fbxgw_sw_config_phy_port(struct mii_struct *mii, int phy_port);
+
+void fbxgw_sw_port_default_vid(struct mii_struct *mii, int swport, u16 vid);
+
+void fbxgw_sw_port_dot1q_secure(struct mii_struct *mii, int swport);
+
+void fbxgw_sw_port_forward_enable(struct mii_struct *mii, int swport);
+
+#define SWPORT(X)	((X) + 0x10)
+#define PHYPORT(X)	(X)
+
+
+#define PORTREG_SWITCH_IDENTIFIER	0x3
+#define PRODUCT_NUM(x)			(((x) >> 4) & 0xfff)
+
+
+#define PDATA_MEMBER_UNMODIFIED		0
+#define PDATA_MEMBER_UNTAGGED		1
+#define PDATA_MEMBER_TAGGED		2
+#define PDATA_NOT_MEMBER		3
+
+#define PCR_PORTSTATE_FORWARDING	3
+
+#endif /* !__FBXGW_SWITCH_H */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/include/mach/board_fbxgw1r.h	2015-09-29 11:15:04.881725130 +0200
@@ -0,0 +1,41 @@
+#ifndef BOARD_FBXGW1R_H_
+#define BOARD_FBXGW1R_H_
+
+/*
+ * board gpios
+ */
+#define GPIO_OLED_DATA_SELECT	7
+#define GPIO_SR_CLK		10
+#define GPIO_BCM_DOWN		11
+#define GPIO_SR_DIN		12
+#define GPIO_TEST_MODE		13
+#define GPIO_SFP_TXDIS		14
+#define GPIO_SR_LOAD		17
+#define GPIO_SW_RESET		28
+#define GPIO_SW_INT		29
+#define GPIO_SFP_PWRGOOD	34
+#define GPIO_SFP_TXFAULT	35
+#define GPIO_SPI_CS_BCM		36
+#define GPIO_SFP_PRESENCE	37
+#define GPIO_SFP_RXLOSS		38
+#define GPIO_BOARD_ID_0		43
+#define GPIO_EXP_RST		44
+#define GPIO_POS_SENSE		45
+#define GPIO_EXP_PWRGOOD	46
+#define GPIO_EXP_PRESENCE	47
+#define GPIO_KP_INT		48
+#define GPIO_BOARD_ID_1		49
+
+/*
+ * shift register outputs
+ */
+#define SROUT_SFP_PWREN		0
+#define SROUT_USB_RST		1
+#define SROUT_AUDIO_RST		2
+#define SROUT_EXP_PWREN		3
+#define SROUT_BCM_RST		4
+#define SROUT_PCIE_RST		5
+#define SROUT_KEYPAD_OLED_RST	6
+#define SROUT_OLED_PWREN	7
+
+#endif /* !BOARD_FBXGW1R_H_ */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/arch/arm/mach-kirkwood/include/mach/board_fbxgw2r.h	2015-09-29 11:15:04.881725130 +0200
@@ -0,0 +1,31 @@
+/*
+ * board_fbxgw2r.h for fbxgw2r
+ * Created by <nschichan@freebox.fr> on Wed May 30 18:35:25 2012
+ */
+
+#ifndef __BOARD_FBXGW2R_H
+# define __BOARD_FBXGW2R_H
+
+#define GPIO_OLED_DATA_SELECT	7
+#define GPIO_SFP_TXDIS		12
+#define GPIO_TEST_MODE		13
+#define GPIO_WLAN_RST		14
+#define GPIO_PCIE_RST		17
+#define GPIO_EXP_PWREN		22
+#define GPIO_SW_RESET		28
+#define GPIO_SW_INT		29
+#define GPIO_BCM_RST		30
+#define GPIO_SFP_PRESENCE	31
+#define GPIO_SFP_PWRGOOD	32
+#define GPIO_SFP_TXFAULT	35
+#define GPIO_AUDIO_RST		33
+#define GPIO_SPI_CS_BCM		34
+#define GPIO_SFP_RXLOSS		38
+#define GPIO_EXP_RST		44
+#define GPIO_POS_SENSE		45
+#define GPIO_EXP_PWRGOOD	46
+#define GPIO_EXP_PRESENCE	47
+#define GPIO_KP_INT		48
+#define GPIO_SFP_PWREN		49
+
+#endif /* __BOARD_FBXGW2R_H */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/config	2015-09-29 17:27:08.861458945 +0200
@@ -0,0 +1,3157 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Linux/arm 3.11.10.14 Kernel Configuration
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_ARCH_HAS_CPUFREQ=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_VECTORS_BASE=0xffff0000
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
+CONFIG_PHYS_OFFSET=0x0
+CONFIG_GENERIC_BUG=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_IRQ_WORK=y
+CONFIG_BUILDTIME_EXTABLE_SORT=y
+
+#
+# General setup
+#
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_CROSS_COMPILE="/opt/toolchains/armeabi-musl-1.1.3.4-gcc-4.8.3-binutils-2.24-gdb-7.7/bin/arm-fbx-linux-muslgnueabi-"
+# CONFIG_COMPILE_TEST is not set
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_XZ=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_HAVE_KERNEL_LZ4=y
+CONFIG_KERNEL_GZIP=y
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_XZ is not set
+# CONFIG_KERNEL_LZO is not set
+# CONFIG_KERNEL_LZ4 is not set
+CONFIG_DEFAULT_HOSTNAME="(none)"
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_FHANDLE is not set
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
+CONFIG_AUDIT_WATCH=y
+CONFIG_AUDIT_TREE=y
+# CONFIG_AUDIT_LOGINUID_IMMUTABLE is not set
+CONFIG_HAVE_GENERIC_HARDIRQS=y
+
+#
+# IRQ subsystem
+#
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_CHIP=y
+CONFIG_IRQ_DOMAIN=y
+# CONFIG_IRQ_DOMAIN_DEBUG is not set
+CONFIG_KTIME_SCALAR=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+
+#
+# Timers subsystem
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_HZ_PERIODIC=y
+# CONFIG_NO_HZ_IDLE is not set
+# CONFIG_NO_HZ is not set
+CONFIG_HIGH_RES_TIMERS=y
+
+#
+# CPU/Task time and stats accounting
+#
+CONFIG_TICK_CPU_ACCOUNTING=y
+# CONFIG_IRQ_TIME_ACCOUNTING is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_TINY_RCU=y
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_RCU_STALL_COMMON is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_FBX_DECRYPT_INITRD is not set
+CONFIG_GENERIC_SCHED_CLOCK=y
+CONFIG_CGROUPS=y
+# CONFIG_CGROUP_DEBUG is not set
+# CONFIG_CGROUP_FREEZER is not set
+# CONFIG_CGROUP_DEVICE is not set
+# CONFIG_CPUSETS is not set
+# CONFIG_CGROUP_CPUACCT is not set
+# CONFIG_RESOURCE_COUNTERS is not set
+# CONFIG_CGROUP_SCHED is not set
+# CONFIG_BLK_CGROUP is not set
+# CONFIG_CHECKPOINT_RESTORE is not set
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_NET_NS=y
+# CONFIG_SCHED_AUTOGROUP is not set
+# CONFIG_SYSFS_DEPRECATED is not set
+# CONFIG_RELAY is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_RD_GZIP is not set
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+CONFIG_HAVE_UID16=y
+CONFIG_EXPERT=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_PCI_QUIRKS=y
+CONFIG_EMBEDDED=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+
+#
+# Kernel Performance Events And Counters
+#
+# CONFIG_PERF_EVENTS is not set
+CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_SLUB_DEBUG is not set
+CONFIG_COMPAT_BRK=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_SLUB_CPU_PARTIAL is not set
+# CONFIG_PROFILING is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+# CONFIG_JUMP_LABEL is not set
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_DMA_ATTRS=y
+CONFIG_HAVE_DMA_CONTIGUOUS=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_IDLE_POLL_SETUP=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_HAVE_SECCOMP_FILTER_JIT=y
+CONFIG_SECCOMP_FILTER=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_OLD_SIGSUSPEND3=y
+CONFIG_OLD_SIGACTION=y
+CONFIG_SECCOMP_FILTER_JIT=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+# CONFIG_MODULE_SIG is not set
+CONFIG_BLOCK=y
+CONFIG_LBDAF=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_AIX_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+CONFIG_EFI_PARTITION=y
+# CONFIG_SYSV68_PARTITION is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
+CONFIG_INLINE_READ_UNLOCK=y
+CONFIG_INLINE_READ_UNLOCK_IRQ=y
+CONFIG_INLINE_WRITE_UNLOCK=y
+CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+CONFIG_MMU=y
+# CONFIG_ARCH_MULTIPLATFORM is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_DOVE is not set
+CONFIG_ARCH_KIRKWOOD=y
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_LPC32XX is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_SHMOBILE is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C24XX is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5P64X0 is not set
+# CONFIG_ARCH_S5PC100 is not set
+# CONFIG_ARCH_S5PV210 is not set
+# CONFIG_ARCH_EXYNOS is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP1 is not set
+# CONFIG_GPIO_PCA953X is not set
+
+#
+# Marvell Kirkwood Implementations
+#
+# CONFIG_MACH_D2NET_V2 is not set
+# CONFIG_MACH_DOCKSTAR is not set
+# CONFIG_MACH_ESATA_SHEEVAPLUG is not set
+# CONFIG_MACH_GURUPLUG is not set
+# CONFIG_MACH_INETSPACE_V2 is not set
+# CONFIG_MACH_MV88F6281GTW_GE is not set
+# CONFIG_MACH_NET2BIG_V2 is not set
+# CONFIG_MACH_NET5BIG_V2 is not set
+# CONFIG_MACH_NETSPACE_MAX_V2 is not set
+# CONFIG_MACH_NETSPACE_V2 is not set
+# CONFIG_MACH_OPENRD_BASE is not set
+# CONFIG_MACH_OPENRD_CLIENT is not set
+# CONFIG_MACH_OPENRD_ULTIMATE is not set
+# CONFIG_MACH_RD88F6192_NAS is not set
+# CONFIG_MACH_RD88F6281 is not set
+# CONFIG_MACH_SHEEVAPLUG is not set
+# CONFIG_MACH_T5325 is not set
+# CONFIG_MACH_TS219 is not set
+# CONFIG_MACH_TS41X is not set
+
+#
+# Device tree entries
+#
+# CONFIG_ARCH_KIRKWOOD_DT is not set
+# CONFIG_MACH_CLOUDBOX_DT is not set
+# CONFIG_MACH_DB88F628X_BP_DT is not set
+# CONFIG_MACH_DLINK_KIRKWOOD_DT is not set
+# CONFIG_MACH_DOCKSTAR_DT is not set
+# CONFIG_MACH_DREAMPLUG_DT is not set
+# CONFIG_MACH_GOFLEXNET_DT is not set
+# CONFIG_MACH_GURUPLUG_DT is not set
+# CONFIG_MACH_IB62X0_DT is not set
+# CONFIG_MACH_ICONNECT_DT is not set
+# CONFIG_MACH_INETSPACE_V2_DT is not set
+# CONFIG_MACH_IOMEGA_IX2_200_DT is not set
+# CONFIG_MACH_KM_KIRKWOOD_DT is not set
+# CONFIG_MACH_LSXL_DT is not set
+# CONFIG_MACH_MPLCEC4_DT is not set
+# CONFIG_MACH_NETSPACE_LITE_V2_DT is not set
+# CONFIG_MACH_NETSPACE_MAX_V2_DT is not set
+# CONFIG_MACH_NETSPACE_MINI_V2_DT is not set
+# CONFIG_MACH_NETSPACE_V2_DT is not set
+# CONFIG_MACH_OPENBLOCKS_A6_DT is not set
+# CONFIG_MACH_READYNAS_DT is not set
+# CONFIG_MACH_SHEEVAPLUG_DT is not set
+# CONFIG_MACH_TOPKICK_DT is not set
+# CONFIG_MACH_TS219_DT is not set
+CONFIG_MACH_FBXGW1R=y
+CONFIG_MACH_FBXGW1R_ETHSW_CONFIG=y
+CONFIG_MACH_FBXGW1R_ETHSW_CONFIG_VLAN=y
+# CONFIG_MACH_FBXGW1R_EXPANSION_BOARD_SETUP is not set
+CONFIG_MACH_FBXGW2R=y
+CONFIG_MACH_FBXGW2R_ETHSW_CONFIG=y
+CONFIG_MACH_FBXGW2R_ETHSW_CONFIG_VLAN=y
+# CONFIG_MACH_FBXGW2R_EXPANSION_BOARD_SETUP is not set
+CONFIG_FBXGW_COMMON=y
+CONFIG_FBXGW_COMMON_PARTS_WRITE_ALL=y
+# CONFIG_FBXGW_COMMON_NAND_SAFE_READ_TIMINGS is not set
+# CONFIG_FBXGW_COMMON_NAND_SAFE_WRITE_TIMINGS is not set
+CONFIG_FBXGW_COMMON_I2C_25K=y
+# CONFIG_FBXGW_COMMON_I2C_50K is not set
+# CONFIG_FBXGW_COMMON_I2C_100K is not set
+# CONFIG_MACH_RD88F6282A is not set
+# CONFIG_MACH_DB88F6282A_BP is not set
+# CONFIG_PLAT_SPEAR is not set
+
+#
+# ST-Ericsson AB U300/U335 Platform
+#
+
+#
+# ST-Ericsson Mobile Platform Products
+#
+
+#
+# ST-Ericsson U300/U335 Feature Selections
+#
+CONFIG_PLAT_ORION=y
+CONFIG_PLAT_ORION_LEGACY=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_FEROCEON=y
+# CONFIG_CPU_FEROCEON_OLD_ID is not set
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5T=y
+CONFIG_CPU_PABRT_LEGACY=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_COPY_FEROCEON=y
+CONFIG_CPU_TLB_FEROCEON=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+CONFIG_CPU_USE_DOMAINS=y
+
+#
+# Processor Features
+#
+# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set
+# CONFIG_ARM_THUMB is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+CONFIG_NEED_KUSER_HELPERS=y
+CONFIG_KUSER_HELPERS=y
+CONFIG_OUTER_CACHE=y
+CONFIG_CACHE_FEROCEON_L2=y
+# CONFIG_CACHE_FEROCEON_L2_WRITETHROUGH is not set
+# CONFIG_CACHE_L2X0 is not set
+CONFIG_ARM_L1_CACHE_SHIFT=5
+CONFIG_ARM_NR_BANKS=8
+
+#
+# Bus support
+#
+CONFIG_PCI=y
+CONFIG_PCI_SYSCALL=y
+# CONFIG_PCI_DEBUG is not set
+# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set
+# CONFIG_PCI_STUB is not set
+# CONFIG_PCI_IOV is not set
+# CONFIG_PCI_PRI is not set
+# CONFIG_PCI_PASID is not set
+
+#
+# PCI host controller drivers
+#
+# CONFIG_PCI_MVEBU is not set
+CONFIG_PCIEPORTBUS=y
+CONFIG_HOTPLUG_PCI_PCIE=y
+CONFIG_PCIEAER=y
+# CONFIG_PCIE_ECRC is not set
+# CONFIG_PCIEAER_INJECT is not set
+# CONFIG_PCIEASPM is not set
+CONFIG_HOTPLUG_PCI=y
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+# CONFIG_HOTPLUG_PCI_SHPC is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+# CONFIG_VMSPLIT_3G is not set
+CONFIG_VMSPLIT_2G=y
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0x80000000
+CONFIG_ARCH_NR_GPIO=0
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_PREEMPT_COUNT=y
+CONFIG_HZ=100
+CONFIG_SCHED_HRTICK=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+CONFIG_HAVE_ARCH_PFN_VALID=y
+# CONFIG_HIGHMEM is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_HAVE_MEMBLOCK=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=999999
+# CONFIG_COMPACTION is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+# CONFIG_CROSS_MEMORY_ATTACH is not set
+CONFIG_NEED_PER_CPU_KM=y
+# CONFIG_CLEANCACHE is not set
+# CONFIG_FRONTSWAP is not set
+# CONFIG_ZBUD is not set
+CONFIG_FORCE_MAX_ZONEORDER=11
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+CONFIG_SECCOMP=y
+# CONFIG_CC_STACKPROTECTOR is not set
+
+#
+# Boot options
+#
+# CONFIG_USE_OF is not set
+CONFIG_ATAGS=y
+# CONFIG_DEPRECATED_PARAM_STRUCT is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="root=/dev/nfs ro ip=:::::eth0.41:dhcp console=ttyS1,115200 user_debug=28 dhcpclass=linux-fbxgw2r"
+# CONFIG_CMDLINE_FROM_BOOTLOADER is not set
+# CONFIG_CMDLINE_EXTEND is not set
+CONFIG_CMDLINE_FORCE=y
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+CONFIG_AUTO_ZRELADDR=y
+
+#
+# CPU Power Management
+#
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
+# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_BINFMT_SCRIPT=y
+# CONFIG_HAVE_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+CONFIG_COREDUMP=y
+
+#
+# Power management options
+#
+# CONFIG_PM_RUNTIME is not set
+# CONFIG_ARM_CPU_SUSPEND is not set
+# CONFIG_ARM_BPF_JIT_HARDEN is not set
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_NETSKBPAD=40
+CONFIG_NETRXTHREAD=y
+CONFIG_NETRXTHREAD_RX_QUEUE=2
+CONFIG_PACKET=y
+# CONFIG_PACKET_DIAG is not set
+CONFIG_UNIX=y
+CONFIG_UNIX_ABSTRACT_IGNORE_NETNS=y
+# CONFIG_UNIX_DIAG is not set
+CONFIG_XFRM=y
+CONFIG_XFRM_ALGO=y
+CONFIG_XFRM_USER=y
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+CONFIG_NET_KEY=y
+# CONFIG_NET_KEY_MIGRATE is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+# CONFIG_IP_FIB_TRIE_STATS is not set
+CONFIG_IP_MULTIPLE_TABLES=y
+# CONFIG_IP_ROUTE_MULTIPATH is not set
+# CONFIG_IP_ROUTE_VERBOSE is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+CONFIG_NET_IPGRE_DEMUX=y
+CONFIG_NET_IP_TUNNEL=y
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_NET_IPVTI is not set
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=y
+CONFIG_INET_XFRM_GC_THRESH=1024
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+# CONFIG_INET_XFRM_MODE_BEET is not set
+CONFIG_INET_LRO=y
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_INET_UDP_DIAG is not set
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+CONFIG_IPV6=y
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+# CONFIG_INET6_AH is not set
+# CONFIG_INET6_ESP is not set
+# CONFIG_INET6_IPCOMP is not set
+# CONFIG_IPV6_MIP6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+CONFIG_INET6_TUNNEL=y
+# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET6_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET6_XFRM_MODE_BEET is not set
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=y
+CONFIG_IPV6_SIT_6RD=y
+CONFIG_IPV6_NDISC_NODETYPE=y
+CONFIG_IPV6_TUNNEL=y
+# CONFIG_IPV6_GRE is not set
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_NETFILTER_ADVANCED=y
+# CONFIG_BRIDGE_NETFILTER is not set
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK_ACCT is not set
+# CONFIG_NETFILTER_NETLINK_QUEUE is not set
+# CONFIG_NETFILTER_NETLINK_LOG is not set
+CONFIG_NF_CONNTRACK=y
+# CONFIG_NF_CONNTRACK_MARK is not set
+CONFIG_NF_CONNTRACK_PROCFS=y
+# CONFIG_NF_CONNTRACK_EVENTS is not set
+# CONFIG_NF_CONNTRACK_TIMEOUT is not set
+# CONFIG_NF_CONNTRACK_TIMESTAMP is not set
+# CONFIG_NF_CT_PROTO_DCCP is not set
+CONFIG_NF_CT_PROTO_GRE=m
+# CONFIG_NF_CT_PROTO_SCTP is not set
+# CONFIG_NF_CT_PROTO_UDPLITE is not set
+# CONFIG_NF_CONNTRACK_AMANDA is not set
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set
+# CONFIG_NF_CONNTRACK_SNMP is not set
+CONFIG_NF_CONNTRACK_PPTP=m
+# CONFIG_NF_CONNTRACK_SANE is not set
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=y
+# CONFIG_NF_CT_NETLINK is not set
+# CONFIG_NF_CT_NETLINK_TIMEOUT is not set
+CONFIG_NF_NAT=y
+CONFIG_NF_NAT_NEEDED=y
+# CONFIG_NF_NAT_AMANDA is not set
+CONFIG_NF_NAT_FTP=y
+CONFIG_NF_NAT_IRC=m
+CONFIG_NF_NAT_SIP=m
+CONFIG_NF_NAT_TFTP=y
+CONFIG_NETFILTER_TPROXY=y
+CONFIG_NETFILTER_XTABLES=y
+
+#
+# Xtables combined modules
+#
+CONFIG_NETFILTER_XT_MARK=y
+# CONFIG_NETFILTER_XT_CONNMARK is not set
+
+#
+# Xtables targets
+#
+# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set
+# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set
+CONFIG_NETFILTER_XT_TARGET_DSCP=y
+# CONFIG_NETFILTER_XT_TARGET_HL is not set
+# CONFIG_NETFILTER_XT_TARGET_HMARK is not set
+# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set
+# CONFIG_NETFILTER_XT_TARGET_LED is not set
+# CONFIG_NETFILTER_XT_TARGET_LOG is not set
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+# CONFIG_NETFILTER_XT_TARGET_NETMAP is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set
+CONFIG_NETFILTER_XT_TARGET_REDIRECT=y
+# CONFIG_NETFILTER_XT_TARGET_TEE is not set
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set
+
+#
+# Xtables matches
+#
+# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_BPF is not set
+# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNLIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNMARK is not set
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+# CONFIG_NETFILTER_XT_MATCH_CPU is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set
+CONFIG_NETFILTER_XT_MATCH_DSCP=y
+# CONFIG_NETFILTER_XT_MATCH_ECN is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_HELPER is not set
+# CONFIG_NETFILTER_XT_MATCH_HL is not set
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set
+CONFIG_NETFILTER_XT_MATCH_OWNER=y
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_RECENT is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_SOCKET is not set
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_IP_VS is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_FFN=y
+# CONFIG_IP_FFN_PROCFS is not set
+CONFIG_NF_DEFRAG_IPV4=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NF_CONNTRACK_PROC_COMPAT=y
+CONFIG_IP_NF_IPTABLES=y
+# CONFIG_IP_NF_MATCH_AH is not set
+# CONFIG_IP_NF_MATCH_ECN is not set
+# CONFIG_IP_NF_MATCH_RPFILTER is not set
+# CONFIG_IP_NF_MATCH_TTL is not set
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+# CONFIG_IP_NF_TARGET_ULOG is not set
+CONFIG_NF_NAT_IPV4=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+# CONFIG_IP_NF_TARGET_NETMAP is not set
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_NF_NAT_PROTO_GRE=m
+CONFIG_NF_NAT_PPTP=m
+CONFIG_NF_NAT_H323=m
+CONFIG_IP_NF_MANGLE=y
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+# CONFIG_IP_NF_TARGET_ECN is not set
+# CONFIG_IP_NF_TARGET_TTL is not set
+# CONFIG_IP_NF_RAW is not set
+# CONFIG_IP_NF_ARPTABLES is not set
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_NF_DEFRAG_IPV6=y
+# CONFIG_NF_CONNTRACK_IPV6 is not set
+CONFIG_IP6_NF_IPTABLES=y
+# CONFIG_IP6_NF_MATCH_AH is not set
+# CONFIG_IP6_NF_MATCH_EUI64 is not set
+# CONFIG_IP6_NF_MATCH_FRAG is not set
+# CONFIG_IP6_NF_MATCH_OPTS is not set
+# CONFIG_IP6_NF_MATCH_HL is not set
+# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set
+# CONFIG_IP6_NF_MATCH_MH is not set
+# CONFIG_IP6_NF_MATCH_RPFILTER is not set
+# CONFIG_IP6_NF_MATCH_RT is not set
+# CONFIG_IP6_NF_TARGET_HL is not set
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+# CONFIG_IP6_NF_RAW is not set
+# CONFIG_BRIDGE_NF_EBTABLES is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_RDS is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_L2TP is not set
+CONFIG_FBXATM=y
+CONFIG_FBXATM_REMOTE=y
+CONFIG_FBXATM_STACK=y
+# CONFIG_FBXATM_REMOTE_STUB is not set
+CONFIG_FBXATM_REMOTE_DRIVER=y
+CONFIG_FBXBRIDGE=y
+CONFIG_STP=y
+CONFIG_BRIDGE=y
+# CONFIG_BRIDGE_IGMP_SNOOPING is not set
+# CONFIG_BRIDGE_VLAN_FILTERING is not set
+CONFIG_HAVE_NET_DSA=y
+CONFIG_VLAN_8021Q=y
+# CONFIG_VLAN_8021Q_GVRP is not set
+# CONFIG_VLAN_8021Q_MVRP is not set
+# CONFIG_DECNET is not set
+CONFIG_LLC=y
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
+CONFIG_NET_SCHED=y
+
+#
+# Queueing/Scheduling
+#
+# CONFIG_NET_SCH_CBQ is not set
+# CONFIG_NET_SCH_HTB is not set
+# CONFIG_NET_SCH_HFSC is not set
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_SCH_MULTIQ=y
+# CONFIG_NET_SCH_RED is not set
+# CONFIG_NET_SCH_SFB is not set
+CONFIG_NET_SCH_SFQ=y
+# CONFIG_NET_SCH_TEQL is not set
+# CONFIG_NET_SCH_TBF is not set
+# CONFIG_NET_SCH_GRED is not set
+# CONFIG_NET_SCH_DSMARK is not set
+# CONFIG_NET_SCH_NETEM is not set
+CONFIG_NET_SCH_DRR=y
+# CONFIG_NET_SCH_MQPRIO is not set
+# CONFIG_NET_SCH_CHOKE is not set
+# CONFIG_NET_SCH_QFQ is not set
+# CONFIG_NET_SCH_CODEL is not set
+CONFIG_NET_SCH_FQ_CODEL=y
+CONFIG_NET_SCH_INGRESS=y
+# CONFIG_NET_SCH_PLUG is not set
+
+#
+# Classification
+#
+CONFIG_NET_CLS=y
+# CONFIG_NET_CLS_BASIC is not set
+# CONFIG_NET_CLS_TCINDEX is not set
+# CONFIG_NET_CLS_ROUTE4 is not set
+# CONFIG_NET_CLS_FW is not set
+CONFIG_NET_CLS_U32=y
+# CONFIG_CLS_U32_PERF is not set
+CONFIG_CLS_U32_MARK=y
+# CONFIG_NET_CLS_RSVP is not set
+# CONFIG_NET_CLS_RSVP6 is not set
+# CONFIG_NET_CLS_FLOW is not set
+# CONFIG_NET_CLS_CGROUP is not set
+# CONFIG_NET_EMATCH is not set
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=y
+# CONFIG_NET_ACT_GACT is not set
+# CONFIG_NET_ACT_MIRRED is not set
+# CONFIG_NET_ACT_IPT is not set
+# CONFIG_NET_ACT_NAT is not set
+# CONFIG_NET_ACT_PEDIT is not set
+# CONFIG_NET_ACT_SIMP is not set
+CONFIG_NET_ACT_SKBEDIT=y
+# CONFIG_NET_ACT_CSUM is not set
+# CONFIG_NET_CLS_IND is not set
+CONFIG_NET_SCH_FIFO=y
+# CONFIG_DCB is not set
+# CONFIG_BATMAN_ADV is not set
+# CONFIG_OPENVSWITCH is not set
+# CONFIG_VSOCKETS is not set
+# CONFIG_NETLINK_MMAP is not set
+# CONFIG_NETLINK_DIAG is not set
+# CONFIG_NET_MPLS_GSO is not set
+# CONFIG_NETPRIO_CGROUP is not set
+CONFIG_NET_RX_BUSY_POLL=y
+CONFIG_BQL=y
+CONFIG_BPF_JIT=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+CONFIG_BT=y
+# CONFIG_BT_RFCOMM is not set
+CONFIG_BT_BNEP=y
+# CONFIG_BT_BNEP_MC_FILTER is not set
+# CONFIG_BT_BNEP_PROTO_FILTER is not set
+# CONFIG_BT_HIDP is not set
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIBTUSB=y
+# CONFIG_BT_HCIUART is not set
+CONFIG_BT_HCIBCM203X=y
+# CONFIG_BT_HCIBPA10X is not set
+# CONFIG_BT_HCIBFUSB is not set
+# CONFIG_BT_HCIVHCI is not set
+CONFIG_BT_MRVL=y
+CONFIG_BT_ATH3K=y
+# CONFIG_AF_RXRPC is not set
+CONFIG_FIB_RULES=y
+CONFIG_WIRELESS=y
+CONFIG_CFG80211=y
+# CONFIG_NL80211_TESTMODE is not set
+# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set
+# CONFIG_CFG80211_REG_DEBUG is not set
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_DEFAULT_PS=y
+# CONFIG_CFG80211_DEBUGFS is not set
+CONFIG_CFG80211_INTERNAL_REGDB=y
+# CONFIG_CFG80211_WEXT is not set
+# CONFIG_LIB80211 is not set
+CONFIG_MAC80211=y
+CONFIG_MAC80211_HAS_RC=y
+# CONFIG_MAC80211_RC_PID is not set
+CONFIG_MAC80211_RC_MINSTREL=y
+CONFIG_MAC80211_RC_MINSTREL_HT=y
+CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y
+CONFIG_MAC80211_RC_DEFAULT="minstrel_ht"
+# CONFIG_MAC80211_MESH is not set
+CONFIG_MAC80211_LEDS=y
+# CONFIG_MAC80211_DEBUGFS is not set
+# CONFIG_MAC80211_MESSAGE_TRACING is not set
+# CONFIG_MAC80211_DEBUG_MENU is not set
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+# CONFIG_CAIF is not set
+# CONFIG_CEPH_LIB is not set
+# CONFIG_NFC is not set
+CONFIG_HAVE_BPF_JIT=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_EXTRA_FIRMWARE=""
+CONFIG_FW_LOADER_USER_HELPER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_GENERIC_CPU_DEVICES is not set
+CONFIG_REGMAP=y
+CONFIG_REGMAP_I2C=y
+CONFIG_REGMAP_SPI=y
+# CONFIG_DMA_SHARED_BUFFER is not set
+# CONFIG_CMA is not set
+
+#
+# Bus devices
+#
+CONFIG_MVEBU_MBUS=y
+# CONFIG_ARM_CCI is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_FREEBOX_PROCFS=y
+CONFIG_MTD=y
+# CONFIG_MTD_TESTS is not set
+CONFIG_MTD_ERASE_PRINTK=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+# CONFIG_MTD_FBX6HD_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_SM_FTL is not set
+# CONFIG_MTD_OOPS is not set
+# CONFIG_MTD_SWAP is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_INTEL_VR_NOR is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_DATAFLASH is not set
+CONFIG_MTD_M25P80=y
+# CONFIG_M25PXX_USE_FAST_READ is not set
+# CONFIG_MTD_SST25L is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOCG3 is not set
+CONFIG_MTD_NAND_ECC=y
+# CONFIG_MTD_NAND_ECC_SMC is not set
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_BCH=y
+CONFIG_MTD_NAND_ECC_BCH=y
+# CONFIG_MTD_SM_COMMON is not set
+# CONFIG_MTD_FORCE_BAD_BLOCK_ERASE is not set
+# CONFIG_MTD_NAND_DENALI is not set
+# CONFIG_MTD_NAND_GPIO is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_RICOH is not set
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_DOCG4 is not set
+# CONFIG_MTD_NAND_CAFE is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+CONFIG_MTD_NAND_ORION=y
+# CONFIG_MTD_NAND_DENALI_FBX is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MTD_UBI_BEB_LIMIT=20
+# CONFIG_MTD_UBI_FASTMAP is not set
+# CONFIG_MTD_UBI_GLUEBI is not set
+# CONFIG_FREEBOX_MTD is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=8
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_DRBD is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_NVME is not set
+# CONFIG_BLK_DEV_SX8 is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=1
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MG_DISK is not set
+# CONFIG_BLK_DEV_RBD is not set
+# CONFIG_BLK_DEV_RSXX is not set
+
+#
+# Misc devices
+#
+# CONFIG_WINTEGRA_MMAP is not set
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_AD525X_DPOT is not set
+# CONFIG_ATMEL_PWM is not set
+# CONFIG_DUMMY_IRQ is not set
+# CONFIG_PHANTOM is not set
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_TIFM_CORE is not set
+# CONFIG_ICS932S401 is not set
+# CONFIG_ATMEL_SSC is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_HP_ILO is not set
+# CONFIG_APDS9802ALS is not set
+# CONFIG_ISL29003 is not set
+# CONFIG_ISL29020 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_SENSORS_BH1780 is not set
+# CONFIG_SENSORS_BH1770 is not set
+# CONFIG_SENSORS_APDS990X is not set
+# CONFIG_HMC6352 is not set
+# CONFIG_DS1682 is not set
+# CONFIG_TI_DAC7512 is not set
+# CONFIG_INTELCE_PIC16PMU is not set
+# CONFIG_BMP085_I2C is not set
+# CONFIG_BMP085_SPI is not set
+# CONFIG_PCH_PHUB is not set
+# CONFIG_USB_SWITCH_FSA9480 is not set
+# CONFIG_LATTICE_ECP3_CONFIG is not set
+# CONFIG_SRAM is not set
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+CONFIG_EEPROM_AT24=m
+# CONFIG_EEPROM_AT25 is not set
+# CONFIG_EEPROM_LEGACY is not set
+# CONFIG_EEPROM_MAX6875 is not set
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_EEPROM_93XX46 is not set
+# CONFIG_CB710_CORE is not set
+
+#
+# Texas Instruments shared transport line discipline
+#
+# CONFIG_TI_ST is not set
+# CONFIG_SENSORS_LIS3_SPI is not set
+# CONFIG_SENSORS_LIS3_I2C is not set
+
+#
+# Altera FPGA firmware download module
+#
+# CONFIG_ALTERA_STAPL is not set
+
+#
+# RemoTI support
+#
+
+#
+# HDMI CEC support
+#
+# CONFIG_HDMI_CEC is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_SCSI_PROC_FS is not set
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+CONFIG_CHR_DEV_SG=y
+# CONFIG_CHR_DEV_SCH is not set
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+CONFIG_SCSI_SCAN_ASYNC=y
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_LOWLEVEL is not set
+# CONFIG_SCSI_DH is not set
+# CONFIG_SCSI_OSD_INITIATOR is not set
+CONFIG_ATA=y
+# CONFIG_ATA_NONSTANDARD is not set
+CONFIG_ATA_VERBOSE_ERROR=y
+CONFIG_SATA_PMP=y
+
+#
+# Controllers with non-SFF native interface
+#
+# CONFIG_SATA_AHCI is not set
+# CONFIG_SATA_AHCI_PLATFORM is not set
+# CONFIG_SATA_INIC162X is not set
+# CONFIG_SATA_ACARD_AHCI is not set
+# CONFIG_SATA_SIL24 is not set
+CONFIG_ATA_SFF=y
+
+#
+# SFF controllers with custom DMA interface
+#
+# CONFIG_PDC_ADMA is not set
+# CONFIG_SATA_QSTOR is not set
+# CONFIG_SATA_SX4 is not set
+CONFIG_ATA_BMDMA=y
+
+#
+# SATA SFF controllers with BMDMA
+#
+# CONFIG_ATA_PIIX is not set
+# CONFIG_SATA_HIGHBANK is not set
+CONFIG_SATA_MV=m
+# CONFIG_SATA_NV is not set
+# CONFIG_SATA_PROMISE is not set
+# CONFIG_SATA_RCAR is not set
+# CONFIG_SATA_SIL is not set
+# CONFIG_SATA_SIS is not set
+# CONFIG_SATA_SVW is not set
+# CONFIG_SATA_ULI is not set
+# CONFIG_SATA_VIA is not set
+# CONFIG_SATA_VITESSE is not set
+
+#
+# PATA SFF controllers with BMDMA
+#
+# CONFIG_PATA_ALI is not set
+# CONFIG_PATA_AMD is not set
+# CONFIG_PATA_ARTOP is not set
+# CONFIG_PATA_ATIIXP is not set
+# CONFIG_PATA_ATP867X is not set
+# CONFIG_PATA_CMD64X is not set
+# CONFIG_PATA_CS5520 is not set
+# CONFIG_PATA_CS5530 is not set
+# CONFIG_PATA_CS5536 is not set
+# CONFIG_PATA_CYPRESS is not set
+# CONFIG_PATA_EFAR is not set
+# CONFIG_PATA_HPT366 is not set
+# CONFIG_PATA_HPT37X is not set
+# CONFIG_PATA_HPT3X2N is not set
+# CONFIG_PATA_HPT3X3 is not set
+# CONFIG_PATA_IT8213 is not set
+# CONFIG_PATA_IT821X is not set
+# CONFIG_PATA_JMICRON is not set
+# CONFIG_PATA_MARVELL is not set
+# CONFIG_PATA_NETCELL is not set
+# CONFIG_PATA_NINJA32 is not set
+# CONFIG_PATA_NS87415 is not set
+# CONFIG_PATA_OLDPIIX is not set
+# CONFIG_PATA_OPTIDMA is not set
+# CONFIG_PATA_PDC2027X is not set
+# CONFIG_PATA_PDC_OLD is not set
+# CONFIG_PATA_RADISYS is not set
+# CONFIG_PATA_RDC is not set
+# CONFIG_PATA_SC1200 is not set
+# CONFIG_PATA_SCH is not set
+# CONFIG_PATA_SERVERWORKS is not set
+# CONFIG_PATA_SIL680 is not set
+# CONFIG_PATA_SIS is not set
+# CONFIG_PATA_TOSHIBA is not set
+# CONFIG_PATA_TRIFLEX is not set
+# CONFIG_PATA_VIA is not set
+# CONFIG_PATA_WINBOND is not set
+
+#
+# PIO-only SFF controllers
+#
+# CONFIG_PATA_CMD640_PCI is not set
+# CONFIG_PATA_MPIIX is not set
+# CONFIG_PATA_NS87410 is not set
+# CONFIG_PATA_OPTI is not set
+# CONFIG_PATA_PLATFORM is not set
+# CONFIG_PATA_RZ1000 is not set
+
+#
+# Generic fallback / legacy drivers
+#
+# CONFIG_ATA_GENERIC is not set
+# CONFIG_PATA_LEGACY is not set
+CONFIG_MD=y
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_BCACHE is not set
+CONFIG_BLK_DEV_DM_BUILTIN=y
+CONFIG_BLK_DEV_DM=y
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=y
+# CONFIG_DM_SNAPSHOT is not set
+# CONFIG_DM_THIN_PROVISIONING is not set
+# CONFIG_DM_CACHE is not set
+# CONFIG_DM_MIRROR is not set
+# CONFIG_DM_RAID is not set
+# CONFIG_DM_ZERO is not set
+# CONFIG_DM_MULTIPATH is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_UEVENT is not set
+# CONFIG_DM_FLAKEY is not set
+# CONFIG_DM_VERITY is not set
+# CONFIG_DM_SWITCH is not set
+# CONFIG_TARGET_CORE is not set
+# CONFIG_FUSION is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_FIREWIRE is not set
+# CONFIG_FIREWIRE_NOSY is not set
+# CONFIG_I2O is not set
+CONFIG_NETDEVICES=y
+CONFIG_MII=y
+CONFIG_NET_CORE=y
+# CONFIG_BONDING is not set
+CONFIG_DUMMY=y
+# CONFIG_EQUALIZER is not set
+# CONFIG_NET_FC is not set
+# CONFIG_IFB is not set
+# CONFIG_NET_TEAM is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_VXLAN is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+CONFIG_TUN=y
+# CONFIG_VETH is not set
+# CONFIG_NLMON is not set
+# CONFIG_ARCNET is not set
+
+#
+# CAIF transport drivers
+#
+
+#
+# Distributed Switch Architecture drivers
+#
+# CONFIG_NET_DSA_MV88E6XXX is not set
+# CONFIG_NET_DSA_MV88E6060 is not set
+# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set
+# CONFIG_NET_DSA_MV88E6131 is not set
+# CONFIG_NET_DSA_MV88E6123_61_65 is not set
+CONFIG_ETHERNET=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ADAPTEC is not set
+# CONFIG_NET_VENDOR_ALTEON is not set
+# CONFIG_NET_VENDOR_AMD is not set
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_ATHEROS is not set
+# CONFIG_NET_CADENCE is not set
+CONFIG_NET_VENDOR_BROADCOM=y
+# CONFIG_B44 is not set
+# CONFIG_BNX2 is not set
+# CONFIG_CNIC is not set
+# CONFIG_TIGON3 is not set
+# CONFIG_BNX2X is not set
+# CONFIG_NET_VENDOR_BROCADE is not set
+# CONFIG_NET_CALXEDA_XGMAC is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_CISCO is not set
+# CONFIG_DM9000 is not set
+# CONFIG_DNET is not set
+# CONFIG_NET_VENDOR_DEC is not set
+# CONFIG_NET_VENDOR_DLINK is not set
+# CONFIG_NET_VENDOR_EMULEX is not set
+# CONFIG_NET_VENDOR_EXAR is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_HP is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_IP1000 is not set
+# CONFIG_JME is not set
+CONFIG_NET_VENDOR_MARVELL=y
+CONFIG_MV643XX_ETH=y
+CONFIG_MVMDIO=y
+# CONFIG_SKGE is not set
+CONFIG_SKY2=m
+# CONFIG_SKY2_DEBUG is not set
+# CONFIG_NET_VENDOR_MELLANOX is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_MYRI is not set
+# CONFIG_FEALNX is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_NVIDIA is not set
+# CONFIG_NET_VENDOR_OKI is not set
+# CONFIG_ETHOC is not set
+# CONFIG_NET_PACKET_ENGINE is not set
+# CONFIG_NET_VENDOR_QLOGIC is not set
+# CONFIG_NET_VENDOR_REALTEK is not set
+# CONFIG_SH_ETH is not set
+# CONFIG_NET_VENDOR_RDC is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SILAN is not set
+# CONFIG_NET_VENDOR_SIS is not set
+# CONFIG_SFC is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_SUN is not set
+# CONFIG_NET_VENDOR_TEHUTI is not set
+# CONFIG_NET_VENDOR_TI is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_AT803X_PHY is not set
+# CONFIG_AMD_PHY is not set
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_BCM87XX_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_STE10XP is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+# CONFIG_MICREL_PHY is not set
+# CONFIG_FIXED_PHY is not set
+# CONFIG_MDIO_BITBANG is not set
+# CONFIG_MICREL_KS8995MA is not set
+CONFIG_PPP=y
+# CONFIG_PPP_BSDCOMP is not set
+# CONFIG_PPP_DEFLATE is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_MPPE=y
+# CONFIG_PPP_MULTILINK is not set
+CONFIG_PPPOE=y
+CONFIG_PPTP=y
+# CONFIG_PPP_ASYNC is not set
+# CONFIG_PPP_SYNC_TTY is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=y
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_RTL8152 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_USB_IPHETH is not set
+CONFIG_WLAN=y
+# CONFIG_LIBERTAS_THINFIRM is not set
+# CONFIG_ATMEL is not set
+# CONFIG_AT76C50X_USB is not set
+# CONFIG_PRISM54 is not set
+# CONFIG_USB_ZD1201 is not set
+# CONFIG_USB_NET_RNDIS_WLAN is not set
+# CONFIG_RTL8180 is not set
+# CONFIG_RTL8187 is not set
+# CONFIG_ADM8211 is not set
+# CONFIG_MAC80211_HWSIM is not set
+CONFIG_MWL8K=m
+# CONFIG_MWL8K_MFG is not set
+CONFIG_ATH_COMMON=y
+CONFIG_ATH_CARDS=y
+# CONFIG_ATH_DEBUG is not set
+# CONFIG_ATH5K is not set
+# CONFIG_ATH5K_PCI is not set
+# CONFIG_ATH9K is not set
+# CONFIG_ATH9K_HTC is not set
+# CONFIG_CARL9170 is not set
+# CONFIG_ATH6KL is not set
+# CONFIG_AR5523 is not set
+# CONFIG_WIL6210 is not set
+CONFIG_ATH10K=y
+CONFIG_ATH10K_PCI=m
+# CONFIG_ATH10K_DEBUG is not set
+# CONFIG_ATH10K_DEBUGFS is not set
+CONFIG_ATH10K_DFS_CERTIFIED=y
+# CONFIG_B43 is not set
+# CONFIG_B43LEGACY is not set
+# CONFIG_BRCMFMAC is not set
+# CONFIG_HOSTAP is not set
+# CONFIG_IPW2100 is not set
+# CONFIG_IWLWIFI is not set
+# CONFIG_IWL4965 is not set
+# CONFIG_IWL3945 is not set
+# CONFIG_LIBERTAS is not set
+# CONFIG_P54_COMMON is not set
+# CONFIG_RT2X00 is not set
+# CONFIG_RTL_CARDS is not set
+# CONFIG_WL_TI is not set
+# CONFIG_ZD1211RW is not set
+# CONFIG_MWIFIEX is not set
+# CONFIG_CW1200 is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+# CONFIG_WAN is not set
+# CONFIG_VMXNET3 is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+CONFIG_INPUT_POLLDEV=y
+# CONFIG_INPUT_SPARSEKMAP is not set
+# CONFIG_INPUT_MATRIXKMAP is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_AD714X is not set
+# CONFIG_INPUT_BMA150 is not set
+# CONFIG_INPUT_MMA8450 is not set
+# CONFIG_INPUT_MPU3050 is not set
+# CONFIG_INPUT_GP2A is not set
+# CONFIG_INPUT_GPIO_TILT_POLLED is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_KXTJ9 is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INPUT_CM109 is not set
+# CONFIG_INPUT_UINPUT is not set
+# CONFIG_INPUT_PCF8574 is not set
+# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set
+# CONFIG_INPUT_ADXL34X is not set
+# CONFIG_INPUT_IMS_PCU is not set
+# CONFIG_INPUT_CMA3000 is not set
+CONFIG_INPUT_SMSC_CAP1066=m
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_TTY=y
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=16
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_NOZOMI is not set
+# CONFIG_N_GSM is not set
+# CONFIG_TRACE_SINK is not set
+# CONFIG_DEVKMEM is not set
+# CONFIG_DEVPHYSMEM is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_PCI=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+# CONFIG_SERIAL_8250_EXTENDED is not set
+# CONFIG_SERIAL_8250_DW is not set
+# CONFIG_SERIAL_8250_EM is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_MAX3100 is not set
+# CONFIG_SERIAL_MAX310X is not set
+# CONFIG_SERIAL_MFD_HSU is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_JSM is not set
+# CONFIG_SERIAL_SCCNXP is not set
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_IFX6X60 is not set
+# CONFIG_SERIAL_PCH_UART is not set
+# CONFIG_SERIAL_ARC is not set
+# CONFIG_SERIAL_RP2 is not set
+# CONFIG_SERIAL_FSL_LPUART is not set
+# CONFIG_TTY_PRINTK is not set
+# CONFIG_HVC_DCC is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_DEVPORT=y
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_COMPAT=y
+CONFIG_I2C_CHARDEV=y
+# CONFIG_I2C_MUX is not set
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# PC SMBus host controller drivers
+#
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+# CONFIG_I2C_AMD756 is not set
+# CONFIG_I2C_AMD8111 is not set
+# CONFIG_I2C_I801 is not set
+# CONFIG_I2C_ISCH is not set
+# CONFIG_I2C_PIIX4 is not set
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+# CONFIG_I2C_SIS96X is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_CBUS_GPIO is not set
+# CONFIG_I2C_DESIGNWARE_PLATFORM is not set
+# CONFIG_I2C_DESIGNWARE_PCI is not set
+# CONFIG_I2C_EG20T is not set
+# CONFIG_I2C_GPIO is not set
+CONFIG_I2C_MV64XXX=y
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_PXA_PCI is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_XILINX is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_DIOLAN_U2C is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+CONFIG_SPI=y
+CONFIG_SPI_DEBUG=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+# CONFIG_SPI_ALTERA is not set
+# CONFIG_SPI_BITBANG is not set
+# CONFIG_SPI_GPIO is not set
+# CONFIG_SPI_OC_TINY is not set
+CONFIG_SPI_ORION=y
+CONFIG_SPI_TDM_ORION=y
+# CONFIG_SPI_PXA2XX is not set
+# CONFIG_SPI_PXA2XX_PCI is not set
+# CONFIG_SPI_SC18IS602 is not set
+# CONFIG_SPI_TOPCLIFF_PCH is not set
+# CONFIG_SPI_XCOMM is not set
+# CONFIG_SPI_XILINX is not set
+# CONFIG_SPI_DESIGNWARE is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+# CONFIG_HSI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+
+#
+# PPS generators support
+#
+
+#
+# PTP clock support
+#
+# CONFIG_PTP_1588_CLOCK is not set
+
+#
+# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks.
+#
+CONFIG_PINCTRL=y
+
+#
+# Pin controllers
+#
+CONFIG_PINMUX=y
+CONFIG_PINCONF=y
+# CONFIG_DEBUG_PINCTRL is not set
+CONFIG_PINCTRL_MVEBU=y
+CONFIG_PINCTRL_KIRKWOOD=y
+CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIO_DEVRES=y
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+# CONFIG_GPIO_SYSFS is not set
+
+#
+# Memory mapped GPIO drivers:
+#
+# CONFIG_GPIO_GENERIC_PLATFORM is not set
+# CONFIG_GPIO_EM is not set
+# CONFIG_GPIO_RCAR is not set
+# CONFIG_GPIO_TS5500 is not set
+# CONFIG_GPIO_VX855 is not set
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX7300 is not set
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCF857X is not set
+# CONFIG_GPIO_SX150X is not set
+# CONFIG_GPIO_ADP5588 is not set
+
+#
+# PCI GPIO expanders:
+#
+# CONFIG_GPIO_BT8XX is not set
+# CONFIG_GPIO_AMD8111 is not set
+# CONFIG_GPIO_ML_IOH is not set
+# CONFIG_GPIO_RDC321X is not set
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
+# CONFIG_GPIO_MC33880 is not set
+# CONFIG_GPIO_74X164 is not set
+
+#
+# AC97 GPIO expanders:
+#
+
+#
+# MODULbus GPIO expanders:
+#
+
+#
+# USB GPIO expanders:
+#
+CONFIG_FREEBOX_GPIO=y
+# CONFIG_FREEBOX_JTAG is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_POWER_AVS is not set
+CONFIG_HWMON=y
+CONFIG_HWMON_VID=y
+# CONFIG_HWMON_DEBUG_CHIP is not set
+
+#
+# Native drivers
+#
+# CONFIG_SENSORS_AD7314 is not set
+# CONFIG_SENSORS_AD7414 is not set
+# CONFIG_SENSORS_AD7418 is not set
+# CONFIG_SENSORS_ADCXX is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1029 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ADM9240 is not set
+# CONFIG_SENSORS_ADT7310 is not set
+# CONFIG_SENSORS_ADT7410 is not set
+# CONFIG_SENSORS_ADT7411 is not set
+# CONFIG_SENSORS_ADT7462 is not set
+# CONFIG_SENSORS_ADT7470 is not set
+CONFIG_SENSORS_ADT7475=y
+# CONFIG_SENSORS_ASC7621 is not set
+# CONFIG_SENSORS_ATXP1 is not set
+# CONFIG_SENSORS_DS620 is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_I5K_AMB is not set
+# CONFIG_SENSORS_F71805F is not set
+# CONFIG_SENSORS_F71882FG is not set
+# CONFIG_SENSORS_F75375S is not set
+# CONFIG_SENSORS_G760A is not set
+# CONFIG_SENSORS_G762 is not set
+# CONFIG_SENSORS_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_GPIO_FAN is not set
+# CONFIG_SENSORS_HIH6130 is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_JC42 is not set
+# CONFIG_SENSORS_LINEAGE is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM70 is not set
+# CONFIG_SENSORS_LM73 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 is not set
+# CONFIG_SENSORS_LM78 is not set
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
+CONFIG_SENSORS_LM85=y
+# CONFIG_SENSORS_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_LM92 is not set
+# CONFIG_SENSORS_LM93 is not set
+# CONFIG_SENSORS_LTC4151 is not set
+# CONFIG_SENSORS_LTC4215 is not set
+# CONFIG_SENSORS_LTC4245 is not set
+# CONFIG_SENSORS_LTC4261 is not set
+# CONFIG_SENSORS_LM95234 is not set
+# CONFIG_SENSORS_LM95241 is not set
+# CONFIG_SENSORS_LM95245 is not set
+# CONFIG_SENSORS_MAX1111 is not set
+# CONFIG_SENSORS_MAX16065 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_MAX1668 is not set
+# CONFIG_SENSORS_MAX197 is not set
+# CONFIG_SENSORS_MAX6639 is not set
+# CONFIG_SENSORS_MAX6642 is not set
+# CONFIG_SENSORS_MAX6650 is not set
+# CONFIG_SENSORS_MAX6697 is not set
+# CONFIG_SENSORS_MCP3021 is not set
+# CONFIG_SENSORS_NCT6775 is not set
+# CONFIG_SENSORS_NTC_THERMISTOR is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_PC87427 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_PMBUS is not set
+# CONFIG_SENSORS_SHT15 is not set
+# CONFIG_SENSORS_SHT21 is not set
+# CONFIG_SENSORS_SIS5595 is not set
+# CONFIG_SENSORS_SMM665 is not set
+# CONFIG_SENSORS_DME1737 is not set
+# CONFIG_SENSORS_EMC1403 is not set
+# CONFIG_SENSORS_EMC2103 is not set
+# CONFIG_SENSORS_EMC6W201 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47M192 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_SCH56XX_COMMON is not set
+# CONFIG_SENSORS_ADS1015 is not set
+# CONFIG_SENSORS_ADS7828 is not set
+# CONFIG_SENSORS_ADS7871 is not set
+# CONFIG_SENSORS_AMC6821 is not set
+# CONFIG_SENSORS_INA209 is not set
+# CONFIG_SENSORS_INA2XX is not set
+# CONFIG_SENSORS_THMC50 is not set
+# CONFIG_SENSORS_TMP102 is not set
+# CONFIG_SENSORS_TMP401 is not set
+# CONFIG_SENSORS_TMP421 is not set
+# CONFIG_SENSORS_VIA686A is not set
+# CONFIG_SENSORS_VT1211 is not set
+# CONFIG_SENSORS_VT8231 is not set
+# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83791D is not set
+# CONFIG_SENSORS_W83792D is not set
+# CONFIG_SENSORS_W83793 is not set
+# CONFIG_SENSORS_W83795 is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83L786NG is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83627EHF is not set
+CONFIG_SENSORS_KIRKWOOD_CORETEMP=m
+# CONFIG_THERMAL is not set
+CONFIG_FREEBOX_WATCHDOG=y
+CONFIG_FREEBOX_WATCHDOG_CHAR=y
+CONFIG_FREEBOX_WATCHDOG_ORION=y
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+CONFIG_BCMA_POSSIBLE=y
+
+#
+# Broadcom specific AMBA
+#
+# CONFIG_BCMA is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_AS3711 is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_MFD_AAT2870_CORE is not set
+# CONFIG_MFD_CROS_EC is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_DA9052_SPI is not set
+# CONFIG_MFD_DA9052_I2C is not set
+# CONFIG_MFD_DA9055 is not set
+# CONFIG_MFD_MC13XXX_SPI is not set
+# CONFIG_MFD_MC13XXX_I2C is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_HTC_I2CPLD is not set
+# CONFIG_LPC_ICH is not set
+# CONFIG_LPC_SCH is not set
+# CONFIG_MFD_JANZ_CMODIO is not set
+# CONFIG_MFD_KEMPLD is not set
+# CONFIG_MFD_88PM800 is not set
+# CONFIG_MFD_88PM805 is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_MAX77686 is not set
+# CONFIG_MFD_MAX77693 is not set
+# CONFIG_MFD_MAX8907 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8997 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_EZX_PCAP is not set
+# CONFIG_MFD_VIPERBOARD is not set
+# CONFIG_MFD_RETU is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_MFD_RDC321X is not set
+# CONFIG_MFD_RTSX_PCI is not set
+# CONFIG_MFD_RC5T583 is not set
+# CONFIG_MFD_SEC_CORE is not set
+# CONFIG_MFD_SI476X_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_SMSC is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_MFD_STMPE is not set
+# CONFIG_MFD_SYSCON is not set
+# CONFIG_MFD_TI_AM335X_TSCADC is not set
+# CONFIG_MFD_LP8788 is not set
+# CONFIG_MFD_PALMAS is not set
+# CONFIG_TPS6105X is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_MFD_TPS65090 is not set
+# CONFIG_MFD_TPS65217 is not set
+# CONFIG_MFD_TPS6586X is not set
+# CONFIG_MFD_TPS65910 is not set
+# CONFIG_MFD_TPS65912 is not set
+# CONFIG_MFD_TPS65912_I2C is not set
+# CONFIG_MFD_TPS65912_SPI is not set
+# CONFIG_MFD_TPS80031 is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_TWL6040_CORE is not set
+# CONFIG_MFD_WL1273_CORE is not set
+# CONFIG_MFD_LM3533 is not set
+# CONFIG_MFD_TIMBERDALE is not set
+# CONFIG_MFD_TC3589X is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_MFD_VX855 is not set
+# CONFIG_MFD_ARIZONA_I2C is not set
+# CONFIG_MFD_ARIZONA_SPI is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM831X_SPI is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8994 is not set
+# CONFIG_VEXPRESS_CONFIG is not set
+# CONFIG_REGULATOR is not set
+CONFIG_MEDIA_SUPPORT=y
+
+#
+# Multimedia core support
+#
+# CONFIG_MEDIA_CAMERA_SUPPORT is not set
+# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set
+CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
+# CONFIG_MEDIA_RADIO_SUPPORT is not set
+CONFIG_MEDIA_RC_SUPPORT=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+CONFIG_DVB_CORE=y
+# CONFIG_DVB_NET is not set
+# CONFIG_TTPCI_EEPROM is not set
+CONFIG_DVB_MAX_ADAPTERS=8
+# CONFIG_DVB_DYNAMIC_MINORS is not set
+
+#
+# Media drivers
+#
+CONFIG_RC_CORE=y
+# CONFIG_RC_MAP is not set
+# CONFIG_RC_DECODERS is not set
+# CONFIG_RC_DEVICES is not set
+CONFIG_MEDIA_USB_SUPPORT=y
+
+#
+# Analog/digital TV USB devices
+#
+# CONFIG_VIDEO_AU0828 is not set
+
+#
+# Digital TV USB devices
+#
+CONFIG_DVB_USB=y
+# CONFIG_DVB_USB_DEBUG is not set
+# CONFIG_DVB_USB_A800 is not set
+# CONFIG_DVB_USB_DIBUSB_MB is not set
+# CONFIG_DVB_USB_DIBUSB_MC is not set
+CONFIG_DVB_USB_DIB0700=m
+# CONFIG_DVB_USB_UMT_010 is not set
+# CONFIG_DVB_USB_CXUSB is not set
+# CONFIG_DVB_USB_M920X is not set
+# CONFIG_DVB_USB_DIGITV is not set
+# CONFIG_DVB_USB_VP7045 is not set
+# CONFIG_DVB_USB_VP702X is not set
+# CONFIG_DVB_USB_GP8PSK is not set
+# CONFIG_DVB_USB_NOVA_T_USB2 is not set
+# CONFIG_DVB_USB_TTUSB2 is not set
+# CONFIG_DVB_USB_DTT200U is not set
+# CONFIG_DVB_USB_OPERA1 is not set
+# CONFIG_DVB_USB_AF9005 is not set
+# CONFIG_DVB_USB_PCTV452E is not set
+# CONFIG_DVB_USB_DW2102 is not set
+# CONFIG_DVB_USB_CINERGY_T2 is not set
+# CONFIG_DVB_USB_DTV5100 is not set
+# CONFIG_DVB_USB_FRIIO is not set
+# CONFIG_DVB_USB_AZ6027 is not set
+# CONFIG_DVB_USB_TECHNISAT_USB2 is not set
+CONFIG_DVB_USB_V2=y
+# CONFIG_DVB_USB_AF9015 is not set
+CONFIG_DVB_USB_AF9035=m
+# CONFIG_DVB_USB_ANYSEE is not set
+# CONFIG_DVB_USB_AU6610 is not set
+# CONFIG_DVB_USB_AZ6007 is not set
+# CONFIG_DVB_USB_CE6230 is not set
+# CONFIG_DVB_USB_EC168 is not set
+# CONFIG_DVB_USB_GL861 is not set
+# CONFIG_DVB_USB_IT913X is not set
+# CONFIG_DVB_USB_LME2510 is not set
+# CONFIG_DVB_USB_MXL111SF is not set
+# CONFIG_DVB_USB_RTL28XXU is not set
+# CONFIG_DVB_TTUSB_BUDGET is not set
+# CONFIG_DVB_TTUSB_DEC is not set
+# CONFIG_SMS_USB_DRV is not set
+# CONFIG_DVB_B2C2_FLEXCOP_USB is not set
+
+#
+# Webcam, TV (analog/digital) USB devices
+#
+# CONFIG_MEDIA_PCI_SUPPORT is not set
+
+#
+# Supported MMC/SDIO adapters
+#
+# CONFIG_CYPRESS_FIRMWARE is not set
+
+#
+# Media ancillary drivers (tuners, sensors, i2c, frontends)
+#
+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set
+CONFIG_MEDIA_ATTACH=y
+
+#
+# Customize TV tuners
+#
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_FC0011 is not set
+# CONFIG_MEDIA_TUNER_FC0012 is not set
+# CONFIG_MEDIA_TUNER_FC0013 is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
+# CONFIG_MEDIA_TUNER_E4000 is not set
+# CONFIG_MEDIA_TUNER_FC2580 is not set
+# CONFIG_MEDIA_TUNER_TUA9001 is not set
+CONFIG_MEDIA_TUNER_IT913X=m
+# CONFIG_MEDIA_TUNER_R820T is not set
+
+#
+# Customise DVB Frontends
+#
+
+#
+# Multistandard (satellite) frontends
+#
+# CONFIG_DVB_STB0899 is not set
+# CONFIG_DVB_STB6100 is not set
+# CONFIG_DVB_STV090x is not set
+# CONFIG_DVB_STV6110x is not set
+
+#
+# Multistandard (cable + terrestrial) frontends
+#
+# CONFIG_DVB_DRXK is not set
+# CONFIG_DVB_TDA18271C2DD is not set
+
+#
+# DVB-S (satellite) frontends
+#
+# CONFIG_DVB_CX24110 is not set
+# CONFIG_DVB_CX24123 is not set
+# CONFIG_DVB_MT312 is not set
+# CONFIG_DVB_ZL10036 is not set
+# CONFIG_DVB_ZL10039 is not set
+# CONFIG_DVB_S5H1420 is not set
+# CONFIG_DVB_STV0288 is not set
+# CONFIG_DVB_STB6000 is not set
+# CONFIG_DVB_STV0299 is not set
+# CONFIG_DVB_STV6110 is not set
+# CONFIG_DVB_STV0900 is not set
+# CONFIG_DVB_TDA8083 is not set
+# CONFIG_DVB_TDA10086 is not set
+# CONFIG_DVB_TDA8261 is not set
+# CONFIG_DVB_VES1X93 is not set
+# CONFIG_DVB_TUNER_ITD1000 is not set
+# CONFIG_DVB_TUNER_CX24113 is not set
+# CONFIG_DVB_TDA826X is not set
+# CONFIG_DVB_TUA6100 is not set
+# CONFIG_DVB_CX24116 is not set
+# CONFIG_DVB_SI21XX is not set
+# CONFIG_DVB_TS2020 is not set
+# CONFIG_DVB_DS3000 is not set
+# CONFIG_DVB_MB86A16 is not set
+# CONFIG_DVB_TDA10071 is not set
+
+#
+# DVB-T (terrestrial) frontends
+#
+# CONFIG_DVB_SP8870 is not set
+# CONFIG_DVB_SP887X is not set
+# CONFIG_DVB_CX22700 is not set
+# CONFIG_DVB_CX22702 is not set
+# CONFIG_DVB_S5H1432 is not set
+# CONFIG_DVB_DRXD is not set
+# CONFIG_DVB_L64781 is not set
+# CONFIG_DVB_TDA1004X is not set
+# CONFIG_DVB_NXT6000 is not set
+# CONFIG_DVB_MT352 is not set
+# CONFIG_DVB_ZL10353 is not set
+# CONFIG_DVB_DIB3000MB is not set
+# CONFIG_DVB_DIB3000MC is not set
+CONFIG_DVB_DIB7000M=m
+CONFIG_DVB_DIB7000P=m
+# CONFIG_DVB_DIB9000 is not set
+# CONFIG_DVB_TDA10048 is not set
+# CONFIG_DVB_AF9013 is not set
+# CONFIG_DVB_EC100 is not set
+# CONFIG_DVB_HD29L2 is not set
+# CONFIG_DVB_STV0367 is not set
+# CONFIG_DVB_CXD2820R is not set
+# CONFIG_DVB_RTL2830 is not set
+# CONFIG_DVB_RTL2832 is not set
+
+#
+# DVB-C (cable) frontends
+#
+# CONFIG_DVB_VES1820 is not set
+# CONFIG_DVB_TDA10021 is not set
+# CONFIG_DVB_TDA10023 is not set
+# CONFIG_DVB_STV0297 is not set
+
+#
+# ATSC (North American/Korean Terrestrial/Cable DTV) frontends
+#
+# CONFIG_DVB_NXT200X is not set
+# CONFIG_DVB_OR51211 is not set
+# CONFIG_DVB_OR51132 is not set
+# CONFIG_DVB_BCM3510 is not set
+# CONFIG_DVB_LGDT330X is not set
+# CONFIG_DVB_LGDT3305 is not set
+# CONFIG_DVB_LG2160 is not set
+# CONFIG_DVB_S5H1409 is not set
+# CONFIG_DVB_AU8522_DTV is not set
+# CONFIG_DVB_S5H1411 is not set
+
+#
+# ISDB-T (terrestrial) frontends
+#
+# CONFIG_DVB_S921 is not set
+# CONFIG_DVB_DIB8000 is not set
+# CONFIG_DVB_MB86A20S is not set
+
+#
+# Digital terrestrial only tuners/PLL
+#
+# CONFIG_DVB_PLL is not set
+CONFIG_DVB_TUNER_DIB0070=m
+# CONFIG_DVB_TUNER_DIB0090 is not set
+
+#
+# SEC control devices for DVB-S
+#
+# CONFIG_DVB_LNBP21 is not set
+# CONFIG_DVB_LNBP22 is not set
+# CONFIG_DVB_ISL6405 is not set
+# CONFIG_DVB_ISL6421 is not set
+# CONFIG_DVB_ISL6423 is not set
+# CONFIG_DVB_A8293 is not set
+# CONFIG_DVB_LGS8GL5 is not set
+# CONFIG_DVB_LGS8GXX is not set
+# CONFIG_DVB_ATBM8830 is not set
+# CONFIG_DVB_TDA665x is not set
+# CONFIG_DVB_IX2505V is not set
+# CONFIG_DVB_IT913X_FE is not set
+# CONFIG_DVB_M88RS2000 is not set
+CONFIG_DVB_AF9033=m
+
+#
+# Tools to develop new frontends
+#
+# CONFIG_DVB_DUMMY_FE is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGA_ARB is not set
+# CONFIG_DRM is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+# CONFIG_FB_CFB_FILLRECT is not set
+# CONFIG_FB_CFB_COPYAREA is not set
+# CONFIG_FB_CFB_IMAGEBLIT is not set
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+CONFIG_FB_SYS_FILLRECT=y
+CONFIG_FB_SYS_COPYAREA=y
+CONFIG_FB_SYS_IMAGEBLIT=y
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+CONFIG_FB_SYS_FOPS=y
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+CONFIG_FB_BACKLIGHT=y
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_CYBER2000 is not set
+CONFIG_FB_SSD1327=y
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_NVIDIA is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_I740 is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VT8623 is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_ARK is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_CARMINE is not set
+# CONFIG_FB_SMSCUFX is not set
+# CONFIG_FB_UDL is not set
+# CONFIG_FB_GOLDFISH is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_BROADSHEET is not set
+# CONFIG_FB_AUO_K190X is not set
+# CONFIG_EXYNOS_VIDEO is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_GENERIC is not set
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+# CONFIG_BACKLIGHT_LM3630 is not set
+# CONFIG_BACKLIGHT_LM3639 is not set
+# CONFIG_BACKLIGHT_LP855X is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE is not set
+# CONFIG_LOGO is not set
+CONFIG_SOUND=y
+# CONFIG_SOUND_OSS_CORE is not set
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+CONFIG_SND_HWDEP=y
+CONFIG_SND_RAWMIDI=y
+CONFIG_SND_COMPRESS_OFFLOAD=y
+CONFIG_SND_JACK=y
+# CONFIG_SND_SEQUENCER is not set
+# CONFIG_SND_MIXER_OSS is not set
+# CONFIG_SND_PCM_OSS is not set
+# CONFIG_SND_HRTIMER is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+# CONFIG_SND_SUPPORT_OLD_API is not set
+CONFIG_SND_VERBOSE_PROCFS=y
+CONFIG_SND_VERBOSE_PRINTK=y
+# CONFIG_SND_DEBUG is not set
+# CONFIG_SND_RAWMIDI_SEQ is not set
+# CONFIG_SND_OPL3_LIB_SEQ is not set
+# CONFIG_SND_OPL4_LIB_SEQ is not set
+# CONFIG_SND_SBAWE_SEQ is not set
+# CONFIG_SND_EMU10K1_SEQ is not set
+# CONFIG_SND_DRIVERS is not set
+# CONFIG_SND_PCI is not set
+# CONFIG_SND_ARM is not set
+# CONFIG_SND_SPI is not set
+CONFIG_SND_USB=y
+CONFIG_SND_USB_AUDIO=y
+# CONFIG_SND_USB_UA101 is not set
+# CONFIG_SND_USB_CAIAQ is not set
+# CONFIG_SND_USB_6FIRE is not set
+# CONFIG_SND_USB_HIFACE is not set
+CONFIG_SND_SOC=y
+# CONFIG_SND_ATMEL_SOC is not set
+# CONFIG_SND_DESIGNWARE_I2S is not set
+CONFIG_SND_KIRKWOOD_SOC=m
+CONFIG_SND_KIRKWOOD_SOC_I2S=m
+CONFIG_SND_KIRKWOOD_SOC_FBXGW2R=m
+CONFIG_SND_SOC_I2C_AND_SPI=y
+# CONFIG_SND_SOC_ALL_CODECS is not set
+CONFIG_SND_SOC_CS42L52=m
+# CONFIG_SND_SIMPLE_CARD is not set
+# CONFIG_SOUND_PRIME is not set
+
+#
+# HID support
+#
+CONFIG_HID=y
+# CONFIG_HIDRAW is not set
+# CONFIG_UHID is not set
+# CONFIG_HID_GENERIC is not set
+
+#
+# Special HID drivers
+#
+# CONFIG_HID_A4TECH is not set
+# CONFIG_HID_ACRUX is not set
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_AUREAL is not set
+# CONFIG_HID_BELKIN is not set
+# CONFIG_HID_CHERRY is not set
+# CONFIG_HID_CHICONY is not set
+# CONFIG_HID_PRODIKEYS is not set
+# CONFIG_HID_CYPRESS is not set
+# CONFIG_HID_DRAGONRISE is not set
+# CONFIG_HID_EMS_FF is not set
+# CONFIG_HID_ELECOM is not set
+# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_KEYTOUCH is not set
+# CONFIG_HID_KYE is not set
+# CONFIG_HID_UCLOGIC is not set
+# CONFIG_HID_WALTOP is not set
+# CONFIG_HID_FBX_REMOTE_AUDIO is not set
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_ICADE is not set
+# CONFIG_HID_TWINHAN is not set
+# CONFIG_HID_KENSINGTON is not set
+# CONFIG_HID_LCPOWER is not set
+# CONFIG_HID_LOGITECH is not set
+# CONFIG_HID_MAGICMOUSE is not set
+# CONFIG_HID_MICROSOFT is not set
+# CONFIG_HID_MONTEREY is not set
+# CONFIG_HID_MULTITOUCH is not set
+# CONFIG_HID_ORTEK is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_PICOLCD is not set
+# CONFIG_HID_PRIMAX is not set
+# CONFIG_HID_SAITEK is not set
+# CONFIG_HID_SAMSUNG is not set
+# CONFIG_HID_SPEEDLINK is not set
+# CONFIG_HID_STEELSERIES is not set
+# CONFIG_HID_SUNPLUS is not set
+# CONFIG_HID_GREENASIA is not set
+# CONFIG_HID_SMARTJOYPLUS is not set
+# CONFIG_HID_TIVO is not set
+# CONFIG_HID_TOPSEED is not set
+# CONFIG_HID_THINGM is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_WACOM is not set
+# CONFIG_HID_WIIMOTE is not set
+# CONFIG_HID_ZEROPLUS is not set
+# CONFIG_HID_ZYDACRON is not set
+# CONFIG_HID_SENSOR_HUB is not set
+
+#
+# USB HID support
+#
+# CONFIG_USB_HID is not set
+# CONFIG_HID_PID is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_MOUSE is not set
+
+#
+# I2C HID support
+#
+# CONFIG_I2C_HID is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_COMMON=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEFAULT_PERSIST=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+# CONFIG_USB_MON is not set
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_XHCI_HCD is not set
+CONFIG_USB_EHCI_HCD=m
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_EHCI_PCI=m
+CONFIG_USB_EHCI_HCD_ORION=m
+# CONFIG_USB_EHCI_HCD_PLATFORM is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_ISP1362_HCD is not set
+# CONFIG_USB_FUSBH200_HCD is not set
+# CONFIG_USB_OHCI_HCD is not set
+# CONFIG_USB_UHCI_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+
+#
+# USB Device Class drivers
+#
+CONFIG_USB_ACM=y
+CONFIG_USB_PRINTER=y
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
+#
+
+#
+# also be needed; see USB_STORAGE Help for more info
+#
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_REALTEK is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_STORAGE_ENE_UB6250 is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+# CONFIG_USB_DWC3 is not set
+# CONFIG_USB_CHIPIDEA is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_YUREX is not set
+# CONFIG_USB_EZUSB_FX2 is not set
+# CONFIG_USB_HSIC_USB3503 is not set
+# CONFIG_USB_PHY is not set
+# CONFIG_USB_GADGET is not set
+# CONFIG_UWB is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+# CONFIG_LEDS_LM3530 is not set
+# CONFIG_LEDS_LM3642 is not set
+# CONFIG_LEDS_PCA9532 is not set
+# CONFIG_LEDS_GPIO is not set
+# CONFIG_LEDS_LP3944 is not set
+# CONFIG_LEDS_LP5521 is not set
+# CONFIG_LEDS_LP5523 is not set
+# CONFIG_LEDS_LP5562 is not set
+# CONFIG_LEDS_PCA955X is not set
+# CONFIG_LEDS_PCA9633 is not set
+# CONFIG_LEDS_DAC124S085 is not set
+# CONFIG_LEDS_BD2802 is not set
+# CONFIG_LEDS_LT3593 is not set
+# CONFIG_LEDS_RENESAS_TPU is not set
+# CONFIG_LEDS_TCA6507 is not set
+# CONFIG_LEDS_LM355x is not set
+# CONFIG_LEDS_OT200 is not set
+# CONFIG_LEDS_BLINKM is not set
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+# CONFIG_LEDS_TRIGGER_TIMER is not set
+# CONFIG_LEDS_TRIGGER_ONESHOT is not set
+# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set
+# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
+# CONFIG_LEDS_TRIGGER_CPU is not set
+# CONFIG_LEDS_TRIGGER_GPIO is not set
+# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set
+
+#
+# iptables trigger is under Netfilter config (LED target)
+#
+# CONFIG_LEDS_TRIGGER_TRANSIENT is not set
+# CONFIG_LEDS_TRIGGER_CAMERA is not set
+# CONFIG_FREEBOX_PANEL is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_INFINIBAND is not set
+# CONFIG_EDAC is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_UIO is not set
+# CONFIG_VIRT_DRIVERS is not set
+
+#
+# Virtio drivers
+#
+# CONFIG_VIRTIO_PCI is not set
+# CONFIG_VIRTIO_MMIO is not set
+
+#
+# Microsoft Hyper-V guest support
+#
+# CONFIG_STAGING is not set
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_HAVE_CLK_PREPARE=y
+CONFIG_COMMON_CLK=y
+
+#
+# Common Clock Framework
+#
+# CONFIG_COMMON_CLK_DEBUG is not set
+# CONFIG_COMMON_CLK_SI5351 is not set
+
+#
+# Hardware Spinlock drivers
+#
+CONFIG_CLKSRC_MMIO=y
+# CONFIG_MAILBOX is not set
+# CONFIG_IOMMU_SUPPORT is not set
+
+#
+# Remoteproc drivers
+#
+# CONFIG_STE_MODEM_RPROC is not set
+
+#
+# Rpmsg drivers
+#
+# CONFIG_PM_DEVFREQ is not set
+# CONFIG_EXTCON is not set
+# CONFIG_MEMORY is not set
+# CONFIG_IIO is not set
+# CONFIG_VME_BUS is not set
+# CONFIG_PWM is not set
+# CONFIG_IPACK_BUS is not set
+# CONFIG_RESET_CONTROLLER is not set
+# CONFIG_FMC is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+# CONFIG_EXT3_FS_XATTR is not set
+CONFIG_EXT4_FS=y
+# CONFIG_EXT4_FS_POSIX_ACL is not set
+# CONFIG_EXT4_FS_SECURITY is not set
+# CONFIG_EXT4_DEBUG is not set
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_JBD2=y
+# CONFIG_JBD2_DEBUG is not set
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_XFS_FS=y
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_POSIX_ACL is not set
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_WARN is not set
+# CONFIG_XFS_DEBUG is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_EXPORTFS=y
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY_USER=y
+CONFIG_FANOTIFY=y
+# CONFIG_QUOTA is not set
+# CONFIG_QUOTACTL is not set
+# CONFIG_AUTOFS4_FS is not set
+CONFIG_FUSE_FS=y
+# CONFIG_CUSE is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=850
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+CONFIG_NTFS_FS=y
+# CONFIG_NTFS_DEBUG is not set
+# CONFIG_NTFS_RW is not set
+CONFIG_EXFAT_FS=y
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_RAMFS_XATTR is not set
+# CONFIG_RAMFS_XATTR_USER is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+CONFIG_HFS_FS=y
+CONFIG_HFSPLUS_FS=y
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+CONFIG_UBIFS_FS=y
+CONFIG_UBIFS_FS_ADVANCED_COMPR=y
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_ZLIB=y
+# CONFIG_LOGFS is not set
+CONFIG_CRAMFS=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_FILE_CACHE=y
+# CONFIG_SQUASHFS_FILE_DIRECT is not set
+CONFIG_SQUASHFS_DECOMP_SINGLE=y
+# CONFIG_SQUASHFS_DECOMP_MULTI is not set
+# CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU is not set
+# CONFIG_SQUASHFS_XATTR is not set
+CONFIG_SQUASHFS_ZLIB=y
+# CONFIG_SQUASHFS_LZO is not set
+# CONFIG_SQUASHFS_XZ is not set
+# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set
+# CONFIG_SQUASHFS_EMBEDDED is not set
+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX6FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_PSTORE=y
+# CONFIG_PSTORE_CONSOLE is not set
+CONFIG_PSTORE_RAM=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_F2FS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V2=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_SWAP is not set
+CONFIG_ROOT_NFS=y
+CONFIG_NFSD=y
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+CONFIG_NFSD_V4=y
+# CONFIG_NFSD_FAULT_INJECTION is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+# CONFIG_SUNRPC_DEBUG is not set
+# CONFIG_CEPH_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+CONFIG_NLS_CODEPAGE_850=y
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=y
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_MAC_ROMAN is not set
+# CONFIG_NLS_MAC_CELTIC is not set
+# CONFIG_NLS_MAC_CENTEURO is not set
+# CONFIG_NLS_MAC_CROATIAN is not set
+# CONFIG_NLS_MAC_CYRILLIC is not set
+# CONFIG_NLS_MAC_GAELIC is not set
+# CONFIG_NLS_MAC_GREEK is not set
+# CONFIG_NLS_MAC_ICELAND is not set
+# CONFIG_NLS_MAC_INUIT is not set
+# CONFIG_NLS_MAC_ROMANIAN is not set
+# CONFIG_NLS_MAC_TURKISH is not set
+CONFIG_NLS_UTF8=y
+
+#
+# Kernel hacking
+#
+
+#
+# printk and dmesg options
+#
+CONFIG_PRINTK_TIME=y
+CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+
+#
+# Compile-time checks and compiler options
+#
+# CONFIG_DEBUG_INFO is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_STRIP_ASM_SYMS is not set
+# CONFIG_READABLE_ASM is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_KERNEL=y
+
+#
+# Memory Debugging
+#
+# CONFIG_DEBUG_PAGEALLOC is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_SLUB_STATS is not set
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_VM is not set
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_DEBUG_SHIRQ is not set
+
+#
+# Debug Lockups and Hangs
+#
+# CONFIG_LOCKUP_DETECTOR is not set
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=1
+# CONFIG_PANIC_ON_OOPS is not set
+CONFIG_PANIC_ON_OOPS_VALUE=0
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+
+#
+# Lock Debugging (spinlocks, mutexes, etc...)
+#
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_PI_LIST=y
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_LIST=y
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_CREDENTIALS is not set
+
+#
+# RCU Debugging
+#
+# CONFIG_SPARSE_RCU_POINTER is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_TRACE is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_NOTIFIER_ERROR_INJECTION is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_TRACING_SUPPORT=y
+# CONFIG_FTRACE is not set
+
+#
+# Runtime Testing
+#
+# CONFIG_LKDTM is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_RBTREE_TEST is not set
+# CONFIG_INTERVAL_TREE_TEST is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_TEST_STRING_HELPERS is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_DMA_API_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_STRICT_DEVMEM is not set
+CONFIG_ARM_UNWIND=y
+CONFIG_DEBUG_USER=y
+# CONFIG_DEBUG_LL is not set
+CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
+CONFIG_UNCOMPRESS_INCLUDE="mach/uncompress.h"
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY_DMESG_RESTRICT is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_PCOMP2=y
+# CONFIG_CRYPTO_BUILTIN_TEST is not set
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+# CONFIG_CRYPTO_USER is not set
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_NULL=y
+CONFIG_CRYPTO_WORKQUEUE=y
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_AUTHENC=y
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+CONFIG_CRYPTO_CCM=y
+# CONFIG_CRYPTO_GCM is not set
+CONFIG_CRYPTO_SEQIV=y
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_CTR=y
+# CONFIG_CRYPTO_CTS is not set
+CONFIG_CRYPTO_ECB=y
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_CMAC is not set
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_VMAC is not set
+
+#
+# Digest
+#
+CONFIG_CRYPTO_CRC32C=y
+# CONFIG_CRYPTO_CRC32 is not set
+# CONFIG_CRYPTO_GHASH is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA1_ARM=y
+CONFIG_CRYPTO_SHA256=y
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_AES_ARM=y
+# CONFIG_CRYPTO_ANUBIS is not set
+CONFIG_CRYPTO_ARC4=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_ZLIB is not set
+CONFIG_CRYPTO_LZO=y
+# CONFIG_CRYPTO_LZ4 is not set
+# CONFIG_CRYPTO_LZ4HC is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_USER_API_HASH is not set
+# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
+# CONFIG_CRYPTO_HW is not set
+# CONFIG_BINARY_PRINTF is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GENERIC_NET_UTILS=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_IO=y
+CONFIG_CRC_CCITT=y
+CONFIG_CRC16=y
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC32_SELFTEST is not set
+CONFIG_CRC32_SLICEBY8=y
+# CONFIG_CRC32_SLICEBY4 is not set
+# CONFIG_CRC32_SARWATE is not set
+# CONFIG_CRC32_BIT is not set
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=y
+# CONFIG_CRC8 is not set
+CONFIG_AUDIT_GENERIC=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+# CONFIG_XZ_DEC is not set
+# CONFIG_XZ_DEC_BCJ is not set
+CONFIG_REED_SOLOMON=y
+CONFIG_REED_SOLOMON_ENC8=y
+CONFIG_REED_SOLOMON_DEC8=y
+CONFIG_BCH=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_DQL=y
+CONFIG_NLATTR=y
+CONFIG_GENERIC_ATOMIC64=y
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_AVERAGE=y
+# CONFIG_CORDIC is not set
+# CONFIG_DDR is not set
+CONFIG_OID_REGISTRY=y
+CONFIG_FBXSERIAL=y
+# CONFIG_VIRTUALIZATION is not set
diff -Nruw linux-3.11.10-fbx/drivers/fbxgpio./fbxgpio_core.c linux-3.11.10-fbx/drivers/fbxgpio/fbxgpio_core.c
--- linux-3.11.10-fbx/drivers/fbxgpio./fbxgpio_core.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxgpio/fbxgpio_core.c	2015-06-12 14:02:21.854954546 +0200
@@ -0,0 +1,312 @@
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/fbxgpio_core.h>
+
+#define PFX	"fbxgpio_core: "
+
+/* #define DEBUG */
+#ifdef DEBUG
+#define dprint(Fmt, Arg...)	printk(PFX Fmt, Arg)
+#else
+#define dprint(Fmt, Arg...)	do { } while (0)
+#endif
+
+static struct class *fbxgpio_class;
+
+/*
+ * show direction in for gpio associated with class_device dev.
+ */
+static ssize_t show_direction(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	struct fbxgpio_pin *p;
+	int dir, ret = 0;
+
+	p = dev_get_drvdata(dev);
+
+	if (p->ops->get_direction)
+		dir = p->ops->get_direction(p->pin_num);
+	else
+		dir = p->direction;
+
+	switch (dir) {
+	case GPIO_DIR_IN:
+		ret += sprintf(buf, "input\n");
+		break;
+	case GPIO_DIR_OUT:
+		ret += sprintf(buf, "output\n");
+		break;
+	default:
+		ret += sprintf(buf, "unknown\n");
+		break;
+	}
+	return ret;
+}
+
+/*
+ * store direction. return -EINVAL if direction string is bad. return
+ * -EPERM if flag FBXGPIO_PIN_DIR_RW is set in flags.
+ */
+static ssize_t store_direction(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	int dir;
+	struct fbxgpio_pin *p;
+	int match_len = 0;
+	int i;
+	static const char *word_match[] = {
+		[GPIO_DIR_IN] = "input",
+		[GPIO_DIR_OUT] = "output",
+	};
+
+	if (*buf == ' ' || *buf == '\t' || *buf == '\r' || *buf == '\n')
+		/* silently eat any spaces/tab/linefeed/carriagereturn */
+		return 1;
+
+	p = dev_get_drvdata(dev);
+	if (!(p->flags & FBXGPIO_PIN_DIR_RW)) {
+		dprint("pin %s direction is read only.\n", p->pin_name);
+		return -EPERM;
+	}
+	dir = 0;
+	for (i = 0; i < 2; ++i) {
+		if (size >= strlen(word_match[i]) &&
+		    !strncmp(buf, word_match[i], strlen(word_match[i]))) {
+			dir = i;
+			match_len = strlen(word_match[i]);
+			break ;
+		}
+	}
+	if (i == 2)
+		return -EINVAL;
+
+	p->ops->set_direction(p->pin_num, dir);
+	return match_len;
+}
+
+/*
+ * show input data for input gpio pins.
+ */
+static ssize_t show_datain(struct device *dev,
+			   struct device_attribute *attr, char *buf)
+{
+	int val;
+	struct fbxgpio_pin *p;
+
+	p = dev_get_drvdata(dev);
+	if (p->direction == GPIO_DIR_OUT)
+		return -EINVAL;
+	val = p->ops->get_datain(p->pin_num);
+
+	if (p->flags & FBXGPIO_PIN_REVERSE_POL)
+		val = 1 - val;
+	return sprintf(buf, "%i\n", val);
+}
+
+/*
+ * show output data for output gpio pins.
+ */
+static ssize_t show_dataout(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	int val;
+	struct fbxgpio_pin *p;
+
+	p = dev_get_drvdata(dev);
+	if (p->direction == GPIO_DIR_IN)
+		return -EINVAL;
+	if (p->ops->get_dataout)
+		val = p->ops->get_dataout(p->pin_num);
+	else
+		val = p->cur_dataout;
+
+	if (p->flags & FBXGPIO_PIN_REVERSE_POL)
+		val = 1 - val;
+	return sprintf(buf, "%i\n", val);
+}
+
+/*
+ * store new dataout value for output gpio pins.
+ */
+static ssize_t store_dataout(struct device *dev,
+	    struct device_attribute *attr, const char *buf, size_t size)
+{
+	int val;
+	struct fbxgpio_pin *p;
+
+	if (*buf == ' ' || *buf == '\t' || *buf == '\r' || *buf == '\n')
+		/* silently eat any spaces/tab/linefeed/carriagereturn */
+		return 1;
+
+	p = dev_get_drvdata(dev);
+
+	if (p->direction != GPIO_DIR_OUT)
+		return -EINVAL;
+
+	switch (*buf) {
+	case '0':
+		val = 0;
+		break ;
+	case '1':
+		val = 1;
+		break ;
+	default:
+		return -EINVAL;
+	}
+
+	p->cur_dataout = val;
+
+	if (p->flags & FBXGPIO_PIN_REVERSE_POL)
+		val = 1 - val;
+	p->ops->set_dataout(p->pin_num, val);
+	return 1;
+}
+
+/*
+ * show pin number associated with gpio pin.
+ */
+static ssize_t show_pinnum(struct device *dev,
+			   struct device_attribute *attr, char *buf)
+{
+	struct fbxgpio_pin *p;
+
+	p = dev_get_drvdata(dev);
+	return sprintf(buf, "%i\n", p->pin_num);
+}
+
+/*
+ * attribute list associated with each class device.
+ */
+static struct device_attribute gpio_attributes[] = {
+	__ATTR(direction, 0600, show_direction, store_direction),
+	__ATTR(data_in,   0400, show_datain, NULL),
+	__ATTR(data_out,  0600, show_dataout, store_dataout),
+	__ATTR(pin_num,   0400, show_pinnum, NULL),
+};
+
+static int fbxgpio_register_pin(struct platform_device *ppdev,
+				struct fbxgpio_pin *pin)
+{
+	struct device *dev;
+	int i, ret;
+
+	dprint("registering pin %s\n", pin->pin_name);
+
+	/* ensure ops is valid */
+	if (!pin->ops) {
+		printk(KERN_ERR PFX "no operation set for pin %s\n",
+		       pin->pin_name);
+		return -EINVAL;
+	}
+
+	dev = device_create(fbxgpio_class, &ppdev->dev, 0, pin,
+			    "%s", pin->pin_name);
+	if (IS_ERR(dev))
+		return PTR_ERR(dev);
+
+	for (i = 0; i < ARRAY_SIZE(gpio_attributes); i++) {
+		ret = device_create_file(dev, &gpio_attributes[i]);
+		if (ret)
+			goto err_out;
+	}
+
+	/* ensure pin direction matches hardware state */
+	if (pin->ops->get_direction &&
+	    pin->direction != pin->ops->get_direction(pin->pin_num)) {
+		printk(KERN_WARNING PFX "pin %s default direction does not "
+		       "match current hardware state, fixing.\n",
+		       pin->pin_name);
+		pin->ops->set_direction(pin->pin_num, pin->direction);
+	}
+	pin->dev = dev;
+	return 0;
+
+err_out:
+	for (; i >= 0; i--)
+		device_remove_file(dev, &gpio_attributes[i]);
+	device_unregister(dev);
+	return ret;
+}
+
+static void fbxgpio_unregister_pin(struct fbxgpio_pin *pin)
+{
+	struct device *dev;
+	int i;
+
+	dprint("unregistering pin %s\n", pin->pin_name);
+	dev = pin->dev;
+	pin->dev = NULL;
+
+	for (i = 0; i < ARRAY_SIZE(gpio_attributes); i++)
+		device_remove_file(dev, &gpio_attributes[i]);
+	device_unregister(dev);
+}
+
+static int fbxgpio_platform_probe(struct platform_device *pdev)
+{
+	struct fbxgpio_pin *p;
+	int err = 0;
+
+	p = pdev->dev.platform_data;
+	while (p->pin_name) {
+		err = fbxgpio_register_pin(pdev, p);
+		if (err)
+			return err;
+		++p;
+	}
+	return 0;
+}
+
+static int fbxgpio_platform_remove(struct platform_device *pdev)
+{
+	struct fbxgpio_pin *p;
+
+	p = pdev->dev.platform_data;
+	while (p->pin_name) {
+		fbxgpio_unregister_pin(p);
+		++p;
+	}
+	return 0;
+}
+
+static struct platform_driver fbxgpio_platform_driver =
+{
+	.probe	= fbxgpio_platform_probe,
+	.remove	= fbxgpio_platform_remove,
+	.driver	= {
+		.name	= "fbxgpio",
+	}
+};
+
+static int __init fbxgpio_init(void)
+{
+	int ret;
+
+	fbxgpio_class = class_create(THIS_MODULE, "fbxgpio");
+	if (IS_ERR(fbxgpio_class))
+		return PTR_ERR(fbxgpio_class);
+
+	ret = platform_driver_register(&fbxgpio_platform_driver);
+	if (ret) {
+		printk(KERN_ERR PFX "unable to register fbxgpio driver.\n");
+		class_destroy(fbxgpio_class);
+		return ret;
+	}
+	return 0;
+}
+
+static void __exit fbxgpio_exit(void)
+{
+	platform_driver_unregister(&fbxgpio_platform_driver);
+	class_destroy(fbxgpio_class);
+}
+
+subsys_initcall(fbxgpio_init);
+module_exit(fbxgpio_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nicolas Schichan <nicolas.schichan@freebox.fr>");
diff -Nruw linux-3.11.10-fbx/drivers/fbxgpio./Kconfig linux-3.11.10-fbx/drivers/fbxgpio/Kconfig
--- linux-3.11.10-fbx/drivers/fbxgpio./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxgpio/Kconfig	2015-06-12 14:02:21.854954546 +0200
@@ -0,0 +1,3 @@
+config FREEBOX_GPIO
+	tristate "Freebox GPIO control interface"
+	default n
diff -Nruw linux-3.11.10-fbx/drivers/fbxgpio./Makefile linux-3.11.10-fbx/drivers/fbxgpio/Makefile
--- linux-3.11.10-fbx/drivers/fbxgpio./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxgpio/Makefile	2015-06-12 14:02:21.854954546 +0200
@@ -0,0 +1 @@
+obj-$(CONFIG_FREEBOX_GPIO)	+= fbxgpio_core.o
diff -Nruw linux-3.11.10-fbx/drivers/fbxjtag./Kconfig linux-3.11.10-fbx/drivers/fbxjtag/Kconfig
--- linux-3.11.10-fbx/drivers/fbxjtag./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxjtag/Kconfig	2015-06-12 14:02:21.854954546 +0200
@@ -0,0 +1,3 @@
+config FREEBOX_JTAG
+	tristate "Freebox JTAG control interface"
+	default n
diff -Nruw linux-3.11.10-fbx/drivers/fbxjtag./Makefile linux-3.11.10-fbx/drivers/fbxjtag/Makefile
--- linux-3.11.10-fbx/drivers/fbxjtag./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxjtag/Makefile	2015-06-12 14:02:21.854954546 +0200
@@ -0,0 +1 @@
+obj-$(CONFIG_FREEBOX_JTAG)	+= fbxjtag.o
diff -Nruw linux-3.11.10-fbx/drivers/fbxmtd./Kconfig linux-3.11.10-fbx/drivers/fbxmtd/Kconfig
--- linux-3.11.10-fbx/drivers/fbxmtd./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxmtd/Kconfig	2015-06-12 14:02:21.858954579 +0200
@@ -0,0 +1,41 @@
+menuconfig FREEBOX_MTD
+	tristate "Freebox Memory Technology Devices (FBXMTD) support"
+
+if FREEBOX_MTD
+
+config FREEBOX_MTD_BACKEND_AMD
+	bool "Support for AMD compatible flash"
+
+config FREEBOX_MTD_BACKEND_INTEL
+	bool "Support for Intel Strataflash"
+
+config FREEBOX_MTD_BLK
+	tristate "Block device access to fbxmtd"
+	depends on BLOCK
+
+config FREEBOX_MTD_CHAR
+	tristate "Character device access to fbxmtd"
+
+
+comment "Mapping drivers"
+
+#
+# Generic mapping driver.
+#
+config FREEBOX_MTD_MAP_DRV_FBX
+	tristate "Freebox mapping Driver."
+	select CRC32
+
+config FREEBOX_MTD_MAP_DRV_BCM963XX
+	tristate "Broadcom 963xx flash format"
+	select CRC32
+
+#
+# Freebox MTD Map Control interface
+#
+config FREEBOX_MTD_MAP_IOCTL
+	tristate "IOCTL control interface"
+	depends on FREEBOX_MTD_MAP_DRV_FBX
+
+endif
+
diff -Nruw linux-3.11.10-fbx/drivers/fbxmtd./Makefile linux-3.11.10-fbx/drivers/fbxmtd/Makefile
--- linux-3.11.10-fbx/drivers/fbxmtd./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxmtd/Makefile	2015-06-12 14:02:21.858954579 +0200
@@ -0,0 +1,24 @@
+
+# core support
+obj-$(CONFIG_FREEBOX_MTD) += fbxmtd.o
+
+fbxmtd-objs += fbxmtd_core.o fbxmtd_core_io.o
+ifeq ($(CONFIG_FREEBOX_MTD_BACKEND_AMD),y)
+fbxmtd-objs += fbxmtd_core_amd.o
+endif
+
+ifeq ($(CONFIG_FREEBOX_MTD_BACKEND_INTEL),y)
+fbxmtd-objs += fbxmtd_core_intel.o
+endif
+
+# generic character device access support (r/w with read erase modify write)
+obj-$(CONFIG_FREEBOX_MTD_CHAR) += fbxmtd_char.o
+fbxmtd_char-objs += fbxmtd_char_dev.o
+
+# generic r/o block device access support
+obj-$(CONFIG_FREEBOX_MTD_BLK) += fbxmtd_blk.o
+fbxmtd_blk-objs += fbxmtd_blk_dev.o
+
+obj-$(CONFIG_FREEBOX_MTD_MAP_DRV_FBX) += fbxmtd_map_drv_fbx.o
+obj-$(CONFIG_FREEBOX_MTD_MAP_DRV_BCM963XX) += fbxmtd_map_drv_bcm963xx.o
+obj-$(CONFIG_FREEBOX_MTD_MAP_IOCTL) += fbxmtd_map_ioctl.o
diff -Nruw linux-3.11.10-fbx/drivers/fbxpanel./Kconfig linux-3.11.10-fbx/drivers/fbxpanel/Kconfig
--- linux-3.11.10-fbx/drivers/fbxpanel./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxpanel/Kconfig	2015-09-29 11:15:05.805732167 +0200
@@ -0,0 +1,13 @@
+menuconfig FREEBOX_PANEL
+	tristate "Freebox Panel Management"
+	default n
+
+if FREEBOX_PANEL
+
+config FREEBOX_PANEL_HW_PIC_FBX
+	tristate "I2C PIC-based Panel driver."
+	default n
+	select I2C
+
+endif
+
diff -Nruw linux-3.11.10-fbx/drivers/fbxpanel./Makefile linux-3.11.10-fbx/drivers/fbxpanel/Makefile
--- linux-3.11.10-fbx/drivers/fbxpanel./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxpanel/Makefile	2015-09-29 11:15:05.805732167 +0200
@@ -0,0 +1,5 @@
+obj-$(CONFIG_FREEBOX_PANEL)	+= fbxpanel.o
+
+fbxpanel-objs = fbxpanel_class.o fbxpanel_anim.o
+
+obj-$(CONFIG_FREEBOX_PANEL_HW_PIC_FBX)	+= fbxpanel_hw_pic_fbx.o
diff -Nruw linux-3.11.10-fbx/drivers/fbxprocfs./fbxprocfs.c linux-3.11.10-fbx/drivers/fbxprocfs/fbxprocfs.c
--- linux-3.11.10-fbx/drivers/fbxprocfs./fbxprocfs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxprocfs/fbxprocfs.c	2015-06-12 14:02:21.858954579 +0200
@@ -0,0 +1,299 @@
+/*
+ * Freebox ProcFs interface
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/sizes.h>
+
+#include <linux/fbxprocfs.h>
+
+#define PFX	"fbxprocfs: "
+
+
+static struct list_head clients;
+static struct mutex clients_mutex;
+
+static struct proc_dir_entry *root;
+
+/*
+ * register  a  fbxprocfs client  with  given  dirname, caller  should
+ * consider returned struct opaque
+ */
+struct fbxprocfs_client *fbxprocfs_add_client(const char *dirname,
+					      struct module *owner)
+{
+	struct fbxprocfs_client *ret, *p;
+
+	ret = NULL;
+	mutex_lock(&clients_mutex);
+
+	/* check for duplicate */
+	list_for_each_entry(p, &clients, list) {
+		if (!strcmp(dirname, p->dirname))
+			goto out;
+	}
+
+	if (!(ret = kmalloc(sizeof (*ret), GFP_KERNEL))) {
+		printk(KERN_ERR PFX "kmalloc failed\n");
+		goto out;
+	}
+
+	/* try to create client directory */
+	if (!(ret->dir = proc_mkdir(dirname, root))) {
+		printk(KERN_ERR PFX "can't create %s dir\n", dirname);
+		kfree(ret);
+		ret = NULL;
+		goto out;
+	}
+
+	atomic_set(&ret->refcount, 1);
+	ret->dirname = dirname;
+	list_add(&ret->list, &clients);
+
+out:
+	mutex_unlock(&clients_mutex);
+	return ret;
+}
+
+/*
+ * unregister  a  fbxprocfs client, make sure usage count is zero
+ */
+int fbxprocfs_remove_client(struct fbxprocfs_client *client)
+{
+	int ret;
+
+	mutex_lock(&clients_mutex);
+
+	ret = 0;
+	if (atomic_read(&client->refcount) > 1) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	remove_proc_entry(client->dirname, root);
+	list_del(&client->list);
+	kfree(client);
+
+out:
+	mutex_unlock(&clients_mutex);
+	return ret;
+}
+
+/*
+ * remove given entries from client directory
+ */
+static int
+__remove_entries(struct fbxprocfs_client *client,
+		 const struct fbxprocfs_desc *ro_desc,
+		 const struct fbxprocfs_desc *rw_desc)
+{
+	int i;
+
+	for (i = 0; ro_desc && ro_desc[i].name; i++) {
+		remove_proc_entry(ro_desc[i].name, client->dir);
+		atomic_dec(&client->refcount);
+	}
+
+	for (i = 0; rw_desc && rw_desc[i].name; i++) {
+		remove_proc_entry(rw_desc[i].name, client->dir);
+		atomic_dec(&client->refcount);
+	}
+
+	return 0;
+}
+
+/*
+ * replacement for NULL rfunc.
+ */
+static int bad_rfunc(struct seq_file *m, void *ptr)
+{
+	return -EACCES;
+}
+
+/*
+ * fbxprocfs write path is now handled by seq_file code. this
+ * simplifies client code greatly.
+ */
+static int fbxprocfs_open(struct inode *inode, struct file *file)
+{
+	const struct fbxprocfs_desc *desc = PDE_DATA(inode);
+
+	return single_open(file, desc->rfunc ? desc->rfunc : bad_rfunc,
+			   (void*)desc->id);
+}
+
+/*
+ * no particular help from kernel in the write path, fetch user buffer
+ * in a kernel buffer and call write func.
+ */
+static int fbxprocfs_write(struct file *file, const char __user *ubuf,
+			   size_t len, loff_t *off)
+{
+	/*
+	 * get fbxprocfs desc via the proc_dir_entry in file inode
+	 */
+	struct fbxprocfs_desc *d = PDE_DATA(file_inode(file));
+	char *kbuf;
+	int ret;
+
+	/*
+	 * must have a wfunc callback.
+	 */
+	if (!d->wfunc)
+		return -EACCES;
+
+	/*
+	 * allow up to SZ_4K bytes to be written.
+	 */
+	if (len > SZ_4K)
+		return -EOVERFLOW;
+
+	/*
+	 * alloc and fetch kernel buffer containing user data.
+	 */
+	kbuf = kmalloc(SZ_4K, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
+
+	ret = -EFAULT;
+	if (copy_from_user(kbuf, ubuf, len))
+		goto kfree;
+
+	ret = d->wfunc(file, kbuf, len, (void*)d->id);
+
+kfree:
+	kfree(kbuf);
+	return ret;
+}
+
+/*
+ * fbxprocfs file operations, read stuff is handled by seq_file code.
+ */
+static const struct file_operations fbxprocfs_fops = {
+	.open		= fbxprocfs_open,
+	.llseek		= seq_lseek,
+	.read		= seq_read,
+	.release	= seq_release,
+	.write		= fbxprocfs_write,
+};
+
+/*
+ * replaces create_proc_read_entry removed in latest kernels.
+ */
+static struct proc_dir_entry *__create_proc_read_entry(
+				       const struct fbxprocfs_desc *desc,
+				       struct proc_dir_entry *base)
+{
+	return proc_create_data(desc->name, 0, base, &fbxprocfs_fops,
+				(void*)desc);
+}
+
+/*
+ * replaces create_proc_entry removed in latest kernels.
+ */
+static struct proc_dir_entry *__create_proc_entry(
+					const struct fbxprocfs_desc *desc,
+					struct proc_dir_entry *base)
+{
+	return proc_create_data(desc->name, S_IFREG | S_IWUSR | S_IRUGO,
+				base, &fbxprocfs_fops, (void*)desc);
+}
+
+/*
+ * create given entries in client directory
+ */
+static int
+__create_entries(struct fbxprocfs_client *client,
+		 const struct fbxprocfs_desc *ro_desc,
+		 const struct fbxprocfs_desc *rw_desc)
+{
+	struct proc_dir_entry	*proc;
+	int			i;
+
+	for (i = 0; ro_desc && ro_desc[i].name; i++) {
+		if (!(proc = __create_proc_read_entry(&ro_desc[i],
+						      client->dir))) {
+			printk(KERN_ERR PFX "can't create %s/%s entry\n",
+			       client->dirname, ro_desc[i].name);
+			goto err;
+		}
+		atomic_inc(&client->refcount);
+	}
+
+	for (i = 0; rw_desc && rw_desc[i].name; i++) {
+		if (!(proc = __create_proc_entry(&rw_desc[i], client->dir))) {
+			printk(KERN_ERR PFX "can't create %s/%s entry\n",
+			       client->dirname, ro_desc[i].name);
+			goto err;
+		}
+		atomic_inc(&client->refcount);
+	}
+
+	return 0;
+
+err:
+	__remove_entries(client, ro_desc, rw_desc);
+	return -1;
+}
+
+int
+fbxprocfs_create_entries(struct fbxprocfs_client *client,
+			 const struct fbxprocfs_desc *ro_desc,
+			 const struct fbxprocfs_desc *rw_desc)
+{
+	int	ret;
+
+	ret = __create_entries(client, ro_desc, rw_desc);
+	return ret;
+}
+
+int
+fbxprocfs_remove_entries(struct fbxprocfs_client *client,
+			 const struct fbxprocfs_desc *ro_desc,
+			 const struct fbxprocfs_desc *rw_desc)
+{
+	int	ret;
+
+	ret = __remove_entries(client, ro_desc, rw_desc);
+	return ret;
+}
+
+
+static int __init
+fbxprocfs_init(void)
+{
+	INIT_LIST_HEAD(&clients);
+	mutex_init(&clients_mutex);
+
+	/* create freebox directory */
+	if (!(root = proc_mkdir("freebox", NULL))) {
+		printk(KERN_ERR PFX "can't create freebox/ dir\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+static void __exit
+fbxprocfs_exit(void)
+{
+	remove_proc_entry("freebox", NULL);
+}
+
+module_init(fbxprocfs_init);
+module_exit(fbxprocfs_exit);
+
+EXPORT_SYMBOL(fbxprocfs_create_entries);
+EXPORT_SYMBOL(fbxprocfs_remove_entries);
+EXPORT_SYMBOL(fbxprocfs_add_client);
+EXPORT_SYMBOL(fbxprocfs_remove_client);
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");
+
diff -Nruw linux-3.11.10-fbx/drivers/fbxprocfs./Kconfig linux-3.11.10-fbx/drivers/fbxprocfs/Kconfig
--- linux-3.11.10-fbx/drivers/fbxprocfs./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxprocfs/Kconfig	2015-06-12 14:02:21.858954579 +0200
@@ -0,0 +1,2 @@
+config FREEBOX_PROCFS
+	tristate "Freebox procfs interface"
diff -Nruw linux-3.11.10-fbx/drivers/fbxprocfs./Makefile linux-3.11.10-fbx/drivers/fbxprocfs/Makefile
--- linux-3.11.10-fbx/drivers/fbxprocfs./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxprocfs/Makefile	2015-06-12 14:02:21.858954579 +0200
@@ -0,0 +1 @@
+obj-$(CONFIG_FREEBOX_PROCFS) += fbxprocfs.o
diff -Nruw linux-3.11.10-fbx/drivers/fbxwatchdog./fbxwatchdog_char.c linux-3.11.10-fbx/drivers/fbxwatchdog/fbxwatchdog_char.c
--- linux-3.11.10-fbx/drivers/fbxwatchdog./fbxwatchdog_char.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxwatchdog/fbxwatchdog_char.c	2015-06-12 14:02:21.858954579 +0200
@@ -0,0 +1,218 @@
+/*
+ * add standard char device interface for fbxwatchdog.
+ */
+
+/*
+ * XXX: results are undefined if attemps are made to access watchdog
+ * from char device interface and sysfs at the same time.
+ */
+
+#define PFX "fbxwatchdog_char: "
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include "fbxwatchdog.h"
+
+#include <asm/uaccess.h>
+
+static struct fbxwatchdog *chardev_wdt;
+static unsigned long chardev_users;
+static unsigned long default_countdown = 60 * 1000;
+static int expect_close;
+
+/*
+ * we support the WDIOF_MAGICCLOSE: is the user writes 'V' to the device,
+ * the release method will stop the watchdog.
+ */
+static int
+wdt_write(struct file *file, const char *__user buf, size_t len, loff_t *ppos)
+{
+	int i;
+
+	if (!len)
+		return 0;
+
+	for (i = 0; i < len; ++i) {
+		char c;
+
+		if (get_user(c, buf + i))
+			return -EFAULT;
+		if (c == 'V')
+			expect_close = 1;
+	}
+	if (len)
+		chardev_wdt->countdown = default_countdown;
+	return len;
+}
+
+static long
+wdt_ioctl(struct file *file,
+	  unsigned int cmd, unsigned long arg)
+{
+	static const struct watchdog_info winfo = {
+		.options		= WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
+					  WDIOF_KEEPALIVEPING,
+		.firmware_version	= 0x42,
+		.identity		= "fbxwatchdog",
+	};
+	int tmp;
+
+	switch (cmd) {
+	case WDIOC_GETSUPPORT:
+		/*
+		 * return watchdog information structure to userland.
+		 */
+		return copy_to_user((struct watchdog_info __user *)arg,
+				    &winfo, sizeof (winfo)) ? -EFAULT: 0;
+
+	case WDIOC_SETTIMEOUT:
+		/*
+		 * set watchdog timeout: if current countdown is
+		 * higher than timeout, set countdown to timeout
+		 * value.
+		 */
+		if (copy_from_user(&tmp, (void __user *) arg,
+				   sizeof (tmp))) {
+			return -EFAULT;
+		}
+		if (1000 * tmp < 0)
+			return -EINVAL;
+		default_countdown = 1000 * tmp;
+		if (chardev_wdt->countdown > default_countdown)
+			chardev_wdt->countdown = default_countdown;
+		return 0;
+
+	case WDIOC_GETTIMEOUT:
+		/*
+		 * get current timeout value.
+		 */
+		tmp = default_countdown / 1000;
+		return copy_to_user((void __user *)arg, &tmp,
+				    sizeof (tmp)) ? -EFAULT : 0;
+
+	case WDIOC_KEEPALIVE:
+		/*
+		 * ping watchdog.
+		 */
+		chardev_wdt->countdown = default_countdown;
+		return 0;
+
+	case WDIOC_GETTIMELEFT:
+		/*
+		 * return current countdown value to userland.
+		 */
+		tmp = chardev_wdt->countdown / 1000;
+		return copy_to_user((void __user *)arg, &tmp, sizeof (tmp)) ?
+		  -EFAULT : 0;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+/*
+ * called when remote process calls close(2) on watchdog fd or
+ * exit(2).
+ */
+static int
+wdt_release(struct inode *inode, struct file *file)
+{
+	unsigned long flags;
+
+	if (expect_close && chardev_wdt->enabled) {
+		spin_lock_irqsave(&chardev_wdt->lock, flags);
+		chardev_wdt->enabled = 0;
+		chardev_wdt->wdt_stop(chardev_wdt);
+		spin_unlock_irqrestore(&chardev_wdt->lock, flags);
+	} else
+		printk(KERN_CRIT PFX "unexpected close: not stopping "
+		       "watchdog.\n");
+	chardev_users = 0;
+	return 0;
+}
+
+/*
+ * open watchdog device file: the test_and_set_bit enforces the fact
+ * that only one process opens the watchdog device file as long as it
+ * does not try to fork(2). dup(2)/dup2(2) might be problematic
+ * too. thus, we assume that watchdogd will do "The right thing" and
+ * won't try to do anything too fancy with the fd opened to
+ * /dev/watchdog.
+ */
+static int wdt_open(struct inode *inode, struct file *file)
+{
+	unsigned long flags;
+
+	if (test_and_set_bit(1, &chardev_users))
+		return -EBUSY;
+
+	expect_close = 0;
+
+	/*
+	 * watchdog is to be enabled when opened.
+	 */
+	if (!chardev_wdt->enabled) {
+		spin_lock_irqsave(&chardev_wdt->lock, flags);
+		chardev_wdt->enabled = 1;
+		chardev_wdt->countdown = default_countdown;
+		chardev_wdt->wdt_start(chardev_wdt);
+		chardev_wdt->countdown_min = INT_MAX;
+		spin_unlock_irqrestore(&chardev_wdt->lock, flags);
+	}
+	return 0;
+}
+
+static struct file_operations wdt_fops = {
+	.owner		= THIS_MODULE,
+	.open		= wdt_open,
+	.write		= wdt_write,
+	.unlocked_ioctl	= wdt_ioctl,
+	.release	= wdt_release,
+};
+
+static struct miscdevice wdt_miscdev = {
+	.minor		= WATCHDOG_MINOR,
+	.name		= "watchdog",
+	.fops		= &wdt_fops,
+};
+
+/*
+ * add watchdog to the char interface. if we are already bound to a
+ * watchdog, return 0, this is not a major no-no.
+ */
+int
+fbxwatchdog_char_add(struct fbxwatchdog *wdt)
+{
+	int err;
+
+	err = misc_register(&wdt_miscdev);
+	if (err) {
+		printk("unable to register misc device.\n");
+		if (err == -EEXIST)
+			return 0;
+		return err;
+	}
+	chardev_wdt = wdt;
+	return 0;
+}
+
+/*
+ * if the watchdog is bound to the char device interface, unregister
+ * the misc device and tell that we are no more bound to a
+ * watchdog. otherwise, do nothing.
+ */
+void
+fbxwatchdog_char_remove(struct fbxwatchdog *wdt)
+{
+	if (wdt != chardev_wdt)
+		return ;
+	misc_deregister(&wdt_miscdev);
+	chardev_wdt = NULL;
+}
+
+EXPORT_SYMBOL(fbxwatchdog_char_add);
+EXPORT_SYMBOL(fbxwatchdog_char_remove);
diff -Nruw linux-3.11.10-fbx/drivers/fbxwatchdog./fbxwatchdog_core.c linux-3.11.10-fbx/drivers/fbxwatchdog/fbxwatchdog_core.c
--- linux-3.11.10-fbx/drivers/fbxwatchdog./fbxwatchdog_core.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxwatchdog/fbxwatchdog_core.c	2015-06-12 14:02:21.858954579 +0200
@@ -0,0 +1,301 @@
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/reboot.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+
+#include "fbxwatchdog.h"
+
+#define SOFTTIMER_FREQ	(HZ / 10)
+
+#define PFX "fbxwatchdog: "
+
+static struct class *fbxwatchdog_class;
+
+static ssize_t
+show_enabled(struct device *dev,
+	     struct device_attribute *attr, char *buf)
+{
+	struct fbxwatchdog *wdt;
+
+	wdt = dev_get_drvdata(dev);
+	if (!wdt) {
+		printk(KERN_DEBUG "ignoring request to dead watchdog.\n");
+		return -ENODEV;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%i\n", wdt->enabled);
+}
+
+/*
+ * start/stop watchdog depending on the value of the first character
+ * of buf. set countdown_min to a sane value.
+ */
+static ssize_t
+store_enabled(struct device *dev,
+	      struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct fbxwatchdog *wdt;
+	unsigned long flags;
+
+	wdt = dev_get_drvdata(dev);
+	if (!wdt) {
+		printk(KERN_DEBUG "ignoring request to dead watchdog.\n");
+		return -ENODEV;
+	}
+
+	if (size == 0)
+		return 0;
+
+
+	spin_lock_irqsave(&wdt->lock, flags);
+	switch (*buf) {
+	case '0':
+		if (wdt->enabled) {
+			wdt->enabled = 0;
+			wdt->wdt_stop(wdt);
+		}
+		break;
+
+	case '1':
+		if (!wdt->enabled) {
+			wdt->enabled = 1;
+			wdt->wdt_start(wdt);
+			wdt->countdown_min = INT_MAX;
+		}
+		break;
+
+	default:
+		break;
+	}
+	spin_unlock_irqrestore(&wdt->lock, flags);
+
+	return size;
+}
+
+static ssize_t
+show_countdown(struct device *dev,
+	       struct device_attribute *attr, char *buf)
+{
+	struct fbxwatchdog *wdt;
+
+	wdt = dev_get_drvdata(dev);
+	if (!wdt) {
+		printk(KERN_DEBUG "ignoring request to dead watchdog.\n");
+		return -ENODEV;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%i\n", wdt->countdown);
+}
+
+/*
+ * update watchdog countdown with the userland value given in buf.
+ */
+static ssize_t
+store_countdown(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct fbxwatchdog *wdt;
+	int countdown;
+	char *ptr;
+
+	wdt = dev_get_drvdata(dev);
+	if (!wdt) {
+		printk(KERN_DEBUG "ignoring request to dead watchdog.\n");
+		return -ENODEV;
+	}
+
+	if (size == 0)
+		return 0;
+
+	ptr = kzalloc(size + 1, GFP_KERNEL);
+	if (!ptr)
+		return -ENOMEM;
+	strlcpy(ptr, buf, size + 1);
+
+	countdown = simple_strtoul(ptr, NULL, 10);
+	wdt->countdown = countdown;
+	kfree(ptr);
+
+	return size;
+}
+
+static ssize_t
+show_countdown_min(struct device *dev,
+		   struct device_attribute *attr, char *buf)
+{
+	struct fbxwatchdog *wdt;
+
+	wdt = dev_get_drvdata(dev);
+	if (!wdt) {
+		printk(KERN_DEBUG "ignoring request to dead watchdog.\n");
+		return -ENODEV;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%i\n", wdt->countdown_min);
+}
+
+static struct device_attribute wdt_attributes[] = {
+	__ATTR(enabled, 0600, show_enabled, store_enabled),
+	__ATTR(countdown, 0600, show_countdown, store_countdown),
+	__ATTR(countdown_min, 0400, show_countdown_min, NULL),
+};
+
+/*
+ * software timer callback: decrement countdown and update
+ * countdown_min if needed. this is called 10 times per second.
+ */
+static void fbxwatchdog_timer_cb(unsigned long data)
+{
+	struct fbxwatchdog *wdt;
+
+	wdt = (struct fbxwatchdog *)data;
+
+	if (wdt->enabled) {
+		wdt->countdown -= jiffies_to_msecs(SOFTTIMER_FREQ);
+		if (wdt->countdown < wdt->countdown_min)
+			wdt->countdown_min = wdt->countdown;
+	}
+
+	wdt->timer.expires = jiffies + SOFTTIMER_FREQ;
+	add_timer(&wdt->timer);
+}
+
+/*
+ * called from half life interrupt handler, panic if countdown is too
+ * low (ie if userland has not reset countdown to before it reached
+ * 0).
+ */
+static void fbxwatchdog_halflife_cb(struct fbxwatchdog *wdt)
+{
+	if (wdt->countdown <= 0) {
+		wdt->wdt_stop(wdt);
+		panic("software fbxwatchdog triggered");
+	}
+}
+
+/*
+ * register a new watchdog device.
+ */
+int fbxwatchdog_register(struct fbxwatchdog *wdt)
+{
+	struct device *dev;
+	int i = 0, err = 0;
+
+	if (wdt == NULL)
+		return -EFAULT;
+
+	printk(KERN_INFO PFX "registering watchdog %s\n", wdt->name);
+
+	dev = device_create(fbxwatchdog_class, NULL, 0, wdt, "%s", wdt->name);
+	if (IS_ERR(dev)) {
+		printk(KERN_ERR PFX "unable to allocate device.\n");
+		err = PTR_ERR(dev);
+		goto out_error;
+	}
+	wdt->dev = dev;
+
+	for (i = 0; i < ARRAY_SIZE(wdt_attributes); i++) {
+		err = device_create_file(dev, &wdt_attributes[i]);
+		if (err)
+			goto out_error;
+	}
+
+	/* start countdown soft timer */
+	init_timer(&wdt->timer);
+	wdt->timer.function = fbxwatchdog_timer_cb;
+	wdt->timer.data = (unsigned long)wdt;
+	wdt->timer.expires = jiffies + SOFTTIMER_FREQ;
+	add_timer(&wdt->timer);
+
+	spin_lock_init(&wdt->lock);
+
+	wdt->cb = fbxwatchdog_halflife_cb;
+	err = wdt->wdt_init(wdt);
+	if (err) {
+		printk(KERN_ERR PFX "unable to do low level init of "
+		       "watchdog %s.\n", wdt->name);
+		goto out_del_timer;
+	}
+
+#ifdef CONFIG_FREEBOX_WATCHDOG_CHAR
+	err = fbxwatchdog_char_add(wdt);
+	if (err) {
+		printk(KERN_ERR PFX "unable to add %s to the fbxwatchdog char "
+		       "device interface.\n", wdt->name);
+		goto out_wdt_cleanup;
+	}
+#endif
+
+	return 0;
+
+#ifdef CONFIG_FREEBOX_WATCHDOG_CHAR
+out_wdt_cleanup:
+	wdt->wdt_cleanup(wdt);
+#endif
+
+out_del_timer:
+	del_timer_sync(&wdt->timer);
+out_error:
+	if (wdt->dev) {
+		for (; i >= 0; i--)
+			device_remove_file(dev, &wdt_attributes[i]);
+		device_unregister(dev);
+	}
+	return err;
+}
+
+int fbxwatchdog_unregister(struct fbxwatchdog *wdt)
+{
+	int i;
+
+	printk(KERN_INFO PFX "registering watchdog %s\n", wdt->name);
+
+	if (wdt->enabled) {
+		unsigned long flags;
+
+		printk(KERN_WARNING "removing enabled watchdog.\n");
+		spin_lock_irqsave(&wdt->lock, flags);
+		wdt->wdt_stop(wdt);
+		spin_unlock_irqrestore(&wdt->lock, flags);
+	}
+
+#ifdef CONFIG_FREEBOX_WATCHDOG_CHAR
+	fbxwatchdog_char_remove(wdt);
+#endif
+	wdt->wdt_cleanup(wdt);
+	del_timer_sync(&wdt->timer);
+	for (i = 0; i < ARRAY_SIZE(wdt_attributes); i++)
+		device_remove_file(wdt->dev, &wdt_attributes[i]);
+	device_unregister(wdt->dev);
+	wdt->dev = NULL;
+	return 0;
+}
+
+static int __init fbxwatchdog_init(void)
+{
+	printk(KERN_INFO PFX "2007, Freebox SA.\n");
+	fbxwatchdog_class = class_create(THIS_MODULE, "fbxwatchdog");
+	if (IS_ERR(fbxwatchdog_class))
+		return PTR_ERR(fbxwatchdog_class);
+	return 0;
+}
+
+static void __exit fbxwatchdog_exit(void)
+{
+	class_destroy(fbxwatchdog_class);
+}
+
+
+EXPORT_SYMBOL_GPL(fbxwatchdog_register);
+EXPORT_SYMBOL_GPL(fbxwatchdog_unregister);
+
+module_init(fbxwatchdog_init);
+module_exit(fbxwatchdog_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nicolas Schichan <nschichan@freebox.fr>");
+MODULE_DESCRIPTION("Freebox Watchdog Core - www.freebox.fr");
diff -Nruw linux-3.11.10-fbx/drivers/fbxwatchdog./fbxwatchdog.h linux-3.11.10-fbx/drivers/fbxwatchdog/fbxwatchdog.h
--- linux-3.11.10-fbx/drivers/fbxwatchdog./fbxwatchdog.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxwatchdog/fbxwatchdog.h	2015-06-12 14:02:21.858954579 +0200
@@ -0,0 +1,47 @@
+#ifndef FBXWATCHDOG_H
+# define FBXWATCHDOG_H
+
+struct fbxwatchdog {
+	const char *name;
+	void *priv;
+
+	int enabled;
+	int countdown;
+	int countdown_min;
+
+	int (*wdt_init)(struct fbxwatchdog *wdt);
+	int (*wdt_cleanup)(struct fbxwatchdog *wdt);
+
+	/*
+	 * wdt_start and wdt_stop are called with wdt->lock held and irq
+	 * disabled.
+	 */
+	int (*wdt_start)(struct fbxwatchdog *wdt);
+	int (*wdt_stop)(struct fbxwatchdog *wdt);
+
+	/*
+	 * cb is called from interrupt/softirq context (depends on the
+	 * underlying driver/hardware).
+	 */
+	void (*cb)(struct fbxwatchdog *wdt);
+
+	struct timer_list timer;
+
+	struct device *dev;
+
+	/*
+	 * protect interrupt handlers & start/stop methods running in
+	 * thead context.
+	 */
+	spinlock_t	lock;
+};
+
+int fbxwatchdog_register(struct fbxwatchdog *wdt);
+int fbxwatchdog_unregister(struct fbxwatchdog *wdt);
+
+#ifdef CONFIG_FREEBOX_WATCHDOG_CHAR
+int fbxwatchdog_char_add(struct fbxwatchdog *wdt);
+void fbxwatchdog_char_remove(struct fbxwatchdog *wdt);
+#endif
+
+#endif /* !FBXWATCHDOG_H */
diff -Nruw linux-3.11.10-fbx/drivers/fbxwatchdog./fbxwatchdog_orion.c linux-3.11.10-fbx/drivers/fbxwatchdog/fbxwatchdog_orion.c
--- linux-3.11.10-fbx/drivers/fbxwatchdog./fbxwatchdog_orion.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxwatchdog/fbxwatchdog_orion.c	2015-09-29 11:15:05.805732167 +0200
@@ -0,0 +1,253 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <mach/bridge-regs.h>
+
+#include "fbxwatchdog.h"
+
+#define PFX "fbxwatchdog_orion: "
+
+struct fbxwatchdog_orion_priv {
+	struct clk		*clk;
+	unsigned int		tclk;
+	void __iomem		*base;
+	struct timer_list	half_life_timer;
+};
+
+/*
+ * Watchdog timer block registers.
+ */
+#define TIMER_CTRL		(TIMER_VIRT_BASE + 0x0000)
+#define  WDT_EN			0x0010
+#define WDT_VAL			(TIMER_VIRT_BASE + 0x0024)
+
+#define WDT_MAX_CYCLE_COUNT	0xffffffff
+#define WDT_IN_USE		0
+#define WDT_OK_TO_CLOSE		1
+
+#define WDT_RESET_OUT_EN	BIT(1)
+
+/*
+ * orion does not trigger interrupts each times the watchdog reaches
+ * the half of it's count down. we emulate this behaviour using a
+ * linux timer that fires every 500 msec.
+ */
+static void
+half_life_timer_cb(unsigned long data)
+{
+	struct fbxwatchdog *wdt;
+	struct fbxwatchdog_orion_priv *priv;
+	unsigned long flags;
+
+	wdt = (struct fbxwatchdog *)data;
+	priv = wdt->priv;
+
+	spin_lock_irqsave(&wdt->lock, flags);
+
+	/* reload counter */
+	writel(priv->tclk * 10, WDT_VAL);
+
+	if (wdt->cb)
+		wdt->cb(wdt);
+
+	priv->half_life_timer.expires = jiffies + HZ / 2;
+	add_timer(&priv->half_life_timer);
+
+	spin_unlock_irqrestore(&wdt->lock, flags);
+}
+
+/*
+ * setup half life timer.
+ */
+static int orion_wdt_init(struct fbxwatchdog *wdt)
+{
+	struct fbxwatchdog_orion_priv *priv;
+
+	priv = wdt->priv;
+	init_timer(&priv->half_life_timer);
+	priv->half_life_timer.function = half_life_timer_cb;
+	priv->half_life_timer.data = (unsigned long)wdt;
+	return 0;
+}
+
+static int orion_wdt_cleanup(struct fbxwatchdog *wdt)
+{
+	return 0;
+}
+
+static int orion_wdt_start(struct fbxwatchdog *wdt)
+{
+	struct fbxwatchdog_orion_priv *priv;
+	uint32_t val;
+
+	printk(KERN_INFO PFX "starting watchdog ...\n");
+
+	priv = wdt->priv;
+	val = readl(TIMER_CTRL);
+	if (val & WDT_EN) {
+		printk(KERN_WARNING PFX "watchdog has been enabled by "
+		       "bootloader.!\n");
+		/* disable it */
+		val &= ~WDT_EN;
+		writel(val, TIMER_CTRL);
+	}
+
+	/* watchdog will blow up after 10 seconds if not refreshed */
+	writel(priv->tclk * 10, WDT_VAL);
+
+	/* enable it */
+	val = readl(TIMER_CTRL);
+	val |= WDT_EN;
+	writel(val, TIMER_CTRL);
+
+	/* enable reset on watchdog */
+	val = readl(RSTOUTn_MASK);
+	val |= WDT_RESET_OUT_EN;
+	writel(val, RSTOUTn_MASK);
+
+	/* will fire every 500 ms */
+	priv->half_life_timer.expires = jiffies + HZ / 2;
+	add_timer(&priv->half_life_timer);
+
+	return 0;
+}
+
+int orion_wdt_stop(struct fbxwatchdog *wdt)
+{
+	struct fbxwatchdog_orion_priv *priv;
+	uint32_t val;
+
+	printk(KERN_INFO PFX "stopping watchdog ...\n");
+
+	priv = wdt->priv;
+	del_timer_sync(&priv->half_life_timer);
+
+	/* disable it */
+	val = readl(TIMER_CTRL);
+	val &= ~WDT_EN;
+	writel(val, TIMER_CTRL);
+
+	/* enable reset on watchdog */
+	val = readl(RSTOUTn_MASK);
+	val |= WDT_RESET_OUT_EN;
+	writel(val, RSTOUTn_MASK);
+
+	return 0;
+}
+
+static int fbxwatchdog_platform_probe(struct platform_device *pdev)
+{
+	struct fbxwatchdog_orion_priv *priv = NULL;
+	struct fbxwatchdog *wdt;
+	struct clk *clk;
+	int err = 0;
+
+	clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "Orion Watchdog missing clock\n");
+		return -ENODEV;
+	}
+	clk_prepare_enable(clk);
+
+	wdt = kzalloc(sizeof (*wdt), GFP_KERNEL);
+	if (!wdt) {
+		printk(KERN_ERR PFX "unable allocate memory for watchdog.\n");
+		err = -ENOMEM;
+		goto out_error;
+	}
+
+	priv = kzalloc(sizeof (*priv), GFP_KERNEL);
+	if (!priv) {
+		printk(KERN_ERR PFX "unable to allocate memory for private "
+		       "structure.\n");
+		err = -ENOMEM;
+		goto out_error;
+	}
+
+	wdt->priv = priv;
+	wdt->name = pdev->name;
+
+	wdt->wdt_init = orion_wdt_init;
+	wdt->wdt_cleanup = orion_wdt_cleanup;
+	wdt->wdt_start = orion_wdt_start;
+	wdt->wdt_stop = orion_wdt_stop;
+
+	priv->tclk = clk_get_rate(clk);
+	priv->clk = clk;
+
+	err = fbxwatchdog_register(wdt);
+	if (err) {
+		printk(KERN_ERR PFX "unable to register watchdog %s\n",
+		       wdt->name);
+		goto out_error;
+	}
+
+	platform_set_drvdata(pdev, wdt);
+
+	return 0;
+
+ out_error:
+	if (wdt)
+		kfree(wdt);
+	if (priv)
+		kfree(priv);
+	clk_disable_unprepare(clk);
+	return err;
+}
+
+/*
+ * unregister and free memory allocated by the probe function.
+ */
+static int
+fbxwatchdog_platform_remove(struct platform_device *pdev)
+{
+	struct fbxwatchdog *wdt;
+	struct fbxwatchdog_orion_priv *priv;
+
+	wdt = platform_get_drvdata(pdev);
+	if (!wdt) {
+		BUG();
+		return -ENODEV;
+	}
+
+	fbxwatchdog_unregister(wdt);
+
+	priv = wdt->priv;
+	clk_disable_unprepare(priv->clk);
+	kfree(priv);
+	kfree(wdt);
+
+	return 0;
+}
+
+struct platform_driver fbxwatchdog_platform_driver = {
+	.probe	= fbxwatchdog_platform_probe,
+	.remove	= fbxwatchdog_platform_remove,
+	.driver	= {
+		.name	= "orion_fbxwdt",
+	}
+};
+
+static int __init fbxwatchdog_orion_init(void)
+{
+	platform_driver_register(&fbxwatchdog_platform_driver);
+	return 0;
+}
+
+static void __exit fbxwatchdog_orion_exit(void)
+{
+	platform_driver_unregister(&fbxwatchdog_platform_driver);
+}
+
+module_init(fbxwatchdog_orion_init);
+module_exit(fbxwatchdog_orion_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nicolas Schichan <nschichan@freebox.fr>");
+MODULE_DESCRIPTION("Freebox Watchdog, orion specific bits");
+
diff -Nruw linux-3.11.10-fbx/drivers/fbxwatchdog./Kconfig linux-3.11.10-fbx/drivers/fbxwatchdog/Kconfig
--- linux-3.11.10-fbx/drivers/fbxwatchdog./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxwatchdog/Kconfig	2015-06-12 14:02:21.858954579 +0200
@@ -0,0 +1,20 @@
+menuconfig FREEBOX_WATCHDOG
+	tristate "Freebox Watchdog"
+	default n
+
+if FREEBOX_WATCHDOG
+
+config FREEBOX_WATCHDOG_CHAR
+	bool "Freebox Watchdog char device interface."
+	default n
+
+config FREEBOX_WATCHDOG_ORION
+	tristate "Marvell Orion support"
+	depends on PLAT_ORION
+
+config FREEBOX_WATCHDOG_BCM63XX
+	tristate "Broadcom 63xx Freebox Watchdog support"
+	depends on BCM63XX
+	default n
+
+endif
diff -Nruw linux-3.11.10-fbx/drivers/fbxwatchdog./Makefile linux-3.11.10-fbx/drivers/fbxwatchdog/Makefile
--- linux-3.11.10-fbx/drivers/fbxwatchdog./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/fbxwatchdog/Makefile	2015-06-12 14:02:21.858954579 +0200
@@ -0,0 +1,9 @@
+obj-$(CONFIG_FREEBOX_WATCHDOG) += fbxwatchdog.o
+
+fbxwatchdog-objs = fbxwatchdog_core.o
+ifeq ($(CONFIG_FREEBOX_WATCHDOG_CHAR),y)
+fbxwatchdog-objs += fbxwatchdog_char.o
+endif
+
+obj-$(CONFIG_FREEBOX_WATCHDOG_ORION)	+= fbxwatchdog_orion.o
+obj-$(CONFIG_FREEBOX_WATCHDOG_BCM63XX)	+= fbxwatchdog_bcm63xx.o
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/drivers/hwmon/kirkwood-coretemp.c	2015-06-12 14:02:22.250957399 +0200
@@ -0,0 +1,168 @@
+/*
+ * kirkwood-coretemp.c for kirkwood-coretemp
+ * Created by <nschichan@freebox.fr> on Wed Jul 11 19:59:27 2012
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+
+#define PFX "kirkwood-coretemp: "
+
+struct kirkwood_coretemp_priv {
+	void __iomem *reg;
+	struct device *hwmon_dev;
+	struct attribute_group attrs;
+};
+
+static int show_kirkwood_coretemp(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct kirkwood_coretemp_priv *priv = platform_get_drvdata(pdev);
+	u32 reg;
+	u32 raw_temp;
+	s32 temp_out;
+
+	reg = readl(priv->reg);
+
+	/*
+	 * TermTValid shall be set.
+	 */
+	if ((reg & (1 << 9)) == 0)
+		return -EIO;
+
+	raw_temp = (reg >> 10) & 0x1ff;
+
+	/*
+	 * out temperature = (322 - raw) / 1.3625
+	 *
+	 * can't use float here, so be creative.
+	 *
+	 * we also have to avoid 32bit integer overflow (hence the
+	 * 1000000 / 1363 division instead of 10000000 / 13625)
+	 */
+	temp_out = (322 - raw_temp);
+	temp_out = (temp_out * 1000000) / 1363;
+
+	return sprintf(buf, "%i\n", temp_out);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_kirkwood_coretemp,
+			  NULL, 0);
+
+static int show_name(struct device *dev, struct device_attribute *devattr,
+		     char *buf)
+{
+	return sprintf(buf, "%s\n", kobject_name(&dev->kobj));
+}
+
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static struct attribute *kirkwood_coretemp_attr[] = {
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	&dev_attr_name.attr,
+	NULL,
+};
+
+static int kirkwood_coretemp_probe(struct platform_device *pdev)
+{
+	struct kirkwood_coretemp_priv *priv;
+	struct resource *resource;
+	int err = 0;
+
+	dev_dbg(&pdev->dev, "probe.\n");
+
+	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!resource)
+		return -ENXIO;
+
+	priv = kzalloc(sizeof (*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->reg = ioremap(resource->start, resource_size(resource));
+	if (!priv->reg) {
+		dev_err(&pdev->dev, "unable to ioremap registers.\n");
+		err = -ENOMEM;
+		goto err_free_priv;
+	}
+
+	priv->attrs.attrs = kirkwood_coretemp_attr;
+	err = sysfs_create_group(&pdev->dev.kobj, &priv->attrs);
+	if (err) {
+		dev_err(&pdev->dev, "unable to greate sysfs group.\n");
+		goto err_iounmap;
+	}
+
+	platform_set_drvdata(pdev, priv);
+
+	priv->hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(priv->hwmon_dev)) {
+		dev_err(&pdev->dev, "unable to register hwmon device.\n");
+		err = PTR_ERR(priv->hwmon_dev);
+		goto err_sysfs_remove_group;
+	}
+
+
+	return 0;
+
+err_sysfs_remove_group:
+	sysfs_remove_group(&pdev->dev.kobj, &priv->attrs);
+err_iounmap:
+	iounmap(priv->reg);
+err_free_priv:
+	kfree(priv);
+	return err;
+}
+
+static int kirkwood_coretemp_remove(struct platform_device *pdev)
+{
+	struct kirkwood_coretemp_priv *priv = platform_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "remove.\n");
+	hwmon_device_unregister(priv->hwmon_dev);
+	sysfs_remove_group(&pdev->dev.kobj, &priv->attrs);
+	iounmap(priv->reg);
+	kfree(priv);
+
+	return 0;
+}
+
+static struct platform_driver kirkwood_coretemp_driver = {
+	.probe		= kirkwood_coretemp_probe,
+	.remove		= kirkwood_coretemp_remove,
+	.driver		= {
+		.name	= "kirkwood-coretemp",
+	}
+};
+
+static int __init kirkwood_coretemp_init(void)
+{
+	int err;
+
+	err = platform_driver_register(&kirkwood_coretemp_driver);
+	if (err) {
+		printk(KERN_ERR PFX "unable to register platform driver.\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static void __exit kirkwood_coretemp_exit(void)
+{
+	platform_driver_unregister(&kirkwood_coretemp_driver);
+}
+
+module_init(kirkwood_coretemp_init);
+module_exit(kirkwood_coretemp_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nicolas Schichan <nschichan@freebox.fr>");
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/drivers/input/misc/smsc_cap1066.c	2015-06-12 14:02:22.438958747 +0200
@@ -0,0 +1,1075 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input-polldev.h>
+#include <linux/smsc_cap1066.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+
+#define PFX		"cap1066: "
+
+/*
+ * list of registers
+ */
+#define SMSC_REG_MAIN_CTRL		0x00
+#define SMSC_REG_BTN_STATUS1		0x03
+#define SMSC_REG_DATA_SENSITIVITY	0x1f
+#define SMSC_REG_CFG			0x20
+#define SMSC_REG_SENS_CFG		0x22
+#define SMSC_REG_MTOUCH_CFG_REG		0x2a
+#define SMSC_REG_CFG2			0x44
+#define SMSC_REG_LED_OUT_TYPE		0x71
+#define SMSC_REG_LED_LINK		0x72
+#define SMSC_REG_LED_OUTPUT_CTL		0x74
+#define SMSC_REG_LED_BEHAVIOUR1		0x81
+#define SMSC_REG_LED_BEHAVIOUR2		0x82
+#define SMSC_REG_LED_DIRECT_DCYCLE	0x93
+#define SMSC_REG_LED_DIRECT_RAMP_RATE	0x94
+#define SMSC_REG_LED_OFF_DELAY		0x95
+#define SMSC_REG_DID			0xfd
+#define SMSC_REG_VID			0xfe
+
+/*
+ * used in cap1066_init_hw and during priv initialization.
+ */
+#define DEFAULT_DUTY_CYCLE_MIN		0x4
+#define DEFAULT_DUTY_CYCLE_MAX		0xf
+#define DEFAULT_RAMP_TIME_FALL		0x1
+#define DEFAULT_RAMP_TIME_RISE		0x2
+
+/*
+ * vendor id / device id
+ */
+#define SMSC_CAP1066_VID	0x5d
+#define SMSC_CAP1066_DID	0x41
+#define SMSC_CAP1166_DID	0x51
+
+static const unsigned short normal_i2c[] = { 0x28, I2C_CLIENT_END };
+
+static const struct i2c_device_id cap1066_id[] = {
+	{ "cap1066", 0 },
+	{ }
+};
+
+/*
+ * private context
+ */
+static unsigned short default_map[CAP1066_MAX_BTNS] = {
+	BTN_0,
+	BTN_1,
+	BTN_2,
+	BTN_3,
+	BTN_4,
+	BTN_5,
+};
+
+struct led_btn_name
+{
+	int code;
+	const char *name;
+};
+
+/*
+ * whenever possible symlinks will be created from led_btn_X to
+ * led_key_y, depending on user provided keymap. add entries here as
+ * you see fit.
+ */
+static const struct led_btn_name led_btn_names[] = {
+	{ KEY_UP, "led_key_up", },
+	{ KEY_DOWN, "led_key_down", },
+	{ KEY_LEFT, "led_key_left", },
+	{ KEY_RIGHT, "led_key_right", },
+	{ KEY_ENTER, "led_key_enter", },
+};
+
+enum {
+	E_SMSC_CAP1066_LED_MODE_AUTO,
+	E_SMSC_CAP1066_LED_MODE_ON,
+	E_SMSC_CAP1066_LED_MODE_OFF,
+};
+
+struct cap1066_led_dev
+{
+	struct cap1066_priv	*parent_priv;
+	struct device		dev;
+	int			led_mode;
+	int			led_index;
+	const char		*btn_link;
+};
+
+struct cap1066_priv {
+	struct input_polled_dev *poll_dev;
+	struct i2c_client	*client;
+	unsigned short		keymap[CAP1066_MAX_BTNS];
+	struct cap1066_led_dev	*led_devices[CAP1066_MAX_BTNS];
+
+	u8			duty_cycle_min;
+	u8			duty_cycle_max;
+	u8			raw_ramp_time_fall;
+	u8			raw_ramp_time_rise;
+
+	bool			has_irq_gpio;
+	unsigned int		irq_gpio;
+};
+
+static const char *get_keycode_btn_name(int key_code)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(led_btn_names); ++i) {
+		if (key_code == led_btn_names[i].code)
+			return led_btn_names[i].name;
+	}
+	return NULL;
+}
+
+/*
+ * single register read
+ */
+static int cap1066_read_reg(struct i2c_client *client, u8 reg, u8 *val)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0) {
+		printk(KERN_ERR PFX "read failed: %d\n", ret);
+		return ret;
+	}
+
+	*val = (u8)ret;
+	return 0;
+}
+
+/*
+ * single register write
+ */
+static int cap1066_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, reg, val);
+	if (ret < 0) {
+		printk(KERN_ERR PFX "write failed: %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+/*
+ * called when an smbus device is detected, make sure it's a cap1066
+ */
+static int cap1066_detect(struct i2c_client *client,
+			  struct i2c_board_info *info)
+
+{
+	int ret;
+	u8 vid, did;
+	const char *name = NULL;
+
+	ret = cap1066_read_reg(client, SMSC_REG_VID, &vid);
+	if (ret)
+		return ret;
+
+	ret = cap1066_read_reg(client, SMSC_REG_DID, &did);
+	if (ret)
+		return ret;
+
+	if (vid != SMSC_CAP1066_VID)
+		goto no_dev;
+
+	switch (did) {
+	case SMSC_CAP1066_DID:
+		name = "cap1066";
+		break;
+	case SMSC_CAP1166_DID:
+		name = "cap1166";
+		break;
+	default:
+		goto no_dev;
+	}
+
+	printk(KERN_INFO PFX "detected SMSC %s chip\n", name);
+	if (info)
+		strlcpy(info->type, name, I2C_NAME_SIZE);
+	return 0;
+
+no_dev:
+	printk(KERN_ERR PFX "bad vid/did: 0x%04x/0x%04x\n", vid, did);
+	return -ENODEV;
+}
+
+/*
+ * reset registers value
+ */
+static int cap1066_init_hw(struct i2c_client *client)
+{
+	unsigned int i;
+	u8 did;
+	int ret;
+
+	static const u8 init_regs[] = {
+		/* power on */
+		SMSC_REG_MAIN_CTRL, 0x0,
+
+		/* default sensitivity */
+		SMSC_REG_DATA_SENSITIVITY, 0x2f,
+
+		/* max duration */
+		SMSC_REG_SENS_CFG, 0xf4,
+
+		/* default configuration */
+		SMSC_REG_CFG, 0x38,
+
+		/* open drain output on all gpios */
+		SMSC_REG_LED_OUT_TYPE, 0x00,
+
+		/* link leds with sensors */
+		SMSC_REG_LED_LINK, 0x3f,
+
+		/* setup direct mode */
+		SMSC_REG_LED_BEHAVIOUR1, 0x00,
+		SMSC_REG_LED_BEHAVIOUR2, 0x00,
+
+		/* set led duty cycle min/max to 10% => 100% */
+		SMSC_REG_LED_DIRECT_DCYCLE,
+			(DEFAULT_DUTY_CYCLE_MAX << 4) |
+			(DEFAULT_DUTY_CYCLE_MIN),
+
+		/* set ramp rate time to 500ms/250ms */
+		SMSC_REG_LED_DIRECT_RAMP_RATE,
+			(DEFAULT_RAMP_TIME_RISE << 3) |
+			(DEFAULT_RAMP_TIME_FALL),
+	};
+
+	static const u8 init_cap11_regs[] = {
+		/* default configuration2 */
+		SMSC_REG_CFG2, 0x44,
+	};
+
+	for (i = 0; i < ARRAY_SIZE(init_regs); i += 2) {
+		int ret;
+
+		ret = cap1066_write_reg(client,
+					init_regs[i], init_regs[i + 1]);
+		if (ret)
+			return ret;
+	}
+
+	ret = cap1066_read_reg(client, SMSC_REG_DID, &did);
+	if (ret)
+		return ret;
+
+	if (did != SMSC_CAP1166_DID)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(init_cap11_regs); i += 2) {
+		int ret;
+
+		ret = cap1066_write_reg(client,
+					init_cap11_regs[i],
+					init_cap11_regs[i + 1]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * input core poll callback
+ */
+static void cap1066_input_poll(struct input_polled_dev *dev)
+{
+	struct cap1066_priv *priv = dev->private;
+	struct input_dev *input = dev->input;
+	unsigned int i;
+	int ret;
+	u8 stat;
+
+	if (priv->has_irq_gpio) {
+		if (gpio_get_value(priv->irq_gpio))
+			return;
+	}
+
+	/* clear interrupt flag */
+	cap1066_write_reg(priv->client, SMSC_REG_MAIN_CTRL, 0);
+
+	ret = cap1066_read_reg(priv->client, SMSC_REG_BTN_STATUS1, &stat);
+	if (ret) {
+		printk(KERN_ERR PFX "unable to read status\n");
+		return;
+	}
+
+	for (i = 0; i < CAP1066_MAX_BTNS; i++)
+		input_report_key(input, priv->keymap[i],
+				 (stat & (1 << i)) ? 1 : 0);
+	input_sync(input);
+}
+
+#define to_cap1066_led_dev(Dev)	container_of(Dev, struct cap1066_led_dev, dev)
+
+/*
+ * called when all sysfs references to the cap1066_led_dev are gone.
+ */
+static void cap1066_led_dev_release(struct device *dev)
+{
+	struct cap1066_led_dev *led_dev;
+
+	led_dev = to_cap1066_led_dev(dev);
+	kfree(led_dev);
+}
+
+static int is_white(int c)
+{
+	return c == '\0' || c == ' ' || c == '\t' || c == '\n' || c == '\r';
+}
+
+/*
+ * store a new control value for a given cap1066_led_dev:
+ * - auto means that the led is linked to the capacitive keys
+ * - on means that the led is always on
+ * - off means that the led is always off
+ */
+static ssize_t store_control(struct device *dev,
+			     struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct cap1066_led_dev *led_dev;
+	int new_mode;
+	int read_len = 0;
+	const char *end;
+	int key;
+	u8 reg;
+	struct i2c_client *client;
+	static const char *valid_keys[] = {
+		[E_SMSC_CAP1066_LED_MODE_AUTO] = "auto",
+		[E_SMSC_CAP1066_LED_MODE_ON] = "on",
+		[E_SMSC_CAP1066_LED_MODE_OFF] = "off",
+	};
+
+
+	led_dev = to_cap1066_led_dev(dev);
+	client = led_dev->parent_priv->client;
+
+	if (is_white(*buf))
+		/*
+		 * eat white spaces silently, upper layer will call us
+		 * again.
+		 */
+		return 1;
+
+	for (key = 0; key < ARRAY_SIZE(valid_keys); ++key) {
+		if (count < strlen(valid_keys[key]))
+			continue ;
+		if (!strncmp(buf, valid_keys[key], strlen(valid_keys[key]))) {
+			break;
+		}
+	}
+
+	if (key == ARRAY_SIZE(valid_keys)) {
+		/*
+		 * end of valid_keys array reached and nothing valid
+		 * was recognized.
+		 */
+		printk(KERN_ERR PFX "invalid control value.\n");
+		return -EINVAL;
+	}
+	new_mode = key;
+	read_len = strlen(valid_keys[key]);
+
+	/*
+	 * check that no garbage is present at end of input.
+	 */
+	end = buf + read_len;
+	if (end < buf + count && !is_white(*end)) {
+		/*
+		 * garbage at end of input.
+		 */
+		printk(KERN_ERR PFX "garbage at end of value for led "
+		       "control.\n");
+		return -EINVAL;
+	}
+
+	if (new_mode == led_dev->led_mode)
+		return read_len;
+
+	if (new_mode == E_SMSC_CAP1066_LED_MODE_AUTO) {
+		cap1066_read_reg(client, SMSC_REG_LED_LINK, &reg);
+		reg |= (1 << led_dev->led_index);
+		cap1066_write_reg(client, SMSC_REG_LED_LINK, reg);
+	} else {
+		cap1066_read_reg(client, SMSC_REG_LED_LINK, &reg);
+		reg &= ~(1 << led_dev->led_index);
+		cap1066_write_reg(client, SMSC_REG_LED_LINK, reg);
+
+		cap1066_read_reg(client, SMSC_REG_LED_OUTPUT_CTL, &reg);
+		if (new_mode == E_SMSC_CAP1066_LED_MODE_ON)
+			reg |= (1 << led_dev->led_index);
+		else
+			reg &= ~(1 << led_dev->led_index);
+		cap1066_write_reg(client, SMSC_REG_LED_OUTPUT_CTL, reg);
+	}
+	led_dev->led_mode = new_mode;
+
+	return read_len;
+}
+
+static ssize_t show_control(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	struct cap1066_led_dev *led_dev;
+	const char *str;
+
+	led_dev = to_cap1066_led_dev(dev);
+	switch (led_dev->led_mode) {
+	case E_SMSC_CAP1066_LED_MODE_AUTO:
+		str = "auto";
+		break;
+
+	case E_SMSC_CAP1066_LED_MODE_ON:
+		str = "on";
+		break;
+
+	case E_SMSC_CAP1066_LED_MODE_OFF:
+		str = "off";
+		break;
+
+	default:
+		str = "invalid";
+		break;
+	}
+
+	return sprintf(buf, "%s\n", str);
+}
+
+static DEVICE_ATTR(control, S_IWUSR | S_IRUSR, show_control, store_control);
+
+static struct device_attribute *cap1066_led_dev_attrs[] = {
+	&dev_attr_control,
+};
+
+/*
+ * helper used to create all attributes given in the attr array.
+ *
+ * if something goes wrong during creation, remove attributes that
+ * have already been created.
+ */
+static int create_sysfs_files(struct device *dev,
+			      struct device_attribute **attrs,
+			      size_t count)
+{
+	int created;
+	int error = 0;
+
+	for (created = 0; created < count; ++created) {
+		error = device_create_file(dev, attrs[created]);
+		if (error)
+			break;
+	}
+
+	if (!error)
+		/*
+		 * no errors, can return.
+		 */
+		return 0;
+
+	/*
+	 * errors during creation, remove already created
+	 * files.
+	 */
+	while (--created >= 0)
+		device_remove_file(dev, attrs[created]);
+
+	return error;
+}
+
+/*
+ * create a led device. This will create a new directory in the sysfs
+ * base of the parent. a symlink will be created if a button name is
+ * found via get_keycode_btn_name().
+ */
+static struct cap1066_led_dev *cap1066_create_led_dev(struct device *parent,
+						      struct cap1066_priv *priv,
+						      int index, int key_code)
+{
+	struct cap1066_led_dev *dev;
+	int error = 0;
+
+	dev = kzalloc(sizeof (*dev), GFP_KERNEL);
+	if (!dev)
+		return NULL;
+
+	dev->led_index = index;
+	dev->parent_priv = priv;
+	dev_set_name(&dev->dev, "led_btn_%i", index);
+	dev->dev.release = cap1066_led_dev_release;
+	dev->dev.parent = parent;
+	if (device_register(&dev->dev) < 0) {
+		kfree(dev);
+		return NULL;
+	}
+
+	/*
+	 * create sysfs attributes.
+	 */
+	error = create_sysfs_files(&dev->dev, cap1066_led_dev_attrs,
+				   ARRAY_SIZE(cap1066_led_dev_attrs));
+	if (error) {
+		device_unregister(&dev->dev);
+		return NULL;
+	}
+
+	/*
+	 * create sysfs symlinks to friendly names, wherever possible.
+	 */
+	dev->btn_link = get_keycode_btn_name(key_code);
+	if (dev->btn_link) {
+		error = sysfs_create_link(&parent->kobj, &dev->dev.kobj,
+					  dev->btn_link);
+		if (error)
+			dev->btn_link = NULL;
+	}
+	return dev;
+}
+
+static void cap1066_remove_led_dev(struct cap1066_led_dev *dev)
+{
+	int i;
+
+	if (dev->btn_link)
+		sysfs_remove_link(&dev->dev.parent->kobj, dev->btn_link);
+
+	for (i = 0; i < ARRAY_SIZE(cap1066_led_dev_attrs); ++i)
+		device_remove_file(&dev->dev, cap1066_led_dev_attrs[i]);
+	device_unregister(&dev->dev);
+
+	/*
+	 * dev->release() kfree the cap1066_led_dev struct
+	 */
+}
+
+/*
+ * helper to exctract an unsigned long from the buffer given in
+ * parameter.
+ *
+ * first store buf in a zero terminated string and strtoul() it.
+ */
+static int get_ulong(const char *buf, size_t count, unsigned long *ret)
+{
+	char local_buf[32];
+	unsigned long val;
+	const char *end;
+
+	strncpy(local_buf, buf, min(count , sizeof (local_buf)));
+	local_buf[min(count, sizeof (local_buf) - 1)] = 0;
+
+	val = simple_strtoul(local_buf, (char**)&end, 0);
+	if (!is_white(*end))
+		/*
+		 * garbage after end of input.
+		 */
+		return -EINVAL;
+
+	*ret = val;
+
+	return 0;
+}
+
+/*
+ * duty cycle sysfs callbacks: things may not work as expected if
+ * duty_cycle_min is >= duty_cycle_max.
+ *
+ * values that can be written in duty_cycle_max/duty_cycle_min
+ * attributes can be on the range [0, 16 [.
+ *
+ * 0 means the lowest possible pwm duty cycle.
+ * 1 means the highest possible pwm duty cycle.
+ */
+
+static ssize_t store_duty_cycle_min(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	unsigned long val;
+	struct i2c_client *client;
+	struct cap1066_priv *priv;
+	int error;
+	u8 reg;
+
+	client = to_i2c_client(dev);
+	priv = i2c_get_clientdata(client);
+
+	if (is_white(*buf))
+		return 1;
+
+	error = get_ulong(buf, count, &val);
+	if (error)
+		return error;
+
+	if (val > 0xf)
+		return -ERANGE;
+
+	cap1066_read_reg(client, SMSC_REG_LED_DIRECT_DCYCLE, &reg);
+	reg &= ~0xf;
+	reg |= val;
+	cap1066_write_reg(client, SMSC_REG_LED_DIRECT_DCYCLE, reg);
+
+	priv->duty_cycle_min = val;
+
+	pr_debug(PFX "store_duty_cycle_min: reg = 0x%02x\n", reg);
+	return count;
+}
+
+static ssize_t show_duty_cycle_min(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct i2c_client *client;
+	struct cap1066_priv *priv;
+
+	client = to_i2c_client(dev);
+	priv = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%u\n", priv->duty_cycle_min);
+}
+
+static ssize_t store_duty_cycle_max(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	unsigned long val;
+	struct i2c_client *client;
+	struct cap1066_priv *priv;
+	int error;
+	u8 reg;
+
+	client = to_i2c_client(dev);
+	priv = i2c_get_clientdata(client);
+
+	if (is_white(*buf))
+		return 1;
+
+	error = get_ulong(buf, count, &val);
+	if (error)
+		return error;
+
+	if (val > 0xf)
+		return -ERANGE;
+
+	cap1066_read_reg(client, SMSC_REG_LED_DIRECT_DCYCLE, &reg);
+	reg &= ~0xf0;
+	reg |= val << 4;
+	cap1066_write_reg(client, SMSC_REG_LED_DIRECT_DCYCLE, reg);
+
+	priv->duty_cycle_max = val;
+
+	pr_debug(PFX "store_duty_cycle_max: reg = 0x%02x\n", reg);
+	return count;
+}
+
+static ssize_t show_duty_cycle_max(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct i2c_client *client;
+	struct cap1066_priv *priv;
+
+	client = to_i2c_client(dev);
+	priv = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%u\n", priv->duty_cycle_max);
+}
+
+/*
+ * convert millisecond value to a "raw" value ready to be written to
+ * the register.
+ */
+static u8 msec_to_raw_ramp_time(unsigned long msec)
+{
+	u8 ret;
+
+	if (msec <= 1500)
+		/*
+		 * register handles 250 msec increments if below 1500
+		 * msec.
+		 */
+		ret = msec / 250;
+	else
+		/*
+		 * there is no 1750 msec step, and 2000 msec is
+		 * encoded as 0x7.
+		 */
+		ret = 0x7;
+
+	return ret;
+}
+
+/*
+ * convert raw register value to a millisecond value.
+ */
+static unsigned long raw_ramp_time_to_msec(u8 raw)
+{
+	unsigned long ret;
+
+	if (raw < 7)
+		ret = 250 * raw;
+	else
+		ret = 2000;
+
+	return ret;
+}
+
+/*
+ * ramp time sysfs callbacks. delays are not reliable if programmed
+ * want time is higher than 1000 msec.
+ *
+ * values that can be written are on the range [0, 2000] and are given
+ * in milliseconds. Values higher than 2000 are clamped to 2000. shown
+ * values are rounded up to the next value supported by the hardware.
+ */
+
+static ssize_t store_ramp_time_rise(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	unsigned long val;
+	u8 raw_val;
+	struct i2c_client *client;
+	struct cap1066_priv *priv;
+	int error;
+	u8 reg;
+
+	client = to_i2c_client(dev);
+	priv = i2c_get_clientdata(client);
+
+	if (is_white(*buf))
+		return 1;
+
+	error = get_ulong(buf, count, &val);
+	if (error)
+		return error;
+
+	raw_val = msec_to_raw_ramp_time(val);
+
+	cap1066_read_reg(client, SMSC_REG_LED_DIRECT_RAMP_RATE, &reg);
+	reg &= ~(0x7 << 3);
+	reg |= raw_val << 3;
+	cap1066_write_reg(client, SMSC_REG_LED_DIRECT_RAMP_RATE, reg);
+
+	priv->raw_ramp_time_rise = raw_val;
+
+	pr_debug(PFX "store_ramp_time_rise: reg = %02x\n", reg);
+	return count;
+}
+
+static ssize_t show_ramp_time_rise(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct i2c_client *client;
+	struct cap1066_priv *priv;
+	unsigned long msec;
+
+	client = to_i2c_client(dev);
+	priv = i2c_get_clientdata(client);
+
+	msec = raw_ramp_time_to_msec(priv->raw_ramp_time_rise);
+
+	return sprintf(buf, "%lu\n", msec);
+}
+
+static ssize_t store_ramp_time_fall(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	unsigned long val;
+	u8 raw_val;
+	struct i2c_client *client;
+	struct cap1066_priv *priv;
+	int error;
+	u8 reg;
+
+	client = to_i2c_client(dev);
+	priv = i2c_get_clientdata(client);
+
+	if (is_white(*buf))
+		return 1;
+
+	error = get_ulong(buf, count, &val);
+	if (error)
+		return error;
+
+	raw_val = msec_to_raw_ramp_time(val);
+
+	cap1066_read_reg(client, SMSC_REG_LED_DIRECT_RAMP_RATE, &reg);
+	reg &= ~0x7;
+	reg |= raw_val;
+	cap1066_write_reg(client, SMSC_REG_LED_DIRECT_RAMP_RATE, reg);
+
+	priv->raw_ramp_time_fall = raw_val;
+
+	pr_debug(PFX "store_ramp_time_rise: reg = %02x\n", reg);
+	return count;
+}
+
+static ssize_t show_ramp_time_fall(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct i2c_client *client;
+	struct cap1066_priv *priv;
+	unsigned long msec;
+
+	client = to_i2c_client(dev);
+	priv = i2c_get_clientdata(client);
+
+	msec = raw_ramp_time_to_msec(priv->raw_ramp_time_fall);
+
+	return sprintf(buf, "%lu\n", msec);
+}
+
+#define MTOUCH_ENABLE		(1 << 7)
+#define MTOUCH_COUNT_MASK	(3 << 2)
+#define MTOUCH_COUNT_SHIFT	(2)
+
+/*
+ * touch limit handling: the hardware can report at most 1 to 4 key
+ * press event or no limit at all.
+ *
+ * Accepted values in touch_limit attribte:
+ * 0 -> no limit
+ * [1, 4] -> limit to the indicated count
+ * [4, +inf [ -> invalid
+ */
+static ssize_t store_touch_limit(struct device *dev,
+				 struct device_attribute *attr, const char *buf,
+				 size_t count)
+{
+	struct i2c_client *client;
+	unsigned long limit;
+	int err;
+	u8 mtouch_reg;
+
+	client = to_i2c_client(dev);
+
+	if (is_white(*buf))
+		return 1;
+
+	err = get_ulong(buf, count, &limit);
+	if (err)
+		return err;
+
+	if (limit > 4)
+		return -EINVAL;
+
+	if (limit == 0) {
+		mtouch_reg = 0;
+	} else {
+		mtouch_reg = MTOUCH_ENABLE |
+			((limit - 1) << MTOUCH_COUNT_SHIFT);
+	}
+	cap1066_write_reg(client, SMSC_REG_MTOUCH_CFG_REG, mtouch_reg);
+
+	return count;
+}
+
+static ssize_t show_touch_limit(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct i2c_client *client;
+	u8 mtouch_reg;
+
+	client = to_i2c_client(dev);
+
+	cap1066_read_reg(client, SMSC_REG_MTOUCH_CFG_REG, &mtouch_reg);
+
+	if (mtouch_reg & MTOUCH_ENABLE) {
+		u8 count = (mtouch_reg & MTOUCH_COUNT_MASK) >>
+			MTOUCH_COUNT_SHIFT;
+		return sprintf(buf, "%d\n", count + 1);
+	} else {
+		return sprintf(buf, "0\n");
+	}
+}
+
+static DEVICE_ATTR(duty_cycle_min, S_IRUSR | S_IWUSR, show_duty_cycle_min,
+		   store_duty_cycle_min);
+
+static DEVICE_ATTR(duty_cycle_max, S_IRUSR | S_IWUSR, show_duty_cycle_max,
+		   store_duty_cycle_max);
+
+static DEVICE_ATTR(ramp_time_rise, S_IRUSR | S_IWUSR, show_ramp_time_rise,
+		   store_ramp_time_rise);
+
+static DEVICE_ATTR(ramp_time_fall, S_IRUSR | S_IWUSR, show_ramp_time_fall,
+		   store_ramp_time_fall);
+
+static DEVICE_ATTR(touch_limit, S_IWUSR | S_IRUSR, show_touch_limit,
+		   store_touch_limit);
+
+static struct device_attribute *cap1066_base_attributes[] = {
+	&dev_attr_duty_cycle_min,
+	&dev_attr_duty_cycle_max,
+	&dev_attr_ramp_time_rise,
+	&dev_attr_ramp_time_fall,
+	&dev_attr_touch_limit,
+};
+
+
+/*
+ * i2c core probe callback, called after sucessful detect
+ */
+static int cap1066_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct input_polled_dev *poll_dev;
+	struct input_dev *input;
+	struct cap1066_priv *priv;
+	int ret, i;
+
+	ret = cap1066_detect(client, NULL);
+	if (ret)
+		dev_warn(&client->dev, "unknown cap1x66 device.\n");
+
+	/* initialize controller */
+	ret = cap1066_init_hw(client);
+	if (ret)
+		return ret;
+
+	/* allocate context */
+	priv = kzalloc(sizeof (*priv), GFP_KERNEL);
+	poll_dev = input_allocate_polled_device();
+	if (!priv || !poll_dev) {
+		ret = -ENOMEM;
+		goto out_fail;
+	}
+
+	/*
+	 * must match what has been setup in cap1066_init_hw().
+	 */
+	priv->duty_cycle_min = DEFAULT_DUTY_CYCLE_MIN;
+	priv->duty_cycle_max = DEFAULT_DUTY_CYCLE_MAX;
+	priv->raw_ramp_time_rise = DEFAULT_RAMP_TIME_RISE;
+	priv->raw_ramp_time_fall = DEFAULT_RAMP_TIME_FALL;
+
+	if (client->dev.platform_data) {
+		struct smsc_cap1066_pdata *pdata;
+
+		pdata = client->dev.platform_data;
+		memcpy(priv->keymap, pdata->key_map, sizeof (pdata->key_map));
+		priv->has_irq_gpio = pdata->has_irq_gpio;
+		priv->irq_gpio = pdata->irq_gpio;
+	} else
+		memcpy(priv->keymap, default_map, sizeof (default_map));
+
+	if (priv->has_irq_gpio)
+		gpio_direction_input(priv->irq_gpio);
+
+	priv->poll_dev = poll_dev;
+	priv->client = client;
+
+	poll_dev->private = priv;
+	poll_dev->poll = cap1066_input_poll;
+	poll_dev->poll_interval = 50 /* ms */;
+
+	input = poll_dev->input;
+	input->name = "smsc_cap1066";
+	input->phys = "smsc_cap1066/input0";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &client->dev;
+
+	input->keycode = priv->keymap;
+	input->keycodemax = ARRAY_SIZE(priv->keymap);
+	input->keycodesize = sizeof (unsigned short);
+
+	set_bit(EV_REP, input->evbit);
+	set_bit(EV_KEY, input->evbit);
+	for (i = 0; i < ARRAY_SIZE(priv->keymap); i++)
+		set_bit(priv->keymap[i], input->keybit);
+
+	i2c_set_clientdata(client, priv);
+
+	ret = input_register_polled_device(poll_dev);
+	if (ret)
+		goto out_fail;
+
+	for (i = 0; i < ARRAY_SIZE(priv->keymap); ++i) {
+		if (!priv->keymap[i])
+			continue;
+		priv->led_devices[i] =
+			cap1066_create_led_dev(&client->dev,
+					       priv, i, priv->keymap[i]);
+	}
+
+	if (create_sysfs_files(&client->dev, cap1066_base_attributes,
+			       ARRAY_SIZE(cap1066_base_attributes)) < 0)
+		goto out_free_led_devs;
+
+	return 0;
+
+out_free_led_devs:
+	for (i = 0; i < ARRAY_SIZE(priv->keymap); ++i)
+		if (priv->led_devices[i])
+			cap1066_remove_led_dev(priv->led_devices[i]);
+out_fail:
+	input_free_polled_device(poll_dev);
+	kfree(priv);
+	i2c_set_clientdata(client, NULL);
+	return ret;
+}
+
+/*
+ * i2c core remove callback
+ */
+static int cap1066_remove(struct i2c_client *client)
+{
+	int i;
+	struct cap1066_priv *priv = i2c_get_clientdata(client);
+
+	for (i = 0; i < ARRAY_SIZE(cap1066_base_attributes); ++i)
+		device_remove_file(&client->dev, cap1066_base_attributes[i]);
+
+	for (i = 0; i < ARRAY_SIZE(priv->keymap); ++i) {
+		if (priv->led_devices[i])
+			cap1066_remove_led_dev(priv->led_devices[i]);
+	}
+
+	input_unregister_polled_device(priv->poll_dev);
+	input_free_polled_device(priv->poll_dev);
+	kfree(priv);
+
+	return 0;
+}
+
+static struct i2c_driver cap1066_driver = {
+	.driver = {
+		.name	= "cap1066",
+	},
+	.probe		= cap1066_probe,
+	.remove		= cap1066_remove,
+	.id_table	= cap1066_id,
+
+	.detect		= cap1066_detect,
+	.class		= I2C_CLASS_HWMON,
+	.address_list	= normal_i2c,
+};
+
+static int __init cap1066_init(void)
+{
+	return i2c_add_driver(&cap1066_driver);
+}
+
+static void __exit cap1066_exit(void)
+{
+	i2c_del_driver(&cap1066_driver);
+}
+
+
+MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");
+MODULE_DESCRIPTION("SMSC CAP1066 driver");
+MODULE_LICENSE("GPL");
+
+module_init(cap1066_init);
+module_exit(cap1066_exit);
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/drivers/md/dm-builtin.c	2014-07-29 21:29:42.609855663 +0200
@@ -0,0 +1,48 @@
+#include "dm.h"
+
+/*
+ * The kobject release method must not be placed in the module itself,
+ * otherwise we are subject to module unload races.
+ *
+ * The release method is called when the last reference to the kobject is
+ * dropped. It may be called by any other kernel code that drops the last
+ * reference.
+ *
+ * The release method suffers from module unload race. We may prevent the
+ * module from being unloaded at the start of the release method (using
+ * increased module reference count or synchronizing against the release
+ * method), however there is no way to prevent the module from being
+ * unloaded at the end of the release method.
+ *
+ * If this code were placed in the dm module, the following race may
+ * happen:
+ *  1. Some other process takes a reference to dm kobject
+ *  2. The user issues ioctl function to unload the dm device
+ *  3. dm_sysfs_exit calls kobject_put, however the object is not released
+ *     because of the other reference taken at step 1
+ *  4. dm_sysfs_exit waits on the completion
+ *  5. The other process that took the reference in step 1 drops it,
+ *     dm_kobject_release is called from this process
+ *  6. dm_kobject_release calls complete()
+ *  7. a reschedule happens before dm_kobject_release returns
+ *  8. dm_sysfs_exit continues, the dm device is unloaded, module reference
+ *     count is decremented
+ *  9. The user unloads the dm module
+ * 10. The other process that was rescheduled in step 7 continues to run,
+ *     it is now executing code in unloaded module, so it crashes
+ *
+ * Note that if the process that takes the foreign reference to dm kobject
+ * has a low priority and the system is sufficiently loaded with
+ * higher-priority processes that prevent the low-priority process from
+ * being scheduled long enough, this bug may really happen.
+ *
+ * In order to fix this module unload race, we place the release method
+ * into a helper code that is compiled directly into the kernel.
+ */
+
+void dm_kobject_release(struct kobject *kobj)
+{
+	complete(dm_get_completion_from_kobject(kobj));
+}
+
+EXPORT_SYMBOL(dm_kobject_release);
diff -Nruw linux-3.11.10-fbx/drivers/media/platform/tango2./Kconfig linux-3.11.10-fbx/drivers/media/platform/tango2/Kconfig
--- linux-3.11.10-fbx/drivers/media/platform/tango2./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/platform/tango2/Kconfig	2015-06-12 14:02:22.802961370 +0200
@@ -0,0 +1,11 @@
+config DVB_TANGO2
+	tristate "Tango2 DVB adapter"
+	depends on ARCH_FBX5_B
+	select I2C
+	select I2C_ALGOBIT
+	select DVB_TDA1004X
+	select DVB_PLL
+
+config DVB_TANGO2_TESTBED
+	bool "extended testing and useful error codes"
+	depends on DVB_TANGO2
diff -Nruw linux-3.11.10-fbx/drivers/media/platform/tango2./Makefile linux-3.11.10-fbx/drivers/media/platform/tango2/Makefile
--- linux-3.11.10-fbx/drivers/media/platform/tango2./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/media/platform/tango2/Makefile	2015-06-12 14:02:22.802961370 +0200
@@ -0,0 +1,5 @@
+obj-$(CONFIG_DVB_TANGO2) = tango2_dvb.o
+
+tango2_dvb-objs := tango2.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/
diff -Nruw linux-3.11.10-fbx/drivers/misc/hdmi-cec./Kconfig linux-3.11.10-fbx/drivers/misc/hdmi-cec/Kconfig
--- linux-3.11.10-fbx/drivers/misc/hdmi-cec./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/misc/hdmi-cec/Kconfig	2015-06-12 14:02:23.002962808 +0200
@@ -0,0 +1,15 @@
+menu "HDMI CEC support"
+
+config HDMI_CEC
+	tristate "HDMI CEC (Consumer Electronics Control) support"
+	---help---
+	   HDMI Consumer Electronics Control support.
+
+config HDMI_CEC_REMOTI
+	tristate "RemoTI CEC driver"
+	depends on HDMI_CEC
+	select REMOTI
+	---help---
+	   HDMI CEC driver using RemoTI IPCs.
+
+endmenu
diff -Nruw linux-3.11.10-fbx/drivers/misc/hdmi-cec./Makefile linux-3.11.10-fbx/drivers/misc/hdmi-cec/Makefile
--- linux-3.11.10-fbx/drivers/misc/hdmi-cec./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/misc/hdmi-cec/Makefile	2015-06-12 14:02:23.002962808 +0200
@@ -0,0 +1,6 @@
+obj-$(CONFIG_HDMI_CEC)		+= hdmi-cec.o
+hdmi-cec-objs			+= core.o dev.o
+
+# drivers
+obj-$(CONFIG_HDMI_CEC_REMOTI)	+= remoti-cec.o
+remoti-cec-objs			:= remoti.o
diff -Nruw linux-3.11.10-fbx/drivers/misc/remoti./Kconfig linux-3.11.10-fbx/drivers/misc/remoti/Kconfig
--- linux-3.11.10-fbx/drivers/misc/remoti./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/misc/remoti/Kconfig	2015-06-12 14:02:23.010962866 +0200
@@ -0,0 +1,26 @@
+menu "RemoTI support"
+
+config REMOTI
+	tristate "RemoTI support"
+	depends on FBX6HD
+	---help---
+	  Texas Instruments RemoTI stack.
+
+config REMOTI_LEDS
+	tristate "RemoTI LEDS support"
+	depends on REMOTI
+	depends on LEDS_CLASS
+	---help---
+	  RemoTI LEDS class driver support.
+
+config REMOTI_GPIO
+	tristate "RemoTI gpio support"
+	depends on REMOTI
+	---help---
+	  gpiochip driver for the RemoTI RNP
+
+config REMOTI_USER
+	tristate "RemoTI userspace access"
+	depends on REMOTI
+
+endmenu
diff -Nruw linux-3.11.10-fbx/drivers/misc/remoti./Makefile linux-3.11.10-fbx/drivers/misc/remoti/Makefile
--- linux-3.11.10-fbx/drivers/misc/remoti./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/misc/remoti/Makefile	2015-06-12 14:02:23.010962866 +0200
@@ -0,0 +1,9 @@
+obj-$(CONFIG_REMOTI)		+= remoti.o
+obj-$(CONFIG_REMOTI_GPIO)	+= remoti-gpio.o
+obj-$(CONFIG_REMOTI_LEDS)	+= remoti-leds.o
+obj-$(CONFIG_REMOTI_USER)	+= remoti-user.o
+
+remoti-objs			:= core.o core-sysfs.o
+remoti-gpio-objs		:= gpio.o
+remoti-leds-objs		:= leds.o
+remoti-user-objs		:= user.o
diff -Nruw linux-3.11.10-fbx/drivers/net/ethernet/sigma./Kconfig linux-3.11.10-fbx/drivers/net/ethernet/sigma/Kconfig
--- linux-3.11.10-fbx/drivers/net/ethernet/sigma./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/ethernet/sigma/Kconfig	2015-09-29 11:15:06.833740001 +0200
@@ -0,0 +1,24 @@
+#
+# Marvell device configuration
+#
+
+config NET_VENDOR_SIGMA
+	bool "Sigma Design devices"
+	default y
+	depends on TANGO2
+
+if NET_VENDOR_SIGMA
+
+config TANGO2_ENET
+	tristate "SMP863x Builtin Ethernet support"
+	select MII
+	select CRC32
+	help
+	 This option adds support for the SMP863x integrated Ethernet
+	 controller.  This driver uses NAPI and generic Linux MII
+	 support.
+
+config TANGO2_PCINET_H
+	tristate "SMP863x network over PCI support (smp863x side)"
+
+endif # NET_VENDOR_SIGMA
diff -Nruw linux-3.11.10-fbx/drivers/net/ethernet/sigma./Makefile linux-3.11.10-fbx/drivers/net/ethernet/sigma/Makefile
--- linux-3.11.10-fbx/drivers/net/ethernet/sigma./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/ethernet/sigma/Makefile	2015-09-29 11:15:06.833740001 +0200
@@ -0,0 +1,2 @@
+obj-$(CONFIG_TANGO2_ENET) += tango2_enet/tango2_enet.o
+obj-$(CONFIG_TANGO2_PCINET_H) += tango2_pcinet/tango2_pcinet_h.o
diff -Nruw linux-3.11.10-fbx/drivers/net/ethernet/wintegra./Kconfig linux-3.11.10-fbx/drivers/net/ethernet/wintegra/Kconfig
--- linux-3.11.10-fbx/drivers/net/ethernet/wintegra./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/ethernet/wintegra/Kconfig	2015-06-12 14:02:23.926969451 +0200
@@ -0,0 +1,10 @@
+config NET_VENDOR_WINTEGRA
+	bool
+
+config WINTEGRA_WINPATH3_ETH
+	tristate "Wintegra Winpath3 internal mac support"
+	depends on WINTEGRA_WINPATH3
+	select NET_VENDOR_WINTEGRA
+	select NET_CORE
+	select MII
+	select PHYLIB
diff -Nruw linux-3.11.10-fbx/drivers/net/ethernet/wintegra./Makefile linux-3.11.10-fbx/drivers/net/ethernet/wintegra/Makefile
--- linux-3.11.10-fbx/drivers/net/ethernet/wintegra./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/net/ethernet/wintegra/Makefile	2015-06-12 14:02:23.926969451 +0200
@@ -0,0 +1 @@
+obj-$(CONFIG_WINTEGRA_WINPATH3_ETH) += wp3_eth.o
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/dfs_pattern_detector.c	2015-09-29 11:15:06.961740969 +0200
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include "dfs_pattern_detector.h"
+#include "dfs_pri_detector.h"
+#include "ath.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 *radar_types;
+};
+
+/* 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)
+/* percentage of pulse width tolerance */
+#define WIDTH_TOLERANCE 5
+#define WIDTH_LOWER(X) ((X*(100-WIDTH_TOLERANCE)+50)/100)
+#define WIDTH_UPPER(X) ((X*(100+WIDTH_TOLERANCE)+50)/100)
+
+#define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB)	\
+{								\
+	ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),		\
+	(PRF2PRI(PMAX) - PRI_TOLERANCE),			\
+	(PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF,	\
+	PPB_THRESH(PPB), PRI_TOLERANCE,				\
+}
+
+/* radar types as defined by ETSI EN-301-893 v1.5.1 */
+static const struct radar_detector_specs etsi_radar_ref_types_v15[] = {
+	ETSI_PATTERN(0,  0,  1,  700,  700, 1, 18),
+	ETSI_PATTERN(1,  0,  5,  200, 1000, 1, 10),
+	ETSI_PATTERN(2,  0, 15,  200, 1600, 1, 15),
+	ETSI_PATTERN(3,  0, 15, 2300, 4000, 1, 25),
+	ETSI_PATTERN(4, 20, 30, 2000, 4000, 1, 20),
+	ETSI_PATTERN(5,  0,  2,  300,  400, 3, 10),
+	ETSI_PATTERN(6,  0,  2,  400, 1200, 3, 15),
+};
+
+static const struct radar_types etsi_radar_types_v15 = {
+	.region			= NL80211_DFS_ETSI,
+	.num_radar_types	= ARRAY_SIZE(etsi_radar_ref_types_v15),
+	.radar_types		= etsi_radar_ref_types_v15,
+};
+
+/* for now, we support ETSI radar types, FCC and JP are TODO */
+static const struct radar_types *dfs_domains[] = {
+	&etsi_radar_types_v15,
+};
+
+/**
+ * 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;
+}
+
+/**
+ * struct channel_detector - detector elements for a DFS channel
+ * @head: list_head
+ * @freq: frequency for this channel detector in MHz
+ * @detectors: array of dynamically created detector elements for this freq
+ *
+ * Channel detectors are required to provide multi-channel DFS detection, e.g.
+ * to support off-channel scanning. A pattern detector has a list of channels
+ * radar pulses have been reported for in the past.
+ */
+struct channel_detector {
+	struct list_head head;
+	u16 freq;
+	struct pri_detector **detectors;
+};
+
+/* channel_detector_reset() - reset detector lines for a given channel */
+static void channel_detector_reset(struct dfs_pattern_detector *dpd,
+				   struct channel_detector *cd)
+{
+	u32 i;
+	if (cd == NULL)
+		return;
+	for (i = 0; i < dpd->num_radar_types; i++)
+		cd->detectors[i]->reset(cd->detectors[i], dpd->last_pulse_ts);
+}
+
+/* channel_detector_exit() - destructor */
+static void channel_detector_exit(struct dfs_pattern_detector *dpd,
+				  struct channel_detector *cd)
+{
+	u32 i;
+	if (cd == NULL)
+		return;
+	list_del(&cd->head);
+	for (i = 0; i < dpd->num_radar_types; i++) {
+		struct pri_detector *de = cd->detectors[i];
+		if (de != NULL)
+			de->exit(de);
+	}
+	kfree(cd->detectors);
+	kfree(cd);
+}
+
+static struct channel_detector *
+channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq)
+{
+	u32 sz, i;
+	struct channel_detector *cd;
+
+	cd = kmalloc(sizeof(*cd), GFP_ATOMIC);
+	if (cd == NULL)
+		goto fail;
+
+	INIT_LIST_HEAD(&cd->head);
+	cd->freq = freq;
+	sz = sizeof(cd->detectors) * dpd->num_radar_types;
+	cd->detectors = kzalloc(sz, GFP_ATOMIC);
+	if (cd->detectors == NULL)
+		goto fail;
+
+	for (i = 0; i < dpd->num_radar_types; i++) {
+		const struct radar_detector_specs *rs = &dpd->radar_spec[i];
+		struct pri_detector *de = pri_detector_init(rs);
+		if (de == NULL)
+			goto fail;
+		cd->detectors[i] = de;
+	}
+	list_add(&cd->head, &dpd->channel_detectors);
+	return cd;
+
+fail:
+	ath_dbg(dpd->common, DFS,
+		"failed to allocate channel_detector for freq=%d\n", freq);
+	channel_detector_exit(dpd, cd);
+	return NULL;
+}
+
+/**
+ * channel_detector_get() - get channel detector for given frequency
+ * @param dpd instance pointer
+ * @param freq frequency in MHz
+ * @return pointer to channel detector on success, NULL otherwise
+ *
+ * Return existing channel detector for the given frequency or return a
+ * newly create one.
+ */
+static struct channel_detector *
+channel_detector_get(struct dfs_pattern_detector *dpd, u16 freq)
+{
+	struct channel_detector *cd;
+	list_for_each_entry(cd, &dpd->channel_detectors, head) {
+		if (cd->freq == freq)
+			return cd;
+	}
+	return channel_detector_create(dpd, freq);
+}
+
+/*
+ * DFS Pattern Detector
+ */
+
+/* dpd_reset(): reset all channel detectors */
+static void dpd_reset(struct dfs_pattern_detector *dpd)
+{
+	struct channel_detector *cd;
+	if (!list_empty(&dpd->channel_detectors))
+		list_for_each_entry(cd, &dpd->channel_detectors, head)
+			channel_detector_reset(dpd, cd);
+
+}
+static void dpd_exit(struct dfs_pattern_detector *dpd)
+{
+	struct channel_detector *cd, *cd0;
+	if (!list_empty(&dpd->channel_detectors))
+		list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
+			channel_detector_exit(dpd, cd);
+	kfree(dpd);
+}
+
+static bool
+dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event)
+{
+	u32 i;
+	struct channel_detector *cd;
+
+	/*
+	 * 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;
+
+	cd = channel_detector_get(dpd, event->freq);
+	if (cd == NULL)
+		return false;
+
+	dpd->last_pulse_ts = event->ts;
+	/* reset detector on time stamp wraparound, caused by TSF reset */
+	if (event->ts < dpd->last_pulse_ts)
+		dpd_reset(dpd);
+
+	/* do type individual pattern matching */
+	for (i = 0; i < dpd->num_radar_types; i++) {
+		struct pri_detector *pd = cd->detectors[i];
+		struct pri_sequence *ps = pd->add_pulse(pd, event);
+		if (ps != NULL) {
+			ath_dbg(dpd->common, DFS,
+				"DFS: radar found on freq=%d: id=%d, pri=%d, "
+				"count=%d, count_false=%d\n",
+				event->freq, pd->rs->type_id,
+				ps->pri, ps->count, ps->count_falses);
+			channel_detector_reset(dpd, cd);
+			return true;
+		}
+	}
+	return false;
+}
+
+static struct ath_dfs_pool_stats
+dpd_get_stats(struct dfs_pattern_detector *dpd)
+{
+	return global_dfs_pool_stats;
+}
+
+static bool dpd_set_domain(struct dfs_pattern_detector *dpd,
+			   enum nl80211_dfs_regions region)
+{
+	const struct radar_types *rt;
+	struct channel_detector *cd, *cd0;
+
+	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 channel detectors for previous DFS domain */
+	if (!list_empty(&dpd->channel_detectors))
+		list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
+			channel_detector_exit(dpd, cd);
+	dpd->radar_spec = rt->radar_types;
+	dpd->num_radar_types = rt->num_radar_types;
+
+	dpd->region = region;
+	return true;
+}
+
+static struct dfs_pattern_detector default_dpd = {
+	.exit		= dpd_exit,
+	.set_dfs_domain	= dpd_set_domain,
+	.add_pulse	= dpd_add_pulse,
+	.get_stats	= dpd_get_stats,
+	.region		= NL80211_DFS_UNSET,
+};
+
+struct dfs_pattern_detector *
+dfs_pattern_detector_init(struct ath_common *common,
+			  enum nl80211_dfs_regions region)
+{
+	struct dfs_pattern_detector *dpd;
+
+	if (!config_enabled(CONFIG_CFG80211_CERTIFICATION_ONUS))
+		return NULL;
+
+	dpd = kmalloc(sizeof(*dpd), GFP_KERNEL);
+	if (dpd == NULL)
+		return NULL;
+
+	*dpd = default_dpd;
+	INIT_LIST_HEAD(&dpd->channel_detectors);
+
+	dpd->common = common;
+	if (dpd->set_dfs_domain(dpd, region))
+		return dpd;
+
+	ath_dbg(common, DFS,"Could not set DFS domain to %d", region);
+	kfree(dpd);
+	return NULL;
+}
+EXPORT_SYMBOL(dfs_pattern_detector_init);
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/dfs_pattern_detector.h	2015-09-29 11:15:06.961740969 +0200
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DFS_PATTERN_DETECTOR_H
+#define DFS_PATTERN_DETECTOR_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/nl80211.h>
+
+/**
+ * struct ath_dfs_pool_stats - DFS Statistics for global pools
+ */
+struct ath_dfs_pool_stats {
+	u32 pool_reference;
+	u32 pulse_allocated;
+	u32 pulse_alloc_error;
+	u32 pulse_used;
+	u32 pseq_allocated;
+	u32 pseq_alloc_error;
+	u32 pseq_used;
+};
+
+/**
+ * struct pulse_event - describing pulses reported by PHY
+ * @ts: pulse time stamp in us
+ * @freq: channel frequency in MHz
+ * @width: pulse duration in us
+ * @rssi: rssi of radar event
+ */
+struct pulse_event {
+	u64 ts;
+	u16 freq;
+	u8 width;
+	u8 rssi;
+};
+
+/**
+ * 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]
+ */
+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;
+};
+
+/**
+ * struct dfs_pattern_detector - DFS pattern detector
+ * @exit(): destructor
+ * @set_dfs_domain(): set DFS domain, resets detector lines upon domain changes
+ * @add_pulse(): add radar pulse to detector, returns true on detection
+ * @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
+ * @radar_detector_specs: array of radar detection specs
+ * @channel_detectors: list connecting channel_detector elements
+ */
+struct dfs_pattern_detector {
+	void (*exit)(struct dfs_pattern_detector *dpd);
+	bool (*set_dfs_domain)(struct dfs_pattern_detector *dpd,
+			   enum nl80211_dfs_regions region);
+	bool (*add_pulse)(struct dfs_pattern_detector *dpd,
+			  struct pulse_event *pe);
+
+	struct ath_dfs_pool_stats (*get_stats)(struct dfs_pattern_detector *dpd);
+	enum nl80211_dfs_regions region;
+	u8 num_radar_types;
+	u64 last_pulse_ts;
+	/* needed for ath_dbg() */
+	struct ath_common *common;
+
+	const struct radar_detector_specs *radar_spec;
+	struct list_head channel_detectors;
+};
+
+/**
+ * dfs_pattern_detector_init() - constructor for pattern detector class
+ * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation
+ * @return instance pointer on success, NULL otherwise
+ */
+extern struct dfs_pattern_detector *
+dfs_pattern_detector_init(struct ath_common *common,
+			  enum nl80211_dfs_regions region);
+#endif /* DFS_PATTERN_DETECTOR_H */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/dfs_pri_detector.c	2015-09-29 11:15:06.961740969 +0200
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "ath.h"
+#include "dfs_pattern_detector.h"
+#include "dfs_pri_detector.h"
+
+struct ath_dfs_pool_stats global_dfs_pool_stats = {};
+
+#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++)
+#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--)
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct pulse_elem {
+	struct list_head head;
+	u64 ts;
+};
+
+/**
+ * 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;
+}
+
+/**
+ * DOC: 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++;
+	DFS_POOL_STAT_INC(pool_reference);
+	spin_unlock_bh(&pool_lock);
+}
+
+static void pool_deregister_ref(void)
+{
+	spin_lock_bh(&pool_lock);
+	singleton_pool_references--;
+	DFS_POOL_STAT_DEC(pool_reference);
+	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);
+			DFS_POOL_STAT_DEC(pulse_allocated);
+			kfree(p);
+		}
+		list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
+			list_del(&ps->head);
+			DFS_POOL_STAT_DEC(pseq_allocated);
+			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);
+	DFS_POOL_STAT_DEC(pulse_used);
+	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);
+	DFS_POOL_STAT_DEC(pseq_used);
+	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);
+		DFS_POOL_STAT_INC(pseq_used);
+	}
+	spin_unlock_bh(&pool_lock);
+	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);
+		DFS_POOL_STAT_INC(pulse_used);
+	}
+	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);
+}
+
+/* remove pulses older than window */
+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);
+	}
+}
+
+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) {
+			DFS_POOL_STAT_INC(pulse_alloc_error);
+			return false;
+		}
+		DFS_POOL_STAT_INC(pulse_allocated);
+		DFS_POOL_STAT_INC(pulse_used);
+	}
+	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;
+}
+
+static bool pseq_handler_create_sequences(struct pri_detector *pde,
+					  u64 ts, u32 min_count)
+{
+	struct pulse_elem *p;
+	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;
+
+		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 = 0;
+		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;
+		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;
+		new_ps = pool_get_pseq_elem();
+		if (new_ps == NULL) {
+			new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC);
+			if (new_ps == NULL) {
+				DFS_POOL_STAT_INC(pseq_alloc_error);
+				return false;
+			}
+			DFS_POOL_STAT_INC(pseq_allocated);
+			DFS_POOL_STAT_INC(pseq_used);
+		}
+		memcpy(new_ps, &ps, sizeof(ps));
+		INIT_LIST_HEAD(&new_ps->head);
+		list_add(&new_ps->head, &pde->sequences);
+	}
+	return true;
+}
+
+/* check new ts and add to all matching existing sequences */
+static u32
+pseq_handler_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;
+}
+
+static struct pri_sequence *
+pseq_handler_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 >= pde->rs->ppb_thresh) &&
+		    (ps->count * pde->rs->num_pri >= ps->count_falses))
+			return ps;
+	}
+	return NULL;
+}
+
+
+/* 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;
+}
+
+static void pri_detector_exit(struct pri_detector *de)
+{
+	pri_detector_reset(de, 0);
+	pool_deregister_ref();
+	kfree(de);
+}
+
+static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de,
+						   struct pulse_event *event)
+{
+	u32 max_updated_seq;
+	struct pri_sequence *ps;
+	u64 ts = event->ts;
+	const struct radar_detector_specs *rs = de->rs;
+
+	/* ignore pulses not within width range */
+	if ((rs->width_min > event->width) || (rs->width_max < event->width))
+		return NULL;
+
+	if ((ts - de->last_ts) < rs->max_pri_tolerance)
+		/* if delta to last pulse is too short, don't use this pulse */
+		return NULL;
+	de->last_ts = ts;
+
+	max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts);
+
+	if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) {
+		pri_detector_reset(de, ts);
+		return false;
+	}
+
+	ps = pseq_handler_check_detection(de);
+
+	if (ps == NULL)
+		pulse_queue_enqueue(de, ts);
+
+	return ps;
+}
+
+struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs)
+{
+	struct pri_detector *de;
+
+	de = kzalloc(sizeof(*de), GFP_ATOMIC);
+	if (de == NULL)
+		return NULL;
+	de->exit = pri_detector_exit;
+	de->add_pulse = pri_detector_add_pulse;
+	de->reset = pri_detector_reset;
+
+	INIT_LIST_HEAD(&de->sequences);
+	INIT_LIST_HEAD(&de->pulses);
+	de->window_size = rs->pri_max * rs->ppb * rs->num_pri;
+	de->max_count = rs->ppb * 2;
+	de->rs = rs;
+
+	pool_register_ref();
+	return de;
+}
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/drivers/net/wireless/ath/dfs_pri_detector.h	2014-07-29 21:29:43.337859273 +0200
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DFS_PRI_DETECTOR_H
+#define DFS_PRI_DETECTOR_H
+
+#include <linux/list.h>
+
+extern struct ath_dfs_pool_stats global_dfs_pool_stats;
+
+/**
+ * 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)
+ */
+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;
+};
+
+/**
+ * struct pri_detector - PRI detector element for a dedicated radar type
+ * @exit(): destructor
+ * @add_pulse(): add pulse event, returns pri_sequence if pattern was detected
+ * @reset(): clear states and reset to given time stamp
+ * @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
+ */
+struct pri_detector {
+	void (*exit)     (struct pri_detector *de);
+	struct pri_sequence *
+	     (*add_pulse)(struct pri_detector *de, struct pulse_event *e);
+	void (*reset)    (struct pri_detector *de, u64 ts);
+
+/* private: internal use only */
+	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 *pri_detector_init(const struct radar_detector_specs *rs);
+
+#endif /* DFS_PRI_DETECTOR_H */
diff -Nruw linux-3.11.10-fbx/drivers/platform/intelce./Kconfig linux-3.11.10-fbx/drivers/platform/intelce/Kconfig
--- linux-3.11.10-fbx/drivers/platform/intelce./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/platform/intelce/Kconfig	2015-06-12 14:02:24.510973651 +0200
@@ -0,0 +1,18 @@
+#
+# IntelCE devices configuration
+#
+
+menu "IntelCE devices"
+
+config INTELCE_GPIO
+	tristate "GPIO support"
+	select ARCH_REQUIRE_GPIOLIB
+	---help---
+	  IntelCE 3100/4100 GPIO support.
+
+config INTELCE_DFX
+	tristate "DFX reporting support"
+	---help---
+	  IntelCE 3100/4100 DFX fuse reporting support.
+
+endmenu
diff -Nruw linux-3.11.10-fbx/drivers/platform/intelce./Makefile linux-3.11.10-fbx/drivers/platform/intelce/Makefile
--- linux-3.11.10-fbx/drivers/platform/intelce./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/platform/intelce/Makefile	2015-06-12 14:02:24.510973651 +0200
@@ -0,0 +1,2 @@
+obj-$(CONFIG_INTELCE_GPIO)	+= gpio-intelce.o
+obj-$(CONFIG_INTELCE_DFX)	+= dfx.o
diff -Nruw linux-3.11.10-fbx/drivers/platform/tango2./Kconfig linux-3.11.10-fbx/drivers/platform/tango2/Kconfig
--- linux-3.11.10-fbx/drivers/platform/tango2./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/platform/tango2/Kconfig	2015-06-12 14:02:24.510973651 +0200
@@ -0,0 +1,30 @@
+#
+# TANGO2 devices configuration
+#
+
+menu "Tango2 devices"
+	depends on TANGO2
+
+config TANGO2_FIP
+	tristate "Front panel support"
+	select INPUT
+	---help---
+	  Tango2 FIP front panel support.
+
+config TANGO2_GPIO
+	tristate "GPIO sysfs support"
+	---help---
+	  Export GPIO attributes in sysfs.
+
+config TANGO2_IR
+	tristate "IR support"
+	---help---
+	  Tango2 IR (NEC/RC5/RC6) support.
+
+config TANGO2_FB
+	tristate "Framebuffer support"
+	depends on FB
+	---help---
+	  Tango2 framebuffer support.
+
+endmenu
diff -Nruw linux-3.11.10-fbx/drivers/platform/tango2./Makefile linux-3.11.10-fbx/drivers/platform/tango2/Makefile
--- linux-3.11.10-fbx/drivers/platform/tango2./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/drivers/platform/tango2/Makefile	2015-06-12 14:02:24.510973651 +0200
@@ -0,0 +1,6 @@
+# Makefile for the TANGO2 device drivers
+
+obj-$(CONFIG_TANGO2_FIP) += fip.o
+obj-$(CONFIG_TANGO2_GPIO) += gpio.o
+obj-$(CONFIG_TANGO2_IR) += ir.o
+obj-$(CONFIG_TANGO2_FB) += fb.o
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/drivers/spi/orion_tdm_spi.c	2015-09-29 11:15:07.369744077 +0200
@@ -0,0 +1,599 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/orion_tdm_spi.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <asm/unaligned.h>
+
+#define DRIVER_NAME			"orion_tdm_spi"
+
+/*
+ * registers
+ */
+#define PCM_CTRL_REG			0x0000
+#define PCM_DAA_CSS_CTRL_MASK		(1 << 15)
+
+#define SPI_CLK_PRESCALE_REG		0x3100
+#define SCLOCK_LOW_SHIFT		0
+#define SCLOCK_LOW_MASK			(0xff << SCLOCK_LOW_SHIFT)
+#define SCLOCK_HIGH_SHIFT		8
+#define SCLOCK_HIGH_MASK		(0xff << SCLOCK_HIGH_SHIFT)
+
+#define SPI_GLOBAL_CTRL_REG		0x3104
+#define CODEC_ENABLE_MASK		(1 << 0)
+
+#define SPI_CTRL_REG			0x3108
+#define SPI_STAT_MASK			(1 << 10)
+
+#define SPI_CODEC_ACCESS_L_REG		0x3130
+#define ACCESS_BYTE0_SHIFT		0
+#define ACCESS_BYTE1_SHIFT		8
+
+#define SPI_CODEC_ACCESS_H_REG		0x3134
+#define ACCESS_BYTE2_SHIFT		0
+#define ACCESS_BYTE3_SHIFT		8
+
+#define SPI_REG_ACCESS_CTRL_REG		0x3138
+#define BYTES_TO_XFER_MASK		0x3
+#define SPI_LSB_MSB_MASK		(1 << 2)
+#define SPI_RD_WR_MASK			(1 << 3)
+#define SPI_BYTES_TO_READ_SHIFT		4
+#define SPI_LO_SPEED_CLK_MASK		(1 << 5)
+#define SPI_READ_CS_HOLD_SHIFT		6
+
+#define SPI_READ_DATA_REG		0x313c
+
+#define SPI_REG_ACCESS_CTRL1_REG	0x3140
+#define SPI_WRITE_CS_HOLD_SHIFT		0
+
+#define SPI_OUT_EN_CTRL_REG		0x4000
+#define SPI_OUT_EN_DISABLE_MASK		(1 << 0)
+
+
+struct orion_tdm_spi {
+	struct spi_master		*master;
+	void __iomem			*base;
+
+	unsigned int			max_speed;
+	unsigned int			min_speed;
+
+	/* current configured speed/divs for each CS, drivers uses "low"
+	 * for CS0 and "high" for CS1 */
+	unsigned int			speeds[2];
+	u32				divs[2];
+
+	unsigned int			current_cs;
+	struct orion_tdm_spi_info	*spi_info;
+	struct clk			*clk;
+};
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+
+static inline u32 spi_readl(struct orion_tdm_spi *priv, u32 reg)
+{
+	u32 val;
+
+	val = readl(priv->base + reg);
+/* 	printk("spi_readl: readl at %08x => 0x%08x\n", */
+/* 	       priv->base + reg, val); */
+	return val;
+}
+
+static inline void spi_writel(struct orion_tdm_spi *priv, u32 val, u32 reg)
+{
+/* 	printk("spi_writl: writl at %08x <= 0x%08x\n", */
+/* 	       priv->base + reg, val); */
+	writel(val, priv->base + reg);
+}
+
+static int spi_baudrate_set(struct orion_tdm_spi *priv,
+			    unsigned int cs, unsigned int speed)
+{
+	u32 tclk_hz;
+	u32 div, val;
+
+	if (priv->speeds[cs] == speed)
+		return 0;
+
+	tclk_hz = clk_get_rate(priv->clk);
+
+	/* find divider, the supported values are: 2...254 (even only) */
+	div = DIV_ROUND_UP(tclk_hz, speed);
+	div = roundup(div, 2);
+
+	if (div > 254)
+		return 1;
+
+	if (div < 2)
+		div = 2;
+
+	/* don't reprogram div if not needed */
+	if (priv->divs[cs] == div) {
+		priv->speeds[cs] = speed;
+		return 0;
+	}
+
+	/* Convert the rate to SPI clock divisor value.	*/
+	val = spi_readl(priv, SPI_CLK_PRESCALE_REG);
+	if (cs) {
+		val &= ~SCLOCK_HIGH_MASK;
+		val |= div << SCLOCK_HIGH_SHIFT;
+	} else {
+		val &= ~SCLOCK_LOW_MASK;
+		val |= div << SCLOCK_LOW_SHIFT;
+	}
+	spi_writel(priv, val, SPI_CLK_PRESCALE_REG);
+
+	priv->speeds[cs] = speed;
+	priv->divs[cs] = div;
+	return 0;
+}
+
+static void spi_set_cs(struct orion_tdm_spi *priv, struct spi_device *spi,
+		       int active)
+{
+	struct orion_tdm_spi_info *spi_info;
+	int gpio;
+
+	spi_info = priv->spi_info;
+
+	/* set correct cs in hardware */
+	if (spi->chip_select != priv->current_cs) {
+		u32 val;
+
+		val = spi_readl(priv, PCM_CTRL_REG);
+		if (spi->chip_select)
+			val |= PCM_DAA_CSS_CTRL_MASK;
+		else
+			val &= ~PCM_DAA_CSS_CTRL_MASK;
+		spi_writel(priv, val, PCM_CTRL_REG);
+		priv->current_cs = spi->chip_select;
+	}
+
+	/* if not using gpio, hardware moves cs for us */
+	gpio = spi_info->cs_use_gpio[priv->current_cs];
+	if (gpio == -1)
+		return;
+
+	gpio_set_value(gpio, 1 - active);
+}
+
+static int do_spi_poll(struct orion_tdm_spi *priv)
+{
+	unsigned int loop;
+	u32 val;
+
+	for (loop = 0; loop < 1000; loop++) {
+		val = spi_readl(priv, SPI_CTRL_REG);
+		if (!(val & SPI_STAT_MASK))
+			return 0;
+	}
+	return 1;
+}
+
+static int do_write_read(struct orion_tdm_spi *priv, struct spi_device *spi,
+			 const u8 *tx, unsigned int tx_len,
+			 u8 *rx, unsigned int rx_len)
+{
+	u32 val;
+
+	if (do_spi_poll(priv)) {
+		dev_err(&spi->dev, "spi_poll timed out\n");
+		return 1;
+	}
+
+	val = tx[0];
+	if (tx_len > 1)
+		val |= tx[1] << 8;
+	spi_writel(priv, val, SPI_CODEC_ACCESS_L_REG);
+
+	if (tx_len > 2) {
+		val = tx[2];
+		if (tx_len > 3)
+			val |= tx[3] << 8;
+		spi_writel(priv, val, SPI_CODEC_ACCESS_H_REG);
+	}
+
+
+	val = tx_len - 1;
+	if (rx_len)
+		val |= SPI_RD_WR_MASK;
+	if (rx_len > 1)
+		val |= (1 << SPI_BYTES_TO_READ_SHIFT);
+	if (spi->chip_select) {
+		/* note: bit set to 1 => use high speed */
+		val |= SPI_LO_SPEED_CLK_MASK;
+	}
+	spi_writel(priv, val, SPI_REG_ACCESS_CTRL_REG);
+
+	val = spi_readl(priv, SPI_CTRL_REG);
+	val |= SPI_STAT_MASK;
+	spi_writel(priv, val, SPI_CTRL_REG);
+
+	if (do_spi_poll(priv)) {
+		dev_err(&spi->dev, "spi_poll timed out\n");
+		return 1;
+	}
+
+	if (rx_len) {
+		val = spi_readl(priv, SPI_READ_DATA_REG);
+		rx[0] = val & 0xff;
+		if (rx_len > 1)
+			rx[1] = (val >> 8) & 0xff;
+	}
+
+	return 0;
+}
+
+static int orion_tdm_spi_setup(struct spi_device *spi)
+{
+	struct orion_tdm_spi *priv;
+
+	priv = spi_master_get_devdata(spi->master);
+
+	if (spi->bits_per_word == 0)
+		spi->bits_per_word = 8;
+
+	if (spi->bits_per_word != 8) {
+		dev_err(&spi->dev, "setup: unsupported transfer width %u\n",
+			spi->bits_per_word);
+		return -EINVAL;
+	}
+
+	if ((spi->max_speed_hz == 0) ||
+	    (spi->max_speed_hz > priv->max_speed))
+		spi->max_speed_hz = priv->max_speed;
+
+	if (spi->max_speed_hz < priv->min_speed) {
+		dev_err(&spi->dev, "setup: requested speed too low %d Hz\n",
+			spi->max_speed_hz);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int orion_tdm_spi_transfer(struct spi_device *spi,
+				  struct spi_message *m)
+{
+	struct orion_tdm_spi *priv;
+	struct spi_transfer *t;
+	int want_write, cs_active;
+
+	m->actual_length = 0;
+	m->status = 0;
+
+/* 	printk("orion_tdm_spi_transfer for device speed %u\n", */
+/* 		spi->max_speed_hz); */
+
+	/* reject invalid messages and transfers */
+	if (list_empty(&m->transfers) || !m->complete)
+		return -EINVAL;
+
+	priv = spi_master_get_devdata(spi->master);
+
+	/*
+	 * first pass for sanity check
+	 *
+	 * hardware  is  dumb,  and   can't  read/write  at  the  same
+	 * time. Worst, you have to write at least one byte to be able
+	 * to read, and you can't not read more than two bytes.
+	 */
+	want_write = 0;
+
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+
+		if (!t->len)
+			continue;
+
+		if (t->tx_buf && t->rx_buf) {
+			dev_err(&spi->dev,
+				"message rejected : "
+				"full duplex transfer not supported\n");
+			m->status = -ENOTSUPP;
+			goto msg_done;
+		}
+
+		if (!t->tx_buf && !t->rx_buf && t->len) {
+			dev_err(&spi->dev,
+				"message rejected : "
+				"invalid transfer data buffers\n");
+			m->status = -EINVAL;
+			goto msg_done;
+		}
+
+		/* check if forced transfer width is valid */
+		if (t->bits_per_word && t->bits_per_word != 8) {
+			dev_err(&spi->dev,
+				"message rejected : "
+				"invalid transfer bits_per_word (%d bits)\n",
+				t->bits_per_word);
+			m->status = -EINVAL;
+			goto msg_done;
+		}
+
+		/* check if forced transfer speed if ok */
+		if (t->speed_hz && t->speed_hz < priv->min_speed) {
+			dev_err(&spi->dev,
+				"message rejected : "
+				"device min speed (%d Hz) exceeds "
+				"required transfer speed (%d Hz)\n",
+				priv->min_speed, t->speed_hz);
+			m->status = -EINVAL;
+			goto msg_done;
+		}
+
+		if (t->tx_buf)
+			want_write += t->len;
+		if (t->rx_buf) {
+			if (t->len > 2) {
+				dev_err(&spi->dev,
+					"message rejected : "
+					"marvell dumb spi can't read "
+					"more than 2 bytes\n");
+				m->status = -EINVAL;
+				goto msg_done;
+
+			}
+
+			if (want_write)
+				want_write = 0;
+			else {
+				dev_err(&spi->dev,
+					"message rejected : "
+					"marvell dumb spi can't read "
+					"without write first\n");
+				m->status = -EINVAL;
+				goto msg_done;
+			}
+		}
+	}
+
+/* 	printk("SPI CS\n"); */
+	spi_set_cs(priv, spi, 0);
+	cs_active = 0;
+
+	/* do the actual transfer, we need to coalesce write and read
+	 * transfer */
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		unsigned int i, speed;
+
+		if (!t->len)
+			continue;
+
+		/* get and configure speed for this transfer */
+		if (t->speed_hz)
+			speed = t->speed_hz;
+		else
+			speed = spi->max_speed_hz;
+
+		if (spi_baudrate_set(priv, spi->chip_select, speed)) {
+			m->status = -EINVAL;
+			goto msg_done;
+		}
+
+		/* write always one byte, if this is the last byte to
+		 * transfer, lookahead next transfer and read if
+		 * needed */
+		BUG_ON(!t->tx_buf);
+
+		for (i = 0; i < t->len;) {
+			struct spi_transfer *nt;
+			u8 *rx;
+			const u8 *tx;
+			unsigned int rx_len, tx_remain;
+
+			rx = NULL;
+			nt = NULL;
+			rx_len = 0;
+
+			tx_remain = t->len - i;
+			if (tx_remain == 1) {
+				struct list_head *e;
+
+				/* last byte to write, check if next
+				 * transfer is a read and coalesce */
+				e = t->transfer_list.next;
+				if (e != &m->transfers) {
+					nt = list_entry(e, struct spi_transfer,
+							transfer_list);
+					if (nt->rx_buf) {
+						rx = nt->rx_buf;
+						rx_len = nt->len;
+					} else
+						nt = NULL;
+				}
+			}
+
+			/* we can write 4 bytes at a time if not
+			 * reading */
+			if (tx_remain > 4)
+				tx_remain = 4;
+			else {
+				/* make sure we leave at least one
+				 * byte in case we need to coalesce
+				 * with next read */
+				if (tx_remain > 1)
+					tx_remain--;
+			}
+			tx = t->tx_buf + i;
+
+			if (!cs_active) {
+				spi_set_cs(priv, spi, 1);
+				cs_active = 1;
+			}
+
+			if (do_write_read(priv, spi, tx, tx_remain,
+					  rx, rx_len)) {
+				m->status = -EIO;
+				goto msg_done;
+			}
+
+			if (t->cs_change) {
+				spi_set_cs(priv, spi, 0);
+				cs_active = 0;
+			}
+
+			m->actual_length += tx_remain + rx_len;
+
+			/* skip next transfer if we coalesced it */
+			if (nt) {
+				t = nt;
+				break;
+			}
+
+			i += tx_remain;
+		}
+
+		if (t->delay_usecs)
+			udelay(t->delay_usecs);
+	}
+
+	if (cs_active)
+		spi_set_cs(priv, spi, 0);
+
+msg_done:
+	if (m->complete)
+		m->complete(m->context);
+	return m->status;
+}
+
+static int __init orion_tdm_spi_probe(struct platform_device *pdev)
+{
+	struct spi_master *master;
+	struct orion_tdm_spi *priv;
+	struct resource *r;
+	struct orion_tdm_spi_info *spi_info;
+	unsigned int tclk_hz;
+	int status = 0;
+	u32 val;
+
+	spi_info = pdev->dev.platform_data;
+
+	master = spi_alloc_master(&pdev->dev, sizeof (*priv));
+	if (master == NULL) {
+		dev_dbg(&pdev->dev, "master allocation failed\n");
+		return -ENOMEM;
+	}
+
+	if (pdev->id != -1)
+		master->bus_num = pdev->id;
+
+	master->setup = orion_tdm_spi_setup;
+	master->transfer = orion_tdm_spi_transfer;
+	master->num_chipselect = 2;
+	master->mode_bits = 0;
+
+	dev_set_drvdata(&pdev->dev, master);
+
+	priv = spi_master_get_devdata(master);
+	priv->master = master;
+	priv->spi_info = spi_info;
+
+	priv->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(priv->clk)) {
+		dev_err(&pdev->dev, "no associated clk\n");
+		status = PTR_ERR(priv->clk);
+		goto out;
+	}
+
+	clk_prepare_enable(priv->clk);
+	tclk_hz = clk_get_rate(priv->clk);
+	priv->max_speed = DIV_ROUND_UP(tclk_hz, 4);
+	priv->min_speed = DIV_ROUND_UP(tclk_hz, 254);
+	priv->current_cs = ~0;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		status = -ENODEV;
+		goto out_clk;
+	}
+
+	if (!request_mem_region(r->start, (r->end - r->start) + 1,
+				dev_name(&pdev->dev))) {
+		status = -EBUSY;
+		goto out_clk;
+	}
+
+	priv->base = ioremap(r->start, SZ_64K);
+
+	/* configure TDM SPI */
+	val = spi_readl(priv, SPI_OUT_EN_CTRL_REG);
+	val &= ~SPI_OUT_EN_DISABLE_MASK;
+	spi_writel(priv, val, SPI_OUT_EN_CTRL_REG);
+
+	val = spi_readl(priv, SPI_GLOBAL_CTRL_REG);
+	val |= CODEC_ENABLE_MASK;
+	spi_writel(priv, val, SPI_GLOBAL_CTRL_REG);
+
+	status = spi_register_master(master);
+	if (status < 0)
+		goto out_rel_mem;
+
+	return status;
+
+out_rel_mem:
+	release_mem_region(r->start, (r->end - r->start) + 1);
+
+out_clk:
+	clk_disable_unprepare(priv->clk);
+	clk_put(priv->clk);
+
+out:
+	spi_master_put(master);
+	return status;
+}
+
+static int __exit orion_tdm_spi_remove(struct platform_device *pdev)
+{
+	struct spi_master *master;
+	struct orion_tdm_spi *priv;
+	struct resource *r;
+
+	master = dev_get_drvdata(&pdev->dev);
+	priv = spi_master_get_devdata(master);
+
+	clk_disable_unprepare(priv->clk);
+	clk_put(priv->clk);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, (r->end - r->start) + 1);
+
+	iounmap(priv->base);
+	spi_unregister_master(master);
+
+	return 0;
+}
+
+static struct platform_driver orion_tdm_spi_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.remove		= __exit_p(orion_tdm_spi_remove),
+};
+
+static int __init orion_tdm_spi_init(void)
+{
+	return platform_driver_probe(&orion_tdm_spi_driver,
+				     orion_tdm_spi_probe);
+}
+
+module_init(orion_tdm_spi_init);
+
+static void __exit orion_tdm_spi_exit(void)
+{
+	platform_driver_unregister(&orion_tdm_spi_driver);
+}
+
+module_exit(orion_tdm_spi_exit);
+
+MODULE_DESCRIPTION("Orion TDM SPI driver");
+MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");
+MODULE_LICENSE("GPL");
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/drivers/video/ssd1327.c	2015-06-12 14:02:25.814983032 +0200
@@ -0,0 +1,700 @@
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ssd1327.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#include <linux/fb.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/backlight.h>
+
+/*
+ * commands
+ */
+#define OPCODE_CONTRAST		0x81
+
+#define OPCODE_SET_COLUMN	0x15
+#define OPCODE_SET_ROW		0x75
+#define OPCODE_SET_REMAP	0xa0
+#define OPCODE_DISPLAY_NORMAL	0xa4
+#define OPCODE_DISPLAY_ALL_ON	0xa5
+#define OPCODE_DISPLAY_ALL_OFF	0xa6
+
+#define OPCODE_DISPLAY_OFF	0xae
+#define OPCODE_DISPLAY_ON	0xaf
+
+#define OPCODE_DEF_GRAY		0xb9
+
+#define SSD1327_MAX_BRIGHTNESS		0x81
+#define SSD1327_NOMINAL_BRIGHTNESS	0x64
+
+/*
+ * fbinfo
+ */
+static struct fb_fix_screeninfo ssd1327_fb_fix = {
+	.id		= "ssd1327",
+	.type		= FB_TYPE_PACKED_PIXELS,
+	.visual		= FB_VISUAL_STATIC_PSEUDOCOLOR,
+	.xpanstep	= 0,
+	.ypanstep	= 1,
+	.ywrapstep	= 0,
+	.accel		= FB_ACCEL_NONE,
+};
+
+static struct fb_var_screeninfo ssd1327_fb_var = {
+	.bits_per_pixel	= 8,
+	.grayscale	= 1,
+	.nonstd		= 1,
+	.red.length	= 8,
+	.green.length	= 8,
+	.blue.length	= 8,
+};
+
+/*
+ * private data
+ */
+#define SSD1327_COLS		64
+#define SSD1327_ROWS		128
+#define GDDRAM_SIZE		SSD1327_COLS * SSD1327_ROWS
+
+struct ssd1327 {
+	struct mutex			mutex;
+
+	/* image of display ram */
+	u8				gddram[GDDRAM_SIZE];
+	u8				old_gddram[GDDRAM_SIZE];
+
+	/* data ram, 8 bits per pixel */
+	u8				*vmem;
+	unsigned int			vmem_size;
+
+	struct fb_info			*fb;
+	struct ssd1327_platform_data	*data;
+	struct spi_device		*spi;
+
+	struct backlight_device		*backlight;
+	unsigned int			brightness;
+
+	/* watchog timer */
+	struct delayed_work		wtd_work;
+	unsigned int			wtd_max;
+	atomic_t			wtd_count;
+};
+
+/*
+ * send command to device
+ */
+static int send_cmd(struct ssd1327 *priv, u8 cmd)
+{
+	struct ssd1327_platform_data *data;
+	int ret;
+
+	data = priv->spi->dev.platform_data;
+
+	mutex_lock(&priv->mutex);
+	gpio_set_value(data->data_select_gpio, 0);
+	ret = spi_write_then_read(priv->spi, &cmd, 1, NULL, 0);
+	mutex_unlock(&priv->mutex);
+	return ret;
+}
+
+/*
+ * send command list to device
+ */
+static int send_cmds(struct ssd1327 *priv, const u8 *cmd, unsigned int len)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < len; i++) {
+		ret = send_cmd(priv, cmd[i]);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+/*
+ * write given data into device gddram
+ */
+static int write_data(struct ssd1327 *priv, u8 *tx, unsigned int size)
+{
+	struct ssd1327_platform_data *data;
+	int ret;
+
+	data = priv->spi->dev.platform_data;
+
+	mutex_lock(&priv->mutex);
+	gpio_set_value(data->data_select_gpio, 1);
+	ret = spi_write(priv->spi, tx, size);
+	mutex_unlock(&priv->mutex);
+	return ret;
+}
+
+/*
+ * soft reset & initialize ssd1327
+ */
+static int ssd1327_init(struct ssd1327 *priv)
+{
+	const u8 init_cmds[] = { OPCODE_DISPLAY_ON,
+
+				 /* set even/odd splitting */
+				 OPCODE_SET_REMAP, (1 << 6),
+				 OPCODE_CONTRAST, SSD1327_NOMINAL_BRIGHTNESS,
+				 OPCODE_DEF_GRAY,
+				 OPCODE_DISPLAY_NORMAL,
+	};
+	int ret;
+
+	/* zero ram */
+	ret = write_data(priv, priv->gddram, GDDRAM_SIZE);
+	if (ret)
+		return ret;
+
+	return send_cmds(priv, init_cmds, sizeof (init_cmds));
+}
+
+/*
+ * update area
+ */
+static int ssd1327_fb_update(struct ssd1327 *priv)
+{
+	unsigned int col, row, w, h, i, count;
+	unsigned char *vmem;
+	u8 *start;
+	u8 ccmds[3] = { OPCODE_SET_COLUMN, 0, 0x3f };
+	u8 rcmds[3] = { OPCODE_SET_ROW, 0, 0x7f };
+	int toggle, last_toggle_pos, moved;
+
+	w = priv->data->width;
+	h = priv->data->height;
+
+	/* backup previous gddram */
+	memcpy(priv->old_gddram, priv->gddram, GDDRAM_SIZE);
+
+	vmem = priv->vmem + w * priv->fb->var.yoffset;
+
+	for (row = 0; row < SSD1327_ROWS; row++) {
+
+		if (row >= h)
+			break;
+
+		for (col = 0; col < SSD1327_COLS; col++) {
+			unsigned int nibble;
+			u8 val;
+
+			val = 0;
+			for (nibble = 0; nibble < 2; nibble++) {
+				unsigned int off, x;
+				u8 vval;
+
+				x = col * 2 + nibble;
+				if (x >= w)
+					break;
+
+				switch (priv->fb->var.rotate) {
+				case 0:
+				default:
+					off = row * w + x;
+					break;
+
+				case 180:
+					off = w * h - (row * w + x) - 1;
+					break;
+
+				case 90:
+					off = (w - x - 1) * w + row;
+					break;
+
+				case 270:
+					off = x * w + (h - row - 1);
+					break;
+				}
+
+				vval = vmem[off] >> 4;
+				val |= vval << (nibble * 4);
+			}
+
+			priv->gddram[row * SSD1327_COLS + col] = val;
+		}
+	}
+
+	/* count consecutive toggled bytes, each column/row address
+	 * change adds 6 bytes to send  */
+	moved = toggle = 0;
+	last_toggle_pos = -INT_MAX;
+	count = 0;
+	for (i = 0; i < GDDRAM_SIZE; i++) {
+		if (priv->gddram[i] ^ priv->old_gddram[i]) {
+			/* if crossing column boundary and first
+			 * address is not 0, we must send column
+			 * command */
+			if (moved && ((i % SSD1327_COLS) == 0)) {
+				count += 3;
+				moved = 0;
+			}
+
+			if (!toggle) {
+				if (i - last_toggle_pos < 6) {
+					unsigned int j;
+
+					/* fake last columns as dirty,
+					 * cheaper than repositionning
+					 * cursor */
+					for (j = last_toggle_pos; j < i; j++)
+						priv->old_gddram[j] =
+							~priv->gddram[j];
+
+					count += i - last_toggle_pos - 1;
+				} else {
+					/* send command to change
+					 * address & column */
+					count += 6;
+
+					/* if we changed first column address
+					 * to non 0, remember it */
+					if ((i % SSD1327_COLS))
+						moved = 1;
+					else
+						moved = 0;
+				}
+			}
+
+			toggle = 1;
+			count++;
+
+		} else {
+			if (toggle)
+				last_toggle_pos = i - 1;
+			toggle = 0;
+		}
+	}
+
+	/* force full gddram update if we would send more bytes
+	 * using clever update */
+	if (count > GDDRAM_SIZE)
+		return write_data(priv, priv->gddram, GDDRAM_SIZE);
+
+	moved = toggle = 0;
+	count = 0;
+	start = NULL;
+	for (i = 0; i < GDDRAM_SIZE; i++) {
+
+		if (priv->gddram[i] ^ priv->old_gddram[i]) {
+			/* if crossed column boundary and first
+			 * address is not 0, we must send command to
+			 * reset column*/
+			if (moved && ((i % SSD1327_COLS) == 0)) {
+				write_data(priv, start, count);
+				start += count;
+				count = 0;
+				ccmds[1] = 0;
+				send_cmds(priv, ccmds, 3);
+				moved = 0;
+			}
+
+			if (!toggle) {
+				ccmds[1] = i % SSD1327_COLS;
+				rcmds[1] = i / SSD1327_COLS;
+				send_cmds(priv, ccmds, 3);
+				send_cmds(priv, rcmds, 3);
+
+				/* if we changed first column address
+				 * to non 0, remember it */
+				if ((i % SSD1327_COLS))
+					moved = 1;
+				else
+					moved = 0;
+				start = &priv->gddram[i];
+			}
+
+			count++;
+			toggle = 1;
+
+		} else {
+			if (count) {
+				write_data(priv, start, count);
+				count = 0;
+			}
+			toggle = 0;
+		}
+	}
+
+	if (count)
+		write_data(priv, start, count);
+
+	/* reset position */
+	ccmds[1] = 0;
+	send_cmds(priv, ccmds, 3);
+	rcmds[1] = 0;
+	send_cmds(priv, rcmds, 3);
+	return 0;
+}
+
+/*
+ * frame buffer fill rect callback
+ */
+static void ssd1327_fb_fillrect(struct fb_info *info,
+				const struct fb_fillrect *rect)
+{
+	struct ssd1327 *priv = info->par;
+	sys_fillrect(info, rect);
+	atomic_set(&priv->wtd_count, priv->wtd_max);
+	ssd1327_fb_update(priv);
+}
+
+/*
+ * frame buffer copy area callback
+ */
+static void ssd1327_fb_copyarea(struct fb_info *info,
+				const struct fb_copyarea *area)
+{
+	struct ssd1327 *priv = info->par;
+	sys_copyarea(info, area);
+	atomic_set(&priv->wtd_count, priv->wtd_max);
+	ssd1327_fb_update(priv);
+}
+
+/*
+ * frame buffer image blit
+ */
+static void ssd1327_fb_imageblit(struct fb_info *info,
+				 const struct fb_image *image)
+{
+	struct ssd1327 *priv = info->par;
+	sys_imageblit(info, image);
+	atomic_set(&priv->wtd_count, priv->wtd_max);
+	ssd1327_fb_update(priv);
+}
+
+/*
+ * frame buffer pan callback
+ */
+static int ssd1327_fb_pan(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	struct ssd1327 *priv = info->par;
+	priv->fb->var.xoffset = var->xoffset;
+	priv->fb->var.yoffset = var->yoffset;
+	atomic_set(&priv->wtd_count, priv->wtd_max);
+	ssd1327_fb_update(priv);
+	return 0;
+}
+
+/*
+ * fram buffer set_par callback, set videomode
+ */
+static int ssd1327_fb_set_par(struct fb_info *info)
+{
+	struct ssd1327 *priv = info->par;
+	/* called after rotate update */
+	atomic_set(&priv->wtd_count, priv->wtd_max);
+	ssd1327_fb_update(priv);
+	return 0;
+}
+
+static int ssd1327_fb_check_var(struct fb_var_screeninfo *var,
+				struct fb_info *info)
+{
+	unsigned int rotate;
+
+	rotate = var->rotate;
+	if (rotate != 0 && rotate != 90 && rotate != 180 && rotate != 270)
+		rotate = 0;
+	*var = info->var;
+	var->rotate = rotate;
+	return 0;
+}
+
+/*
+ * frame buffer blank callback
+ */
+static int ssd1327_fb_blank(int blank, struct fb_info *info)
+{
+	return 0;
+}
+
+/*
+ * frame buffer write from userspace
+ */
+static ssize_t ssd1327_fb_write(struct fb_info *info, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct ssd1327 *priv = info->par;
+	unsigned long p = *ppos;
+	void *dst;
+	int err = 0;
+	unsigned long total_size;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return -EPERM;
+
+	total_size = info->fix.smem_len;
+
+	if (p > total_size)
+		return -EFBIG;
+
+	if (count > total_size) {
+		err = -EFBIG;
+		count = total_size;
+	}
+
+	if (count + p > total_size) {
+		if (!err)
+			err = -ENOSPC;
+
+		count = total_size - p;
+	}
+
+	dst = (void __force *)(info->screen_base + p);
+
+	if (copy_from_user(dst, buf, count))
+		err = -EFAULT;
+
+	if  (!err)
+		*ppos += count;
+
+	atomic_set(&priv->wtd_count, priv->wtd_max);
+	ssd1327_fb_update(priv);
+
+	return (err) ? err : count;
+}
+
+static struct fb_ops ssd1327_fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_write	= ssd1327_fb_write,
+	.fb_fillrect	= ssd1327_fb_fillrect,
+	.fb_copyarea	= ssd1327_fb_copyarea,
+	.fb_imageblit	= ssd1327_fb_imageblit,
+	.fb_pan_display	= ssd1327_fb_pan,
+	.fb_blank	= ssd1327_fb_blank,
+	.fb_check_var	= ssd1327_fb_check_var,
+	.fb_set_par	= ssd1327_fb_set_par,
+};
+
+/*
+ * watchdog timer
+ */
+static void wtd_work_cb(struct work_struct *t)
+{
+	struct ssd1327 *priv;
+	struct delayed_work *dwork;
+
+	dwork = container_of(t, struct delayed_work, work);
+	priv = container_of(dwork, struct ssd1327, wtd_work);
+
+	if (atomic_dec_and_test(&priv->wtd_count)) {
+		dev_err(&priv->spi->dev, "watchdog triggered\n");
+		memset(priv->vmem, 0, priv->vmem_size);
+		ssd1327_fb_update(priv);
+	}
+
+	schedule_delayed_work(&priv->wtd_work, HZ);
+}
+
+/*
+ * backlight control
+ */
+static int ssd1327_bl_update_status(struct backlight_device *bl)
+{
+	struct ssd1327 *priv;
+	u8 bl_cmds[2];
+	int ret;
+
+	priv = bl_get_data(bl);
+
+	bl_cmds[0] = OPCODE_CONTRAST;
+	bl_cmds[1] = bl->props.brightness;
+
+	ret = send_cmds(priv, bl_cmds, sizeof (bl_cmds));
+	if (ret < 0)
+		return ret;
+	priv->brightness = bl->props.brightness;
+	return 0;
+}
+
+static int ssd1327_bl_get_brightness(struct backlight_device *bl)
+{
+	struct ssd1327 *priv;
+	priv = bl_get_data(bl);
+	return priv->brightness;
+}
+
+static struct backlight_ops ssd1327_bl_ops = {
+	.update_status		= ssd1327_bl_update_status,
+	.get_brightness		= ssd1327_bl_get_brightness,
+};
+
+static const struct backlight_properties ssd1327_bl_props = {
+	.power		= FB_BLANK_UNBLANK,
+	.fb_blank	= FB_BLANK_UNBLANK,
+	.max_brightness	= SSD1327_MAX_BRIGHTNESS,
+	.type		= BACKLIGHT_RAW,
+};
+
+static int init_backlight(struct ssd1327 *priv)
+{
+	struct backlight_device *bl;
+
+	bl = backlight_device_register("ssd1327", &priv->spi->dev,
+				       priv, &ssd1327_bl_ops,
+				       &ssd1327_bl_props);
+	if (IS_ERR(bl)) {
+		dev_err(&priv->spi->dev, "error %ld on backlight register\n",
+			PTR_ERR(bl));
+		return PTR_ERR(bl);
+	}
+	priv->backlight = bl;
+	bl->props.brightness = priv->brightness;
+	return 0;
+}
+
+/*
+ * platform device probe callback
+ */
+static int ssd1327_probe(struct spi_device *spi)
+{
+	struct ssd1327 *priv;
+	struct ssd1327_platform_data *data;
+	struct fb_info *fb;
+	int ret;
+
+	data = spi->dev.platform_data;
+	if (!data) {
+		dev_err(&spi->dev, "no screen description\n");
+		return -ENODEV;
+	}
+
+	/* sanity check on screen size */
+	if (data->width > SSD1327_COLS * 2 ||
+	    data->height > SSD1327_ROWS) {
+		dev_err(&spi->dev, "unsupported screen dimension\n");
+		return -ENODEV;
+	}
+
+	fb = framebuffer_alloc(sizeof (*priv), &spi->dev);
+	if (!fb)
+		return -ENOMEM;
+	priv = fb->par;
+	mutex_init(&priv->mutex);
+	priv->spi = spi;
+	priv->data = data;
+	priv->fb = fb;
+	priv->brightness = SSD1327_NOMINAL_BRIGHTNESS;
+	priv->wtd_max = data->watchdog;
+
+	/* setup framebuffer */
+	fb->fbops = &ssd1327_fb_ops;
+	fb->flags = FBINFO_FLAG_DEFAULT | FBINFO_HWACCEL_YPAN;
+	fb->var = ssd1327_fb_var;
+	fb->fix = ssd1327_fb_fix;
+
+	fb->var.xres = data->width;
+	fb->var.yres = data->height;
+	fb->var.xres_virtual = data->width;
+	fb->var.yres_virtual = data->height * 2;
+
+	/* twice lcd size so we can pan in one direction */
+	fb->fix.smem_len = (data->width * data->height) * 2;
+	fb->fix.line_length = data->width;
+	fb->var.rotate = data->rotate;
+
+	/* allocate video memory */
+	priv->vmem_size = PAGE_ALIGN(fb->fix.smem_len);
+	priv->vmem = vmalloc(priv->vmem_size);
+	if (!priv->vmem) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	memset(priv->vmem, 0, priv->vmem_size);
+	fb->screen_base = (char __iomem *)priv->vmem;
+
+	ret = ssd1327_init(priv);
+	if (ret)
+		goto fail;
+
+	if (init_backlight(priv))
+		goto fail;
+
+	/* register frame buffer */
+	ret = register_framebuffer(fb);
+	if (ret < 0)
+		goto fail;
+
+	INIT_DELAYED_WORK(&priv->wtd_work, wtd_work_cb);
+
+	if (priv->wtd_max) {
+		atomic_set(&priv->wtd_count, priv->wtd_max);
+		schedule_delayed_work(&priv->wtd_work, HZ);
+	}
+
+	dev_info(&spi->dev,
+		 "fb%d: SSD1327 frame buffer device (%ux%u screen)\n",
+		 fb->node, data->width, data->height);
+
+	dev_set_drvdata(&spi->dev, priv);
+	return 0;
+
+fail:
+	if (priv->vmem)
+		vfree(priv->vmem);
+	if (priv->backlight)
+		backlight_device_unregister(priv->backlight);
+	framebuffer_release(fb);
+	return ret;
+}
+
+/*
+ * platform device remove callback
+ */
+static int ssd1327_remove(struct spi_device *spi)
+{
+	struct ssd1327 *priv;
+	unsigned int i;
+
+	priv = dev_get_drvdata(&spi->dev);
+	cancel_delayed_work_sync(&priv->wtd_work);
+	unregister_framebuffer(priv->fb);
+	for (i = 0; i < priv->vmem_size; i += PAGE_SIZE) {
+		struct page *page;
+		page = vmalloc_to_page(priv->vmem + i);
+		page->mapping = NULL;
+	}
+	vfree(priv->vmem);
+	backlight_device_unregister(priv->backlight);
+	framebuffer_release(priv->fb);
+	return 0;
+}
+
+static struct spi_driver ssd1327_driver = {
+	.driver = {
+		.name		= "ssd1327",
+		.owner		= THIS_MODULE,
+	},
+	.probe		= ssd1327_probe,
+	.remove		= ssd1327_remove,
+};
+
+static int __init ssd1327_module_init(void)
+{
+	return spi_register_driver(&ssd1327_driver);
+}
+
+static void __exit ssd1327_module_exit(void)
+{
+	spi_unregister_driver(&ssd1327_driver);
+}
+
+module_init(ssd1327_module_init);
+module_exit(ssd1327_module_exit);
+
+MODULE_DESCRIPTION("SSD1327 driver");
+MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");
+MODULE_LICENSE("GPL");
diff -Nruw linux-3.11.10-fbx/fs/exfat./bitmap.c linux-3.11.10-fbx/fs/exfat/bitmap.c
--- linux-3.11.10-fbx/fs/exfat./bitmap.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/bitmap.c	2015-06-12 14:02:26.806990175 +0200
@@ -0,0 +1,600 @@
+/*
+ * bitmap.c for exfat
+ * Created by <nschichan@freebox.fr> on Thu Aug  8 19:21:05 2013
+ */
+
+#include <linux/buffer_head.h>
+#include <linux/fs.h>
+
+#include "exfat.h"
+#include "exfat_fs.h"
+
+
+static inline sector_t exfat_bitmap_sector(struct exfat_sb_info *sbi,
+					   u32 cluster)
+{
+	return sbi->first_bitmap_sector + ((cluster / 8) >> sbi->sectorbits);
+}
+
+static inline u32 exfat_bitmap_off(struct exfat_sb_info *sbi,
+				   u32 cluster)
+{
+	return (cluster / 8) & sbi->sectormask;
+}
+
+static inline u32 exfat_bitmap_shift(u32 cluster)
+{
+	return cluster & 7;
+}
+
+static int __find_get_free_cluster(struct inode *inode, u32 *out_cluster)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+
+	while (1) {
+		sector_t sect = exfat_bitmap_sector(sbi,
+						    sbi->cur_bitmap_cluster);
+		u32 off = exfat_bitmap_off(sbi, sbi->cur_bitmap_cluster);
+		u32 shift = exfat_bitmap_shift(sbi->cur_bitmap_cluster);
+
+		/* disk is full */
+		if (!sbi->free_clusters)
+			break;
+
+		if (!sbi->cur_bitmap_bh ||
+		    sect != sbi->cur_bitmap_sector) {
+			if (sbi->cur_bitmap_bh)
+				brelse(sbi->cur_bitmap_bh);
+			sbi->cur_bitmap_bh = sb_bread(inode->i_sb, sect);
+			sbi->cur_bitmap_sector = sect;
+			if (!sbi->cur_bitmap_bh) {
+				exfat_msg(inode->i_sb, KERN_ERR,
+					  "unable to read bitmap sector "
+					  "at %llu", sect);
+				return -EIO;
+			}
+		}
+
+		if (!(sbi->cur_bitmap_bh->b_data[off] & (1 << shift))) {
+			sbi->cur_bitmap_bh->b_data[off] |= (1 << shift);
+			*out_cluster = sbi->cur_bitmap_cluster;
+			goto found;
+		}
+
+		++sbi->cur_bitmap_cluster;
+		if (sbi->cur_bitmap_cluster == sbi->cluster_count)
+			sbi->cur_bitmap_cluster = 0;
+	}
+	return -ENOSPC;
+
+found:
+	sbi->prev_free_cluster = *out_cluster;
+	--sbi->free_clusters;
+	mark_buffer_dirty(sbi->cur_bitmap_bh);
+	return 0;
+}
+
+static int __put_cluster(struct inode *inode, u32 cluster)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+	sector_t sect = exfat_bitmap_sector(sbi, cluster);
+	u32 off = exfat_bitmap_off(sbi, cluster);
+	u32 shift = exfat_bitmap_shift(cluster);
+
+
+	if (!sbi->cur_bitmap_bh || sect != sbi->cur_bitmap_sector) {
+		if (sbi->cur_bitmap_bh)
+			brelse(sbi->cur_bitmap_bh);
+		sbi->cur_bitmap_bh = sb_bread(inode->i_sb, sect);
+		if (!sbi->cur_bitmap_bh) {
+			exfat_msg(inode->i_sb, KERN_ERR,
+				  "unable to read bitmap sector at %llu", sect);
+			return -EIO;
+		}
+		sbi->cur_bitmap_sector = sect;
+		sbi->cur_bitmap_cluster = cluster;
+	}
+	if ((sbi->cur_bitmap_bh->b_data[off] & (1 << shift)) == 0) {
+		exfat_fs_error(inode->i_sb, "put_cluster: cluster %u "
+			  "already free.", cluster);
+		return -EIO;
+	}
+
+	++sbi->free_clusters;
+	sbi->cur_bitmap_bh->b_data[off] &= ~(1 << shift);
+	sbi->prev_free_cluster = cluster;
+	mark_buffer_dirty(sbi->cur_bitmap_bh);
+	/* sync_dirty_buffer(sbi->cur_bitmap_bh); */
+	return 0;
+}
+
+/*
+ * setup search to start at given cluster.
+ */
+static void __exfat_reset_bitmap(struct exfat_sb_info *sbi, u32 cluster)
+{
+	sector_t sect;
+
+	if (cluster >= sbi->cluster_count)
+		cluster = 0;
+
+	sect = exfat_bitmap_sector(sbi, cluster);
+	if (sbi->cur_bitmap_sector != sect) {
+		sbi->cur_bitmap_sector = sect;
+		if (sbi->cur_bitmap_bh) {
+			brelse(sbi->cur_bitmap_bh);
+			sbi->cur_bitmap_bh = NULL;
+		}
+	}
+	sbi->cur_bitmap_cluster = cluster;
+}
+
+static bool all_contiguous(u32 *clusters, u32 nr)
+{
+	u32 i;
+
+	for (i = 0; i < nr - 1; ++i) {
+		if (clusters[i] != clusters[i + 1] - 1)
+			return false;
+	}
+	return true;
+}
+
+/*
+ * hint must be the immediately after the last allocated cluster of
+ * the inode.
+ */
+int exfat_alloc_clusters(struct inode *inode, u32 hint, u32 *clusters, u32 nr)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+	struct exfat_inode_info *info = EXFAT_I(inode);
+	u32 i;
+
+	mutex_lock(&sbi->bitmap_mutex);
+	__exfat_reset_bitmap(sbi, hint - 2);
+	for (i = 0; i < nr; ++i) {
+		u32 new;
+		int error;
+
+		error = __find_get_free_cluster(inode, &new);
+		if (error) {
+			mutex_unlock(&sbi->bitmap_mutex);
+			return error;
+		}
+
+		clusters[i] = new + 2;
+	}
+	mutex_unlock(&sbi->bitmap_mutex);
+
+	/*
+	 * all clusters found: now see if we need to update/create a
+	 * fat chain.
+	 */
+	if (info->first_cluster == 0) {
+		info->first_cluster = clusters[0];
+		if (all_contiguous(clusters, nr)) {
+			/*
+			 * first cluster alloc on inode and all
+			 * clusters are contiguous.
+			 */
+			info->flags |= EXFAT_I_FAT_INVALID;
+		} else {
+			/*
+			 * first alloc and already fragmented.
+			 */
+			return exfat_write_fat(inode, 0, clusters, nr);
+		}
+	} else {
+		int error;
+		if ((info->flags & EXFAT_I_FAT_INVALID) &&
+		    (clusters[0] != hint || !all_contiguous(clusters, nr))) {
+			/*
+			 * must now use fat chain instead of bitmap.
+			 */
+			info->flags &= ~(EXFAT_I_FAT_INVALID);
+
+			/*
+			 * write the contiguous chain that would
+			 * previously be accessed without the FAT
+			 * chain.
+			 */
+			error = exfat_write_fat_contiguous(inode,
+						  info->first_cluster,
+						  hint - info->first_cluster);
+			if (error)
+				return error;
+		}
+
+		if ((info->flags & EXFAT_I_FAT_INVALID) == 0) {
+			/*
+			 * link the allocated clusters after hint.
+			 */
+			error = exfat_write_fat(inode, hint - 1, clusters, nr);
+			if (error)
+				return  error;
+		}
+
+	}
+
+	/*
+	 * update i_blocks.
+	 */
+	inode->i_blocks += nr << (sbi->clusterbits - 9);
+	info->allocated_clusters += nr;
+
+	/*
+	 * caller must call mark_inode_dirty so that inode
+	 * first_cluster and inode flags get written to the disk.
+	 * caller must update inode size (directory and regular file
+	 * have different rules).
+	 */
+	return 0;
+}
+
+
+static int exfat_free_clusters_contiguous(struct inode *inode,
+					  u32 start, u32 nr)
+{
+	u32 cluster;
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+	int error = 0;
+
+	mutex_lock(&sbi->bitmap_mutex);
+	for (cluster = start; cluster < start + nr; ++cluster) {
+		error = __put_cluster(inode, cluster - 2);
+		if (error)
+			break;
+	}
+	mutex_unlock(&sbi->bitmap_mutex);
+	return error;
+}
+
+static int exfat_free_clusters_fat(struct inode *inode,
+				   u32 fcluster_start, u32 nr)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+	u32 fcluster;
+	int error = 0;
+
+	mutex_lock(&sbi->bitmap_mutex);
+	for (fcluster = fcluster_start; fcluster < fcluster_start + nr;
+	     ++fcluster) {
+		u32 dcluster;
+		int error;
+
+		error = exfat_get_fat_cluster(inode, fcluster, &dcluster);
+		if (error)
+			break;
+
+		error = __put_cluster(inode, dcluster - 2);
+		if (error)
+			break;
+	}
+	mutex_unlock(&sbi->bitmap_mutex);
+
+	/*
+	 * per-inode file cluster to disk cluster translation cache
+	 * mostly now holds entries to the zone we just truncated, so
+	 * they must not be kept (this could lead to FS corruption).
+	 */
+	exfat_inode_cache_drop(inode);
+
+	return error;
+}
+
+int exfat_free_clusters_inode(struct inode *inode, u32 fcluster_start)
+{
+	struct exfat_inode_info *info = EXFAT_I(inode);
+	int error;
+	u32 nr_to_free = info->allocated_clusters - fcluster_start;
+
+	if (info->first_cluster == 0 || nr_to_free == 0)
+		/*
+		 * no clusters allocated, or nothing to do
+		 */
+		return 0;
+
+	if (info->flags & EXFAT_I_FAT_INVALID)
+		error = exfat_free_clusters_contiguous(inode,
+				       info->first_cluster + fcluster_start,
+				       nr_to_free);
+	else
+		error = exfat_free_clusters_fat(inode, fcluster_start,
+					nr_to_free);
+	if (error)
+		return error;
+
+	info->allocated_clusters -= nr_to_free;
+	inode->i_blocks = EXFAT_I(inode)->allocated_clusters <<
+		(EXFAT_SB(inode->i_sb)->clusterbits - 9);
+
+	/*
+	 * update inode info, caller must call mark_inode_dirty and
+	 * update inode->i_size.
+	 */
+	if (fcluster_start == 0) {
+		info->first_cluster = 0;
+		info->flags &= ~(EXFAT_I_FAT_INVALID);
+	}
+	return 0;
+}
+
+static u32 count_clusters_bh(struct buffer_head *bh, u32 count)
+{
+	u8 *ptr = bh->b_data;
+	u32 ret = 0;
+	u8 val;
+
+	while (count >= sizeof (u64) * 8) {
+		u64 val = *(u64*)ptr;
+
+		ret += hweight64(~val);
+		count -= sizeof (u64) * 8;
+		ptr += sizeof (u64);
+	}
+	if (count >= sizeof (u32) * 8) {
+		u32 val = *(u32*)ptr;
+
+		ret += hweight32(~val);
+		count -= sizeof (u32) * 8;
+		ptr += sizeof (u32);
+	}
+	if (count >= sizeof (u16) * 8) {
+		u16 val = *(u16*)ptr;
+
+		ret += hweight16(~val);
+		count -= sizeof (u16) * 8;
+		ptr += sizeof (u16);
+	}
+	while (count >= sizeof (u8) * 8) {
+		u8 val = *ptr;
+
+		ret += hweight8(~val);
+		count -= sizeof (u8) * 8;
+		ptr += sizeof (u8);
+	}
+	val = *ptr;
+	while (count) {
+		ret += (~val & 1);
+		val >>= 1;
+		--count;
+	}
+	return ret;
+}
+
+/*
+ * only called during mount, so taking sbi->bitmap_mutex should not be
+ * needed.
+ */
+static int exfat_get_free_cluster_count(struct super_block *sb, u32 *out_count)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	u32 clusters_per_sector = 8 * sbi->sectorsize;
+	u32 cluster;
+
+	*out_count = 0;
+	for (cluster = 0; cluster < sbi->cluster_count;
+	     cluster += clusters_per_sector) {
+		sector_t sect = exfat_bitmap_sector(sbi, cluster);
+		struct buffer_head *bh;
+		u32 count = clusters_per_sector;
+
+		if (cluster + clusters_per_sector > sbi->cluster_count)
+			count = sbi->cluster_count - cluster;
+
+		bh = sb_bread(sb, sect);
+		if (!bh) {
+			exfat_msg(sb, KERN_ERR,
+				  "unable to read bitmap sector at %llu", sect);
+			return -EIO;
+		}
+		*out_count += count_clusters_bh(bh, count);
+		brelse(bh);
+	}
+	return 0;
+}
+
+/*
+ * setup a bitmap context, preload a bh from the requested starting
+ * cluster.
+ */
+int exfat_init_bitmap_context(struct super_block *sb,
+			      struct exfat_bitmap_ctx *ctx,
+			      u32 cluster)
+{
+	memset(ctx, 0, sizeof (*ctx));
+	ctx->sb = sb;
+
+	cluster -= 2;
+	if (cluster >= EXFAT_SB(sb)->cluster_count)
+		return -ENOSPC;
+
+	ctx->cur_sector = exfat_bitmap_sector(EXFAT_SB(sb), cluster);
+	ctx->bh = sb_bread(ctx->sb, ctx->cur_sector);
+
+	if (!ctx->bh) {
+		exfat_msg(sb, KERN_ERR, "unable to read bitmap sector at %llu",
+			  ctx->cur_sector);
+		return -EIO;
+	}
+	return 0;
+}
+
+/*
+ * release bh in an already setup bitmap context.
+ */
+void exfat_exit_bitmap_context(struct exfat_bitmap_ctx *ctx)
+{
+	if (ctx->bh)
+		brelse(ctx->bh);
+}
+
+/*
+ * test a specific cluster usage in the bitmap. reuse the bh in the
+ * exfat_bitmap_ctx or read a new one if starting cluster is outside
+ * the current one.
+ */
+static int exfat_test_bitmap_cluster(struct exfat_bitmap_ctx *ctx,
+				     uint32_t cluster, bool *cluster_in_use)
+{
+	sector_t sect;
+	uint32_t off = exfat_bitmap_off(EXFAT_SB(ctx->sb), cluster);
+	int shift = exfat_bitmap_shift(cluster);
+
+	sect = exfat_bitmap_sector(EXFAT_SB(ctx->sb), cluster);
+	if (sect != ctx->cur_sector) {
+		ctx->cur_sector = sect;
+		ctx->bh = sb_bread(ctx->sb, ctx->cur_sector);
+		if (!ctx->bh) {
+			exfat_msg(ctx->sb, KERN_ERR,
+				  "unable to read bitmap sector at %llu", sect);
+			return -EIO;
+		}
+	}
+
+	*cluster_in_use = !!(ctx->bh->b_data[off] & (1 << shift));
+	return 0;
+}
+
+/*
+ * update first_in_use and nr_in_use with the first zone of used
+ * clusters starting from start_cluster.
+ */
+int exfat_test_bitmap(struct exfat_bitmap_ctx *ctx, uint32_t start_cluster,
+		      uint32_t *first_in_use, uint32_t *nr_in_use)
+{
+	bool in_use = false;
+	int error = 0;
+	struct exfat_sb_info *sbi = EXFAT_SB(ctx->sb);
+
+	start_cluster -= 2;
+
+	/*
+	 * scan bitmap until we find a cluster that is in use.
+	 */
+	while (1) {
+		if (start_cluster == sbi->cluster_count) {
+			/*
+			 * readched end of disk: no more in use
+			 * cluster found.
+			 */
+			*first_in_use = sbi->cluster_count;
+			*nr_in_use = 0;
+			return 0;
+		}
+		error = exfat_test_bitmap_cluster(ctx, start_cluster, &in_use);
+		if (error)
+			return error;
+		if (in_use)
+			break;
+		++start_cluster;
+	}
+
+
+	/*
+	 * update first_in_use, and scan until a free cluster is
+	 * found.
+	 */
+	*first_in_use = start_cluster + 2;
+	*nr_in_use = 0;
+	while (1) {
+		error = exfat_test_bitmap_cluster(ctx, start_cluster, &in_use);
+		if (error)
+			return error;
+		if (!in_use)
+			break;
+		++(*nr_in_use);
+		++start_cluster;
+	}
+	return 0;
+}
+
+int exfat_init_bitmap(struct inode *root)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(root->i_sb);
+	struct exfat_bitmap_entry *be;
+	struct exfat_dir_ctx dctx;
+	u32 first_bitmap_cluster;
+	u32 last_bitmap_cluster;
+
+	int error;
+
+	mutex_init(&sbi->bitmap_mutex);
+
+	error = exfat_init_dir_ctx(root, &dctx, 0);
+	if (error)
+		return error;
+
+try_bitmap:
+	error = -ENOENT;
+	be = __exfat_dentry_next(&dctx, E_EXFAT_BITMAP, 0xff, true, NULL);
+	if (!be) {
+		exfat_msg(root->i_sb, KERN_ERR, "root directory does not "
+			  "have a bitmap entry.");
+		goto fail;
+	}
+
+	if (exfat_bitmap_nr(be->flags) != 0)
+		/*
+		 * not expected to find a second bitmap entry here
+		 * since we checked during superblock fill that we
+		 * were not on a texFAT volume ...
+		 */
+		goto try_bitmap;
+
+
+	error = -EINVAL;
+	if (__le64_to_cpu(be->length) * 8 < sbi->cluster_count) {
+		exfat_msg(root->i_sb, KERN_INFO, "bitmap does not cover "
+			  "the whole cluster heap.");
+		goto fail;
+	}
+
+	first_bitmap_cluster = __le32_to_cpu(be->cluster_addr);
+	last_bitmap_cluster = first_bitmap_cluster +
+		(__le32_to_cpu(be->length) >> sbi->clusterbits);
+
+	/*
+	 * check that bitmap start and end clusters are inside the
+	 * disk.
+	 */
+	error = -ERANGE;
+	if (first_bitmap_cluster < 2 &&
+	    first_bitmap_cluster >= sbi->cluster_count) {
+		exfat_msg(root->i_sb, KERN_ERR, "bitmap start cluster is "
+			  "outside disk limits.");
+		goto fail;
+	}
+	if (last_bitmap_cluster < 2 &&
+	    last_bitmap_cluster >= sbi->cluster_count) {
+		exfat_msg(root->i_sb, KERN_ERR, "bitmap last cluster is "
+			  "outside disk limits.");
+		goto fail;
+	}
+
+	sbi->bitmap_length = __le32_to_cpu(be->length);
+	sbi->first_bitmap_sector = exfat_cluster_sector(sbi,
+					__le32_to_cpu(be->cluster_addr));
+	sbi->last_bitmap_sector = sbi->first_bitmap_sector +
+		DIV_ROUND_UP(sbi->bitmap_length, sbi->sectorsize);
+
+	error = exfat_get_free_cluster_count(root->i_sb, &sbi->free_clusters);
+	if (error)
+		goto fail;
+
+	sbi->prev_free_cluster = 0;
+
+	exfat_cleanup_dir_ctx(&dctx);
+	return 0;
+fail:
+	exfat_cleanup_dir_ctx(&dctx);
+	return error;
+}
+
+void exfat_exit_bitmap(struct super_block *sb)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+	if (sbi->cur_bitmap_bh)
+		brelse(sbi->cur_bitmap_bh);
+}
diff -Nruw linux-3.11.10-fbx/fs/exfat./dir.c linux-3.11.10-fbx/fs/exfat/dir.c
--- linux-3.11.10-fbx/fs/exfat./dir.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/dir.c	2015-06-12 14:02:26.806990175 +0200
@@ -0,0 +1,400 @@
+/*
+ * dir.c for exfat
+ * Created by <nschichan@freebox.fr> on Tue Aug 20 11:42:46 2013
+ */
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/slab.h>
+#include <linux/nls.h>
+
+#include "exfat.h"
+#include "exfat_fs.h"
+
+/*
+ * setup an exfat_dir_ctx structure so that __exfat_dentry_next can
+ * work with it.
+ */
+int exfat_init_dir_ctx(struct inode *inode, struct exfat_dir_ctx *ctx,
+		       off_t start)
+{
+	u32 cluster = EXFAT_I(inode)->first_cluster;
+
+	memset(ctx, 0, sizeof (*ctx));
+
+	if (cluster == 0) {
+		ctx->empty = true;
+		ctx->sb = inode->i_sb;
+		return 0;
+	}
+
+	if (cluster < EXFAT_CLUSTER_FIRSTVALID ||
+	    cluster > EXFAT_CLUSTER_LASTVALID) {
+		exfat_msg(inode->i_sb, KERN_ERR, "exfat_init_dir_ctx: invalid "
+			  "cluster %u", cluster);
+		return -EINVAL;
+	}
+
+	start &= ~(0x20 - 1);
+	if (start == 0)
+		ctx->off = -1;
+	else
+		ctx->off = start - 0x20;
+
+	ctx->sb = inode->i_sb;
+	ctx->inode = inode;
+
+	return 0;
+}
+
+void exfat_cleanup_dir_ctx(struct exfat_dir_ctx *dctx)
+{
+	if (dctx->bh)
+		brelse(dctx->bh);
+}
+
+/*
+ * calculate the checksum for the current direntry. fields containing
+ * the checksum for the first entry is not part of the checksum
+ * calculation.
+ */
+u16 exfat_direntry_checksum(void *data, u16 checksum, bool first)
+{
+	u8 *ptr = data;
+	int i;
+
+	for (i = 0; i < 0x20; ++i) {
+		if (first && (i == 2 || i == 3))
+			continue ;
+		checksum = ((checksum << 15) | (checksum >> 1)) + (u16)ptr[i];
+	}
+	return checksum;
+}
+
+u32 exfat_dctx_fpos(struct exfat_dir_ctx *dctx)
+{
+	return dctx->off;
+}
+
+u64 exfat_dctx_dpos(struct exfat_dir_ctx *dctx)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(dctx->sb);
+
+	return (dctx->sector << sbi->sectorbits) +
+		(dctx->off & sbi->sectormask);
+}
+
+static int exfat_get_dctx_disk_cluster(struct exfat_dir_ctx *dctx,
+				       u32 file_cluster, u32 *disk_cluster)
+{
+	struct exfat_inode_info *info = EXFAT_I(dctx->inode);
+
+	if (info->flags & EXFAT_I_FAT_INVALID) {
+		*disk_cluster = info->first_cluster + file_cluster;
+		return 0;
+	} else {
+		return exfat_get_fat_cluster(dctx->inode, file_cluster,
+					     disk_cluster);
+	}
+}
+
+/*
+ * get the next typed dentry in the exfat_dir_ctx structure. can_skip
+ * indicates whether the entry must be immediately there in the entry
+ * stream. *end indicates whether end of directory entry stream is
+ * reached or not.
+ *
+ * only one buffer_head is kept at a time. subsequent calls to
+ * __exfat_dentry_next can invalidate pointers from previous calls due
+ * to that.
+ */
+void *__exfat_dentry_next(struct exfat_dir_ctx *dctx, int type, int mask,
+			  bool can_skip, bool *end)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(dctx->sb);
+
+	if (dctx->empty) {
+		if (end)
+			*end = true;
+		return NULL;
+	}
+
+	if (end)
+		*end = false;
+
+	if (dctx->off == -1)
+		dctx->off = 0;
+	else
+		dctx->off += 0x20;
+
+	for (;;) {
+		sector_t wanted_sector;
+		u32 file_cluster = dctx->off >> sbi->clusterbits;
+		u32 disk_cluster;
+		int error;
+		int sector_offset;
+		sector_t sector_in_cluster;
+
+		if (dctx->off >= dctx->inode->i_size) {
+			*end = true;
+			return NULL;
+		}
+
+
+		error = exfat_get_dctx_disk_cluster(dctx, file_cluster,
+						    &disk_cluster);
+		if (error)
+			return NULL;
+
+		sector_in_cluster = (dctx->off >> sbi->sectorbits) %
+			sbi->sectors_per_cluster;
+
+		wanted_sector = exfat_cluster_sector(sbi, disk_cluster) +
+			sector_in_cluster;
+		if (wanted_sector != dctx->sector || !dctx->bh) {
+			/*
+			 * need to fetch a new sector from the current
+			 * cluster.
+			 */
+			dctx->sector = wanted_sector;
+			if (dctx->bh)
+				brelse(dctx->bh);
+			dctx->bh = sb_bread(dctx->sb, dctx->sector);
+			if (!dctx->bh)
+				return NULL;
+		}
+
+		sector_offset = dctx->off & sbi->sectormask;
+		if ((dctx->bh->b_data[sector_offset] & mask) == (type & mask))
+			/*
+			 * return pointer to entry if type matches the
+			 * one given.
+			 */
+			return dctx->bh->b_data + sector_offset;
+
+		if (dctx->bh->b_data[sector_offset] == 0 && end)
+			/*
+			 * set end if no more entries in this directory.
+			 */
+			*end = true;
+
+		if (dctx->bh->b_data[sector_offset] == 0 || !can_skip)
+			/*
+			 * handle can_skip / end of directory.
+			 */
+			return NULL;
+
+		/*
+		 * move to next entry.
+		 */
+		dctx->off += 0x20;
+	}
+	return NULL;
+}
+
+/*
+ * helper around __exfat_dentry_next that copies the content of the
+ * found entry in a user supplied buffer.
+ */
+int exfat_dentry_next(void *out, struct exfat_dir_ctx *dctx,
+			     int type, bool can_skip)
+{
+	bool end;
+
+	void *ptr = __exfat_dentry_next(dctx, type, 0xff, can_skip, &end);
+
+	if (!ptr) {
+		if (end)
+			return -ENOENT;
+		else {
+			exfat_msg(dctx->sb, KERN_INFO, "no ptr and "
+				  "end not reached: "
+				  "type %02x, can_skip %s\n", type,
+				  can_skip ? "true" : "false");
+			return -EIO;
+		}
+	}
+	memcpy(out, ptr, 0x20);
+	return 0;
+}
+
+/*
+ * extract name by parsing consecutive E_EXFAT_FILENAME entries in a
+ * caller provided buffer. also update the checksum on the fly.
+ *
+ * no utf16 to utf8 conversion is performed.
+ */
+int __exfat_get_name(struct exfat_dir_ctx *dctx, u32 name_length,
+			    __le16 *name, u16 *calc_checksum,
+			    struct exfat_iloc *iloc)
+{
+	__le16 *ptr;
+	int error;
+	int nr;
+
+	ptr = name;
+
+	error = -EIO;
+	nr = 0;
+	while (name_length) {
+		struct exfat_filename_entry *e;
+		u32 len = 15;
+
+		e = __exfat_dentry_next(dctx, E_EXFAT_FILENAME, 0xff,
+					false, NULL);
+		if (!e)
+			goto fail;
+		*calc_checksum = exfat_direntry_checksum(e, *calc_checksum,
+							 false);
+
+		if (iloc)
+			iloc->disk_offs[nr + 2] = exfat_dctx_dpos(dctx);
+		if (name_length < 15)
+			len = name_length;
+
+		memcpy(ptr, e->name_frag, len * sizeof (__le16));
+		name_length -= len;
+		ptr += len;
+		nr++;
+	}
+	return 0;
+
+fail:
+	return error;
+}
+
+/*
+ * walk the directory and invoke filldir on all found entries.
+ */
+static int __exfat_iterate(struct exfat_dir_ctx *dctx, struct file *file,
+			   struct dir_context *ctx)
+{
+	int error;
+	char *name = __getname();
+	__le16 *utf16name = __getname();
+
+	if (!name)
+		return -ENOMEM;
+	if (!utf16name) {
+		__putname(name);
+		return -ENOMEM;
+	}
+
+	for (;;) {
+		struct exfat_filedir_entry *efd;
+		struct exfat_stream_extension_entry *esx;
+		int dtype = DT_REG;
+		int name_length;
+		bool end;
+		u16 calc_checksum;
+		u16 expect_checksum;
+
+		/*
+		 * get the next filedir entry, we are allowed to skip
+		 * entries for that.
+		 */
+		error = -EIO;
+		efd = __exfat_dentry_next(dctx, E_EXFAT_FILEDIR, 0xff,
+					  true, &end);
+		if (!efd) {
+			if (end)
+				break;
+			else
+				goto fail;
+		}
+		expect_checksum = __le16_to_cpu(efd->set_checksum);
+		calc_checksum = exfat_direntry_checksum(efd, 0, true);
+
+		if (__le16_to_cpu(efd->attributes & E_EXFAT_ATTR_DIRECTORY))
+			dtype = DT_DIR;
+
+		/*
+		 * get immediate stream extension entry.
+		 */
+		esx = __exfat_dentry_next(dctx, E_EXFAT_STREAM_EXT, 0xff, false,
+					  NULL);
+		if (!esx)
+			goto fail;
+		calc_checksum = exfat_direntry_checksum(esx, calc_checksum,
+							false);
+
+		/*
+		 * get immediate name.
+		 */
+		error = __exfat_get_name(dctx, esx->name_length, utf16name,
+					 &calc_checksum, NULL);
+		if (error) {
+			exfat_msg(dctx->sb, KERN_INFO, "__exfat_get_name "
+				  "has failed with %i", error);
+			goto fail;
+		}
+
+		if (calc_checksum != expect_checksum) {
+			exfat_msg(dctx->sb, KERN_INFO, "checksum: "
+				  "calculated %04x, expect %04x",
+				  calc_checksum, expect_checksum);
+			error = -EIO;
+			goto fail;
+		}
+
+		/*
+		 * convert utf16 to utf8 for kernel filldir callback.
+		 */
+		name_length = utf16s_to_utf8s(utf16name, esx->name_length,
+						   UTF16_LITTLE_ENDIAN,
+						   name, NAME_MAX + 2);
+		if (name_length < 0) {
+			error = name_length;
+			goto fail;
+		}
+		if (name_length > 255) {
+			error = -ENAMETOOLONG;
+			goto fail;
+		}
+
+		/*
+		 * tell the kernel we have an entry by calling
+		 * dir_emit
+		 */
+		if (dir_emit(ctx, name, name_length, 1, dtype))
+			ctx->pos = 2 + exfat_dctx_fpos(dctx);
+		else
+			goto fail;
+	}
+	__putname(name);
+	__putname(utf16name);
+	ctx->pos = file_inode(file)->i_size + 2;
+	return 0;
+fail:
+	__putname(name);
+	__putname(utf16name);
+	return error;
+}
+
+/*
+ * readdir callback for VFS. fill "." and "..", then invoke
+ * __exfat_iterate.
+ */
+int exfat_iterate(struct file *file, struct dir_context *ctx)
+{
+	struct exfat_dir_ctx dctx;
+	int error;
+	struct inode *inode = file_inode(file);
+
+	switch (ctx->pos) {
+	case 0:
+		return dir_emit_dots(file, ctx);
+	default:
+		if (ctx->pos >= inode->i_size + 2)
+			return 0;
+		error = exfat_init_dir_ctx(inode, &dctx, ctx->pos - 2);
+		if (error)
+			return error;
+		exfat_lock_super(inode->i_sb);
+		error = __exfat_iterate(&dctx, file, ctx);
+		exfat_unlock_super(inode->i_sb);
+		exfat_cleanup_dir_ctx(&dctx);
+		return error;
+	}
+}
diff -Nruw linux-3.11.10-fbx/fs/exfat./exfat_fs.h linux-3.11.10-fbx/fs/exfat/exfat_fs.h
--- linux-3.11.10-fbx/fs/exfat./exfat_fs.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/exfat_fs.h	2015-06-12 14:02:26.806990175 +0200
@@ -0,0 +1,200 @@
+/*
+ * exfat_fs.h for exfat
+ * Created by <nschichan@freebox.fr> on Mon Jul 29 15:06:38 2013
+ */
+
+#ifndef __EXFAT_FS_H
+# define __EXFAT_FS_H
+
+/*
+ * exfat on disk structures and constants
+ */
+
+#include <linux/types.h>
+
+struct exfat_vbr {
+	u8	jump[3];
+	u8	fsname[8];
+	u8	reserved1[53];
+
+	__le64	partition_offset;
+	__le64	volume_length;
+
+	__le32	fat_offset;
+	__le32	fat_length;
+
+	__le32	cluster_heap_offset;
+	__le32	cluster_count;
+	__le32	cluster_root_dir;
+
+	__le32	serial_number;
+
+	__le16	fs_rev;
+	__le16	volume_flags;
+
+	u8	bytes_per_sector;
+	u8	sectors_per_cluster;
+
+	u8	fat_num;
+	u8	drive_select;
+	u8	heap_use_percent;
+
+	u8	reserved2[7];
+	u8	boot_code[390];
+
+	u8	boot_sig[2];
+};
+
+enum {
+	EXFAT_CLUSTER_FIRSTVALID	= 0x00000002,
+	EXFAT_CLUSTER_LASTVALID		= 0xfffffff6,
+	EXFAT_CLUSTER_BADBLK		= 0xfffffff7,
+	EXFAT_CLUSTER_MEDIATYPE		= 0xfffffff8,
+	EXFAT_CLUSTER_EOF		= 0xffffffff,
+};
+
+enum {
+	EXFAT_ACTIVEFAT_MASK = (1 << 0),
+	EXFAT_FLAG_DIRTY = (1 << 1),
+	EXFAT_FLAG_MEDIA_FAILURE = (1 << 2),
+};
+
+static inline int exfat_active_fat(u16 flags)
+{
+	return flags & EXFAT_ACTIVEFAT_MASK;
+}
+
+#define EXFAT_CHECKSUM_SECTORS	11
+
+enum {
+	EXFAT_I_ALLOC_POSSIBLE = (1 << 0),
+	EXFAT_I_FAT_INVALID = (1 << 1),
+};
+
+/*
+ * directory cluster content
+ */
+
+/*
+ * entry types
+ */
+enum {
+	E_EXFAT_EOD		= 0x00,
+	E_EXFAT_VOLUME_LABEL	= 0x83,
+	E_EXFAT_BITMAP		= 0x81,
+	E_EXFAT_UPCASE_TABLE	= 0x82,
+	E_EXFAT_GUID		= 0xa0,
+	E_EXFAT_PADDING		= 0xa1,
+	E_EXFAT_ACL		= 0xe2,
+	E_EXFAT_FILEDIR		= 0x85,
+	E_EXFAT_STREAM_EXT	= 0xc0,
+	E_EXFAT_FILENAME	= 0xc1,
+};
+
+/*
+ * file attributes in exfat_filedir_entry
+ */
+enum {
+	E_EXFAT_ATTR_RO		= (1 << 0),
+	E_EXFAT_ATTR_HIDDEN	= (1 << 1),
+	E_EXFAT_ATTR_SYSTEM	= (1 << 2),
+	/* bit 3 reserved */
+	E_EXFAT_ATTR_DIRECTORY	= (1 << 4),
+	E_EXFAT_ATTR_ARCHIVE	= (1 << 5),
+	/* bits 6-15 reserved */
+};
+
+/* type 0x83 */
+struct exfat_volume_label_entry {
+	u8 type;
+	u8 charcount;
+	__u16 label[11];
+	u8 reserved1[8];
+};
+
+static inline int exfat_bitmap_nr(u8 flags)
+{
+	return flags & 1;
+}
+
+/* type 0x81 */
+struct exfat_bitmap_entry {
+	u8 type;
+	u8 flags;
+	u8 reserved1[18];
+	__le32 cluster_addr;
+	__le64 length;
+};
+
+/* type 0x82 */
+struct exfat_upcase_entry {
+	u8 type;
+	u8 reserved1[3];
+	__le32 checksum;
+	u8 reserved2[12];
+	__le32 cluster_addr;
+	__le64 length;
+};
+
+/* type 0xa0 */
+struct exfat_guid_entry {
+	u8 type;
+	u8 secondary_count;
+	__le16 set_checksum;
+	__le16 flags;
+	u8 guid[16];
+	u8 reserved1[10];
+};
+
+/* type 0xa1 */
+struct exfat_padding_entry {
+	u8 type;
+	u8 reserved1[31];
+};
+
+/* type 0xe2 */
+struct exfat_acl_entry {
+	u8 type;
+	u8 reserved1[31];
+};
+
+/* type 0x85 */
+struct exfat_filedir_entry {
+	u8 type;
+	u8 secondary_count;
+	__le16 set_checksum;
+	__le16 attributes;
+	u8 reserved1[2];
+	__le32 create;
+	__le32 modified;
+	__le32 accessed;
+	u8 create_10ms;
+	u8 modified_10ms;
+	s8 create_tz_offset;
+	s8 modified_tz_offset;
+	s8 accessed_tz_offset;
+	u8 reserved2[7];
+};
+
+/* 0xc0 */
+struct exfat_stream_extension_entry {
+	u8 type;
+	u8 flags;
+	u8 reserved1;
+	u8 name_length;
+	__le16 name_hash;
+	u8 reserved2[2];
+	__le64 valid_data_length;
+	u8 reserved3[4];
+	__le32 first_cluster;
+	__le64 data_length;
+};
+
+/* 0xc1 */
+struct exfat_filename_entry {
+	u8 type;
+	u8 flags;
+	__le16 name_frag[15];
+};
+
+#endif /*! __EXFAT_FS_H */
diff -Nruw linux-3.11.10-fbx/fs/exfat./exfat.h linux-3.11.10-fbx/fs/exfat/exfat.h
--- linux-3.11.10-fbx/fs/exfat./exfat.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/exfat.h	2015-06-12 14:02:26.806990175 +0200
@@ -0,0 +1,325 @@
+/*
+ * exfat.h for exfat
+ * Created by <nschichan@freebox.fr> on Tue Jul 23 12:37:12 2013
+ */
+
+#ifndef __EXFAT_H
+# define __EXFAT_H
+
+#define EXFAT_HASH_BITS	(8)
+#define EXFAT_HASH_SIZE	(1 << EXFAT_HASH_BITS)
+
+/*
+ * special inode number for root directory.
+ */
+#define EXFAT_ROOT_INO	1
+
+enum {
+	EXFAT_ERROR_ACTION_CONTINUE,
+	EXFAT_ERROR_ACTION_REMOUNT_RO,
+	EXFAT_ERROR_ACTION_PANIC,
+};
+
+struct exfat_sb_options {
+	kuid_t	uid;
+	kgid_t	gid;
+	mode_t	dmask;
+	mode_t	fmask;
+	int	time_offset;
+	int	time_offset_set;
+	int	error_action;
+};
+
+struct exfat_sb_info {
+	struct exfat_sb_options options;
+
+	struct buffer_head *sb_bh;
+	struct exfat_vbr *vbr;
+	bool dirty;
+
+	u32 sectorsize; /* in bytes*/
+	u32 clustersize; /* in bytes */
+	u32 sectors_per_cluster;
+	int sectorbits;
+	int clusterbits;
+	u32 sectormask;
+	u32 clustermask;
+
+	u32 fat_offset;
+	u32 fat_length;
+
+	u32 root_dir_cluster;
+	u32 cluster_heap_offset;
+	u32 cluster_count;
+
+	__le16	*upcase_table;
+	u32	upcase_len;
+
+	/*
+	 * bitmap fields
+	 */
+	struct mutex		bitmap_mutex;
+	u32			bitmap_length;
+	sector_t		first_bitmap_sector;
+	sector_t		last_bitmap_sector;
+	sector_t		cur_bitmap_sector;
+	u32			cur_bitmap_cluster;
+	struct buffer_head	*cur_bitmap_bh;
+	u32			free_clusters;
+	u32			prev_free_cluster;
+
+	/*
+	 * inode hash fields
+	 */
+	spinlock_t		inode_hash_lock;
+	struct hlist_head	inode_hash[EXFAT_HASH_SIZE];
+
+	struct mutex		sb_mutex;
+};
+
+struct exfat_cache_entry {
+	struct list_head list;
+	u32 file_cluster;
+	u32 disk_cluster;
+	u32 nr_contig;
+};
+
+struct exfat_cache {
+	struct mutex		mutex;
+	struct list_head	entries;
+	u32			nr_entries;
+};
+
+struct exfat_iloc {
+	u8 nr_secondary;
+	u32 file_off;
+	u64 disk_offs[19];
+};
+
+struct exfat_inode_info {
+	u8			flags;
+	u16			attributes;
+	u32			first_cluster;
+	u32			allocated_clusters;
+	loff_t			mmu_private;
+	struct exfat_iloc	iloc;
+	struct hlist_node	hash_list;
+
+	struct exfat_cache	exfat_cache;
+	struct inode		vfs_inode;
+};
+
+static inline struct exfat_sb_info *EXFAT_SB(struct super_block *sb)
+{
+	return sb->s_fs_info;
+}
+
+static inline struct exfat_inode_info *EXFAT_I(struct inode *inode)
+{
+	return container_of(inode, struct exfat_inode_info, vfs_inode);
+}
+
+loff_t exfat_dir_links(struct inode *inode);
+
+int exfat_write_fat_contiguous(struct inode *inode, u32 first_cluster,
+			       u32 nr_clusters);
+int exfat_write_fat(struct inode *inode, u32 prev_cluster, u32 *clusters,
+		    u32 nr_clusters);
+
+__printf(3, 4) void exfat_msg(struct super_block *sb, const char *level,
+			      const char *fmt, ...);
+__printf(2, 3) void exfat_fs_error(struct super_block *sb,
+				   const char *fmt, ...);
+int exfat_get_fat_cluster(struct inode *inode, u32 fcluster, u32 *dcluster);
+int __exfat_get_fat_cluster(struct inode *inode, u32 fcluster, u32 *dcluster,
+			    bool eof_is_fatal);
+
+void exfat_inode_cache_init(struct inode *inode);
+void exfat_inode_cache_drop(struct inode *inode);
+
+int exfat_init_fat(struct super_block *sb);
+
+int exfat_init_bitmap(struct inode *root);
+void exfat_exit_bitmap(struct super_block *sb);
+int exfat_alloc_clusters(struct inode *inode, u32 hint_cluster,
+			 u32 *cluster, u32 nr);
+int exfat_free_clusters_inode(struct inode *inode, u32 start);
+
+
+/*
+ * read only bitmap accessors: used by EXFAT_IOCGETBITMAP ioctl.
+ */
+struct exfat_bitmap_ctx {
+	struct super_block *sb;
+	struct buffer_head *bh;
+	sector_t cur_sector;
+};
+
+int exfat_init_bitmap_context(struct super_block *sb,
+			      struct exfat_bitmap_ctx *ctx, u32 cluster);
+void exfat_exit_bitmap_context(struct exfat_bitmap_ctx *ctx);
+int exfat_test_bitmap(struct exfat_bitmap_ctx *ctx, uint32_t start_cluster,
+		      uint32_t *first_in_use, uint32_t *nr_in_use);
+
+
+/*
+ * return the physical sector address for a given cluster.
+ */
+static inline sector_t exfat_cluster_sector(struct exfat_sb_info *sbi,
+					    u32 cluster)
+{
+	return (sector_t)sbi->cluster_heap_offset + (cluster - 2) *
+		(sector_t)sbi->sectors_per_cluster;
+}
+
+/*
+ * in dir.c
+ */
+struct exfat_dir_ctx {
+	struct super_block	*sb;
+	struct inode		*inode;
+	struct buffer_head	*bh;
+
+	off_t			off; /* from beginning of directory */
+	sector_t		sector;
+	bool empty;
+};
+
+int exfat_init_dir_ctx(struct inode *inode, struct exfat_dir_ctx *ctx,
+		       off_t off);
+void exfat_cleanup_dir_ctx(struct exfat_dir_ctx *dctx);
+int exfat_get_cluster_hint(struct inode *inode, u32 *out_hint);
+int exfat_dentry_next(void *, struct exfat_dir_ctx *, int, bool);
+void *__exfat_dentry_next(struct exfat_dir_ctx *dctx, int type, int mask,
+			  bool can_skip, bool *end);
+u16 exfat_direntry_checksum(void *data, u16 checksum, bool first);
+u32 exfat_dctx_fpos(struct exfat_dir_ctx *dctx);
+u64 exfat_dctx_dpos(struct exfat_dir_ctx *dctx);
+int __exfat_get_name(struct exfat_dir_ctx *dctx, u32 name_length, __le16 *name,
+		     u16 *calc_checksum, struct exfat_iloc *iloc);
+
+/*
+ * in namei.c
+ */
+
+/*
+ * hold a pointer to an exfat dir entry, with the corresponding bh.
+ */
+struct dir_entry_buffer {
+	struct buffer_head *bh;
+	u32 off; /* in bytes, inside the buffer_head b_data array */
+	void *start;
+};
+
+int exfat_get_dir_entry_buffers(struct inode *dir, struct exfat_iloc *iloc,
+				struct dir_entry_buffer *entries,
+				size_t nr_entries);
+u16 exfat_dir_entries_checksum(struct dir_entry_buffer *entries, u32 nr);
+void exfat_dirty_dir_entries(struct dir_entry_buffer *entries,
+			     size_t nr_entries, bool sync);
+void exfat_write_time(struct exfat_sb_info *sbi, struct timespec *ts,
+		      __le32 *datetime, u8 *time_cs, u8 *tz_offset);
+
+/*
+ * in inode.c
+ */
+
+int exfat_init_inodes(void);
+void exfat_exit_inodes(void);
+
+struct inode *exfat_iget(struct super_block *sb, loff_t disk_pos);
+void exfat_insert_inode_hash(struct inode *inode);
+void exfat_remove_inode_hash(struct inode *inode);
+int __exfat_write_inode(struct inode *inode, bool sync);
+
+/*
+ * in upcase.c
+ */
+int exfat_upcase_init(struct inode *root);
+static inline __le16 exfat_upcase_convert(struct super_block *sb, __le16 _c)
+{
+	u16 c = __le16_to_cpu(_c);
+
+	if (c >= EXFAT_SB(sb)->upcase_len)
+		return _c;
+	return EXFAT_SB(sb)->upcase_table[c];
+}
+
+/*
+ * superblock operations
+ */
+struct inode *exfat_alloc_inode(struct super_block *sb);
+void exfat_destroy_inode(struct inode *_inode);
+int exfat_drop_inode(struct inode *inode);
+void exfat_evict_inode(struct inode *inode);
+
+/*
+ * file operations
+ */
+int exfat_iterate(struct file *f, struct dir_context *ctx);
+long exfat_ioctl(struct file *, unsigned int, unsigned long);
+int exfat_truncate_blocks(struct inode *inode, loff_t newsize);
+
+/*
+ * inode operations
+ */
+struct dentry *exfat_inode_lookup(struct inode *, struct dentry *,
+				  unsigned int);
+int exfat_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+		       bool excl);
+int exfat_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode);
+
+mode_t exfat_make_mode(struct exfat_sb_info *sbi, mode_t mode, u16 attrs);
+
+int exfat_write_inode(struct inode *inode, struct writeback_control *wbc);
+
+int exfat_inode_unlink(struct inode *inode, struct dentry *dentry);
+
+int exfat_inode_rmdir(struct inode *inode, struct dentry *dentry);
+
+int exfat_getattr(struct vfsmount *, struct dentry *, struct kstat *);
+int exfat_setattr(struct dentry *, struct iattr *);
+int exfat_rename(struct inode *, struct dentry *,
+		 struct inode *, struct dentry *);
+
+/*
+ * address space operations
+ */
+int exfat_readpage(struct file *file, struct page *page);
+int exfat_readpages(struct file *file, struct address_space *mapping,
+		    struct list_head *pages, unsigned nr_pages);
+int exfat_write_begin(struct file *file, struct address_space *mapping,
+		      loff_t pos, unsigned len, unsigned flags,
+		      struct page **pagep, void **fsdata);
+int exfat_write_end(struct file *file, struct address_space *mapping,
+		    loff_t pos, unsigned len, unsigned copied,
+		    struct page *page, void *fsdata);
+int exfat_writepage(struct page *page, struct writeback_control *wbc);
+int exfat_writepages(struct address_space *, struct writeback_control *);
+
+
+extern const struct inode_operations exfat_dir_inode_operations;
+extern const struct inode_operations exfat_file_inode_operations;
+extern const struct file_operations exfat_dir_operations;
+extern const struct file_operations exfat_file_operations;
+extern const struct address_space_operations exfat_address_space_operations;
+
+/*
+ * time functions
+ */
+void exfat_time_2unix(struct timespec *ts, u32 datetime, u8 time_cs,
+		      s8 tz_offset);
+void exfat_time_2exfat(struct exfat_sb_info *sbi, struct timespec *ts,
+		       u32 *datetime, u8 *time_cs, s8 *tz_offset);
+
+static inline void exfat_lock_super(struct super_block *sb)
+{
+	mutex_lock(&EXFAT_SB(sb)->sb_mutex);
+}
+
+static inline void exfat_unlock_super(struct super_block *sb)
+{
+	mutex_unlock(&EXFAT_SB(sb)->sb_mutex);
+}
+
+#endif /*! __EXFAT_H */
diff -Nruw linux-3.11.10-fbx/fs/exfat./fat.c linux-3.11.10-fbx/fs/exfat/fat.c
--- linux-3.11.10-fbx/fs/exfat./fat.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/fat.c	2015-06-12 14:02:26.806990175 +0200
@@ -0,0 +1,424 @@
+/*
+ * fat.c for exfat
+ * Created by <nschichan@freebox.fr> on Mon Jul 29 19:43:38 2013
+ */
+
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/slab.h>
+
+#include "exfat.h"
+#include "exfat_fs.h"
+
+#define MAX_CACHED_FAT	16
+
+/*
+ * helpers for exfat_next_fat_cluster.
+ */
+
+/*
+ * get the sector number in the fat where the next requested cluster
+ * number is to be found.
+ */
+static inline sector_t cluster_sector(struct exfat_sb_info *sbi, u32 cluster)
+{
+	return sbi->fat_offset + (((u64)cluster * sizeof (u32)) >> sbi->sectorbits);
+}
+
+/*
+ * get the offset in the fat sector where the next requested cluster
+ * number is to be found.
+ */
+static inline off_t cluster_offset(struct exfat_sb_info *sbi, u32 cluster)
+{
+	return (cluster * sizeof (u32)) & sbi->sectormask;
+}
+
+/*
+ * walk one step in the fat chain.
+ */
+static int exfat_next_fat_cluster(struct super_block *sb, u32 *cluster)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	sector_t sect = cluster_sector(sbi, *cluster);
+	off_t off = cluster_offset(sbi, *cluster);
+	struct buffer_head *bh;
+
+	bh = sb_bread(sb, sect);
+	if (!bh) {
+		exfat_msg(sb, KERN_ERR, "unable to read FAT sector at %llu",
+			  sect);
+		return -EIO;
+	}
+
+	*cluster = __le32_to_cpu(*(u32*)&bh->b_data[off]);
+	brelse(bh);
+	return 0;
+}
+
+/*
+ * setup inode cache
+ */
+void exfat_inode_cache_init(struct inode *inode)
+{
+	mutex_init(&EXFAT_I(inode)->exfat_cache.mutex);
+	EXFAT_I(inode)->exfat_cache.nr_entries = 0;
+	INIT_LIST_HEAD(&EXFAT_I(inode)->exfat_cache.entries);
+}
+
+/*
+ * drop inode cache content
+ */
+void exfat_inode_cache_drop(struct inode *inode)
+{
+	struct exfat_cache *cache = &EXFAT_I(inode)->exfat_cache;
+	struct exfat_cache_entry *e, *tmp;
+
+	mutex_lock(&cache->mutex);
+	list_for_each_entry_safe (e, tmp, &cache->entries, list) {
+		kfree(e);
+	}
+	INIT_LIST_HEAD(&cache->entries);
+	cache->nr_entries = 0;
+	mutex_unlock(&cache->mutex);
+}
+
+/*
+ * move the entry to the head of the list, this will make it less
+ * likely to be the victim in when caching new entries.
+ *
+ * caller must hold cache->mutex.
+ */
+static void __exfat_fat_lru(struct exfat_cache *cache,
+			  struct exfat_cache_entry *e)
+{
+	if (cache->entries.next != &e->list)
+		list_move(&e->list, &cache->entries);
+}
+
+/*
+ * find a cache entry that is close to the wanted fcluster (ideally
+ * spanning over the requested file cluster).
+ *
+ * caller must hold cache->mutex.
+ */
+static struct exfat_cache_entry *__exfat_cache_lookup(struct exfat_cache *cache,
+						      u32 fcluster)
+{
+	struct exfat_cache_entry *e;
+	struct exfat_cache_entry *best = NULL;
+
+	list_for_each_entry (e, &cache->entries, list) {
+		if (e->file_cluster <= fcluster &&
+		    e->file_cluster + e->nr_contig >= fcluster)
+			return e;
+
+		if (!best && e->file_cluster < fcluster)
+			best = e;
+		if (best && best->file_cluster < e->file_cluster &&
+		    e->file_cluster < fcluster)
+			best = e;
+	}
+	return best;
+}
+
+/*
+ * caller must hold cache->mutex.
+ */
+static int __exfat_cache_cluster(struct exfat_cache *cache,
+			       struct exfat_cache_entry *nearest,
+			       u32 fcluster, u32 dcluster)
+{
+	struct exfat_cache_entry *e;
+
+	/*
+	 * see if we can merge with the nearest entry. in the ideal
+	 * case, all cluster in the chain are contiguous, and only
+	 * one entry is needed for a single file.
+	 */
+	if (nearest &&
+	    nearest->file_cluster + nearest->nr_contig + 1 == fcluster &&
+	    nearest->disk_cluster + nearest->nr_contig + 1 == dcluster) {
+		list_move(&nearest->list, &cache->entries);
+		nearest->nr_contig++;
+		return 0;
+	}
+
+	/*
+	 * allocate a new entry or reuse an existing one if the number
+	 * of cached entries is too hihc.
+	 */
+	if (cache->nr_entries < MAX_CACHED_FAT) {
+		e = kmalloc(sizeof (*e), GFP_NOFS);
+		list_add(&e->list, &cache->entries);
+		++cache->nr_entries;
+	} else {
+		e = list_entry(cache->entries.prev, struct exfat_cache_entry,
+			       list);
+		list_move(&e->list, &cache->entries);
+	}
+
+	if (!e)
+		return -ENOMEM;
+
+	e->file_cluster = fcluster;
+	e->disk_cluster = dcluster;
+	e->nr_contig = 0;
+
+	return 0;
+}
+
+int __exfat_get_fat_cluster(struct inode *inode, u32 fcluster, u32 *dcluster,
+			    bool eof_is_fatal)
+{
+	struct exfat_inode_info *info = EXFAT_I(inode);
+	struct exfat_cache *cache = &info->exfat_cache;
+	int error;
+	struct exfat_cache_entry *e;
+	u32 fcluster_start;
+
+	/*
+	 * intial translation: first file cluster is found in the
+	 * inode info.
+	 */
+	if (fcluster == 0) {
+		*dcluster = info->first_cluster;
+		return 0;
+	}
+
+	mutex_lock(&cache->mutex);
+	/*
+	 * try to find a cached entry either covering the file cluster
+	 * we want or at least close to the file cluster.
+	 */
+	e = __exfat_cache_lookup(cache, fcluster);
+	if (e && e->file_cluster <= fcluster &&
+	    e->file_cluster + e->nr_contig >= fcluster) {
+		/*
+		 * perfect match, entry zone covers the requested file
+		 * cluster.
+		 */
+		__exfat_fat_lru(cache, e);
+		*dcluster = e->disk_cluster + (fcluster - e->file_cluster);
+		mutex_unlock(&cache->mutex);
+		return 0;
+	}
+
+	if (e) {
+		/*
+		 * we have an entry, hopefully close enough, setup
+		 * cluster walk from there.
+		 */
+		*dcluster = e->disk_cluster + e->nr_contig;
+		fcluster_start = e->file_cluster + e->nr_contig;
+	} else {
+		/*
+		 * no entry, walk the FAT chain from the start of the
+		 * file.
+		 */
+		fcluster_start = 0;
+		*dcluster = info->first_cluster;
+	}
+
+	/*
+	 * walk fhe FAT chain the number of time required to get the
+	 * disk cluster corresponding to the file cluster.
+	 */
+	while (fcluster_start != fcluster) {
+		error = exfat_next_fat_cluster(inode->i_sb, dcluster);
+		if (error) {
+			mutex_unlock(&cache->mutex);
+			return error;
+		}
+		if (*dcluster == EXFAT_CLUSTER_EOF) {
+			if (eof_is_fatal)
+				/*
+				 * exfat_fill_root uses
+				 * __exfat_get_fat_cluster with
+				 * eof_is_fatal set to false, as the
+				 * root inode does not have a size
+				 * field and thus requires a complete
+				 * FAT walk to compute the size.
+				 */
+				exfat_fs_error(inode->i_sb, "premature EOF in FAT "
+					       "chain. file cluster %u out "
+					       "of %u\n", fcluster_start,
+					       fcluster);
+			mutex_unlock(&cache->mutex);
+			return -EIO;
+		}
+		if (*dcluster < EXFAT_CLUSTER_FIRSTVALID) {
+			exfat_fs_error(inode->i_sb, "invalid cluster %u found "
+				       "in fat chain.", *dcluster);
+			mutex_unlock(&cache->mutex);
+			return -EIO;
+		}
+		++fcluster_start;
+	}
+
+	/*
+	 * cache the result.
+	 */
+	__exfat_cache_cluster(cache, e, fcluster, *dcluster);
+	mutex_unlock(&cache->mutex);
+	return 0;
+}
+
+int exfat_get_fat_cluster(struct inode *inode, u32 fcluster, u32 *dcluster)
+{
+	return __exfat_get_fat_cluster(inode, fcluster, dcluster, true);
+}
+
+int exfat_init_fat(struct super_block *sb)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	struct buffer_head *bh;
+	int error = 0;
+	u32 first, second;
+
+	bh = sb_bread(sb, sbi->fat_offset);
+	if (!bh) {
+		exfat_msg(sb, KERN_ERR, "unable to read FAT sector at %u",
+			  sbi->fat_offset);
+		return -EIO;
+	}
+
+	first = __le32_to_cpu(*(__le32*)(bh->b_data + 0));
+	second = __le32_to_cpu(*(__le32*)(bh->b_data + sizeof (__le32)));
+
+	if (first != 0xf8ffffff && second != 0xffffffff) {
+		exfat_msg(sb, KERN_INFO, "invalid FAT start: %08x, %08x",
+			  first, second);
+		error = -ENXIO;
+	}
+
+	brelse(bh);
+	return error;
+}
+
+/*
+ * fat write context, store the current buffer_head and current
+ * cluster to avoid having sb_bread all the time when the clusters are
+ * contiguous or at least not too far apart.
+ */
+struct fat_write_ctx {
+	struct super_block *sb;
+	struct buffer_head *bh;
+	u32 cur_cluster;
+};
+
+static void fat_init_write_ctx(struct fat_write_ctx *fwctx,
+				struct super_block *sb)
+{
+	memset(fwctx, 0, sizeof (*fwctx));
+	fwctx->sb = sb;
+}
+
+static void fat_exit_write_ctx(struct fat_write_ctx *fwctx)
+{
+	if (fwctx->bh)
+		brelse(fwctx->bh);
+}
+
+static int __fat_write_entry(struct fat_write_ctx *fwctx,
+			       u32 cluster, u32 next)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(fwctx->sb);
+	sector_t current_sector = cluster_sector(sbi, fwctx->cur_cluster);
+	sector_t wanted_sector = cluster_sector(sbi, cluster);
+	off_t off = cluster_offset(sbi, cluster);
+
+	/*
+	 * first see if we need a different buffer head from the
+	 * current one in the fat_write_ctx.
+	 */
+	if (current_sector != wanted_sector || !fwctx->bh) {
+		if (fwctx->bh)
+			brelse(fwctx->bh);
+		fwctx->bh = sb_bread(fwctx->sb, wanted_sector);
+		if (!fwctx->bh) {
+			exfat_msg(fwctx->sb, KERN_ERR,
+				  "unable to read FAT sector at %llu",
+				  wanted_sector);
+			return -EIO;
+		}
+	}
+
+	/*
+	 * set fat cluster to point to the next cluster, and mark bh
+	 * dirty so that the change hits the storage device.
+	 */
+	fwctx->cur_cluster = cluster;
+	*(__le32*)(fwctx->bh->b_data + off) = __cpu_to_le32(next);
+	mark_buffer_dirty(fwctx->bh);
+	return 0;
+}
+
+/*
+ * write nr_clusters contiguous clusters starting at first_cluster.
+ */
+int exfat_write_fat_contiguous(struct inode *inode, u32 first_cluster,
+			       u32 nr_clusters)
+{
+	u32 cluster;
+	struct fat_write_ctx fwctx;
+	int error = 0;
+
+	fat_init_write_ctx(&fwctx, inode->i_sb);
+	for (cluster = first_cluster;
+	     cluster < first_cluster + nr_clusters - 1;
+	     ++cluster) {
+		error = __fat_write_entry(&fwctx, cluster, cluster + 1);
+		if (error)
+			goto end;
+	}
+
+	/*
+	 * set EOF
+	 */
+	error = __fat_write_entry(&fwctx, cluster, EXFAT_CLUSTER_EOF);
+end:
+	fat_exit_write_ctx(&fwctx);
+	return error;
+
+}
+
+/*
+ * write cluster nr_clusters stored in clusters array, link with prev_cluster.
+ */
+int exfat_write_fat(struct inode *inode, u32 prev_cluster, u32 *clusters,
+		    u32 nr_clusters)
+{
+	u32 i;
+	struct fat_write_ctx fwctx;
+	int error;
+
+	if (!nr_clusters)
+		/* ??! */
+		return 0;
+
+	fat_init_write_ctx(&fwctx, inode->i_sb);
+
+	if (prev_cluster) {
+		/*
+		 * link with previous cluster if applicable.
+		 */
+		error = __fat_write_entry(&fwctx, prev_cluster, clusters[0]);
+		if (error)
+			goto end;
+	}
+	for (i = 0; i < nr_clusters - 1; ++i) {
+		error = __fat_write_entry(&fwctx, clusters[i], clusters[i + 1]);
+		if (error)
+			goto end;
+	}
+
+	/*
+	 * set EOF.
+	 */
+	error = __fat_write_entry(&fwctx, clusters[i], EXFAT_CLUSTER_EOF);
+
+ end:
+	fat_exit_write_ctx(&fwctx);
+	return error;
+}
diff -Nruw linux-3.11.10-fbx/fs/exfat./file.c linux-3.11.10-fbx/fs/exfat/file.c
--- linux-3.11.10-fbx/fs/exfat./file.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/file.c	2015-09-29 11:15:08.397751907 +0200
@@ -0,0 +1,427 @@
+/*
+ * file.c for exfat
+ * Created by <nschichan@freebox.fr> on Tue Aug 20 14:39:41 2013
+ */
+
+#include <linux/buffer_head.h>
+#include <linux/fs.h>
+#include <linux/exfat_user.h>
+
+#include "exfat.h"
+#include "exfat_fs.h"
+
+static int append_fragment(struct exfat_fragment __user *ufrag,
+			   struct exfat_fragment *kfrag)
+{
+	if (copy_to_user(ufrag, kfrag, sizeof (*kfrag)))
+		return -EFAULT;
+	return 0;
+}
+
+static void setup_fragment(struct exfat_sb_info *sbi,
+			  struct exfat_fragment *fragment, uint32_t fcluster,
+			  uint32_t dcluster)
+{
+	fragment->fcluster_start = fcluster;
+	fragment->dcluster_start = dcluster;
+	fragment->sector_start = exfat_cluster_sector(sbi, dcluster);
+	fragment->nr_clusters = 1;
+}
+
+static int exfat_ioctl_get_fragments(struct inode *inode,
+				     struct exfat_fragment_head __user *uhead)
+{
+	struct exfat_fragment_head head;
+	struct exfat_fragment fragment;
+	u32 fcluster;
+	u32 prev_dcluster;
+	u32 cur_fragment;
+	struct exfat_inode_info *info = EXFAT_I(inode);
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+	int error;
+
+	memset(&fragment, 0, sizeof (fragment));
+
+	if (copy_from_user(&head, uhead, sizeof (head)))
+		return -EFAULT;
+
+
+	if (put_user(sbi->sectorsize, &uhead->sector_size) ||
+	    put_user(sbi->clustersize, &uhead->cluster_size))
+		return -EFAULT;
+
+	if (!head.nr_fragments) {
+		/*
+		 * user did not provide space for fragments after
+		 * header.
+		 */
+		return 0;
+	}
+
+	if (head.fcluster_start >= info->allocated_clusters) {
+		/*
+		 * requested start cluster is after file EOF
+		 */
+		if (put_user(0, &uhead->nr_fragments))
+			return -EFAULT;
+		return 0;
+	}
+
+	if (info->flags & EXFAT_I_FAT_INVALID) {
+		/*
+		 * not FAT chain, this file has only one fragment.
+		 */
+		fragment.fcluster_start = head.fcluster_start;
+		fragment.dcluster_start =
+			info->first_cluster + head.fcluster_start;
+		fragment.nr_clusters = info->allocated_clusters -
+			head.fcluster_start;
+		fragment.sector_start =
+			exfat_cluster_sector(sbi, fragment.dcluster_start);
+
+		if (copy_to_user(&uhead->fragments[0], &fragment,
+				 sizeof (fragment)))
+			return -EFAULT;
+		if (put_user(1, &uhead->nr_fragments))
+			return -EFAULT;
+		if (put_user(info->first_cluster + info->allocated_clusters,
+			     &uhead->fcluster_start))
+			return -EFAULT;
+		return 0;
+	}
+
+	fcluster = head.fcluster_start;
+	cur_fragment = 0;
+
+	/*
+	 * initial fragment setup
+	 */
+	error = exfat_get_fat_cluster(inode, fcluster,
+				      &prev_dcluster);
+	if (error)
+		return error;
+	setup_fragment(sbi, &fragment, fcluster, prev_dcluster);
+	++fcluster;
+	while (fcluster < info->allocated_clusters) {
+		int error;
+		u32 dcluster;
+
+		/*
+		 * walk one step in the FAT.
+		 */
+		error = exfat_get_fat_cluster(inode, fcluster, &dcluster);
+		if (error)
+			return error;
+
+		if (prev_dcluster == dcluster - 1) {
+			/*
+			 * dcluster and prev_dcluster are contiguous.
+			 */
+			++fragment.nr_clusters;
+		} else {
+			/*
+			 * put this cluster in the user array
+			 */
+			error = append_fragment(&uhead->fragments[cur_fragment],
+						&fragment);
+			if (error)
+				return error;
+
+			++cur_fragment;
+			if (cur_fragment == head.nr_fragments)
+				break;
+
+			/*
+			 * setup a new fragment.
+			 */
+			setup_fragment(sbi, &fragment, fcluster, dcluster);
+		}
+		++fcluster;
+		prev_dcluster = dcluster;
+	}
+
+	if (cur_fragment < head.nr_fragments) {
+		append_fragment(&uhead->fragments[cur_fragment], &fragment);
+		++cur_fragment;
+	}
+
+	/*
+	 * update nr_fragments in user supplied head.
+	 */
+	if (cur_fragment != head.nr_fragments &&
+	    put_user(cur_fragment, &uhead->nr_fragments))
+		return -EFAULT;
+
+	/*
+	 * update fcluster_start in user supplied head.
+	 */
+	if (put_user(fcluster, &uhead->fcluster_start))
+		return -EFAULT;
+
+
+	return 0;
+}
+
+static int exfat_ioctl_get_bitmap(struct super_block *sb,
+				  struct exfat_bitmap_head __user *uhead)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	struct exfat_bitmap_head head;
+	uint32_t i;
+	int error;
+	struct exfat_bitmap_ctx ctx;
+	uint32_t start_cluster;
+
+	if (copy_from_user(&head, uhead, sizeof (head)))
+		return -EFAULT;
+
+	start_cluster = head.start_cluster;
+	if (start_cluster < 2)
+		return -EINVAL;
+
+
+	error = exfat_init_bitmap_context(sb, &ctx, head.start_cluster);
+	if (error)
+		return error;
+	for (i = 0; i < head.nr_entries; ++i) {
+		uint32_t first_in_use;
+		uint32_t nr_in_use;
+		int error;
+
+		error = exfat_test_bitmap(&ctx, start_cluster, &first_in_use,
+					  &nr_in_use);
+		if (error)
+			goto out_error;
+
+		if (first_in_use == sbi->cluster_count)
+			break;
+		if (put_user(first_in_use, &uhead->entries[i].start_cluster))
+			goto out_efault;
+		if (put_user(nr_in_use, &uhead->entries[i].nr_clusters))
+			goto out_efault;
+		if (put_user(exfat_cluster_sector(sbi, first_in_use),
+			     &uhead->entries[i].sector_start))
+			goto out_efault;
+		if (put_user((u64)nr_in_use * sbi->sectors_per_cluster,
+			     &uhead->entries[i].nr_sectors))
+			goto out_efault;
+		start_cluster = first_in_use + nr_in_use + 1;
+	}
+
+	exfat_exit_bitmap_context(&ctx);
+	if (put_user(i, &uhead->nr_entries))
+		return -EFAULT;
+	if (put_user(start_cluster, &uhead->start_cluster))
+		return -EFAULT;
+
+	return 0;
+
+out_efault:
+	error = -EFAULT;
+out_error:
+	exfat_exit_bitmap_context(&ctx);
+	return error;
+}
+
+static int exfat_ioctl_get_dirents(struct inode *inode,
+				   struct exfat_dirent_head __user *uhead)
+{
+	struct exfat_dir_ctx dctx;
+	struct exfat_dirent_head head;
+	int error;
+	uint32_t i;
+
+	if (!S_ISDIR(inode->i_mode))
+		return -ENOTDIR;
+
+	if (copy_from_user(&head, uhead, sizeof (head)))
+		return -EFAULT;
+
+	/* make sure we're aligned on an entry boundary */
+	head.offset &= ~0x1f;
+
+	error = exfat_init_dir_ctx(inode, &dctx, head.offset);
+	if (error < 0)
+		return error;
+
+	error = 0;
+	for (i = 0; i < head.nr_entries; ++i) {
+		bool end;
+		u8 *entry = __exfat_dentry_next(&dctx, 0, 0, false, &end);
+		u8 type;
+
+		if (!entry && end)
+			/* genuine end of file */
+			break;
+		if (!entry) {
+			/* something went wrong */
+			error = -EIO;
+			goto out;
+		}
+		type = *entry;
+
+		if (put_user(type, &uhead->entries[i])) {
+			error = -EFAULT;
+			goto out;
+		}
+	}
+
+	/*
+	 * update head nr_entries and offset.
+	 */
+	if (put_user(i, &uhead->nr_entries))  {
+		error = -EFAULT;
+		goto out;
+	}
+	if (put_user(head.offset + 0x20 * i, &uhead->offset)) {
+		error = -EFAULT;
+		goto out;
+	}
+
+ out:
+	exfat_cleanup_dir_ctx(&dctx);
+	return error;
+}
+
+long exfat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	switch (cmd) {
+	case EXFAT_IOCGETFRAGMENTS:
+		return exfat_ioctl_get_fragments(file_inode(file),
+						 (void __user*)arg);
+	case EXFAT_IOCGETBITMAP:
+		return exfat_ioctl_get_bitmap(file_inode(file)->i_sb,
+					      (void __user*)arg);
+	case EXFAT_IOCGETDIRENTS:
+		return exfat_ioctl_get_dirents(file_inode(file),
+					       (void __user*)arg);
+	default:
+		return -ENOTTY;
+	}
+}
+
+static int exfat_cont_expand(struct inode *inode, loff_t newsize)
+{
+	int error;
+
+	error = generic_cont_expand_simple(inode, newsize);
+	if (error)
+		return error;
+
+	inode->i_mtime = CURRENT_TIME_SEC;
+	mark_inode_dirty(inode);
+
+	if (IS_SYNC(inode))
+		exfat_msg(inode->i_sb, KERN_ERR, "TODO: cont_expand with "
+			  "sync mode.");
+	return 0;
+}
+
+int exfat_truncate_blocks(struct inode *inode, loff_t newsize)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+	u32 fcluster = (newsize + sbi->clustersize - 1) >> sbi->clusterbits;
+	int error;
+
+	if (EXFAT_I(inode)->mmu_private > newsize)
+		EXFAT_I(inode)->mmu_private = newsize;
+
+	error = exfat_free_clusters_inode(inode, fcluster);
+	if (error) {
+		exfat_msg(inode->i_sb, KERN_INFO, "exfat_free_clusters_inode: "
+			  "%i", error);
+		return error;
+	}
+
+	return 0;
+}
+
+int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry,
+		  struct kstat *stat)
+{
+	struct inode *inode = dentry->d_inode;
+	generic_fillattr(inode, stat);
+	stat->blksize = EXFAT_SB(inode->i_sb)->clustersize;
+	return 0;
+}
+
+#define EXFAT_VALID_MODE       (S_IFREG | S_IFDIR | S_IRWXUGO)
+
+static int exfat_mode_fixup(struct inode *inode, mode_t *mode)
+{
+	mode_t mask, perm;
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+
+	if (S_ISDIR(*mode))
+		mask = sbi->options.dmask;
+	else
+		mask = sbi->options.fmask;
+
+	perm = *mode & ~(S_IFMT | mask);
+
+	/*
+	 * we want 'r' and 'x' bits when mask allows for it.
+	 */
+	if ((perm & (S_IRUGO | S_IXUGO)) !=
+	    (inode->i_mode & ~mask & (S_IRUGO | S_IXUGO))) {
+		return -EPERM;
+	}
+
+	/*
+	 * we want all 'w' bits or none, depending on mask.
+	 */
+	if ((perm & S_IWUGO) && (perm & S_IWUGO) != (~mask & S_IWUGO))
+		return -EPERM;
+	*mode &= ~mask;
+	return 0;
+}
+
+int exfat_setattr(struct dentry *dentry, struct iattr *attrs)
+{
+	struct inode *inode = dentry->d_inode;
+	int error;
+
+	/*
+	 * can set uid/gid, only if it the same as the current one in
+	 * the inode.
+	 */
+	if (attrs->ia_valid & ATTR_UID &&
+	    inode->i_uid != attrs->ia_uid)
+		return -EPERM;
+
+	if (attrs->ia_valid & ATTR_GID &&
+	    inode->i_gid != attrs->ia_gid)
+		return -EPERM;
+
+	if (attrs->ia_valid & ATTR_MODE &&
+	    (attrs->ia_mode & ~EXFAT_VALID_MODE ||
+	     exfat_mode_fixup(inode, &attrs->ia_mode) < 0)) {
+		/*
+		 * silently ignore mode change if we're not OK with
+		 * it (same behavior as vfat).
+		 */
+		attrs->ia_valid &= ~ATTR_MODE;
+	}
+
+	if (attrs->ia_valid & ATTR_SIZE) {
+		inode_dio_wait(inode);
+		if (attrs->ia_size > inode->i_size) {
+			/*
+			 * expand file
+			 */
+			error = exfat_cont_expand(inode, attrs->ia_size);
+			if (error)
+				return error;
+		} else {
+			/*
+			 * shrink file
+			 */
+			truncate_setsize(inode, attrs->ia_size);
+			exfat_truncate_blocks(inode, attrs->ia_size);
+		}
+	}
+
+	setattr_copy(inode, attrs);
+	mark_inode_dirty(inode);
+	return 0;
+}
diff -Nruw linux-3.11.10-fbx/fs/exfat./inode.c linux-3.11.10-fbx/fs/exfat/inode.c
--- linux-3.11.10-fbx/fs/exfat./inode.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/inode.c	2015-06-12 14:02:26.806990175 +0200
@@ -0,0 +1,278 @@
+/*
+ * inode.c<2> for exfat
+ * Created by <nschichan@freebox.fr> on Wed Jul 24 16:15:52 2013
+ */
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/buffer_head.h>
+#include <linux/writeback.h>
+#include <linux/hash.h>
+
+#include "exfat_fs.h"
+#include "exfat.h"
+
+static struct kmem_cache *exfat_inodes_cachep;
+
+/*
+ * inode callbacks.
+ */
+struct inode *exfat_alloc_inode(struct super_block *sb)
+{
+	struct exfat_inode_info *ei = kmem_cache_alloc(exfat_inodes_cachep,
+						       GFP_NOFS);
+
+	if (!ei)
+		return NULL;
+
+	return &ei->vfs_inode;
+}
+
+static void exfat_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+
+	kmem_cache_free(exfat_inodes_cachep, EXFAT_I(inode));
+}
+
+void exfat_destroy_inode(struct inode *_inode)
+{
+	struct exfat_inode_info *inode = EXFAT_I(_inode);
+
+	call_rcu(&inode->vfs_inode.i_rcu, exfat_i_callback);
+}
+
+static void exfat_inode_init_once(void *ptr)
+{
+	struct exfat_inode_info *info = ptr;
+
+	INIT_HLIST_NODE(&info->hash_list);
+	exfat_inode_cache_init(&info->vfs_inode);
+	inode_init_once(&info->vfs_inode);
+}
+
+/*
+ * inode cache create/destroy.
+ */
+int exfat_init_inodes(void)
+{
+	exfat_inodes_cachep = kmem_cache_create("exfat-inodes",
+				       sizeof (struct exfat_inode_info), 0,
+				       SLAB_RECLAIM_ACCOUNT |SLAB_MEM_SPREAD,
+				       exfat_inode_init_once);
+	if (!exfat_inodes_cachep)
+		return -ENOMEM;
+	return 0;
+}
+
+void exfat_exit_inodes(void)
+{
+	kmem_cache_destroy(exfat_inodes_cachep);
+}
+
+int exfat_drop_inode(struct inode *inode)
+{
+	return generic_drop_inode(inode);
+}
+
+void exfat_evict_inode(struct inode *inode)
+{
+	if (inode->i_data.nrpages)
+		truncate_inode_pages(&inode->i_data, 0);
+	if (!inode->i_nlink) {
+		inode->i_size = 0;
+		exfat_free_clusters_inode(inode, 0);
+	}
+	invalidate_inode_buffers(inode);
+	clear_inode(inode);
+	exfat_remove_inode_hash(inode);
+	exfat_inode_cache_drop(inode);
+}
+
+static u32 exfat_hash(loff_t disk_pos)
+{
+	return hash_32(disk_pos, EXFAT_HASH_BITS);
+}
+
+struct inode *exfat_iget(struct super_block *sb, loff_t disk_pos)
+{
+	struct exfat_inode_info *info;
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	struct hlist_head *head = sbi->inode_hash + exfat_hash(disk_pos);
+	struct inode *ret = NULL;
+
+
+	spin_lock(&sbi->inode_hash_lock);
+	hlist_for_each_entry (info, head, hash_list) {
+		if (info->iloc.disk_offs[0] != disk_pos)
+			continue ;
+		ret = igrab(&info->vfs_inode);
+		if (ret)
+			break;
+	}
+	spin_unlock(&sbi->inode_hash_lock);
+	return ret;
+}
+
+void exfat_insert_inode_hash(struct inode *inode)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+	struct exfat_inode_info *info = EXFAT_I(inode);
+	struct hlist_head *head = sbi->inode_hash +
+		exfat_hash(info->iloc.disk_offs[0]);
+
+	spin_lock(&sbi->inode_hash_lock);
+	hlist_add_head(&info->hash_list, head);
+	spin_unlock(&sbi->inode_hash_lock);
+}
+
+void exfat_remove_inode_hash(struct inode *inode)
+{
+	struct exfat_inode_info *info = EXFAT_I(inode);
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+
+	spin_lock(&sbi->inode_hash_lock);
+	info->iloc.disk_offs[0] = 0;
+	hlist_del_init(&info->hash_list);
+	spin_unlock(&sbi->inode_hash_lock);
+}
+
+/*
+ * calculate the number of links in a directory. this is the number of
+ * EXFAT_FILEDIR_ENTRY typed elements in the directory stream. This
+ * does not include the '.' and '..' entries.
+ */
+loff_t exfat_dir_links(struct inode *inode)
+{
+	size_t ret = 0;
+	struct exfat_dir_ctx dctx;
+	int error;
+	bool end;
+
+	error = exfat_init_dir_ctx(inode, &dctx, 0);
+	if (error)
+		return error;
+
+	error = -EIO;
+	for (;;) {
+		struct exfat_filedir_entry *e =
+			__exfat_dentry_next(&dctx, E_EXFAT_FILEDIR, 0xff,
+					    true, &end);
+		if (!e) {
+			if (end)
+				error = 0;
+			goto out;
+		}
+		++ret;
+	}
+out:
+	exfat_cleanup_dir_ctx(&dctx);
+	if (error)
+		return error;
+	return ret;
+}
+
+int exfat_get_cluster_hint(struct inode *inode, u32 *out_hint)
+{
+	struct exfat_inode_info *info = EXFAT_I(inode);
+	int error;
+	u32 first_cluster = info->first_cluster;
+
+
+	if (!first_cluster) {
+		/*
+		 * empty file, return a cluster likely to be free.
+		 */
+		*out_hint = EXFAT_SB(inode->i_sb)->prev_free_cluster + 2;
+		return 0;
+	}
+
+	if (info->flags & EXFAT_I_FAT_INVALID) {
+		/*
+		 * not fat run, all clusters are contiguous, set hint
+		 * to next last file cluster.
+		 */
+		*out_hint = first_cluster + info->allocated_clusters;
+		return 0;
+	}
+
+	/*
+	 * fat run available, walk it to get the last physical cluster
+	 * address and set hint to the immediate next physical
+	 * cluster.
+	 */
+	error = exfat_get_fat_cluster(inode, info->allocated_clusters - 1,
+				      out_hint);
+	if (error)
+		return error;
+	(*out_hint)++;
+	return 0;
+}
+
+int __exfat_write_inode(struct inode *inode, bool sync)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+	struct exfat_inode_info *info = EXFAT_I(inode);
+	struct dir_entry_buffer entries[info->iloc.nr_secondary];
+	int error;
+	struct exfat_filedir_entry *efd;
+	struct exfat_stream_extension_entry *esx;
+	u16 checksum;
+
+	if (inode->i_ino == EXFAT_ROOT_INO)
+		return 0;
+
+	if (info->iloc.disk_offs[0] == 0) {
+		/*
+		 * write_inode() to unlinked inode: don't corrupt
+		 * superblock.
+		 */
+		return 0;
+	}
+
+	error = exfat_get_dir_entry_buffers(inode, &info->iloc,
+					    entries, info->iloc.nr_secondary);
+	if (error)
+		return error;
+
+	if (inode->i_mode & S_IWUGO)
+		info->attributes &= ~E_EXFAT_ATTR_RO;
+	else
+		info->attributes |= E_EXFAT_ATTR_RO;
+
+	efd = entries[0].start;
+	esx = entries[1].start;
+
+	efd->attributes = __cpu_to_le16(info->attributes);
+	esx->data_length = __cpu_to_le64(inode->i_size);
+	esx->valid_data_length = esx->data_length =
+		__cpu_to_le64(inode->i_size);
+	esx->flags = info->flags;
+	esx->first_cluster = __cpu_to_le32(info->first_cluster);
+
+	exfat_write_time(sbi, &inode->i_ctime, &efd->create, &efd->create_10ms,
+			 &efd->create_tz_offset);
+	exfat_write_time(sbi, &inode->i_mtime, &efd->modified,
+			 &efd->modified_10ms, &efd->modified_tz_offset);
+	exfat_write_time(sbi, &inode->i_atime, &efd->accessed, NULL,
+			 &efd->accessed_tz_offset);
+
+	checksum = exfat_dir_entries_checksum(entries, info->iloc.nr_secondary);
+	efd->set_checksum = __cpu_to_le16(checksum);
+
+	exfat_dirty_dir_entries(entries, info->iloc.nr_secondary, sync);
+
+
+	return 0;
+}
+
+int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+	int ret;
+
+	exfat_lock_super(inode->i_sb);
+	ret = __exfat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
+	exfat_unlock_super(inode->i_sb);
+	return ret;
+}
diff -Nruw linux-3.11.10-fbx/fs/exfat./Kconfig linux-3.11.10-fbx/fs/exfat/Kconfig
--- linux-3.11.10-fbx/fs/exfat./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/Kconfig	2015-06-12 14:02:26.806990175 +0200
@@ -0,0 +1,3 @@
+
+config EXFAT_FS
+	tristate "exFAT fs support"
diff -Nruw linux-3.11.10-fbx/fs/exfat./Makefile linux-3.11.10-fbx/fs/exfat/Makefile
--- linux-3.11.10-fbx/fs/exfat./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/Makefile	2015-06-12 14:02:26.806990175 +0200
@@ -0,0 +1,13 @@
+
+obj-$(CONFIG_EXFAT_FS)	+= exfat.o
+
+exfat-y	= super.o				\
+	inode.o					\
+	fat.o					\
+	read-write.o				\
+	upcase.o				\
+	bitmap.o				\
+	time.o					\
+	dir.o					\
+	namei.o					\
+	file.o
diff -Nruw linux-3.11.10-fbx/fs/exfat./namei.c linux-3.11.10-fbx/fs/exfat/namei.c
--- linux-3.11.10-fbx/fs/exfat./namei.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/namei.c	2015-06-12 14:02:26.806990175 +0200
@@ -0,0 +1,924 @@
+/*
+ * namei.c for exfat
+ * Created by <nschichan@freebox.fr> on Tue Aug 20 12:00:27 2013
+ */
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/nls.h>
+
+#include "exfat.h"
+#include "exfat_fs.h"
+
+static u16 exfat_filename_hash_cont(struct super_block *sb,
+				    const __le16 *name, u16 hash, size_t len);
+
+
+void exfat_write_time(struct exfat_sb_info *sbi, struct timespec *ts,
+		      __le32 *datetime, u8 *time_cs, u8 *tz_offset)
+{
+	u32 cpu_datetime;
+
+	exfat_time_2exfat(sbi, ts, &cpu_datetime, time_cs, tz_offset);
+	*datetime = __cpu_to_le32(cpu_datetime);
+}
+
+static void exfat_read_time(struct timespec *ts, __le32 datetime, u8 time_cs,
+			    u8 tz_offset)
+{
+	u32 cpu_datetime = __le32_to_cpu(datetime);
+	exfat_time_2unix(ts, cpu_datetime, time_cs, tz_offset);
+}
+
+static int exfat_zero_cluster(struct super_block *sb, u32 cluster, bool sync)
+{
+	sector_t start = exfat_cluster_sector(EXFAT_SB(sb), cluster);
+	sector_t end = start + EXFAT_SB(sb)->sectors_per_cluster;
+	sector_t sect;
+
+	for (sect = start; sect < end; ++sect) {
+		struct buffer_head *bh = sb_bread(sb, sect);
+		if (!bh) {
+			exfat_msg(sb, KERN_WARNING,
+				  "unable to read sector %llu for zeroing.",
+				  sect);
+			return -EIO;
+		}
+		memset(bh->b_data, 0, bh->b_size);
+		mark_buffer_dirty(bh);
+		if (sync)
+			sync_dirty_buffer(bh);
+		brelse(bh);
+	}
+	return 0;
+}
+
+/*
+ * use per superblock fmask or dmaks, depending on provided entry
+ * attribute to restrict the provided mode even more.
+ */
+mode_t exfat_make_mode(struct exfat_sb_info *sbi, mode_t mode, u16 attrs)
+{
+	if (attrs & E_EXFAT_ATTR_DIRECTORY)
+		mode = (mode & ~sbi->options.dmask) | S_IFDIR;
+	else
+		mode = (mode & ~sbi->options.fmask) | S_IFREG;
+	if (attrs & E_EXFAT_ATTR_RO)
+		mode &= ~S_IWUGO;
+	return mode;
+}
+
+/*
+ * populate inode fields.
+ */
+static struct inode *exfat_populate_inode(struct super_block *sb,
+			  const struct exfat_filedir_entry *efd,
+			  const struct exfat_stream_extension_entry *esx,
+			  const struct exfat_iloc *iloc)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	struct inode *inode;
+
+	inode = exfat_iget(sb, iloc->disk_offs[0]);
+	if (inode)
+		return inode;
+
+	inode = new_inode(sb);
+	if (!inode)
+		return NULL;
+
+	inode->i_ino = iunique(sb, EXFAT_ROOT_INO);
+	EXFAT_I(inode)->first_cluster = __le32_to_cpu(esx->first_cluster);
+	EXFAT_I(inode)->flags = esx->flags;
+	EXFAT_I(inode)->iloc = *iloc;
+	EXFAT_I(inode)->attributes = __le16_to_cpu(efd->attributes);
+
+	inode->i_size = __le64_to_cpu(esx->data_length);
+	EXFAT_I(inode)->allocated_clusters = inode->i_size >> sbi->clusterbits;
+	if (inode->i_size & sbi->clustermask)
+		EXFAT_I(inode)->allocated_clusters++;
+	inode->i_blocks = EXFAT_I(inode)->allocated_clusters <<
+		(sbi->clusterbits - 9);
+	EXFAT_I(inode)->mmu_private = inode->i_size;
+
+	inode->i_uid = sbi->options.uid;
+	inode->i_gid = sbi->options.gid;
+	inode->i_mode = exfat_make_mode(sbi, S_IRWXUGO,
+					EXFAT_I(inode)->attributes);
+
+	if (EXFAT_I(inode)->attributes & E_EXFAT_ATTR_DIRECTORY) {
+		loff_t nlinks = exfat_dir_links(inode);
+		if (nlinks < 0)
+			goto iput;
+		set_nlink(inode, nlinks + 2);
+	} else
+		set_nlink(inode, 1);
+
+	if (esx->data_length != esx->valid_data_length)
+		exfat_msg(sb, KERN_WARNING, "data length (%llu) != valid data "
+			  "length (%llu)", __le64_to_cpu(esx->data_length),
+			  __le64_to_cpu(esx->valid_data_length));
+
+	if (S_ISDIR(inode->i_mode)) {
+		inode->i_fop = &exfat_dir_operations;
+		inode->i_op = &exfat_dir_inode_operations;
+	} else {
+		/* until we support write */
+		inode->i_fop = &exfat_file_operations;
+		inode->i_op = &exfat_file_inode_operations;
+		inode->i_data.a_ops = &exfat_address_space_operations;
+	}
+
+
+	exfat_read_time(&inode->i_ctime, efd->create, efd->create_10ms,
+			efd->create_tz_offset);
+	exfat_read_time(&inode->i_mtime, efd->modified, efd->modified_10ms,
+			efd->modified_tz_offset);
+	exfat_read_time(&inode->i_atime, efd->accessed, 0,
+			efd->accessed_tz_offset);
+
+	exfat_insert_inode_hash(inode);
+	insert_inode_hash(inode);
+	return inode;
+iput:
+	iput(inode);
+	return NULL;
+}
+
+/*
+ * lookup an inode.
+ */
+struct dentry *exfat_inode_lookup(struct inode *parent, struct dentry *dentry,
+				  unsigned int flags)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct exfat_dir_ctx dctx;
+	int error;
+	struct exfat_filedir_entry efd;
+	struct exfat_stream_extension_entry esx;
+	__le16 *name = __getname();
+	__le16 *utf16_name = __getname();
+	unsigned int utf16_name_length;
+	__le16 name_hash;
+
+	exfat_lock_super(parent->i_sb);
+
+	if (!name || !utf16_name) {
+		error = -ENOMEM;
+		goto putnames;
+	}
+
+	utf16_name_length = utf8s_to_utf16s(dentry->d_name.name,
+					    dentry->d_name.len,
+					    UTF16_LITTLE_ENDIAN,
+					    utf16_name, 255 + 2);
+	if (utf16_name_length > 255) {
+		error = -ENAMETOOLONG;
+		goto putnames;
+	}
+
+	/*
+	 * get the name hash of the wanted inode early so that we can
+	 * skip entries with only an efd and an esx entry.
+	 */
+	name_hash = __cpu_to_le16(exfat_filename_hash_cont(sb, utf16_name, 0,
+							   utf16_name_length));
+
+	/*
+	 * create a dir ctx from the parent so that we can iterate on
+	 * it.
+	 */
+	error = exfat_init_dir_ctx(parent, &dctx, 0);
+	if (error)
+		goto putnames;
+
+	for (;;) {
+		u32 name_length;
+		struct inode *inode;
+		u16 calc_checksum;
+		u16 expect_checksum;
+		struct exfat_iloc iloc;
+
+		memset(&iloc, 0, sizeof (iloc));
+		/*
+		 * get filedir and stream extension entries.
+		 */
+		error = exfat_dentry_next(&efd, &dctx, E_EXFAT_FILEDIR, true);
+		if (error < 0)
+			/* end of directory reached, or other error */
+			goto cleanup;
+
+		error = -EINVAL;
+		if (efd.secondary_count > 18)
+			goto cleanup;
+
+		iloc.file_off = exfat_dctx_fpos(&dctx);
+		iloc.disk_offs[0] = exfat_dctx_dpos(&dctx);
+		iloc.nr_secondary = efd.secondary_count + 1;
+
+		error = exfat_dentry_next(&esx, &dctx, E_EXFAT_STREAM_EXT,
+					  false);
+		if (error)
+			goto cleanup;
+
+		if (esx.name_hash != name_hash)
+			/*
+			 * stored name hash is not the same as the
+			 * wanted hash: no point in processing the
+			 * remaining entries for the current efd/esx
+			 * any further.
+			 */
+			continue ;
+
+		/*
+		 * now that the hash matches it is ok to update the
+		 * checksum for the efd and esx entries.
+		 */
+		expect_checksum = __le16_to_cpu(efd.set_checksum);
+		calc_checksum = exfat_direntry_checksum(&efd, 0, true);
+
+		calc_checksum = exfat_direntry_checksum(&esx,
+							calc_checksum, false);
+		iloc.disk_offs[1] = exfat_dctx_dpos(&dctx);
+
+		/*
+		 * fetch name.
+		 */
+		name_length = esx.name_length;
+		error = __exfat_get_name(&dctx, name_length, name,
+					 &calc_checksum, &iloc);
+		if (error)
+			goto cleanup;
+
+		if (calc_checksum != expect_checksum) {
+			exfat_msg(dctx.sb, KERN_INFO, "checksum: "
+				  "calculated %04x, expect %04x",
+				  calc_checksum, expect_checksum);
+			error = -EIO;
+			goto cleanup;
+		}
+
+
+		if (utf16_name_length != name_length)
+			continue ;
+
+		if (memcmp(utf16_name, name, name_length * sizeof (__le16)))
+			continue ;
+
+		inode = exfat_populate_inode(sb, &efd, &esx, &iloc);
+		if (inode) {
+			d_add(dentry, inode);
+			error = 0;
+		} else
+			error = -EIO;
+		goto cleanup;
+	}
+
+cleanup:
+	exfat_cleanup_dir_ctx(&dctx);
+putnames:
+	if (name)
+		__putname(name);
+	if (utf16_name)
+		__putname(utf16_name);
+	exfat_unlock_super(parent->i_sb);
+	if (error && error != -ENOENT)
+		return ERR_PTR(error);
+	return NULL;
+}
+
+/*
+ * find nr unused directory entries (type & 0x80 == 0).
+ */
+static int exfat_find_dir_iloc(struct inode *inode, int nr,
+			       struct exfat_iloc *iloc)
+{
+	struct exfat_dir_ctx dctx;
+	bool end = false;
+	int error;
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+	u32 nr_new_clusters, i;
+	u32 new_clusters[2];
+	u32 hint_cluster;
+
+retry:
+	memset(iloc, 0, sizeof (*iloc));
+	iloc->nr_secondary = nr;
+
+	error = exfat_init_dir_ctx(inode, &dctx, 0);
+	if (error)
+		return error;
+
+	while (1) {
+		int nr_free;
+		void *ent;
+
+		ent = __exfat_dentry_next(&dctx, 0x00, 0x80, true, &end);
+		if (end)
+			break;
+		if (!ent) {
+			exfat_cleanup_dir_ctx(&dctx);
+			return -EIO;
+		}
+
+		nr_free = 1;
+		iloc->file_off = exfat_dctx_fpos(&dctx);
+		iloc->disk_offs[0] = exfat_dctx_dpos(&dctx);
+		while (__exfat_dentry_next(&dctx, 0x00, 0x80, false, &end)
+		       != NULL && nr_free < nr) {
+			iloc->disk_offs[nr_free] = exfat_dctx_dpos(&dctx);
+			++nr_free;
+		}
+		if (nr_free == nr) {
+			/*
+			 * we found enough consecutive free entries.
+			 */
+			exfat_cleanup_dir_ctx(&dctx);
+			return 0;
+		}
+
+	}
+
+	/*
+	 * not enough consecutive free entries found, kick the cluster
+	 * allocator and retry.
+	 */
+	exfat_cleanup_dir_ctx(&dctx);
+
+	/*
+	 * with the smallest cluster size, a file can take more than
+	 * two clusters. allocate two in that case reardless of what
+	 * is needed to make code simplier.
+	 */
+	switch (sbi->clustersize) {
+	case 512:
+		nr_new_clusters = 2;
+		break;
+	default:
+		nr_new_clusters = 1;
+		break;
+	}
+
+	/*
+	 * get a hint cluster for the cluster allocator.
+	 */
+	error = exfat_get_cluster_hint(inode, &hint_cluster);
+	if (error)
+		return error;
+
+	/*
+	 * peform the allocation.
+	 */
+	error = exfat_alloc_clusters(inode, hint_cluster, new_clusters,
+				     nr_new_clusters);
+	if (error)
+		return error;
+
+	/*
+	 * fill new cluster(s) with zero.
+	 */
+	for (i = 0; i < nr_new_clusters; ++i)
+		exfat_zero_cluster(inode->i_sb, new_clusters[i], false);
+
+	/*
+	 * update size and mark inode as dirty so that write_inode()
+	 * can update it's size, and the other fields updated by
+	 * exfat_alloc_clusters.
+	 */
+	inode->i_size += nr_new_clusters << sbi->clusterbits;
+	mark_inode_dirty(inode);
+
+	/*
+	 * kick the whole place search again, this time with the newly
+	 * allocated clusters.
+	 */
+	goto retry;
+}
+
+/*
+ * setup dir_entry_buffers starting at using iloc.
+ */
+int exfat_get_dir_entry_buffers(struct inode *dir, struct exfat_iloc *iloc,
+				struct dir_entry_buffer *entries,
+				size_t nr_entries)
+{
+	size_t i;
+	int error;
+	struct exfat_sb_info *sbi = EXFAT_SB(dir->i_sb);
+
+	BUG_ON(iloc->nr_secondary != nr_entries);
+
+	memset(entries, 0, sizeof (*entries) * nr_entries);
+	for (i = 0; i < nr_entries; ++i) {
+		sector_t sector = iloc->disk_offs[i] >> sbi->sectorbits;
+
+		entries[i].off = iloc->disk_offs[i] & sbi->sectormask;
+		entries[i].bh = sb_bread(dir->i_sb, sector);
+		if (!entries[i].bh) {
+			error = -EIO;
+			goto fail;
+		}
+		entries[i].start = entries[i].bh->b_data + entries[i].off;
+	}
+	return 0;
+
+fail:
+	for (i = 0; i < nr_entries; ++i)
+		if (entries[i].bh)
+			brelse(entries[i].bh);
+	return error;
+}
+
+static u16 exfat_filename_hash_cont(struct super_block *sb,
+				    const __le16 *name, u16 hash, size_t len)
+{
+	while (len) {
+		u16 c = __le16_to_cpu(exfat_upcase_convert(sb, *name));
+
+		hash = ((hash << 15) | (hash >> 1)) + (c & 0xff);
+		hash = ((hash << 15) | (hash >> 1)) + (c >> 8);
+		--len;
+		++name;
+	}
+	return hash;
+}
+
+u16 exfat_dir_entries_checksum(struct dir_entry_buffer *entries, u32 nr)
+{
+	u32 checksum = 0;
+
+	if (nr) {
+		checksum = exfat_direntry_checksum(entries->start,
+						   checksum, true);
+		--nr;
+		++entries;
+	}
+	while (nr) {
+		checksum = exfat_direntry_checksum(entries->start,
+						   checksum, false);
+		--nr;
+		++entries;
+	}
+	return checksum;
+}
+
+/*
+ * setup exfat_filedir_entry and exfat_stream_extension_entry for a
+ * new entry, with attribute attrs, and named name.
+ */
+static void exfat_fill_dir_entries(struct super_block *sb,
+				  struct dir_entry_buffer *entries,
+				  size_t nr_entries, u8 attrs,
+				  __le16 *name, int name_length)
+{
+	struct exfat_filedir_entry *efd;
+	struct exfat_stream_extension_entry *esx;
+	int i;
+	u16 name_hash;
+	u16 checksum;
+	struct timespec ts = CURRENT_TIME_SEC;
+
+	efd = entries[0].start;
+	esx = entries[1].start;
+
+	/*
+	 * fill exfat filedir entry
+	 */
+	memset(efd, 0, sizeof (*efd));
+	efd->type = E_EXFAT_FILEDIR;
+	efd->secondary_count = nr_entries - 1;
+	efd->set_checksum = 0;
+	efd->attributes = __cpu_to_le16(attrs);
+
+	/*
+	 * update file directory entry times
+	 */
+	efd = entries[0].start;
+	exfat_write_time(EXFAT_SB(sb), &ts, &efd->create, &efd->create_10ms,
+			 &efd->create_tz_offset);
+	efd->modified = efd->accessed = efd->create;
+	efd->modified_10ms = efd->create_10ms;
+	efd->accessed_tz_offset = efd->modified_tz_offset =
+		efd->create_tz_offset;
+
+	/*
+	 * fill exfat stream extension entry
+	 */
+	memset(esx, 0, sizeof (*esx));
+	esx->type = E_EXFAT_STREAM_EXT;
+	esx->flags = EXFAT_I_ALLOC_POSSIBLE;
+	esx->first_cluster = __cpu_to_le32(0);
+	esx->data_length = __cpu_to_le64(0);
+	esx->valid_data_length = __cpu_to_le64(0);
+	esx->name_length = name_length;
+
+	/*
+	 * fill name fragments.
+	 */
+	name_hash = 0;
+	for (i = 0; i < nr_entries - 2; ++i, name_length -= 15) {
+		struct exfat_filename_entry *efn = entries[i + 2].start;
+		int len = 15;
+
+		if (name_length < 15)
+			len = name_length;
+
+		memset(efn, 0, sizeof (*efn));
+		efn->type = E_EXFAT_FILENAME;
+		memcpy(efn->name_frag, name + i * 15, len * sizeof (__le16));
+		name_hash = exfat_filename_hash_cont(sb, efn->name_frag,
+						     name_hash, len);
+	}
+	esx->name_hash = __cpu_to_le16(name_hash);
+
+	checksum = exfat_dir_entries_checksum(entries, nr_entries);
+	efd->set_checksum = __cpu_to_le16(checksum);
+}
+
+/*
+ * mark all buffer heads in the entries array as dirty. optionally
+ * sync them if required.
+ */
+void exfat_dirty_dir_entries(struct dir_entry_buffer *entries,
+			     size_t nr_entries, bool sync)
+{
+	size_t i;
+
+	for (i = 0; i < nr_entries; ++i) {
+		mark_buffer_dirty(entries[i].bh);
+		if (sync)
+			sync_dirty_buffer(entries[i].bh);
+		brelse(entries[i].bh);
+	}
+}
+
+/*
+ * cleanup all buffer heads in entries.
+ */
+static void exfat_cleanup_dir_entries(struct dir_entry_buffer *entries,
+				     size_t nr_entries)
+{
+	size_t i;
+
+	for (i = 0; i < nr_entries; ++i)
+		brelse(entries[i].bh);
+}
+
+/*
+ * create an inode
+ */
+static int __exfat_inode_create(struct inode *dir, struct dentry *dentry,
+				umode_t mode, bool is_dir)
+{
+	int nr_entries;
+	struct dir_entry_buffer entries[19];
+	struct inode *new;
+	struct exfat_iloc iloc;
+	int error;
+	u8 attr = 0;
+	__le16 *utf16_name;
+	int utf16_name_length;
+
+	if (is_dir)
+		attr |= E_EXFAT_ATTR_DIRECTORY;
+
+	exfat_lock_super(dir->i_sb);
+
+	utf16_name = __getname();
+	if (!utf16_name) {
+		error = -ENOMEM;
+		goto unlock_super;
+	}
+
+	utf16_name_length = utf8s_to_utf16s(dentry->d_name.name,
+					    dentry->d_name.len,
+					    UTF16_LITTLE_ENDIAN, utf16_name,
+					    255 + 2);
+	if (utf16_name_length < 0) {
+		error = utf16_name_length;
+		goto putname;
+	}
+	if (utf16_name_length > 255) {
+		error = -ENAMETOOLONG;
+		goto putname;
+	}
+
+
+	nr_entries = 2 + DIV_ROUND_UP(utf16_name_length, 15);
+	if (nr_entries > 19) {
+		error = -ENAMETOOLONG;
+		goto putname;
+	}
+
+	error = exfat_find_dir_iloc(dir, nr_entries, &iloc);
+	if (error < 0)
+		goto putname;
+
+	error = exfat_get_dir_entry_buffers(dir, &iloc, entries, nr_entries);
+	if (error)
+		goto putname;
+	exfat_fill_dir_entries(dir->i_sb, entries, nr_entries, attr,
+				       utf16_name, utf16_name_length);
+
+	/*
+	 * create an inode with it.
+	 */
+	error = -ENOMEM;
+	new = exfat_populate_inode(dir->i_sb, entries[0].start,
+				   entries[1].start, &iloc);
+	if (!new)
+		goto cleanup;
+	inc_nlink(dir);
+	d_instantiate(dentry, new);
+
+	/*
+	 * update directory atime / ctime.
+	 */
+	dir->i_atime = dir->i_mtime = CURRENT_TIME_SEC;
+	if (IS_DIRSYNC(dir))
+		__exfat_write_inode(dir, true);
+	else
+		mark_inode_dirty(dir);
+
+	/*
+	 * write to disk
+	 */
+	exfat_dirty_dir_entries(entries, nr_entries, false);
+	__putname(utf16_name);
+	exfat_unlock_super(dir->i_sb);
+	return 0;
+
+cleanup:
+	exfat_cleanup_dir_entries(entries, nr_entries);
+putname:
+	__putname(utf16_name);
+unlock_super:
+	exfat_unlock_super(dir->i_sb);
+	return error;
+}
+
+int exfat_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+		       bool excl)
+{
+	return __exfat_inode_create(dir, dentry, mode, false);
+}
+
+int exfat_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+	return __exfat_inode_create(dir, dentry, mode, true);
+}
+
+/*
+ * inode unlink: find all direntry buffers and clear seventh bit of
+ * the entry type to mark the as unused.
+ */
+static int __exfat_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+	struct exfat_inode_info *info = EXFAT_I(inode);
+	struct dir_entry_buffer entries[info->iloc.nr_secondary];
+	int error;
+	u32 i;
+
+	error = exfat_get_dir_entry_buffers(inode, &info->iloc,
+					    entries, info->iloc.nr_secondary);
+	if (error)
+		return error;
+
+	for (i = 0; i < info->iloc.nr_secondary; ++i) {
+		u8 *type = entries[i].start;
+
+		*type &= 0x7f;
+	}
+
+	drop_nlink(dir);
+	clear_nlink(inode);
+	inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
+
+	/*
+	 * update atime & mtime for parent directory.
+	 */
+	dir->i_mtime = dir->i_atime = CURRENT_TIME_SEC;
+	if (IS_DIRSYNC(dir))
+		__exfat_write_inode(dir, true);
+	else
+		mark_inode_dirty(dir);
+
+	exfat_dirty_dir_entries(entries, info->iloc.nr_secondary, false);
+	exfat_remove_inode_hash(inode);
+	return 0;
+}
+
+int exfat_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+	int ret;
+
+	exfat_lock_super(dir->i_sb);
+	ret = __exfat_inode_unlink(dir, dentry);
+	exfat_unlock_super(dir->i_sb);
+	return ret;
+}
+
+/*
+ * inode rmdir: check that links is not greater than 2 (meaning that
+ * the directory is empty) and invoke unlink.
+ */
+static int __exfat_inode_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+
+	if (inode->i_nlink > 2)
+		return -ENOTEMPTY;
+
+	return __exfat_inode_unlink(dir, dentry);
+}
+
+int exfat_inode_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	int ret;
+
+	exfat_lock_super(dir->i_sb);
+	ret = __exfat_inode_rmdir(dir, dentry);
+	exfat_unlock_super(dir->i_sb);
+	return ret;
+}
+
+int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
+		 struct inode *new_dir, struct dentry *new_dentry)
+{
+	struct inode *old_inode = old_dentry->d_inode;
+	struct inode *new_inode = new_dentry->d_inode;
+	int new_nr_entries;
+	int error = 0;
+	struct exfat_iloc new_iloc;
+	struct exfat_inode_info *old_info = EXFAT_I(old_inode);
+	struct dir_entry_buffer old_buffers[old_info->iloc.nr_secondary];
+	struct dir_entry_buffer new_buffers[19];
+	struct exfat_filedir_entry *efd;
+	struct exfat_stream_extension_entry *esx;
+	int name_length;
+	__le16 *name;
+	u16 name_hash;
+	int i;
+
+	exfat_lock_super(new_dir->i_sb);
+
+	/*
+	 * convert new name to utf16
+	 */
+	name = __getname();
+	if (!name) {
+		error = -ENOMEM;
+		goto unlock_super;
+	}
+	name_length = utf8s_to_utf16s(new_dentry->d_name.name,
+				      new_dentry->d_name.len,
+				      UTF16_LITTLE_ENDIAN, name, 255 + 2);
+
+	if (name_length > 255) {
+		error = -ENAMETOOLONG;
+		goto err_putname;
+	}
+	if (name_length < 0) {
+		error = name_length;
+		goto err_putname;
+	}
+
+	new_nr_entries = 2 + DIV_ROUND_UP(name_length, 15);
+
+	/*
+	 * find space for new entry
+	 */
+	error = exfat_find_dir_iloc(new_dir, new_nr_entries, &new_iloc);
+	if (error < 0)
+		goto err_putname;
+
+	/*
+	 * get buffers for old and new entries.
+	 */
+	error = exfat_get_dir_entry_buffers(old_dir, &old_info->iloc,
+				    old_buffers, old_info->iloc.nr_secondary);
+	if (error < 0)
+		goto err_putname;
+
+	error = exfat_get_dir_entry_buffers(new_dir, &new_iloc, new_buffers,
+					    new_nr_entries);
+	if (error < 0)
+		goto err_cleanup_old_buffers;
+
+
+	/*
+	 * remove new inode, if it exists.
+	 */
+	if (new_inode) {
+		if (S_ISDIR(new_inode->i_mode))
+			error = __exfat_inode_rmdir(new_dir, new_dentry);
+		else
+			error = __exfat_inode_unlink(new_dir, new_dentry);
+		if (error < 0)
+			goto err_cleanup_new_buffers;
+	}
+
+	/*
+	 * move old esd to new esd (and ditto for esx).
+	 */
+	efd = new_buffers[0].start;
+	esx = new_buffers[1].start;
+	memcpy(efd, old_buffers[0].start, sizeof (*efd));
+	memcpy(esx, old_buffers[1].start, sizeof (*esx));
+
+	efd->secondary_count = new_nr_entries - 1;
+
+	/*
+	 * patch new name after that.
+	 */
+	esx->name_length = __cpu_to_le16(name_length);
+
+	/*
+	 * fill name fragments.
+	 */
+	name_hash = 0;
+	for (i = 0; i < new_nr_entries - 2; ++i, name_length -= 15) {
+		struct exfat_filename_entry *efn = new_buffers[i + 2].start;
+		int len = 15;
+
+		if (name_length < 15)
+			len = name_length;
+
+		memset(efn, 0, sizeof (*efn));
+		efn->type = E_EXFAT_FILENAME;
+		memcpy(efn->name_frag, name + i * 15, len * sizeof (__le16));
+		name_hash = exfat_filename_hash_cont(new_dir->i_sb,
+						     efn->name_frag,
+						     name_hash, len);
+	}
+	__putname(name);
+	esx->name_hash = __cpu_to_le16(name_hash);
+	efd->set_checksum = exfat_dir_entries_checksum(new_buffers,
+						       new_nr_entries);
+	efd->set_checksum = __cpu_to_le16(efd->set_checksum);
+
+	/*
+	 * mark old buffer entries as unused.
+	 */
+	for (i = 0; i < old_info->iloc.nr_secondary; ++i)
+		*((u8*)old_buffers[i].start) &= 0x7f;
+
+	/*
+	 * dirty old & new entries buffers.
+	 */
+	exfat_dirty_dir_entries(new_buffers, new_nr_entries, false);
+	exfat_dirty_dir_entries(old_buffers, old_info->iloc.nr_secondary,
+				false);
+
+	/*
+	 * update links if new_dir and old_dir are differents.
+	 */
+	if (new_dir != old_dir) {
+		drop_nlink(old_dir);
+		inc_nlink(new_dir);
+	}
+
+	/*
+	 * make old inode use the new iloc, and update sb inode hash.
+	 */
+	exfat_remove_inode_hash(old_inode);
+	old_info->iloc = new_iloc;
+	exfat_insert_inode_hash(old_inode);
+
+	/*
+	 * update new dir & old dir mtime/atime
+	 */
+	if (new_dir == old_dir) {
+		new_dir->i_mtime = new_dir->i_atime = CURRENT_TIME_SEC;
+		if (IS_DIRSYNC(new_dir))
+			__exfat_write_inode(new_dir, true);
+		else
+			mark_inode_dirty(new_dir);
+	} else {
+		new_dir->i_mtime = new_dir->i_atime =
+			old_dir->i_mtime = old_dir->i_atime = CURRENT_TIME_SEC;
+		if (IS_DIRSYNC(new_dir)) {
+			__exfat_write_inode(new_dir, true);
+			__exfat_write_inode(old_dir, true);
+		} else {
+			mark_inode_dirty(new_dir);
+			mark_inode_dirty(old_dir);
+		}
+	}
+
+	exfat_unlock_super(new_dir->i_sb);
+	return 0;
+
+err_cleanup_new_buffers:
+	exfat_cleanup_dir_entries(new_buffers, new_nr_entries);
+err_cleanup_old_buffers:
+	exfat_cleanup_dir_entries(old_buffers, old_info->iloc.nr_secondary);
+err_putname:
+	__putname(name);
+unlock_super:
+	exfat_unlock_super(new_dir->i_sb);
+	return error;
+}
diff -Nruw linux-3.11.10-fbx/fs/exfat./read-write.c linux-3.11.10-fbx/fs/exfat/read-write.c
--- linux-3.11.10-fbx/fs/exfat./read-write.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/read-write.c	2015-09-29 11:15:08.397751907 +0200
@@ -0,0 +1,150 @@
+/*
+ * read-write.c for exfat
+ * Created by <nschichan@freebox.fr> on Wed Jul 31 16:37:51 2013
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mpage.h>
+#include <linux/buffer_head.h>
+
+#include "exfat.h"
+#include "exfat_fs.h"
+
+/*
+ * map file sector to disk sector.
+ */
+static int exfat_bmap(struct inode *inode, sector_t fsect, sector_t *dsect)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+	struct exfat_inode_info *info = EXFAT_I(inode);
+	u32 cluster_nr = fsect >> (sbi->clusterbits - sbi->sectorbits);
+	u32 cluster;
+	unsigned int offset = fsect & (sbi->sectors_per_cluster - 1);
+
+	if (info->flags & EXFAT_I_FAT_INVALID)
+		cluster = info->first_cluster + cluster_nr;
+	else {
+		int error;
+
+		error = exfat_get_fat_cluster(inode, cluster_nr, &cluster);
+		if (error)
+			return error;
+	}
+
+	*dsect = exfat_cluster_sector(sbi, cluster) + offset;
+	return 0;
+}
+
+static int exfat_get_block(struct inode *inode, sector_t block,
+			   struct buffer_head *bh, int create)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+	struct exfat_inode_info *info = EXFAT_I(inode);
+	sector_t last_block;
+	unsigned int offset;
+	sector_t dblock;
+	int error;
+
+	last_block = (i_size_read(inode) + sbi->sectorsize - 1) >>
+		sbi->sectorbits;
+	offset = block & (sbi->sectors_per_cluster - 1);
+
+	if (!create && block >= last_block)
+		return 0;
+
+	if (create && block >= last_block && offset == 0) {
+		u32 hint, cluster;
+
+		/*
+		 * request for first sector in a cluster immediate to
+		 * the last allocated cluster of the file: must
+		 * allocate a new clluster.
+		 */
+		error = exfat_get_cluster_hint(inode, &hint);
+		if (error)
+			return error;
+
+		error = exfat_alloc_clusters(inode, hint, &cluster, 1);
+		if (error)
+			return error;
+	}
+
+	error = exfat_bmap(inode, block, &dblock);
+	if (error)
+		return error;
+
+	if (create && block >= last_block) {
+		/*
+		 * currently in create mode: we need to update
+		 * mmu_private.
+		 */
+		info->mmu_private += sbi->sectorsize;
+		set_buffer_new(bh);
+	}
+	map_bh(bh, inode->i_sb, dblock);
+	return 0;
+}
+
+int exfat_readpage(struct file *file, struct page *page)
+{
+	return mpage_readpage(page, exfat_get_block);
+}
+
+int exfat_readpages(struct file *file, struct address_space *mapping,
+		    struct list_head *pages, unsigned nr_pages)
+{
+	return mpage_readpages(mapping, pages, nr_pages, exfat_get_block);
+}
+
+static int exfat_write_error(struct inode *inode, loff_t to)
+{
+	if (to > inode->i_size) {
+		truncate_pagecache(inode, to, inode->i_size);
+		exfat_truncate_blocks(inode, inode->i_size);
+	}
+	return 0;
+}
+
+int exfat_write_begin(struct file *file, struct address_space *mapping,
+		      loff_t pos, unsigned len, unsigned flags,
+		      struct page **pagep, void **fsdata)
+{
+	struct inode *inode = mapping->host;
+	int error;
+
+	*pagep = NULL;
+	error = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				 exfat_get_block, &EXFAT_I(inode)->mmu_private);
+
+	if (error)
+		exfat_write_error(inode, pos + len);
+	return error;
+}
+
+int exfat_write_end(struct file *file, struct address_space *mapping,
+		    loff_t pos, unsigned len, unsigned copied,
+		    struct page *page, void *fsdata)
+{
+	struct inode *inode = mapping->host;
+	int error;
+
+	error = generic_write_end(file, mapping, pos, len, copied, page,
+				  fsdata);
+
+	if (error < len)
+		exfat_write_error(inode, pos + len);
+	return error;
+}
+
+int exfat_writepage(struct page *page, struct writeback_control *wbc)
+{
+	return block_write_full_page(page, exfat_get_block, wbc);
+}
+
+int exfat_writepages(struct address_space *mapping,
+		     struct writeback_control *wbc)
+{
+	return mpage_writepages(mapping, wbc, exfat_get_block);
+}
diff -Nruw linux-3.11.10-fbx/fs/exfat./super.c linux-3.11.10-fbx/fs/exfat/super.c
--- linux-3.11.10-fbx/fs/exfat./super.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/super.c	2015-09-29 11:15:08.397751907 +0200
@@ -0,0 +1,737 @@
+/*
+ * super.c<2> for exfat
+ * Created by <nschichan@freebox.fr> on Tue Jul 23 12:33:53 2013
+ */
+
+#include <linux/kernel.h>
+#include <linux/bug.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/buffer_head.h>
+#include <linux/statfs.h>
+#include <linux/parser.h>
+#include <linux/seq_file.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+
+#include "exfat_fs.h"
+#include "exfat.h"
+
+
+#define PFX	"exFAT: "
+
+static void exfat_put_super(struct super_block *sb);
+static int exfat_statfs(struct dentry *dentry, struct kstatfs *kstat);
+static int exfat_show_options(struct seq_file *m, struct dentry *root);
+static int exfat_remount(struct super_block *sb, int *flags, char *opts);
+
+static const struct super_operations exfat_super_ops = {
+	.alloc_inode	= exfat_alloc_inode,
+	.destroy_inode	= exfat_destroy_inode,
+	.drop_inode	= exfat_drop_inode,
+	.evict_inode	= exfat_evict_inode,
+	.write_inode	= exfat_write_inode,
+	.statfs         = exfat_statfs,
+	.put_super      = exfat_put_super,
+	.show_options	= exfat_show_options,
+	.remount_fs	= exfat_remount,
+};
+
+const struct file_operations exfat_dir_operations = {
+	.llseek = generic_file_llseek,
+	.read = generic_read_dir,
+	.iterate = exfat_iterate,
+	.unlocked_ioctl	= exfat_ioctl,
+};
+
+const struct file_operations exfat_file_operations = {
+	.llseek		= generic_file_llseek,
+	.read		= do_sync_read,
+	.write		= do_sync_write,
+	.aio_read	= generic_file_aio_read,
+	.aio_write	= generic_file_aio_write,
+	.mmap		= generic_file_mmap,
+	.splice_read	= generic_file_splice_read,
+	.unlocked_ioctl	= exfat_ioctl,
+	.fsync		= generic_file_fsync,
+};
+
+const struct inode_operations exfat_dir_inode_operations =
+{
+	.create = exfat_inode_create,
+	.mkdir	= exfat_inode_mkdir,
+	.lookup = exfat_inode_lookup,
+	.rmdir	= exfat_inode_rmdir,
+	.unlink	= exfat_inode_unlink,
+	.rename	= exfat_rename,
+	.setattr = exfat_setattr,
+	.getattr = exfat_getattr,
+};
+
+const struct inode_operations exfat_file_inode_operations = {
+	.setattr = exfat_setattr,
+	.getattr = exfat_getattr,
+};
+
+const struct address_space_operations exfat_address_space_operations = {
+	.readpage	= exfat_readpage,
+	.readpages	= exfat_readpages,
+	.write_begin	= exfat_write_begin,
+	.write_end	= exfat_write_end,
+	.writepage	= exfat_writepage,
+	.writepages	= exfat_writepages,
+};
+
+void exfat_msg(struct super_block *sb, const char *prefix,
+		const char *fmt, ...)
+{
+	struct va_format vaf;
+	va_list args;
+
+	va_start(args, fmt);
+	vaf.fmt = fmt;
+	vaf.va = &args;
+	printk("%sexFAT-fs (%s): %pV\n", prefix, sb->s_id, &vaf);
+	va_end(args);
+}
+
+void exfat_fs_error(struct super_block *sb, const char *fmt, ...)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	struct va_format vaf;
+	va_list args;
+
+	va_start(args, fmt);
+	vaf.fmt = fmt;
+	vaf.va = &args;
+	exfat_msg(sb, KERN_ERR, "error: %pV", &vaf);
+	va_end(args);
+
+	if (sbi->options.error_action == EXFAT_ERROR_ACTION_REMOUNT_RO &&
+	    !(sb->s_flags & MS_RDONLY)) {
+		sb->s_flags |= MS_RDONLY;
+		exfat_msg(sb, KERN_ERR, "remounted read-only due to fs error.");
+	} else if (sbi->options.error_action == EXFAT_ERROR_ACTION_PANIC)
+		panic("exFAT-fs (%s): panic due fs error.\n", sb->s_id);
+}
+
+/*
+ * process checksum on buffer head. first indicates if the special
+ * treatment of the first sector needs to be done or not.
+ *
+ * first sector can be changed (volume flags, and heap use percent),
+ * those fields are excluded from the checksum to allow updating
+ * without recalculating the checksum.
+ */
+static u32 exfat_sb_checksum_process(struct buffer_head *bh, u32 checksum,
+				     unsigned int size,
+				     bool first)
+{
+	unsigned int i;
+
+	for (i = 0; i < size; ++i) {
+		if (first && (i == 106 || i == 107 || i == 112))
+			continue ;
+		checksum = ((checksum << 31) | (checksum >> 1)) +
+			(unsigned char)bh->b_data[i];
+	}
+	return checksum;
+}
+
+static int exfat_check_sb_checksum(struct super_block *sb)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	u32 checksum;
+	int i;
+	int err;
+	struct buffer_head *bh[EXFAT_CHECKSUM_SECTORS + 1];
+
+	/*
+	 * fetch needed sectors, reuse first sector from sbi.
+	 */
+	err = -ENOMEM;
+	memset(bh, 0, sizeof (struct buffer_head*) *
+	       (EXFAT_CHECKSUM_SECTORS + 1));
+	bh[0] = sbi->sb_bh;
+	for (i = 1; i < EXFAT_CHECKSUM_SECTORS + 1; ++i) {
+		bh[i] = sb_bread(sb, i);
+		if (!bh[i])
+			goto out;
+	}
+
+	/*
+	 * calculate checksum.
+	 */
+	checksum = exfat_sb_checksum_process(bh[0], 0, sbi->sectorsize, true);
+	for (i = 1; i < EXFAT_CHECKSUM_SECTORS; ++i) {
+		checksum = exfat_sb_checksum_process(bh[i], checksum,
+						     sbi->sectorsize, false);
+	}
+
+	/*
+	 * compare with the checksum sector.
+	 */
+	err = -EINVAL;
+	for (i = 0; i < sbi->sectorsize; i += sizeof (u32)) {
+		__le32 val = *(u32*)(bh[EXFAT_CHECKSUM_SECTORS]->b_data + i);
+
+		if (__le32_to_cpu(val) != checksum) {
+			exfat_msg(sb, KERN_INFO, "at offset %i, checksum "
+				  "%08x != %08x", i, __le32_to_cpu(val), checksum);
+			goto out;
+		}
+	}
+	err = 0;
+
+out:
+	for (i = 1; i < EXFAT_CHECKSUM_SECTORS; ++i)
+		if (bh[i])
+			brelse(bh[i]);
+	return err;
+}
+
+static int exfat_check_sb(struct super_block *sb)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	struct exfat_vbr *vbr = sbi->vbr;
+	u16 fs_rev;
+	u16 flags;
+	int active_fat;
+	u16 num_fats;
+
+	if (memcmp(vbr->jump, "\xeb\x76\x90", sizeof (vbr->jump))) {
+		exfat_msg(sb, KERN_INFO, "invalid jump field in vbr.");
+		return -EINVAL;
+	}
+
+	if (memcmp(vbr->fsname, "EXFAT   ", 8)) {
+		exfat_msg(sb, KERN_INFO, "invalid fsname field in vbr: %s.",
+			  vbr->fsname);
+		return -EINVAL;
+	}
+
+	fs_rev = __le16_to_cpu(vbr->fs_rev);
+	if (fs_rev != 0x0100) {
+		exfat_msg(sb, KERN_INFO, "filesystem version invalid: "
+			  "have 0x%04x, need 0x0100", fs_rev);
+		return -EINVAL;
+	}
+
+	flags = __le16_to_cpu(vbr->volume_flags);
+	active_fat = exfat_active_fat(flags);
+	if (active_fat != 0) {
+		exfat_msg(sb, KERN_INFO, "filesystems with active fat > 0 are "
+			  "not supported.");
+		return -EINVAL;
+	}
+
+	if (flags & EXFAT_FLAG_MEDIA_FAILURE)
+		exfat_msg(sb, KERN_WARNING, "filesystem had media failure(s)");
+
+	/*
+	 * bytes per sectors are on the range 2^9 - 2^12 (512 - 4096)
+	 */
+	if (vbr->bytes_per_sector < 9 || vbr->bytes_per_sector > 12) {
+		exfat_msg(sb, KERN_ERR, "invalid byte per sectors: %u",
+			  (1 << vbr->bytes_per_sector));
+		return -EINVAL;
+	}
+
+	/*
+	 * sectors per cluster can be as low as 0, and must not result
+	 * in a cluster size higher than 32MB (byte_per_sector +
+	 * sectors_per_cluster must not be creater than 25)
+	 */
+	if (vbr->bytes_per_sector + vbr->sectors_per_cluster > 25) {
+		exfat_msg(sb, KERN_ERR, "invalid cluster size: %u",
+		  1 << (vbr->bytes_per_sector + vbr->sectors_per_cluster));
+		return -EINVAL;
+	}
+
+	num_fats = __le16_to_cpu(vbr->fat_num);
+	if (num_fats == 0) {
+		exfat_msg(sb, KERN_ERR, "superblock reports no FAT.");
+		return -EINVAL;
+	}
+	if (num_fats > 1) {
+		exfat_msg(sb, KERN_ERR, "TexFAT is not supported.");
+		return -EINVAL;
+	}
+
+	if (memcmp(vbr->boot_sig, "\x55\xaa", 2)) {
+		exfat_msg(sb, KERN_ERR, "invalid end boot signature: %02x%02x.",
+			  vbr->boot_sig[0], vbr->boot_sig[1]);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int exfat_fill_root(struct super_block *sb, struct inode *root)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	u32 nclust;
+	u32 dummy;
+	loff_t links;
+
+	root->i_ino = EXFAT_ROOT_INO;
+	root->i_version = 1;
+	EXFAT_I(root)->first_cluster =
+		__le32_to_cpu(sbi->root_dir_cluster);
+	EXFAT_I(root)->attributes = E_EXFAT_ATTR_DIRECTORY;
+
+	root->i_uid = sbi->options.uid;
+	root->i_gid = sbi->options.gid;
+
+	root->i_mode = exfat_make_mode(sbi, S_IRWXUGO, E_EXFAT_ATTR_DIRECTORY);
+	root->i_version++;
+	root->i_generation = 0;
+
+	root->i_op = &exfat_dir_inode_operations;
+	root->i_fop = &exfat_dir_operations;
+
+	/*
+	 * root inode cannot use bitmap.
+	 */
+	EXFAT_I(root)->flags = EXFAT_I_ALLOC_POSSIBLE;
+
+	/*
+	 * set i_size
+	 */
+	nclust = 0;
+	while (__exfat_get_fat_cluster(root, nclust, &dummy, false) == 0)
+		++nclust;
+	root->i_size = nclust << sbi->clusterbits;
+	root->i_blocks = nclust << (sbi->clusterbits - 9);
+	EXFAT_I(root)->allocated_clusters = nclust;
+
+	/*
+	 * +2 to account for '.' and '..'
+	 */
+	links = exfat_dir_links(root);
+	if (links < 0)
+		return links;
+	set_nlink(root, links + 2);
+
+	root->i_mtime = root->i_atime = root->i_ctime = CURRENT_TIME_SEC;
+
+	return 0;
+}
+
+static loff_t exfat_file_max_byte(struct exfat_sb_info *sbi)
+{
+	u32 max_clusters = EXFAT_CLUSTER_LASTVALID -
+		EXFAT_CLUSTER_FIRSTVALID + 1;
+
+	return (loff_t)max_clusters << sbi->clusterbits;
+}
+
+static int exfat_show_options(struct seq_file *m, struct dentry *root)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(root->d_inode->i_sb);
+
+	if (!uid_eq(sbi->options.uid, GLOBAL_ROOT_UID))
+		seq_printf(m, ",uid=%u",
+			   from_kuid_munged(&init_user_ns, sbi->options.uid));
+	if (!uid_eq(sbi->options.gid, GLOBAL_ROOT_GID))
+		seq_printf(m, ",gid=%u",
+			   from_kgid_munged(&init_user_ns, sbi->options.gid));
+
+	seq_printf(m, ",fmask=%04o", sbi->options.fmask);
+	seq_printf(m, ",dmask=%04o", sbi->options.dmask);
+
+	if (sbi->options.time_offset_set)
+		seq_printf(m, ",time_offset=%d", sbi->options.time_offset);
+
+	switch (sbi->options.error_action) {
+	case EXFAT_ERROR_ACTION_PANIC:
+		seq_printf(m, ",errors=panic");
+		break;
+	case EXFAT_ERROR_ACTION_REMOUNT_RO:
+		seq_printf(m, ",errors=remount-ro");
+		break;
+	default:
+		seq_printf(m, ",errors=continue");
+		break;
+	}
+
+	return 0;
+}
+
+enum {
+	Opt_exfat_uid,
+	Opt_exfat_gid,
+	Opt_exfat_dmask,
+	Opt_exfat_fmask,
+	Opt_exfat_time_offset,
+	Opt_exfat_error_continue,
+	Opt_exfat_error_remount_ro,
+	Opt_exfat_error_panic,
+	Opt_exfat_err,
+};
+
+static const match_table_t exfat_tokens = {
+	{ Opt_exfat_uid, "uid=%u", },
+	{ Opt_exfat_gid, "gid=%u", },
+	{ Opt_exfat_dmask, "dmask=%04o", },
+	{ Opt_exfat_fmask, "fmask=%04o", },
+	{ Opt_exfat_time_offset, "time_offset=%d", },
+	{ Opt_exfat_error_continue, "errors=continue", },
+	{ Opt_exfat_error_remount_ro, "errors=remount-ro", },
+	{ Opt_exfat_error_panic, "errors=panic", },
+	{ Opt_exfat_err, NULL },
+};
+
+static int exfat_parse_options(struct super_block *sb, char *opts, int silent)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	char *p;
+
+	sbi->options.uid = current_uid();
+	sbi->options.gid = current_gid();
+
+	sbi->options.dmask = current_umask();
+	sbi->options.fmask = current_umask();
+	sbi->options.time_offset_set = 0;
+	sbi->options.error_action = EXFAT_ERROR_ACTION_CONTINUE;
+
+	while (1) {
+		int token;
+		substring_t args[MAX_OPT_ARGS];
+		unsigned int optval;
+
+		p = strsep(&opts, ",");
+		if (!p)
+			break;
+		token = match_token(p, exfat_tokens, args);
+
+		switch (token) {
+		case Opt_exfat_uid:
+			if (match_int(&args[0], &optval))
+				return -EINVAL;
+			sbi->options.uid = make_kuid(current_user_ns(), optval);
+			break;
+
+		case Opt_exfat_gid:
+			if (match_int(&args[0], &optval))
+				return -EINVAL;
+			sbi->options.gid = make_kgid(current_user_ns(), optval);
+			break;
+
+		case Opt_exfat_dmask:
+			if (match_octal(&args[0], &optval))
+				return -EINVAL;
+			sbi->options.dmask = optval;
+			break;
+
+		case Opt_exfat_fmask:
+			if (match_octal(&args[0], &optval))
+				return -EINVAL;
+			sbi->options.fmask = optval;
+			break;
+
+		case Opt_exfat_time_offset:
+			if (match_int(&args[0], &optval))
+				return -EINVAL;
+			if (optval < -12 * 60 && optval > 12 * 60) {
+				if (!silent)
+					exfat_msg(sb, KERN_INFO, "invalid "
+						  "time_offset value %d: "
+						  "should be between %d and %d",
+						  optval, -12 * 60, 12 * 60);
+				return -EINVAL;
+			}
+			sbi->options.time_offset = optval;
+			sbi->options.time_offset_set = 1;
+			break;
+
+		case Opt_exfat_error_continue:
+			sbi->options.error_action = EXFAT_ERROR_ACTION_CONTINUE;
+			break;
+
+		case Opt_exfat_error_remount_ro:
+			sbi->options.error_action =
+				EXFAT_ERROR_ACTION_REMOUNT_RO;
+			break;
+
+		case Opt_exfat_error_panic:
+			sbi->options.error_action = EXFAT_ERROR_ACTION_PANIC;
+			break;
+
+		default:
+			if (!silent)
+				exfat_msg(sb, KERN_INFO, "Unrecognized mount "
+					  "option %s or missing parameter.\n",
+					  p);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static void exfat_set_sb_dirty(struct super_block *sb, bool set, bool force)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	u16 flags;
+
+	/*
+	 * do not change anything if mounted read only and not
+	 * forced. the force case would happen during remount.
+	 */
+	if ((sb->s_flags & MS_RDONLY) && !force)
+		return ;
+
+	if (sbi->dirty) {
+		if (set)
+			exfat_msg(sb, KERN_WARNING, "Volume was not cleanly "
+				  "umounted. fsck should probably be needed.");
+		return ;
+	}
+
+	flags = __le16_to_cpu(sbi->vbr->volume_flags);
+	if (set)
+		flags |= EXFAT_FLAG_DIRTY;
+	else
+		flags &= ~EXFAT_FLAG_DIRTY;
+	sbi->vbr->volume_flags = __cpu_to_le16(flags);
+
+	mark_buffer_dirty(sbi->sb_bh);
+	sync_dirty_buffer(sbi->sb_bh);
+}
+
+static int exfat_remount(struct super_block *sb, int *flags, char *opts)
+{
+	int new_rdonly = *flags & MS_RDONLY;
+
+	if (new_rdonly != (sb->s_flags & MS_RDONLY)) {
+		if (new_rdonly)
+			exfat_set_sb_dirty(sb, false, false);
+		else
+			/*
+			 * sb->s_flag still has MS_RDONLY, so we need
+			 * to force the dirty state
+			 */
+			exfat_set_sb_dirty(sb, true, true);
+	}
+	return 0;
+}
+
+static int exfat_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct exfat_sb_info *sbi = NULL;
+	int ret = -ENOMEM;
+	struct inode *root = NULL;
+	int i;
+
+	sbi = kzalloc(sizeof (*sbi), GFP_KERNEL);
+	if (!sbi)
+		return -ENOMEM;
+
+	sb->s_fs_info = sbi;
+	if (exfat_parse_options(sb, data, silent) < 0)
+		return -EINVAL;
+
+	mutex_init(&sbi->sb_mutex);
+	spin_lock_init(&sbi->inode_hash_lock);
+
+	/*
+	 * first block, before we know sector size.
+	 */
+	sbi->sb_bh = sb_bread(sb, 0);
+	if (!sbi->sb_bh)
+		goto fail;
+
+	sbi->vbr = (struct exfat_vbr*)sbi->sb_bh->b_data;
+	sb->s_op = &exfat_super_ops;
+
+
+	ret = exfat_check_sb(sb);
+	if (ret)
+		goto fail;
+
+	/*
+	 * vbr seems sane, fill sbi.
+	 */
+	sbi->sectorsize = (1 << sbi->vbr->bytes_per_sector);
+	sbi->clustersize = sbi->sectorsize *
+		(1 << sbi->vbr->sectors_per_cluster);
+
+	sbi->sectors_per_cluster = sbi->clustersize / sbi->sectorsize;
+
+	sbi->sectorbits = sbi->vbr->bytes_per_sector;
+	sbi->clusterbits = sbi->vbr->sectors_per_cluster + sbi->sectorbits;
+	sbi->sectormask = sbi->sectorsize - 1;
+	sbi->clustermask = sbi->clustersize - 1;
+
+
+	sbi->fat_offset = __le32_to_cpu(sbi->vbr->fat_offset);
+	sbi->fat_length = __le32_to_cpu(sbi->vbr->fat_length);
+
+	sbi->root_dir_cluster = __le32_to_cpu(sbi->vbr->cluster_root_dir);
+
+	sbi->cluster_heap_offset = __le32_to_cpu(sbi->vbr->cluster_heap_offset);
+	sbi->cluster_count = __le32_to_cpu(sbi->vbr->cluster_count);
+
+	sbi->dirty = !!(__le16_to_cpu(sbi->vbr->volume_flags) &
+			EXFAT_FLAG_DIRTY);
+
+	/*
+	 * now that we know sector size, reread superblock with
+	 * correct sector size.
+	 */
+	ret = -EIO;
+	if (sb->s_blocksize != sbi->sectorsize) {
+		if (!sb_set_blocksize(sb, sbi->sectorsize)) {
+			exfat_msg(sb, KERN_INFO, "bad block size %d.",
+				  sbi->sectorsize);
+			goto fail;
+		}
+
+		brelse(sbi->sb_bh);
+		sbi->vbr = NULL;
+
+		sbi->sb_bh = sb_bread(sb, 0);
+		if (!sbi->sb_bh)
+			goto fail;
+		sbi->vbr = (struct exfat_vbr*)sbi->sb_bh->b_data;
+		sb->s_fs_info = sbi;
+	}
+
+	ret = exfat_check_sb_checksum(sb);
+	if (ret)
+		goto fail;
+
+	sb->s_maxbytes = exfat_file_max_byte(sbi);
+
+	ret = exfat_init_fat(sb);
+	if (ret)
+		goto fail;
+
+	for (i = 0 ; i < EXFAT_HASH_SIZE; ++i) {
+		INIT_HLIST_HEAD(&sbi->inode_hash[i]);
+	}
+
+	/*
+	 * create root inode.
+	 */
+	root = new_inode(sb);
+	if (!root)
+		goto fail;
+
+	exfat_fill_root(sb, root);
+
+	ret = exfat_upcase_init(root);
+	if (ret)
+		goto fail_iput;
+
+	ret = exfat_init_bitmap(root);
+	if (ret)
+		goto fail_iput;
+
+
+	sb->s_root = d_make_root(root);
+	if (!sb->s_root)
+		goto fail_iput;
+
+	exfat_set_sb_dirty(sb, true, false);
+	return 0;
+
+fail_iput:
+	iput(root);
+
+fail:
+	if (sbi->sb_bh)
+		brelse(sbi->sb_bh);
+	if (sbi)
+		kfree(sbi);
+	return ret;
+}
+
+static struct dentry *exfat_mount(struct file_system_type *fstype,
+				  int flags, const char *dev_name, void *data)
+{
+	return mount_bdev(fstype, flags, dev_name, data, exfat_fill_super);
+}
+
+static void exfat_put_super(struct super_block *sb)
+{
+	struct exfat_sb_info *sbi;
+
+	sbi = EXFAT_SB(sb);
+	if (sbi) {
+		exfat_set_sb_dirty(sb, false, false);
+		exfat_exit_bitmap(sb);
+		brelse(sbi->sb_bh);
+		kfree(sbi->upcase_table);
+		kfree(sbi);
+	}
+}
+
+static int exfat_statfs(struct dentry *dentry, struct kstatfs *kstat)
+{
+	struct super_block *sb = dentry->d_inode->i_sb;
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
+
+	memset(kstat, 0, sizeof (*kstat));
+
+
+	kstat->f_bsize = sbi->clustersize;
+	kstat->f_blocks = sbi->cluster_count;
+	kstat->f_bfree = sbi->free_clusters;
+	kstat->f_bavail = sbi->free_clusters;
+	kstat->f_namelen = 255;
+	kstat->f_fsid.val[0] = (u32)id;
+	kstat->f_fsid.val[1] = (u32)(id >> 32);
+
+	return 0;
+}
+
+static struct file_system_type exfat_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "exfat",
+	.mount		= exfat_mount,
+	.kill_sb	= kill_block_super,
+	.fs_flags	= FS_REQUIRES_DEV,
+};
+
+static int __init exfat_init(void)
+{
+	int error;
+
+	/* some sanity check on internal structure sizes */
+	BUILD_BUG_ON(sizeof (struct exfat_vbr) != 512);
+
+	BUILD_BUG_ON(sizeof (struct exfat_volume_label_entry) != 0x20);
+	BUILD_BUG_ON(sizeof (struct exfat_bitmap_entry) != 0x20);
+	BUILD_BUG_ON(sizeof (struct exfat_upcase_entry) != 0x20);
+	BUILD_BUG_ON(sizeof (struct exfat_guid_entry) != 0x20);
+	BUILD_BUG_ON(sizeof (struct exfat_padding_entry) != 0x20);
+	BUILD_BUG_ON(sizeof (struct exfat_acl_entry) != 0x20);
+	BUILD_BUG_ON(sizeof (struct exfat_filedir_entry) != 0x20);
+	BUILD_BUG_ON(sizeof (struct exfat_stream_extension_entry) != 0x20);
+	BUILD_BUG_ON(sizeof (struct exfat_filename_entry) != 0x20);
+
+	error = exfat_init_inodes();
+	if (error)
+		return error;
+
+
+	error = register_filesystem(&exfat_fs_type);
+	if (error)
+		exfat_exit_inodes();
+	return error;
+}
+
+static void __exit exfat_exit(void)
+{
+	unregister_filesystem(&exfat_fs_type);
+	exfat_exit_inodes();
+}
+
+module_init(exfat_init);
+module_exit(exfat_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nicolas Schichan <nschichan@freebox.fr>");
diff -Nruw linux-3.11.10-fbx/fs/exfat./time.c linux-3.11.10-fbx/fs/exfat/time.c
--- linux-3.11.10-fbx/fs/exfat./time.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/time.c	2015-06-12 14:02:26.810990199 +0200
@@ -0,0 +1,126 @@
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+
+#include "exfat.h"
+#include "exfat_fs.h"
+
+
+
+extern struct timezone sys_tz;
+
+/*
+ * The epoch of FAT timestamp is 1980.
+ *     :  bits :     value
+ * date:  0 -  4: day	(1 -  31)
+ * date:  5 -  8: month	(1 -  12)
+ * date:  9 - 15: year	(0 - 127) from 1980
+ * time:  0 -  4: sec	(0 -  29) 2sec counts
+ * time:  5 - 10: min	(0 -  59)
+ * time: 11 - 15: hour	(0 -  23)
+ */
+#define SECS_PER_MIN	60
+#define SECS_PER_HOUR	(60 * 60)
+#define SECS_PER_DAY	(SECS_PER_HOUR * 24)
+/* days between 1.1.70 and 1.1.80 (2 leap days) */
+#define DAYS_DELTA	(365 * 10 + 2)
+/* 120 (2100 - 1980) isn't leap year */
+#define YEAR_2100	120
+#define IS_LEAP_YEAR(y)	(!((y) & 3) && (y) != YEAR_2100)
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+static time_t days_in_year[] = {
+	/* Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec */
+	0,   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0,
+};
+
+/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
+void exfat_time_2unix(struct timespec *ts, u32 datetime, u8 time_cs,
+		      s8 tz_offset)
+{
+	u16 date = (datetime >> 16);
+	u16 time = (datetime & 0xffff);
+	time_t second, day, leap_day, month, year;
+
+	year  = date >> 9;
+	month = max(1, (date >> 5) & 0xf);
+	day   = max(1, date & 0x1f) - 1;
+
+	if (((tz_offset & (1 << 6)) == 0))
+		tz_offset &= ~(1 << 7);
+
+	leap_day = (year + 3) / 4;
+	if (year > YEAR_2100)		/* 2100 isn't leap year */
+		leap_day--;
+	if (IS_LEAP_YEAR(year) && month > 2)
+		leap_day++;
+
+	second =  (time & 0x1f) << 1;
+	second += ((time >> 5) & 0x3f) * SECS_PER_MIN;
+	second += (time >> 11) * SECS_PER_HOUR;
+	second += (year * 365 + leap_day
+		   + days_in_year[month] + day
+		   + DAYS_DELTA) * SECS_PER_DAY;
+
+	second -= tz_offset * 15 * SECS_PER_MIN;
+
+	if (time_cs) {
+		ts->tv_sec = second + (time_cs / 100);
+		ts->tv_nsec = (time_cs % 100) * 10000000;
+	} else {
+		ts->tv_sec = second;
+		ts->tv_nsec = 0;
+	}
+}
+
+/* Convert linear UNIX date to a FAT time/date pair. */
+void exfat_time_2exfat(struct exfat_sb_info *sbi, struct timespec *ts,
+		       u32 *datetime, u8 *time_cs, s8 *tz_offset)
+{
+	struct tm tm;
+	u16 time;
+	u16 date;
+	int offset;
+
+	if (sbi->options.time_offset_set) {
+		offset = -sbi->options.time_offset;
+	} else
+		offset = sys_tz.tz_minuteswest;
+
+	time_to_tm(ts->tv_sec, -offset * SECS_PER_MIN, &tm);
+
+	/*  FAT can only support year between 1980 to 2107 */
+	if (tm.tm_year < 1980 - 1900) {
+		time = 0;
+		date = cpu_to_le16((0 << 9) | (1 << 5) | 1);
+		if (time_cs)
+			*time_cs = 0;
+		*tz_offset = 0;
+		return;
+	}
+	if (tm.tm_year > 2107 - 1900) {
+		time = cpu_to_le16((23 << 11) | (59 << 5) | 29);
+		date = cpu_to_le16((127 << 9) | (12 << 5) | 31);
+		if (time_cs)
+			*time_cs = 199;
+		*tz_offset = 0;
+		return;
+	}
+
+	/* from 1900 -> from 1980 */
+	tm.tm_year -= 80;
+	/* 0~11 -> 1~12 */
+	tm.tm_mon++;
+	/* 0~59 -> 0~29(2sec counts) */
+	tm.tm_sec >>= 1;
+
+	time = cpu_to_le16(tm.tm_hour << 11 | tm.tm_min << 5 | tm.tm_sec);
+	date = cpu_to_le16(tm.tm_year << 9 | tm.tm_mon << 5 | tm.tm_mday);
+
+	*datetime = (date << 16) | time;
+
+	if (time_cs)
+		*time_cs = (ts->tv_sec & 1) * 100 + ts->tv_nsec / 10000000;
+	*tz_offset = -offset / 15;
+	*tz_offset |= (1 << 7);
+}
diff -Nruw linux-3.11.10-fbx/fs/exfat./upcase.c linux-3.11.10-fbx/fs/exfat/upcase.c
--- linux-3.11.10-fbx/fs/exfat./upcase.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/fs/exfat/upcase.c	2015-06-12 14:02:26.810990199 +0200
@@ -0,0 +1,136 @@
+/*
+ * upcase.c for exfat
+ * Created by <nschichan@freebox.fr> on Wed Aug  7 11:51:37 2013
+ */
+
+#include <linux/buffer_head.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+
+#include "exfat.h"
+#include "exfat_fs.h"
+
+static u32 exfat_calc_upcase_checksum(const u8 *data, u32 checksum,
+				      size_t count)
+{
+	while (count) {
+		checksum = ((checksum << 31) | (checksum >> 1)) + *data;
+		--count;
+		++data;
+	}
+	return checksum;
+}
+
+static int exfat_load_upcase_table(struct super_block *sb, u32 disk_cluster,
+				   u32 *out_checksum)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	struct buffer_head *bh;
+	sector_t start, sect, end;
+	u32 off = 0;
+	u32 byte_len = sbi->upcase_len * sizeof (__le16);
+	u32 checksum = 0;
+
+	/*
+	 * up-case table are not fragmented, so sequential cluster
+	 * read will do here.
+	 */
+	start = exfat_cluster_sector(sbi, disk_cluster);
+	end = start + DIV_ROUND_UP(byte_len,
+			   sbi->sectorsize);
+	for (sect = start; sect < end; ++sect) {
+		u32 len = sbi->sectorsize;
+
+		if (sect == end - 1)
+			len = byte_len & sbi->sectormask;
+
+		bh = sb_bread(sb, sect);
+		if (!bh) {
+			exfat_msg(sb, KERN_ERR,
+				  "unable to read upcase sector %llu", sect);
+			return -EIO;
+		}
+		memcpy((u8*)sbi->upcase_table + off, bh->b_data,
+		       len);
+
+		checksum = exfat_calc_upcase_checksum(bh->b_data, checksum,
+						      len);
+
+		off += len;
+		brelse(bh);
+	}
+
+	BUG_ON(off != byte_len);
+	*out_checksum = checksum;
+	return 0;
+}
+
+int exfat_upcase_init(struct inode *root)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(root->i_sb);
+	struct exfat_upcase_entry *upcase;
+	struct exfat_dir_ctx dctx;
+	int error;
+	u64 upcase_length;
+	u32 checksum;
+
+	/*
+	 * configure directory context and look for an upcase table
+	 * entry.
+	 */
+	if (exfat_init_dir_ctx(root, &dctx, 0) < 0)
+		return -EIO;
+
+	error = -EIO;
+	upcase = __exfat_dentry_next(&dctx, E_EXFAT_UPCASE_TABLE, 0xff,
+				     true, NULL);
+	if (!upcase)
+		goto fail;
+
+	/*
+	 * check upcase table length. we need it to be non-zero,
+	 * ending on a __le16 boundary and provide at most a
+	 * conversion for the whole __le16 space.
+	 */
+	upcase_length = __le64_to_cpu(upcase->length);
+	if (upcase_length == 0 ||
+	    upcase_length & (sizeof (__le16) - 1) ||
+	    upcase_length > 0xffff * sizeof (__le16)) {
+		exfat_msg(root->i_sb, KERN_ERR, "invalid upcase length %llu",
+			  upcase_length);
+		goto fail;
+	}
+
+	/*
+	 * load complete upcase table in memory.
+	 */
+	error = -ENOMEM;
+	sbi->upcase_len = upcase_length / sizeof (__le16);
+	sbi->upcase_table = kmalloc(upcase_length, GFP_NOFS);
+	if (!sbi->upcase_table)
+		goto fail;
+
+	error = exfat_load_upcase_table(root->i_sb,
+					__le32_to_cpu(upcase->cluster_addr),
+					&checksum);
+	if (error)
+		goto fail;
+
+	if (checksum != __le32_to_cpu(upcase->checksum)) {
+		exfat_msg(root->i_sb, KERN_INFO,
+			  "upcase table checksum mismatch: have %08x, "
+			  "expect %08x", checksum,
+			  __le32_to_cpu(upcase->checksum));
+		error = -EINVAL;
+		goto fail;
+	}
+
+	exfat_cleanup_dir_ctx(&dctx);
+	return 0;
+
+fail:
+	if (sbi->upcase_table)
+		kfree(sbi->upcase_table);
+	exfat_cleanup_dir_ctx(&dctx);
+	return error;
+}
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/fs/squashfs/decompressor_single.c	2014-07-29 21:29:44.905867048 +0200
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/buffer_head.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "decompressor.h"
+#include "squashfs.h"
+
+/*
+ * This file implements single-threaded decompression in the
+ * decompressor framework
+ */
+
+struct squashfs_stream {
+	void		*stream;
+	struct mutex	mutex;
+};
+
+void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
+						void *comp_opts)
+{
+	struct squashfs_stream *stream;
+	int err = -ENOMEM;
+
+	stream = kmalloc(sizeof(*stream), GFP_KERNEL);
+	if (stream == NULL)
+		goto out;
+
+	stream->stream = msblk->decompressor->init(msblk, comp_opts);
+	if (IS_ERR(stream->stream)) {
+		err = PTR_ERR(stream->stream);
+		goto out;
+	}
+
+	kfree(comp_opts);
+	mutex_init(&stream->mutex);
+	return stream;
+
+out:
+	kfree(stream);
+	return ERR_PTR(err);
+}
+
+void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
+{
+	struct squashfs_stream *stream = msblk->stream;
+
+	if (stream) {
+		msblk->decompressor->free(stream->stream);
+		kfree(stream);
+	}
+}
+
+int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
+	int b, int offset, int length, struct squashfs_page_actor *output)
+{
+	int res;
+	struct squashfs_stream *stream = msblk->stream;
+
+	mutex_lock(&stream->mutex);
+	res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
+		offset, length, output);
+	mutex_unlock(&stream->mutex);
+
+	if (res < 0)
+		ERROR("%s decompression failed, data probably corrupt\n",
+			msblk->decompressor->name);
+
+	return res;
+}
+
+int squashfs_max_decompressors(void)
+{
+	return 1;
+}
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/fs/squashfs/file_cache.c	2014-07-29 21:29:44.905867048 +0200
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+#include <linux/mutex.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+/* Read separately compressed datablock and memcopy into page cache */
+int squashfs_readpage_block(struct page *page, u64 block, int bsize)
+{
+	struct inode *i = page->mapping->host;
+	struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
+		block, bsize);
+	int res = buffer->error;
+
+	if (res)
+		ERROR("Unable to read page, block %llx, size %x\n", block,
+			bsize);
+	else
+		squashfs_copy_cache(page, buffer, buffer->length, 0);
+
+	squashfs_cache_put(buffer);
+	return res;
+}
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/fs/squashfs/page_actor.h	2014-07-29 21:29:44.905867048 +0200
@@ -0,0 +1,81 @@
+#ifndef PAGE_ACTOR_H
+#define PAGE_ACTOR_H
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef CONFIG_SQUASHFS_FILE_DIRECT
+struct squashfs_page_actor {
+	void	**page;
+	int	pages;
+	int	length;
+	int	next_page;
+};
+
+static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page,
+	int pages, int length)
+{
+	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
+
+	if (actor == NULL)
+		return NULL;
+
+	actor->length = length ? : pages * PAGE_CACHE_SIZE;
+	actor->page = page;
+	actor->pages = pages;
+	actor->next_page = 0;
+	return actor;
+}
+
+static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
+{
+	actor->next_page = 1;
+	return actor->page[0];
+}
+
+static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
+{
+	return actor->next_page == actor->pages ? NULL :
+		actor->page[actor->next_page++];
+}
+
+static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
+{
+	/* empty */
+}
+#else
+struct squashfs_page_actor {
+	union {
+		void		**buffer;
+		struct page	**page;
+	};
+	void	*pageaddr;
+	void    *(*squashfs_first_page)(struct squashfs_page_actor *);
+	void    *(*squashfs_next_page)(struct squashfs_page_actor *);
+	void    (*squashfs_finish_page)(struct squashfs_page_actor *);
+	int	pages;
+	int	length;
+	int	next_page;
+};
+
+extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int);
+extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page
+							 **, int, int);
+static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
+{
+	return actor->squashfs_first_page(actor);
+}
+static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
+{
+	return actor->squashfs_next_page(actor);
+}
+static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
+{
+	actor->squashfs_finish_page(actor);
+}
+#endif
+#endif
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/linux/fbxatm_dev.h	2015-06-12 14:02:27.278993571 +0200
@@ -0,0 +1,434 @@
+#ifndef LINUX_FBXATM_DEV_H_
+#define LINUX_FBXATM_DEV_H_
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/err.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+#include <linux/mutex.h>
+#include <linux/fbxatm.h>
+#include <linux/device.h>
+#include <asm/atomic.h>
+#include <linux/if_vlan.h>
+#include <linux/fbxatm_remote.h>
+
+/*
+ * atm cell helper
+ */
+#define ATM_CELL_HDR_SIZE	5
+
+#define ATM_GET_GFC(h)		(((h)[0] & 0xf0) >> 4)
+#define ATM_SET_GFC(h,v)	do {					\
+					(h)[0] &= ~0xf0;		\
+					(h)[0] |= (v) << 4;		\
+				} while (0)
+
+#define ATM_GET_VPI(h)		((((h)[0] & 0x0f) << 4) |		\
+				 (((h)[1] & 0xf0) >> 4))
+#define ATM_SET_VPI(h,v)	do {					\
+					(h)[0] &= ~0xf;			\
+					(h)[1] &= ~0xf0;		\
+					(h)[0] |= (v) >> 4;		\
+					(h)[1] |= ((v) & 0xf) << 4;	\
+				} while (0)
+
+#define ATM_GET_VCI(h)		((((h)[1] & 0x0f) << 12) |		\
+				 ((h)[2] << 4) |			\
+				 ((((h)[3] & 0xf0) >> 4)))
+#define ATM_SET_VCI(h,v)	do {					\
+					(h)[1] &= ~0xf;			\
+					(h)[3] &= ~0xf0;		\
+					(h)[1] |= (v) >> 12;		\
+					(h)[2] = ((v) & 0xff0) >> 4;	\
+					(h)[3] |= ((v) & 0xf) << 4;	\
+				} while (0)
+
+
+#define ATM_GET_PT(h)		(((h)[3] & 0x0e) >> 1)
+#define ATM_SET_PT(h,v)		do {					\
+					(h)[3] &= ~0xe;			\
+					(h)[3] |= (v) << 1;		\
+				} while (0)
+
+#define ATM_GET_CLP(h)		(((h)[3] & 0x01))
+#define ATM_SET_CLP(h,v)	do {					\
+					(h)[3] &= ~1;			\
+					(h)[3] |= (v);			\
+				} while (0)
+
+#define ATM_GET_HEC(h)		((h)[4])
+#define ATM_SET_HEC(h,v)	do {					\
+					(h)[4] = (v);			\
+				} while (0)
+
+
+/*
+ * OAM definition
+ */
+#define OAM_VCI_SEG_F4			3
+#define OAM_VCI_END2END_F4		4
+
+#define OAM_PTI_SEG_F5			0x4
+#define OAM_PTI_END2END_F5		0x5
+
+#define OAM_TYPE_SHIFT			4
+#define OAM_TYPE_MASK			(0xf << OAM_TYPE_SHIFT)
+#define OAM_TYPE_FAULT_MANAGEMENT	0x1
+#define OAM_TYPE_PERF_MANAGEMENT	0x2
+#define OAM_TYPE_ACTIVATION		0x8
+
+#define FUNC_TYPE_SHIFT			0
+#define FUNC_TYPE_MASK			(0xf << FUNC_TYPE_SHIFT)
+#define FUNC_TYPE_AIS			0x0
+#define FUNC_TYPE_FERF			0x1
+#define FUNC_TYPE_CONT_CHECK		0x4
+#define FUNC_TYPE_OAM_LOOPBACK		0x8
+
+struct fbxatm_oam_cell_payload {
+	u8			cell_hdr[5];
+	u8			cell_type;
+	u8			loopback_indication;
+	u8			correlation_tag[4];
+	u8			loopback_id[16];
+	u8			source_id[16];
+	u8			reserved[8];
+	u8			crc10[2];
+};
+
+struct fbxatm_oam_cell {
+	struct fbxatm_oam_cell_payload	payload;
+	struct list_head		next;
+};
+
+struct fbxatm_oam_ping {
+	struct fbxatm_oam_ping_req	req;
+	u32				correlation_id;
+	int				replied;
+	wait_queue_head_t		wq;
+	struct list_head		next;
+};
+
+/*
+ * vcc/device stats
+ */
+struct fbxatm_vcc_stats {
+	unsigned long			rx_bytes;
+	unsigned long			tx_bytes;
+	unsigned long			rx_aal5;
+	unsigned long			tx_aal5;
+};
+
+struct fbxatm_dev_stats {
+	unsigned long			rx_bytes;
+	unsigned long			tx_bytes;
+	unsigned long			rx_aal5;
+	unsigned long			tx_aal5;
+	unsigned long			rx_f4_oam;
+	unsigned long			tx_f4_oam;
+	unsigned long			rx_f5_oam;
+	unsigned long			tx_f5_oam;
+	unsigned long			rx_bad_oam;
+	unsigned long			rx_bad_llid_oam;
+	unsigned long			rx_other_oam;
+	unsigned long			rx_dropped;
+	unsigned long			tx_drop_nolink;
+};
+
+/*
+ * vcc user ops
+ */
+struct fbxatm_vcc_uops {
+	void	(*link_change)(void *cb_data, int link,
+			       unsigned int rx_cell_rate,
+			       unsigned int tx_cell_rate);
+	void	(*rx_pkt)(struct sk_buff *skb, void *cb_data);
+	void	(*tx_done)(void *cb_data);
+};
+
+/*
+ * vcc status flags
+ */
+enum {
+	FBXATM_VCC_F_FULL		= (1 << 0),
+
+	FBXATM_VCC_F_LINK_UP		= (1 << 1),
+};
+
+
+/*
+ * vcc definition
+ */
+struct fbxatm_dev;
+
+struct fbxatm_vcc {
+	unsigned int			vpi;
+	unsigned int			vci;
+
+	struct fbxatm_vcc_qos		qos;
+
+	struct fbxatm_vcc_stats		stats;
+
+	enum fbxatm_vcc_user		user;
+	void				*user_priv;
+
+	struct fbxatm_dev		*adev;
+	void				*dev_priv;
+
+	spinlock_t			user_ops_lock;
+	const struct fbxatm_vcc_uops	*user_ops;
+	void				*user_cb_data;
+
+	unsigned int			to_drop_pkt;
+
+	spinlock_t			tx_lock;
+	unsigned long			vcc_flags;
+
+	struct list_head		next;
+};
+
+/*
+ * fbxatm device operation
+ */
+struct fbxatm_dev_ops {
+	int (*open)(struct fbxatm_vcc *vcc);
+
+	void (*close)(struct fbxatm_vcc *vcc);
+
+	int (*ioctl)(struct fbxatm_dev *adev,
+		     unsigned int cmd, void __user *arg);
+
+	int (*send)(struct fbxatm_vcc *vcc, struct sk_buff *skb);
+
+	int (*send_oam)(struct fbxatm_dev *adev,
+			struct fbxatm_oam_cell *cell);
+
+	int (*init_procfs)(struct fbxatm_dev *adev);
+	void (*release_procfs)(struct fbxatm_dev *adev);
+
+	struct module			*owner;
+};
+
+/*
+ * device flags
+ */
+enum {
+	FBXATM_DEV_F_LINK_UP		= (1 << 0),
+};
+
+/*
+ * fbxatm device definition
+ */
+struct fbxatm_dev {
+	int				ifindex;
+	unsigned long			dev_flags;
+
+	unsigned int			max_vcc;
+	unsigned int			vci_mask;
+	unsigned int			vpi_mask;
+	unsigned int			max_priority;
+	unsigned int			max_rx_priority;
+	unsigned int			tx_headroom;
+
+	char				*name;
+
+	/* unit: b/s */
+	unsigned int			link_rate_ds;
+	unsigned int			link_rate_us;
+
+	unsigned int			link_cell_rate_ds;
+	unsigned int			link_cell_rate_us;
+
+	const struct fbxatm_dev_ops	*ops;
+
+	spinlock_t			stats_lock;
+	struct fbxatm_dev_stats		stats;
+
+	struct list_head		vcc_list;
+
+	struct device			dev;
+
+	spinlock_t			oam_lock;
+	struct list_head		rx_oam_cells;
+	unsigned int			rx_oam_cells_count;
+	struct work_struct		oam_work;
+
+	struct list_head		oam_pending_ping;
+	u32				oam_correlation_id;
+
+	struct proc_dir_entry		*dev_proc_entry;
+	void				*priv;
+	struct list_head		next;
+};
+
+/*
+ * API for device drivers
+ */
+struct fbxatm_dev *fbxatm_alloc_device(int sizeof_priv);
+
+int fbxatm_register_device(struct fbxatm_dev *adev,
+			   const char *base_name,
+			   const struct fbxatm_dev_ops *ops);
+
+void fbxatm_free_device(struct fbxatm_dev *adev);
+
+void fbxatm_dev_set_link_up(struct fbxatm_dev *adev);
+
+void fbxatm_dev_set_link_down(struct fbxatm_dev *adev);
+
+int fbxatm_unregister_device(struct fbxatm_dev *adev);
+
+void fbxatm_netifrx_oam(struct fbxatm_dev *adev,
+			struct fbxatm_oam_cell *cell);
+
+
+static inline int fbxatm_vcc_link_is_up(struct fbxatm_vcc *vcc)
+{
+	return test_bit(FBXATM_VCC_F_LINK_UP, &vcc->vcc_flags);
+}
+
+#define	FBXATMDEV_ALIGN		4
+
+static inline void *fbxatm_dev_priv(struct fbxatm_dev *adev)
+{
+	return (u8 *)adev + ((sizeof(struct fbxatm_dev)
+			      + (FBXATMDEV_ALIGN - 1))
+			     & ~(FBXATMDEV_ALIGN - 1));
+}
+
+/*
+ * API for FBXATM stack user
+ */
+struct fbxatm_ioctl {
+	int (*handler)(struct socket *sock,
+		       unsigned int cmd, void __user *useraddr);
+
+	void (*release)(struct socket *sock);
+
+	struct module		*owner;
+	struct list_head	next;
+};
+
+void fbxatm_set_uops(struct fbxatm_vcc *vcc,
+		     const struct fbxatm_vcc_uops *user_ops,
+		     void *user_cb_data);
+
+struct fbxatm_vcc *
+fbxatm_bind_to_vcc(const struct fbxatm_vcc_id *id,
+		   enum fbxatm_vcc_user user);
+
+void fbxatm_unbind_vcc(struct fbxatm_vcc *vcc);
+
+
+static inline int fbxatm_vcc_queue_full(struct fbxatm_vcc *vcc)
+{
+	return test_bit(FBXATM_VCC_F_FULL, &vcc->vcc_flags);
+}
+
+#ifdef CONFIG_FBXATM_STACK
+/*
+ * stack user callback to send data on given vcc
+ */
+static inline int fbxatm_send(struct fbxatm_vcc *vcc, struct sk_buff *skb)
+{
+	int ret;
+	unsigned int len;
+
+	len = skb->len;
+
+	spin_lock_bh(&vcc->tx_lock);
+	if (!test_bit(FBXATM_VCC_F_LINK_UP, &vcc->vcc_flags)) {
+		spin_unlock_bh(&vcc->tx_lock);
+		dev_kfree_skb(skb);
+		spin_lock(&vcc->adev->stats_lock);
+		vcc->adev->stats.tx_drop_nolink++;
+		spin_unlock(&vcc->adev->stats_lock);
+		return 0;
+	}
+
+	ret = vcc->adev->ops->send(vcc, skb);
+	if (!ret) {
+		vcc->stats.tx_bytes += len;
+		vcc->stats.tx_aal5++;
+	}
+	spin_unlock_bh(&vcc->tx_lock);
+
+	if (!ret) {
+		spin_lock_bh(&vcc->adev->stats_lock);
+		vcc->adev->stats.tx_bytes += len;
+		vcc->adev->stats.tx_aal5++;
+		spin_unlock_bh(&vcc->adev->stats_lock);
+	}
+	return ret;
+}
+
+/*
+ * device callback when packet comes in
+ */
+static inline void fbxatm_netifrx(struct fbxatm_vcc *vcc, struct sk_buff *skb)
+{
+	unsigned int len;
+
+	len = skb->len;
+
+	spin_lock_bh(&vcc->user_ops_lock);
+	if (!vcc->user_ops) {
+		spin_unlock_bh(&vcc->user_ops_lock);
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	if (vcc->to_drop_pkt) {
+		vcc->to_drop_pkt--;
+		spin_unlock_bh(&vcc->user_ops_lock);
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	vcc->stats.rx_bytes += len;
+	vcc->stats.rx_aal5++;
+
+	vcc->user_ops->rx_pkt(skb, vcc->user_cb_data);
+	spin_unlock_bh(&vcc->user_ops_lock);
+
+	spin_lock_bh(&vcc->adev->stats_lock);
+	vcc->adev->stats.rx_bytes += len;
+	vcc->adev->stats.rx_aal5++;
+	spin_unlock_bh(&vcc->adev->stats_lock);
+}
+
+/*
+ * device callback when tx is done on vcc
+ */
+static inline void fbxatm_tx_done(struct fbxatm_vcc *vcc)
+{
+	spin_lock_bh(&vcc->user_ops_lock);
+	if (vcc->user_ops)
+		vcc->user_ops->tx_done(vcc->user_cb_data);
+	spin_unlock_bh(&vcc->user_ops_lock);
+}
+#else
+int fbxatm_send(struct fbxatm_vcc *vcc, struct sk_buff *skb);
+void fbxatm_netifrx(struct fbxatm_vcc *vcc, struct sk_buff *skb);
+void fbxatm_tx_done(struct fbxatm_vcc *vcc);
+#endif
+
+static inline unsigned int fbxatm_rx_reserve(void)
+{
+#ifdef CONFIG_FBXATM_STACK
+	/* normal stack, no headroom needed */
+	return 0;
+#else
+	/* remote stub, we need to send rx skb to another location,
+	 * adding the fbxatm_remote header, an ethernet header (with
+	 * possible vlan) */
+	return ALIGN(sizeof (struct fbxatm_remote_hdr) + VLAN_ETH_HLEN, 4);
+#endif
+}
+
+void fbxatm_register_ioctl(struct fbxatm_ioctl *ioctl);
+
+void fbxatm_unregister_ioctl(struct fbxatm_ioctl *ioctl);
+
+#endif /* !LINUX_FBXATM_DEV_H_ */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/linux/fbxatm.h	2015-09-29 11:15:08.661753918 +0200
@@ -0,0 +1,158 @@
+/*
+ * Generic fbxatm definition, exported to userspace
+ */
+#ifndef LINUX_FBXATM_H_
+#define LINUX_FBXATM_H_
+
+#include <linux/types.h>
+#include <linux/if.h>
+
+#define FBXATM_IOCTL_MAGIC		0xd3
+
+/* allow userspace usage without up to date kernel headers */
+#ifndef PF_FBXATM
+#define PF_FBXATM			32
+#define AF_FBXATM			PF_FBXATM
+#endif
+
+struct fbxatm_vcc_id {
+	int				dev_idx;
+	__u32				vpi;
+	__u32				vci;
+};
+
+enum fbxatm_vcc_user {
+	FBXATM_VCC_USER_NONE = 0,
+	FBXATM_VCC_USER_2684,
+	FBXATM_VCC_USER_PPPOA,
+};
+
+enum fbxatm_vcc_traffic_class {
+	FBXATM_VCC_TC_UBR_NO_PCR = 0,
+	FBXATM_VCC_TC_UBR,
+};
+
+struct fbxatm_vcc_qos {
+	__u32				traffic_class;
+	__u32				max_sdu;
+	__u32				max_buffered_pkt;
+	__u32				priority;
+	__u32				rx_priority;
+};
+
+
+/*
+ * VCC related
+ */
+struct fbxatm_vcc_params {
+	/* ADD/DEL/GET */
+	struct fbxatm_vcc_id		id;
+
+	/* ADD/GET */
+	struct fbxatm_vcc_qos		qos;
+
+	/* GET */
+	enum fbxatm_vcc_user		user;
+};
+
+#define FBXATM_IOCADD		_IOW(FBXATM_IOCTL_MAGIC,	1,	\
+					struct fbxatm_vcc_params)
+
+#define FBXATM_IOCDEL		_IOR(FBXATM_IOCTL_MAGIC,	2,	\
+					struct fbxatm_vcc_params)
+
+#define FBXATM_IOCGET		_IOWR(FBXATM_IOCTL_MAGIC,	3,	\
+					struct fbxatm_vcc_params)
+
+
+struct fbxatm_vcc_drop_params {
+	struct fbxatm_vcc_id		id;
+	unsigned int			drop_count;
+};
+
+#define FBXATM_IOCDROP		_IOWR(FBXATM_IOCTL_MAGIC,	5,	\
+					struct fbxatm_vcc_drop_params)
+
+/*
+ * OAM related
+ */
+enum fbxatm_oam_ping_type {
+	FBXATM_OAM_PING_SEG_F4	= 0,
+	FBXATM_OAM_PING_SEG_F5,
+	FBXATM_OAM_PING_E2E_F4,
+	FBXATM_OAM_PING_E2E_F5,
+};
+
+struct fbxatm_oam_ping_req {
+	/* only dev_idx for F4 */
+	struct fbxatm_vcc_id		id;
+
+	__u8				llid[16];
+	enum fbxatm_oam_ping_type	type;
+};
+
+#define FBXATM_IOCOAMPING	_IOWR(FBXATM_IOCTL_MAGIC,	10,	\
+				      struct fbxatm_oam_ping_req)
+
+
+/*
+ * PPPOA related
+ */
+enum fbxatm_pppoa_encap {
+	FBXATM_EPPPOA_AUTODETECT = 0,
+	FBXATM_EPPPOA_VCMUX,
+	FBXATM_EPPPOA_LLC,
+};
+
+struct fbxatm_pppoa_vcc_params {
+	struct fbxatm_vcc_id		id;
+	__u32				encap;
+	__u32				cur_encap;
+};
+
+#define FBXATM_PPPOA_IOCADD	_IOW(FBXATM_IOCTL_MAGIC,	20,	\
+					struct fbxatm_pppoa_vcc_params)
+
+#define FBXATM_PPPOA_IOCDEL	_IOW(FBXATM_IOCTL_MAGIC,	21,	\
+					struct fbxatm_pppoa_vcc_params)
+
+#define FBXATM_PPPOA_IOCGET	_IOWR(FBXATM_IOCTL_MAGIC,	22,	\
+					struct fbxatm_pppoa_vcc_params)
+
+
+
+/*
+ * 2684 related
+ */
+enum fbxatm_2684_encap {
+	FBXATM_E2684_VCMUX = 0,
+	FBXATM_E2684_LLC,
+};
+
+enum fbxatm_2684_payload {
+	FBXATM_P2684_BRIDGE = 0,
+	FBXATM_P2684_ROUTED,
+};
+
+#define FBXATM_2684_MAX_VCC		8
+
+struct fbxatm_2684_vcc_params {
+	struct fbxatm_vcc_id		id_list[FBXATM_2684_MAX_VCC];
+	size_t				id_count;
+
+	__u32				encap;
+	__u32				payload;
+	char				dev_name[IFNAMSIZ];
+};
+
+
+#define FBXATM_2684_IOCADD	_IOW(FBXATM_IOCTL_MAGIC,	30,	\
+					struct fbxatm_2684_vcc_params)
+
+#define FBXATM_2684_IOCDEL	_IOW(FBXATM_IOCTL_MAGIC,	31,	\
+					struct fbxatm_2684_vcc_params)
+
+#define FBXATM_2684_IOCGET	_IOWR(FBXATM_IOCTL_MAGIC,	32,	\
+					struct fbxatm_2684_vcc_params)
+
+#endif /* LINUX_FBXATM_H_ */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/linux/fbxatm_remote.h	2015-06-12 14:02:27.282993596 +0200
@@ -0,0 +1,216 @@
+#ifndef FBXATM_REMOTE_H_
+#define FBXATM_REMOTE_H_
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+
+/*
+ * fbxatm remote protocol messages
+ */
+#define ETH_P_FBXATM_REMOTE	0x8844
+#define FBXATM_REMOTE_MAGIC	0xd76f8d2f
+
+enum fbxatm_remote_flags {
+	FBXATM_RFLAGS_ACK = (1 << 0),
+};
+
+enum fbxatm_remote_mtype {
+	/* driver => stub */
+	FBXATM_RMT_CONNECT = 0,
+
+	/* stub => driver */
+	FBXATM_RMT_DEV_LINK,
+	FBXATM_RMT_DEV_RX_OAM,
+
+	/* driver => stub */
+	FBXATM_RMT_KEEPALIVE,
+	FBXATM_RMT_DEV_SEND_OAM,
+	FBXATM_RMT_VCC_ACTION,
+
+	/* driver => stub */
+	FBXATM_RMT_VCC_SEND,
+
+	/* stub => driver */
+	FBXATM_RMT_VCC_QEMPTY,
+	FBXATM_RMT_VCC_RX,
+};
+
+struct fbxatm_remote_hdr {
+	u32	magic;
+	u8	flags;
+	u8	seq;
+	u16	len;
+	u16	sport;
+	u16	dport;
+
+	u32	session_id;
+	u32	mtype;
+};
+
+/*
+ * sent to destination port 0
+ */
+struct fbxatm_remote_connect {
+	u8	name[32];
+
+	u16	dev_link_port;
+	u16	dev_rx_oam_port;
+};
+
+struct fbxatm_remote_connect_ack {
+	u16	vcc_action_port;
+	u16	dev_send_oam_port;
+	u16	keepalive_port;
+	u16	pad;
+
+	u32	max_vcc;
+	u32	vci_mask;
+	u32	vpi_mask;
+	u32	max_priority;
+	u32	max_rx_priority;
+
+	u32	link;
+	u32	link_rate_ds;
+	u32	link_rate_us;
+	u32	link_cell_rate_ds;
+	u32	link_cell_rate_us;
+};
+
+/*
+ * sent on dev_link port
+ */
+struct fbxatm_remote_dev_link {
+	u32	link;
+	u32	link_rate_ds;
+	u32	link_rate_us;
+	u32	link_cell_rate_ds;
+	u32	link_cell_rate_us;
+};
+
+/*
+ * sent on vcc_action port
+ */
+struct fbxatm_remote_vcc_action {
+	/* 1: open - 0: close */
+	u32	action;
+
+	/*
+	 * open args
+	 */
+	u16	vcc_rx_port;
+	u16	vcc_qempty_port;
+
+	/* from vcc id struct */
+	u32	vpi;
+	u32	vci;
+
+	/* from qos struct */
+	u32	traffic_class;
+	u32	max_sdu;
+	u32	max_buffered_pkt;
+	u32	priority;
+	u32	rx_priority;
+
+	/*
+	 * close args
+	 */
+	u32	vcc_remote_id;
+};
+
+struct fbxatm_remote_vcc_action_ack {
+	u32	ret;
+
+	/* open args ack */
+	u32	vcc_remote_id;
+	u16	vcc_send_port;
+	u16	pad;
+};
+
+/*
+ * sent on vcc_send port
+ */
+struct fbxatm_remote_vcc_send_ack {
+	u32	full;
+};
+
+/*
+ * pseudo socket layer
+ */
+struct fbxatm_remote_sock;
+struct fbxatm_remote_ctx;
+
+struct fbxatm_remote_sockaddr {
+	u16		lport;
+	u16		dport;
+	u32		mtype;
+	int		infinite_retry;
+	int		(*deliver)(void *priv, struct sk_buff *skb,
+				   struct sk_buff **ack);
+	void		(*response)(void *priv, struct sk_buff *skb);
+	void		*priv;
+};
+
+struct sk_buff *fbxatm_remote_alloc_skb(struct fbxatm_remote_ctx *ctx,
+					unsigned int size);
+
+unsigned int fbxatm_remote_headroom(struct fbxatm_remote_ctx *ctx);
+
+void fbxatm_remote_sock_getaddr(struct fbxatm_remote_sock *sock,
+				struct fbxatm_remote_sockaddr *addr);
+
+void fbxatm_remote_sock_purge(struct fbxatm_remote_sock *sock);
+
+int fbxatm_remote_sock_pending(struct fbxatm_remote_sock *sock);
+
+struct fbxatm_remote_ctx *fbxatm_remote_alloc_ctx(struct net_device *netdev,
+						  u8 *remote_mac,
+						  u32 session_id,
+						  void (*timeout)(void *priv),
+						  void *priv);
+
+struct fbxatm_remote_sock *
+fbxatm_remote_sock_bind(struct fbxatm_remote_ctx *ctx,
+			struct fbxatm_remote_sockaddr *addr,
+			int send_ack);
+
+struct fbxatm_remote_sock *
+fbxatm_remote_sock_connect(struct fbxatm_remote_ctx *ctx,
+			   struct fbxatm_remote_sockaddr *addr,
+			   int need_ack);
+
+int fbxatm_remote_sock_send(struct fbxatm_remote_sock *sock,
+			    struct sk_buff *skb);
+
+int fbxatm_remote_sock_send_ack(struct fbxatm_remote_sock *sock,
+				struct sk_buff *skb);
+
+int fbxatm_remote_sock_send_raw_ack(struct fbxatm_remote_ctx *ctx,
+				    struct net_device *dev,
+				    u8 *remote_mac,
+				    struct fbxatm_remote_hdr *hdr,
+				    struct sk_buff *ack);
+
+void fbxatm_remote_sock_close(struct fbxatm_remote_sock *sock);
+
+void fbxatm_remote_set_unknown_cb(void (*cb)(struct net_device *,
+					     struct sk_buff *));
+
+void fbxatm_remote_free_ctx(struct fbxatm_remote_ctx *ctx);
+
+void fbxatm_remote_ctx_set_dead(struct fbxatm_remote_ctx *ctx);
+
+int fbxatm_remote_init(void);
+
+void fbxatm_remote_exit(void);
+
+/*
+ * platform data for fbxatm_remote driver
+ */
+struct fbxatm_remote_pdata {
+	u8	remote_mac[ETH_ALEN];
+	char	netdev_name[IFNAMSIZ];
+	char	remote_name[32];
+};
+
+#endif /* !FBXATM_REMOTE_H_ */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/linux/fbxbridge.h	2015-06-12 14:02:27.282993596 +0200
@@ -0,0 +1,242 @@
+#ifndef _FBXBRIDGE_H
+# define _FBXBRIDGE_H
+
+#include <linux/if.h>
+#include <linux/if_ether.h>
+
+#define MAX_ALIASES		3
+
+#define FBXBRIDGE_PFX		"fbxbridge: "
+
+#define FBXBRIDGE_LAN_TO_WAN	1
+#define FBXBRIDGE_WAN_TO_LAN	2
+
+#define FBXBRIDGE_FLAGS_FILTER			(1 << 0)
+#define FBXBRIDGE_FLAGS_DHCPD			(1 << 1)
+#define FBXBRIDGE_FLAGS_NETFILTER		(1 << 2)
+
+/*
+ * ioctl command
+ */
+
+enum fbxbridge_ioctl_cmd
+{
+	E_CMD_BR_CHG = 0,
+	E_CMD_BR_DEV_CHG,
+	E_CMD_BR_PARAMS,
+};
+
+struct fbxbridge_ioctl_chg
+{
+	char	brname[IFNAMSIZ];
+	int	action;
+};
+
+struct fbxbridge_ioctl_dev_chg
+{
+	char	brname[IFNAMSIZ];
+	char	devname[IFNAMSIZ];
+	int	wan;
+	int	action;
+};
+
+struct fbxbridge_port_info
+{
+	char	name[IFNAMSIZ];
+	int	present;
+};
+
+struct fbxbridge_ioctl_params
+{
+	int				action;
+	char				brname[IFNAMSIZ];
+	struct fbxbridge_port_info	wan_dev;
+	struct fbxbridge_port_info	lan_dev;
+	unsigned int			flags;
+	unsigned char			lan_hwaddr[ETH_ALEN];
+	unsigned char			have_hw_addr;
+	unsigned int			dns1_addr;
+	unsigned int			dns2_addr;
+	unsigned long			ip_aliases[MAX_ALIASES];
+
+	unsigned long			dhcpd_renew_time;
+	unsigned long			dhcpd_rebind_time;
+	unsigned long			dhcpd_lease_time;
+	unsigned int			inputmark;
+};
+
+struct fbxbridge_ioctl_req
+{
+	enum fbxbridge_ioctl_cmd	cmd;
+	unsigned long			arg;
+};
+
+#ifdef __KERNEL__
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+
+#define ARP_RATE_LIMIT		(HZ)
+#define ARP_ETHER_SIZE		(8 + ETH_ALEN * 2 + 4 * 2)
+
+#define FBXBR_PORT_WAN		0
+#define FBXBR_PORT_LAN		1
+
+#define FBXBR_CACHE_SIZE	128
+#define FBXBR_MAX_RULES		256
+
+struct fbxbridge_port;
+
+struct fbxbridge_fp_rule {
+	u32			sip;
+	u32			dip;
+	u16			sport;
+	u16			dport;
+
+	u8			dest_hwaddr[6];
+	struct fbxbridge_port	*oport;
+
+	struct hlist_node	hnext;
+	struct list_head	next;
+};
+
+struct fbxbridge_fp {
+	struct hlist_head	hrules[FBXBR_CACHE_SIZE];
+	struct list_head	rules;
+	unsigned int		count;
+};
+
+struct fbxbridge_port {
+	struct net_device	*dev;
+	struct net_device	*master_dev;
+	u16			vlan1;
+	u16			vlan2;
+	struct fbxbridge_fp	tcp_fp;
+	struct fbxbridge_fp	udp_fp;
+};
+
+struct fbxbridge {
+	struct fbxbridge	*next;
+	unsigned int		refcount;
+
+	char			name[IFNAMSIZ];
+	struct net_device	*dev;
+
+	/* local and remote (fbx) ip address */
+	unsigned long		br_ipaddr;
+	unsigned long		br_remote_ipaddr;
+
+	/* list of ip we consider to be local */
+	unsigned long		ip_aliases[MAX_ALIASES];
+
+	/* wan side inet info */
+	unsigned long		wan_ipaddr;
+	unsigned long		wan_netmask;
+	unsigned long		lan_netmask;
+	/* this is the _client_ gw */
+	unsigned long		wan_gw;
+
+	char			wan_dev_name[IFNAMSIZ];
+	char			lan_dev_name[IFNAMSIZ];
+	int			fast_path_enabled;
+	struct fbxbridge_port	ports[2];
+
+	unsigned char		lan_hwaddr[ETH_ALEN];
+	unsigned char		have_hw_addr;
+
+	unsigned int		flags;
+	unsigned int		inputmark;
+
+	unsigned int		dns1_ipaddr;
+	unsigned int		dns2_ipaddr;
+
+	unsigned long		last_arp_send;
+
+	unsigned long		dhcpd_renew_time;
+	unsigned long		dhcpd_rebind_time;
+	unsigned long		dhcpd_lease_time;
+};
+
+extern struct fbxbridge *fbxbridge_list;
+
+/* fbxbridge_dev.c */
+void __fbxbridge_keep_hw_addr(struct fbxbridge *br, unsigned char *hwaddr);
+
+/* fbxbridge_dhcp.c */
+void fbxbridge_dhcpd(struct fbxbridge *br, struct sk_buff *skb);
+
+
+/* fbxbridge_forward.c */
+struct sk_buff *fbxbridge_handle_frame(struct fbxbridge *br,
+				       struct sk_buff *skb);
+
+
+/* fbxbridge_filter.c */
+int
+fbxbridge_filter_lan_to_wan_packet(struct fbxbridge *br, struct sk_buff *skb);
+
+int
+fbxbridge_filter_wan_to_lan_packet(struct fbxbridge *br, struct sk_buff *skb);
+
+int fbxbridge_nf_hook(struct fbxbridge *br, uint8_t pf, unsigned int hook,
+		      struct sk_buff *skb, struct net_device *in,
+		      struct net_device *out);
+
+/* fbxbridge_local.c */
+void
+handle_local_input_lan_frame(struct fbxbridge *br, struct sk_buff *skb);
+
+int
+handle_local_output_frame(struct fbxbridge *br, struct sk_buff *skb);
+
+
+/* fbxbridge_output.c */
+void output_arp_frame(struct fbxbridge *br, struct net_device *dev,
+		      unsigned short type,
+		      unsigned long src_ip, unsigned char *src_hw,
+		      unsigned long target_ip, unsigned char *target_hw);
+
+void output_lan_frame(struct fbxbridge *br, struct sk_buff *skb);
+
+void output_lan_mcast_frame(struct fbxbridge *br, struct sk_buff *skb);
+
+void output_wan_frame(struct fbxbridge *br, struct sk_buff *skb);
+
+
+/* fbxbridge_utils.c */
+void fbxbridge_snat_packet(struct sk_buff *skb, unsigned long new_addr);
+
+void fbxbridge_dnat_packet(struct sk_buff *skb, unsigned long new_addr);
+
+int fbxbridge_check_ip_packet(struct sk_buff *skb);
+
+int fbxbridge_check_udp_tcp_packet(struct sk_buff *skb);
+
+/* fbxbridge_fastpath.c */
+int __fbxbridge_fp_in_vlan_tcp4(struct net_device *idev, struct sk_buff *skb);
+
+int __fbxbridge_fp_in_vlan_udp4(struct net_device *idev, struct sk_buff *skb);
+
+int __fbxbridge_fp_add_wan_to_lan(struct fbxbridge *br,
+				  const struct sk_buff *skb,
+				  const uint8_t *new_dest_hw_addr);
+
+int __fbxbridge_fp_add_lan_to_wan(struct fbxbridge *br,
+				  const struct sk_buff *skb,
+				  const uint8_t *new_dest_hw_addr);
+
+void __fbxbridge_fp_flush_by_dip(struct fbxbridge_port *bport, uint32_t dip);
+
+void __fbxbridge_fp_flush(struct fbxbridge *br);
+
+void fbxbridge_fp_flush_all(void);
+
+void fbxbridge_fp_init(struct fbxbridge *br);
+
+void __fbxbridge_fp_check(struct fbxbridge *br);
+
+#endif /* ! __KERNEL__ */
+
+#endif
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/linux/fbxgpio_core.h	2015-06-12 14:02:27.282993596 +0200
@@ -0,0 +1,39 @@
+/*
+ * fbxgpio.h for linux-freebox
+ * Created by <nschichan@freebox.fr> on Wed Feb 21 22:09:46 2007
+ * Freebox SA
+ */
+
+#ifndef FBXGPIO_H
+# define FBXGPIO_H
+
+# include <linux/types.h>
+
+/* can change pin direction */
+#define FBXGPIO_PIN_DIR_RW	(1 << 0)
+#define FBXGPIO_PIN_REVERSE_POL	(1 << 1)
+
+struct fbxgpio_operations {
+	int  (*get_datain)(int gpio);
+	void (*set_dataout)(int gpio, int val);
+	int  (*get_dataout)(int gpio);
+	void (*set_direction)(int gpio, int dir);
+	int  (*get_direction)(int gpio);
+};
+
+
+struct fbxgpio_pin {
+	const struct fbxgpio_operations	*ops;
+	const char			*pin_name;
+	uint32_t			flags;
+	int				direction;
+	int				pin_num;
+	unsigned int			cur_dataout;
+	struct device			*dev;
+};
+
+
+#define GPIO_DIR_IN	0x1
+#define GPIO_DIR_OUT	0x0
+
+#endif /* !FBXGPIO_H */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/linux/fbxjtag.h	2015-06-12 14:02:27.282993596 +0200
@@ -0,0 +1,88 @@
+#ifndef FBXJTAG_H_
+# define FBXJTAG_H_
+
+#ifdef __KERNEL__
+# include <linux/types.h>
+#endif
+
+# define JTAG_RESET_STEPS	16
+# define JTAG_DATA_READ_SIZE	128
+# define JTAG_INST_READ_SIZE	128
+# define JTAG_DEF_CLOCK_DELAY	500
+# define JTAG_DEF_WAIT_TMS	0
+
+enum jtag_main_state {
+	JTAG_STATE_TEST_MASK	=	0x10,
+	JTAG_STATE_RUN_MASK	=	0x20,
+	JTAG_STATE_DR_MASK	=	0x40,
+	JTAG_STATE_IR_MASK	=	0x80,
+};
+#define JTAG_STATE_MASK			0xF0
+
+enum jtag_sub_state {
+	JTAG_SUB_STATE_SELECT	=	0x0,
+	JTAG_SUB_STATE_CAPTURE	=	0x1,
+	JTAG_SUB_STATE_SHIFT	=	0x2,
+	JTAG_SUB_STATE_EXIT1	=	0x3,
+	JTAG_SUB_STATE_PAUSE	=	0x4,
+	JTAG_SUB_STATE_EXIT2	=	0x5,
+	JTAG_SUB_STATE_UPDATE	=	0x6,
+};
+#define JTAG_SUB_STATE_MASK		0xF
+
+enum jtag_state {
+	JTAG_STATE_UNDEF	= 0,
+	JTAG_STATE_TEST_LOGIC_RESET	= JTAG_STATE_TEST_MASK,
+	JTAG_STATE_RUN_TEST_IDLE	= JTAG_STATE_RUN_MASK,
+
+	JTAG_STATE_SELECT_DR	= JTAG_STATE_DR_MASK | JTAG_SUB_STATE_SELECT,
+	JTAG_STATE_CAPTURE_DR	= JTAG_STATE_DR_MASK | JTAG_SUB_STATE_CAPTURE,
+	JTAG_STATE_SHIFT_DR	= JTAG_STATE_DR_MASK | JTAG_SUB_STATE_SHIFT,
+	JTAG_STATE_EXIT1_DR	= JTAG_STATE_DR_MASK | JTAG_SUB_STATE_EXIT1,
+	JTAG_STATE_PAUSE_DR	= JTAG_STATE_DR_MASK | JTAG_SUB_STATE_PAUSE,
+	JTAG_STATE_EXIT2_DR	= JTAG_STATE_DR_MASK | JTAG_SUB_STATE_EXIT2,
+	JTAG_STATE_UPDATE_DR	= JTAG_STATE_DR_MASK | JTAG_SUB_STATE_UPDATE,
+
+	JTAG_STATE_SELECT_IR	= JTAG_STATE_IR_MASK | JTAG_SUB_STATE_SELECT,
+	JTAG_STATE_CAPTURE_IR	= JTAG_STATE_IR_MASK | JTAG_SUB_STATE_CAPTURE,
+	JTAG_STATE_SHIFT_IR	= JTAG_STATE_IR_MASK | JTAG_SUB_STATE_SHIFT,
+	JTAG_STATE_EXIT1_IR	= JTAG_STATE_IR_MASK | JTAG_SUB_STATE_EXIT1,
+	JTAG_STATE_PAUSE_IR	= JTAG_STATE_IR_MASK | JTAG_SUB_STATE_PAUSE,
+	JTAG_STATE_EXIT2_IR	= JTAG_STATE_IR_MASK | JTAG_SUB_STATE_EXIT2,
+	JTAG_STATE_UPDATE_IR	= JTAG_STATE_IR_MASK | JTAG_SUB_STATE_UPDATE,
+
+	JTAG_STATE_MAX
+};
+
+#define JTAG_STATE_IN_DR(state)	((state) & JTAG_STATE_DR_MASK)
+#define JTAG_STATE_IN_IR(state)	((state) & JTAG_STATE_IR_MASK)
+
+#ifdef __KERNEL__
+
+#define JTAG_BUF_SIZE	2048
+
+struct fbxjtag_data {
+	const char	*name;
+	struct {
+		struct fbxgpio_pin	*tck;
+		struct fbxgpio_pin	*tms;
+		struct fbxgpio_pin	*tdi;
+		struct fbxgpio_pin	*tdo;
+	}		gpios;
+	unsigned long	clock_delay;
+	unsigned long	wait_tms;
+	unsigned long	data_read_size;
+	unsigned long	instruction_read_size;
+	struct device	*dev;
+	enum jtag_state state;
+	char		nb_reset;
+	char		dr_buf[JTAG_BUF_SIZE];
+	unsigned 	dr_w;
+	unsigned 	dr_r;
+	char		ir_buf[JTAG_BUF_SIZE];
+	unsigned 	ir_r;
+	unsigned 	ir_w;
+};
+#endif
+
+#endif /* !FBXJTAG_H_ */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/linux/fbxmtd_map_ioctl.h	2015-06-12 14:02:27.282993596 +0200
@@ -0,0 +1,52 @@
+/*
+ * fbxmtd_map_ioctl.h for linux-freebox
+ * Created by <nschichan@freebox.fr> on Thu Feb  8 20:37:28 2007
+ * Freebox SA
+ */
+
+#ifndef FBXMTD_MAP_IOCTL_H
+# define FBXMTD_MAP_IOCTL_H
+
+/*
+ * IOCTL interface
+ */
+#define FBXMTD_MINOR	242
+
+#define FBXMTD_MAP_IOCTL_MAX_DEV	2
+#define FBXMTD_MAP_IOCTL_MAX_PART	16
+
+struct fbxmtd_map_ioctl_part
+{
+	char		name[32];
+	uint32_t	offset;
+	uint32_t	size;
+	uint32_t	flags;
+};
+
+struct fbxmtd_map_ioctl_dev
+{
+	char				name[32];
+	uint32_t			base_phys;
+	int				bus_width;
+	uint32_t			size;
+	uint32_t			status;
+	struct fbxmtd_map_ioctl_part	parts[FBXMTD_MAP_IOCTL_MAX_PART];
+	int				num_parts;
+};
+
+#define FBXMTD_MAP_IOCTL_NR	0x42
+
+struct fbxmtd_map_ioctl_query
+{
+	uint32_t	cmd;
+	uint32_t	param;
+	int		result;
+	void __user	*user_buf;
+	uint32_t	user_buf_size;
+};
+
+#define FBXMTDCTL_CMD_GET_DEVICES	0x1
+#define FBXMTDCTL_CMD_ADD_DEVICE	0x2
+#define FBXMTDCTL_CMD_DEL_DEVICE	0x3
+
+#endif /* !FBXMTD_MAP_IOCTL_H */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/linux/fbxprocfs.h	2015-06-12 14:02:27.282993596 +0200
@@ -0,0 +1,40 @@
+#ifndef FBXPROCFS_H_
+#define FBXPROCFS_H_
+
+#include <linux/proc_fs.h>
+#include <asm/atomic.h>
+#include <linux/seq_file.h>
+
+struct fbxprocfs_client
+{
+	const char *dirname;
+	struct module *owner;
+	struct proc_dir_entry *dir;
+	atomic_t refcount;
+	struct list_head list;
+};
+
+struct fbxprocfs_desc {
+	char		*name;
+	unsigned long	id;
+	int	(*rfunc)(struct seq_file *, void *);
+	int	(*wfunc)(struct file *, const char *, unsigned long, void *);
+};
+
+struct fbxprocfs_client *fbxprocfs_add_client(const char *dirname,
+					      struct module *owner);
+
+int fbxprocfs_remove_client(struct fbxprocfs_client *client);
+
+
+int
+fbxprocfs_create_entries(struct fbxprocfs_client *client,
+			 const struct fbxprocfs_desc *ro_desc,
+			 const struct fbxprocfs_desc *rw_desc);
+
+int
+fbxprocfs_remove_entries(struct fbxprocfs_client *client,
+			 const struct fbxprocfs_desc *ro_desc,
+			 const struct fbxprocfs_desc *rw_desc);
+
+#endif /* FBXPROCFS_H_ */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/linux/fbxserial.h	2015-09-29 11:15:08.661753918 +0200
@@ -0,0 +1,122 @@
+#ifndef FBXSERIAL_H_
+#define FBXSERIAL_H_
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+/*
+ * some part of serial may vary, we use abstract struct to store this,
+ * data content depends on type.
+ */
+#define EXTINFO_SIZE		128
+#define EXTINFO_MAX_COUNT	16
+
+/*
+ * extdev desc
+ */
+#define EXTINFO_TYPE_EXTDEV	1
+
+#define EXTDEV_TYPE_BUNDLE	1
+#define EXTDEV_TYPE_MAX		2
+
+struct fbx_serial_extinfo {
+	u32			type;
+
+	union {
+		/* extdev */
+		struct {
+			u32	type;
+			u32	model;
+			char	serial[64];
+		} extdev;
+
+		/* raw access */
+		unsigned char	data[EXTINFO_SIZE];
+	} u;
+}  __attribute__ ((packed));;
+
+
+/*
+ * master serial structure
+ */
+
+#define FBXSERIAL_VERSION	1
+
+#define FBXSERIAL_MAGIC		0x2d9521ab
+
+#define MAC_ADDR_SIZE		6
+#define RANDOM_DATA_SIZE	32
+
+/*
+ * this  is the  maximum size  we accept  to check  crc32  against, so
+ * structure may no grow larger than this
+ */
+#define FBXSERIAL_MAX_SIZE	8192
+
+struct fbx_serial {
+	u32	crc32;
+	u32	magic;
+	u32	struct_version;
+	u32	len;
+
+	/* board serial */
+	u16	type;
+	u8	version;
+	u8	manufacturer;
+	u16	year;
+	u8	week;
+	u32	number;
+	u32	flags;
+
+	/* mac address base */
+	u8	mac_addr_base[MAC_ADDR_SIZE];
+
+	/* mac address count */
+	u8	mac_count;
+
+	/* random data */
+	u8	random_data[RANDOM_DATA_SIZE];
+
+	/* last update of data (seconds since epoch) */
+	u32	last_modified;
+
+	/* count of following extinfo tag */
+	u32	extinfo_count;
+
+	/* beginning of extended info */
+	struct fbx_serial_extinfo	extinfos[EXTINFO_MAX_COUNT];
+
+} __attribute__ ((packed));
+
+
+/*
+ * default value to use in case magic is wrong (no cksum in that case)
+ */
+static inline void fbxserial_set_default(struct fbx_serial *s)
+{
+	memset(s, 0, sizeof (*s));
+	s->magic = FBXSERIAL_MAGIC;
+	s->struct_version = FBXSERIAL_VERSION;
+	s->len = sizeof (*s);
+	s->manufacturer = '_';
+	memcpy(s->mac_addr_base, "\x00\x07\xCB\x00\x00\xFD", 6);
+	s->mac_count = 1;
+}
+
+void
+fbxserialinfo_get_random(unsigned char *data, unsigned int len);
+
+void
+fbxserialinfo_get_mac_addr(unsigned char *data);
+
+int
+fbxserialinfo_read(void *data, struct fbx_serial *out);
+
+struct fbx_serial *fbxserialinfo_get(void);
+
+/*
+ * implemented in board specific code
+ */
+const struct fbx_serial *arch_get_fbxserial(void);
+
+#endif /* FBXSERIAL_H_ */
diff -Nruw linux-3.11.10-fbx/include/linux/hdmi-cec./dev.h linux-3.11.10-fbx/include/linux/hdmi-cec/dev.h
--- linux-3.11.10-fbx/include/linux/hdmi-cec./dev.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/include/linux/hdmi-cec/dev.h	2015-06-12 14:02:27.290993656 +0200
@@ -0,0 +1,30 @@
+#ifndef __HDMI_CEC_DEV_H
+#define __HDMI_CEC_DEV_H
+
+#include <linux/ioctl.h>
+#include <linux/hdmi-cec/hdmi-cec.h>
+
+#define CEC_IOCTL_BASE	'C'
+
+#define CEC_SET_LOGICAL_ADDRESS	_IOW(CEC_IOCTL_BASE, 0, int)
+#define CEC_RESET_DEVICE	_IOW(CEC_IOCTL_BASE, 3, int)
+#define CEC_GET_COUNTERS	_IOR(CEC_IOCTL_BASE, 4, struct cec_counters)
+#define CEC_SET_RX_MODE		_IOW(CEC_IOCTL_BASE, 5, enum cec_rx_mode)
+#define CEC_GET_TX_STATUS	_IOW(CEC_IOCTL_BASE, 6, struct cec_tx_status)
+#define CEC_SET_DETACHED_CONFIG	_IOW(CEC_IOCTL_BASE, 7, struct cec_detached_config)
+
+#define CEC_MAX_DEVS	(10)
+
+#ifdef __KERNEL__
+
+struct cec_adapter;
+
+int __init cec_cdev_init(void);
+void __exit cec_cdev_exit(void);
+
+int cec_create_adapter_node(struct cec_adapter *);
+void cec_remove_adapter_node(struct cec_adapter *);
+
+#endif /* __KERNEL__ */
+
+#endif /* __HDMI_CEC_DEV_H */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/linux/smsc_cap1066.h	2015-06-12 14:02:27.390994371 +0200
@@ -0,0 +1,12 @@
+#ifndef SMSC_CAP1066_H_
+#define SMSC_CAP1066_H_
+
+#define CAP1066_MAX_BTNS	6
+
+struct smsc_cap1066_pdata {
+	unsigned short	key_map[CAP1066_MAX_BTNS];
+	unsigned int	irq_gpio;
+	bool		has_irq_gpio;
+};
+
+#endif /* ! SMSC_CAP1066_H_ */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/linux/spi/orion_tdm_spi.h	2015-06-12 14:02:27.390994371 +0200
@@ -0,0 +1,8 @@
+#ifndef __LINUX_SPI_ORION_TDM_SPI_H
+#define __LINUX_SPI_ORION_TDM_SPI_H
+
+struct orion_tdm_spi_info {
+	int	cs_use_gpio[2];
+};
+
+#endif
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/linux/spi/ssd1327.h	2015-06-12 14:02:27.390994371 +0200
@@ -0,0 +1,20 @@
+#ifndef __LINUX_SPI_SSD1327_H
+#define __LINUX_SPI_SSD1327_H
+
+struct ssd1327_platform_data {
+	/* attached screen info */
+	unsigned int		width;
+	unsigned int		height;
+
+	int			rotate;
+
+	/* gpio used to select command/data */
+	int			data_select_gpio;
+
+	/* watchdog (second), enabled if non zero, screen is blanked
+	 * if nothing is written for this number of seconds */
+	unsigned int		watchdog;
+};
+
+#endif /* ! __LINUX_SPI_SSD1327_H */
+
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/uapi/linux/exfat_user.h	2015-06-12 14:02:27.490995091 +0200
@@ -0,0 +1,47 @@
+/*
+ * exfat_user.h for exfat
+ * Created by <nschichan@freebox.fr> on Fri Aug 23 15:31:08 2013
+ */
+
+#ifndef __EXFAT_USER_H
+# define __EXFAT_USER_H
+
+struct exfat_fragment {
+	uint32_t	fcluster_start;
+	uint32_t	dcluster_start;
+	uint32_t	nr_clusters;
+	uint64_t	sector_start;
+};
+
+struct exfat_fragment_head {
+	uint32_t		fcluster_start;
+	uint32_t		nr_fragments;
+	uint32_t		sector_size;
+	uint32_t		cluster_size;
+	struct exfat_fragment	fragments[0];
+};
+
+struct exfat_bitmap_data {
+	uint32_t		start_cluster;
+	uint32_t		nr_clusters;
+	uint64_t		sector_start;
+	uint64_t		nr_sectors;
+};
+
+struct exfat_bitmap_head {
+	uint32_t			start_cluster;
+	uint32_t			nr_entries;
+	struct exfat_bitmap_data	entries[0];
+};
+
+struct exfat_dirent_head {
+	uint32_t offset;
+	uint32_t nr_entries;
+	uint8_t entries[0];
+};
+
+#define EXFAT_IOCGETFRAGMENTS	_IOR('X', 0x01, struct exfat_fragment_head)
+#define EXFAT_IOCGETBITMAP	_IOR('X', 0x02, struct exfat_bitmap_head)
+#define EXFAT_IOCGETDIRENTS	_IOR('X', 0x03, struct exfat_dirent_head)
+
+#endif /* !__EXFAT_USER_H */
diff -Nruw linux-3.11.10-fbx/include/uapi/linux/hdmi-cec./hdmi-cec.h linux-3.11.10-fbx/include/uapi/linux/hdmi-cec/hdmi-cec.h
--- linux-3.11.10-fbx/include/uapi/linux/hdmi-cec./hdmi-cec.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/hdmi-cec/hdmi-cec.h	2015-06-12 14:02:27.490995091 +0200
@@ -0,0 +1,153 @@
+#ifndef __UAPI_HDMI_CEC_H
+#define __UAPI_HDMI_CEC_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+/* Common defines for HDMI CEC */
+#define CEC_BCAST_ADDR		(0x0f)
+#define CEC_ADDR_MAX		CEC_BCAST_ADDR
+
+#define CEC_MAX_MSG_LEN		(16)	/* 16 blocks */
+
+enum cec_rx_msg_flags {
+	/*
+	 * an ACK was received for this message
+	 */
+	CEC_RX_F_ACKED			= (1 << 0),
+
+	/*
+	 * message was fully received
+	 */
+	CEC_RX_F_COMPLETE		= (1 << 1),
+};
+
+/**
+ * struct cec_rx_msg - user-space exposed cec message cookie
+ * @data:	cec message payload
+ * @len:	cec message length
+ * @valid:	0 for invalid message
+ * @flags:	flag field (cec_rx_msg_flags)
+ */
+struct cec_rx_msg {
+	__u8	data[CEC_MAX_MSG_LEN];
+	__u8	len;
+	__u8	valid;
+	__u8	flags;
+
+} __attribute__((packed));
+
+enum cec_tx_status_flags {
+	/*
+	 * message was nacked at some point
+	 */
+	CEC_TX_F_NACK			= (1 << 0),
+
+	/*
+	 * abort sending because total time to send was elapsed
+	 */
+	CEC_TX_F_TIMEOUT		= (1 << 1),
+
+	/*
+	 * abort sending because maximum number of retry has passed
+	 */
+	CEC_TX_F_MAX_RETRIES		= (1 << 2),
+
+	/*
+	 * abort sending because of arbitration loss
+	 */
+	CEC_TX_F_ARBITRATION_LOST	= (1 << 3),
+
+	/*
+	 * message failed for other reason
+	 */
+	CEC_TX_F_UNKNOWN_ERROR		= (1 << 7),
+};
+
+/**
+ * struct cec_tx_msg - user-space exposed cec message cookie
+ * @expire_ms:	how long we try to send message (milliseconds)
+ * @data:	cec message payload
+ * @len:	cec message length
+ * @success:	0 => message was sent, else => failed to send message
+ * @flags:	flag field (cec_tx_msg_flags)
+ * @tries:	number of try done to send message
+ */
+struct cec_tx_msg {
+	__u16	expire_ms;
+	__u8	data[CEC_MAX_MSG_LEN];
+	__u8	len;
+	__u8	success;
+	__u8	flags;
+	__u8	tries;
+} __attribute__((packed));
+
+struct cec_tx_status {
+	__u8	sent;
+	__u8	success;
+	__u8	flags;
+	__u8	tries;
+} __attribute__((packed));
+
+#define DETACH_CFG_F_WAKEUP		(1 << 0)
+
+struct cec_detached_config {
+	__u8	phys_addr_valid;
+	__u8	phys_addr[2];
+	__u8	flags;
+} __attribute__((packed));
+
+/* Counters */
+
+/**
+ * struct cec_rx_counters - cec adpater RX counters
+ */
+struct cec_rx_counters {
+	__u8	pkts;
+	__u8	filtered_pkts;
+	__u8	valid_pkts;
+	__u8	rx_queue_full;
+	__u8	late_ack;
+	__u8	error;
+	__u8	rx_timeout_abort;
+	__u8	rx_throttled;
+};
+
+/**
+ * struct cec_tx_counters - cec adapter TX counters
+ */
+struct cec_tx_counters {
+	__u8	done;
+	__u8	fail;
+	__u8	timeout;
+	__u8	arb_loss;
+	__u8	bad_ack_timings;
+	__u8	tx_miss_early;
+	__u8	tx_miss_late;
+};
+
+/**
+ * struct cec_counters - tx and rx cec counters
+ * @rx:	struct cec_rx_counters
+ * @tx: struct cec_tx_counters
+ */
+struct cec_counters {
+	struct cec_rx_counters	rx;
+	struct cec_tx_counters	tx;
+};
+
+/**
+ * enum cec_rx_mode - cec adapter rx mode
+ * @CEC_RX_MODE_DISABLED:	RX path is disabled (default)
+ * @CEC_RX_MODE_DEFAULT:	accept only unicast traffic
+ * @CEC_RX_MODE_ACCEPT_ALL:	accept all incoming RX traffic (sniffing mode)
+ * @CEC_RX_MODE_MAX:		sentinel
+ */
+enum cec_rx_mode {
+	CEC_RX_MODE_DISABLED = 0,
+	CEC_RX_MODE_DEFAULT,
+	CEC_RX_MODE_ACCEPT_ALL,
+	CEC_RX_MODE_MAX
+};
+
+#endif /* __UAPI_HDMI_CEC_H */
diff -Nruw linux-3.11.10-fbx/include/uapi/linux/hdmi-cec./Kbuild linux-3.11.10-fbx/include/uapi/linux/hdmi-cec/Kbuild
--- linux-3.11.10-fbx/include/uapi/linux/hdmi-cec./Kbuild	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/hdmi-cec/Kbuild	2015-06-12 14:02:27.490995091 +0200
@@ -0,0 +1 @@
+header-y	+= hdmi-cec.h dev.h
diff -Nruw linux-3.11.10-fbx/include/uapi/linux/remoti./Kbuild linux-3.11.10-fbx/include/uapi/linux/remoti/Kbuild
--- linux-3.11.10-fbx/include/uapi/linux/remoti./Kbuild	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/remoti/Kbuild	2015-06-12 14:02:27.502995182 +0200
@@ -0,0 +1 @@
+header-y	+= remoti.h
diff -Nruw linux-3.11.10-fbx/include/uapi/linux/remoti./remoti.h linux-3.11.10-fbx/include/uapi/linux/remoti/remoti.h
--- linux-3.11.10-fbx/include/uapi/linux/remoti./remoti.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/include/uapi/linux/remoti/remoti.h	2015-06-12 14:02:27.502995182 +0200
@@ -0,0 +1,137 @@
+#ifndef _UAPI_REMOTI_H
+#define _UAPI_REMOTI_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/*
+ * subsystem definitions
+ */
+#define NPI_SYS_RES0		0
+#define NPI_SYS_SYS		1
+#define NPI_SYS_MAC		2
+#define NPI_SYS_NWK		3
+#define NPI_SYS_AF		4
+#define NPI_SYS_ZDO		5
+#define NPI_SYS_SAPI		6
+#define NPI_SYS_UTIL		7
+#define NPI_SYS_DBG		8
+#define NPI_SYS_APP		9
+#define NPI_SYS_RCAF		10
+#define NPI_SYS_RCN		11
+#define NPI_SYS_RCN_CLI		12
+#define NPI_SYS_BOOT		13
+#define NPI_SYS_MAX		14
+#define NPI_SYS_MASK		0x1F
+
+/*
+ * type definitions
+ */
+#define NPI_POLL		0
+#define NPI_SREQ		1
+#define NPI_AREQ		2
+#define NPI_SRSP		3
+#define NPI_TYPE_MAX		4
+#define NPI_TYPE_MASK		3
+#define NPI_TYPE_SHIFT		5
+
+
+/* common error codes (see RemoTI API) */
+#define RTI_SUCCESS		0x00
+
+/*
+ * rti user message
+ */
+#define NPI_MAX_DATA_LEN	123
+
+struct rti_msg {
+	__u8	type;
+	__u8	subsys;
+	__u8	cmd;
+
+	__u8	data_len;
+	__u8	data[NPI_MAX_DATA_LEN];
+
+	__u8	custom_reply_cmd;
+	__u8	reply_cmd;
+	__u8	reply_len;
+	__u8	reply[NPI_MAX_DATA_LEN];
+};
+
+/*
+ * socket addr family on "user" device
+ */
+#ifndef PF_REMOTI
+#define PF_REMOTI			37
+#define AF_REMOTI			PF_REMOTI
+#endif
+
+struct sockaddr_rti {
+	__u32	device_id;
+};
+
+#define SOL_REMOTI			280
+#define REMOTI_REGISTER_CB		0
+
+struct rti_callback {
+	__u8	subsys;
+	__u8	cmd;
+};
+
+/*
+ * ioctl on uart device
+ */
+enum rti_dev_state {
+	RTI_DEV_S_STOPPED = 0,
+	RTI_DEV_S_BOOTING,
+	RTI_DEV_S_BOOT_FAILED,
+	RTI_DEV_S_OPERATIONAL,
+	RTI_DEV_S_STOPPING,
+	RTI_DEV_S_DEAD,
+};
+
+struct rti_dev_status {
+	__u32	dev_state;
+	__u32	fw_version;
+};
+
+struct rti_dev_stats {
+	__u64	tx_bytes;
+	__u64	tx_packets;
+
+	__u64	tx_boot_packets;
+	__u64	tx_rcaf_packets;
+	__u64	tx_util_packets;
+	__u64	tx_other_packets;
+
+
+	__u64	rx_bytes;
+	__u64	rx_packets;
+	__u64	rx_bad_sof;
+	__u64	rx_len_errors;
+	__u64	rx_fcs_errors;
+	__u64	rx_tty_errors;
+	__u64	rx_full_errors;
+	__u64	rx_subsys_errors;
+	__u64	rx_type_errors;
+	__u64	rx_no_callback;
+
+	__u64	rx_boot_packets;
+	__u64	rx_rcaf_packets;
+	__u64	rx_util_packets;
+	__u64	rx_other_packets;
+};
+
+enum {
+	RTI_BOOT_FLAGS_FORCE_UPDATE	= (1 << 0),
+};
+
+#define RTI_IOCTL_MAGIC		0xd4
+#define RTI_ATTACH_DEVICE	_IOR(RTI_IOCTL_MAGIC, 1, __u32)
+#define RTI_GET_STATUS		_IOW(RTI_IOCTL_MAGIC, 2, struct rti_dev_status)
+#define RTI_GET_STATS		_IOW(RTI_IOCTL_MAGIC, 3, struct rti_dev_stats)
+
+#define RTI_START_DEVICE	_IOR(RTI_IOCTL_MAGIC, 8, __u32)
+#define RTI_STOP_DEVICE		_IO(RTI_IOCTL_MAGIC, 9)
+
+#endif /* _UAPI_REMOTI_H */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/uapi/xen/gntalloc.h	2014-07-29 21:29:45.269868853 +0200
@@ -0,0 +1,82 @@
+/******************************************************************************
+ * gntalloc.h
+ *
+ * Interface to /dev/xen/gntalloc.
+ *
+ * Author: Daniel De Graaf <dgdegra@tycho.nsa.gov>
+ *
+ * This file is in the public domain.
+ */
+
+#ifndef __LINUX_PUBLIC_GNTALLOC_H__
+#define __LINUX_PUBLIC_GNTALLOC_H__
+
+/*
+ * Allocates a new page and creates a new grant reference.
+ */
+#define IOCTL_GNTALLOC_ALLOC_GREF \
+_IOC(_IOC_NONE, 'G', 5, sizeof(struct ioctl_gntalloc_alloc_gref))
+struct ioctl_gntalloc_alloc_gref {
+	/* IN parameters */
+	/* The ID of the domain to be given access to the grants. */
+	uint16_t domid;
+	/* Flags for this mapping */
+	uint16_t flags;
+	/* Number of pages to map */
+	uint32_t count;
+	/* OUT parameters */
+	/* The offset to be used on a subsequent call to mmap(). */
+	uint64_t index;
+	/* The grant references of the newly created grant, one per page */
+	/* Variable size, depending on count */
+	uint32_t gref_ids[1];
+};
+
+#define GNTALLOC_FLAG_WRITABLE 1
+
+/*
+ * Deallocates the grant reference, allowing the associated page to be freed if
+ * no other domains are using it.
+ */
+#define IOCTL_GNTALLOC_DEALLOC_GREF \
+_IOC(_IOC_NONE, 'G', 6, sizeof(struct ioctl_gntalloc_dealloc_gref))
+struct ioctl_gntalloc_dealloc_gref {
+	/* IN parameters */
+	/* The offset returned in the map operation */
+	uint64_t index;
+	/* Number of references to unmap */
+	uint32_t count;
+};
+
+/*
+ * Sets up an unmap notification within the page, so that the other side can do
+ * cleanup if this side crashes. Required to implement cross-domain robust
+ * mutexes or close notification on communication channels.
+ *
+ * Each mapped page only supports one notification; multiple calls referring to
+ * the same page overwrite the previous notification. You must clear the
+ * notification prior to the IOCTL_GNTALLOC_DEALLOC_GREF if you do not want it
+ * to occur.
+ */
+#define IOCTL_GNTALLOC_SET_UNMAP_NOTIFY \
+_IOC(_IOC_NONE, 'G', 7, sizeof(struct ioctl_gntalloc_unmap_notify))
+struct ioctl_gntalloc_unmap_notify {
+	/* IN parameters */
+	/* Offset in the file descriptor for a byte within the page (same as
+	 * used in mmap). If using UNMAP_NOTIFY_CLEAR_BYTE, this is the byte to
+	 * be cleared. Otherwise, it can be any byte in the page whose
+	 * notification we are adjusting.
+	 */
+	uint64_t index;
+	/* Action(s) to take on unmap */
+	uint32_t action;
+	/* Event channel to notify */
+	uint32_t event_channel_port;
+};
+
+/* Clear (set to zero) the byte specified by index */
+#define UNMAP_NOTIFY_CLEAR_BYTE 0x1
+/* Send an interrupt on the indicated event channel */
+#define UNMAP_NOTIFY_SEND_EVENT 0x2
+
+#endif /* __LINUX_PUBLIC_GNTALLOC_H__ */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/include/uapi/xen/gntdev.h	2014-07-29 21:29:45.269868853 +0200
@@ -0,0 +1,150 @@
+/******************************************************************************
+ * gntdev.h
+ * 
+ * Interface to /dev/xen/gntdev.
+ * 
+ * Copyright (c) 2007, D G Murray
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef __LINUX_PUBLIC_GNTDEV_H__
+#define __LINUX_PUBLIC_GNTDEV_H__
+
+struct ioctl_gntdev_grant_ref {
+	/* The domain ID of the grant to be mapped. */
+	uint32_t domid;
+	/* The grant reference of the grant to be mapped. */
+	uint32_t ref;
+};
+
+/*
+ * Inserts the grant references into the mapping table of an instance
+ * of gntdev. N.B. This does not perform the mapping, which is deferred
+ * until mmap() is called with @index as the offset.
+ */
+#define IOCTL_GNTDEV_MAP_GRANT_REF \
+_IOC(_IOC_NONE, 'G', 0, sizeof(struct ioctl_gntdev_map_grant_ref))
+struct ioctl_gntdev_map_grant_ref {
+	/* IN parameters */
+	/* The number of grants to be mapped. */
+	uint32_t count;
+	uint32_t pad;
+	/* OUT parameters */
+	/* The offset to be used on a subsequent call to mmap(). */
+	uint64_t index;
+	/* Variable IN parameter. */
+	/* Array of grant references, of size @count. */
+	struct ioctl_gntdev_grant_ref refs[1];
+};
+
+/*
+ * Removes the grant references from the mapping table of an instance of
+ * of gntdev. N.B. munmap() must be called on the relevant virtual address(es)
+ * before this ioctl is called, or an error will result.
+ */
+#define IOCTL_GNTDEV_UNMAP_GRANT_REF \
+_IOC(_IOC_NONE, 'G', 1, sizeof(struct ioctl_gntdev_unmap_grant_ref))
+struct ioctl_gntdev_unmap_grant_ref {
+	/* IN parameters */
+	/* The offset was returned by the corresponding map operation. */
+	uint64_t index;
+	/* The number of pages to be unmapped. */
+	uint32_t count;
+	uint32_t pad;
+};
+
+/*
+ * Returns the offset in the driver's address space that corresponds
+ * to @vaddr. This can be used to perform a munmap(), followed by an
+ * UNMAP_GRANT_REF ioctl, where no state about the offset is retained by
+ * the caller. The number of pages that were allocated at the same time as
+ * @vaddr is returned in @count.
+ *
+ * N.B. Where more than one page has been mapped into a contiguous range, the
+ *      supplied @vaddr must correspond to the start of the range; otherwise
+ *      an error will result. It is only possible to munmap() the entire
+ *      contiguously-allocated range at once, and not any subrange thereof.
+ */
+#define IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR \
+_IOC(_IOC_NONE, 'G', 2, sizeof(struct ioctl_gntdev_get_offset_for_vaddr))
+struct ioctl_gntdev_get_offset_for_vaddr {
+	/* IN parameters */
+	/* The virtual address of the first mapped page in a range. */
+	uint64_t vaddr;
+	/* OUT parameters */
+	/* The offset that was used in the initial mmap() operation. */
+	uint64_t offset;
+	/* The number of pages mapped in the VM area that begins at @vaddr. */
+	uint32_t count;
+	uint32_t pad;
+};
+
+/*
+ * Sets the maximum number of grants that may mapped at once by this gntdev
+ * instance.
+ *
+ * N.B. This must be called before any other ioctl is performed on the device.
+ */
+#define IOCTL_GNTDEV_SET_MAX_GRANTS \
+_IOC(_IOC_NONE, 'G', 3, sizeof(struct ioctl_gntdev_set_max_grants))
+struct ioctl_gntdev_set_max_grants {
+	/* IN parameter */
+	/* The maximum number of grants that may be mapped at once. */
+	uint32_t count;
+};
+
+/*
+ * Sets up an unmap notification within the page, so that the other side can do
+ * cleanup if this side crashes. Required to implement cross-domain robust
+ * mutexes or close notification on communication channels.
+ *
+ * Each mapped page only supports one notification; multiple calls referring to
+ * the same page overwrite the previous notification. You must clear the
+ * notification prior to the IOCTL_GNTALLOC_DEALLOC_GREF if you do not want it
+ * to occur.
+ */
+#define IOCTL_GNTDEV_SET_UNMAP_NOTIFY \
+_IOC(_IOC_NONE, 'G', 7, sizeof(struct ioctl_gntdev_unmap_notify))
+struct ioctl_gntdev_unmap_notify {
+	/* IN parameters */
+	/* Offset in the file descriptor for a byte within the page (same as
+	 * used in mmap). If using UNMAP_NOTIFY_CLEAR_BYTE, this is the byte to
+	 * be cleared. Otherwise, it can be any byte in the page whose
+	 * notification we are adjusting.
+	 */
+	uint64_t index;
+	/* Action(s) to take on unmap */
+	uint32_t action;
+	/* Event channel to notify */
+	uint32_t event_channel_port;
+};
+
+/* Clear (set to zero) the byte specified by index */
+#define UNMAP_NOTIFY_CLEAR_BYTE 0x1
+/* Send an interrupt on the indicated event channel */
+#define UNMAP_NOTIFY_SEND_EVENT 0x2
+
+#endif /* __LINUX_PUBLIC_GNTDEV_H__ */
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/lib/fbxserial.c	2015-09-29 11:15:08.905755777 +0200
@@ -0,0 +1,117 @@
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/crc32.h>
+
+#include <asm/io.h>
+
+#include <linux/fbxserial.h>
+
+#define PFX "builtin-fbxserial: "
+
+static void __init
+fbxserialinfo_use_default(struct fbx_serial *serial)
+{
+	printk(KERN_WARNING PFX "warning: using default serial infos\n");
+	fbxserial_set_default(serial);
+}
+
+/*
+ * add trailing 0 for bundle string here.
+ */
+static void __init
+bundle_fixup(struct fbx_serial *serial)
+{
+	struct fbx_serial_extinfo *p;
+	int i;
+
+	for (i = 0; i < be32_to_cpu(serial->extinfo_count); i++) {
+
+		if (i >= EXTINFO_MAX_COUNT)
+			break;
+
+		p = &serial->extinfos[i];
+		if (be32_to_cpu(p->type) == EXTINFO_TYPE_EXTDEV &&
+		    be32_to_cpu(p->u.extdev.type) == EXTDEV_TYPE_BUNDLE) {
+			int size;
+
+			size = sizeof (p->u.extdev.serial);
+			p->u.extdev.serial[size - 1] = 0;
+		}
+	}
+}
+
+/*
+ * called from  arch code early  in the boot sequence.   This function
+ * returns 1  in case serial infos are  invalid/unreadable and default
+ * values have been used.
+ */
+int __init
+fbxserialinfo_read(void *data, struct fbx_serial *out)
+{
+	uint32_t sum;
+
+	/*
+	 * get partial serial data from flash/whatever.
+	 */
+	memcpy(out, data, sizeof (*out));
+
+	/* check magic first */
+	if (be32_to_cpu(out->magic) != FBXSERIAL_MAGIC) {
+		printk(KERN_NOTICE PFX "invalid magic (%08x, expected %08x), "
+			"using defaults !\n", be32_to_cpu(out->magic),
+		       FBXSERIAL_MAGIC);
+		goto out_default;
+	}
+
+	/* fetch size for which we have to check CRC */
+	if (be32_to_cpu(out->len) > FBXSERIAL_MAX_SIZE) {
+		printk(KERN_NOTICE PFX "structure size too big (%d), "
+		       "using defaults !\n", be32_to_cpu(out->len));
+		goto out_default;
+	}
+
+	/* compute and check checksum */
+	sum = crc32(0, data + 4, be32_to_cpu(out->len) - 4);
+
+	if (be32_to_cpu(out->crc32) != sum) {
+		printk(KERN_NOTICE PFX "invalid checksum (%08x, "
+		       "expected %08x), using defaults !\n", sum,
+		       be32_to_cpu(out->crc32));
+		goto out_default;
+	}
+
+	printk(KERN_INFO PFX "Found valid serial infos !\n");
+	bundle_fixup(out);
+	return 0;
+
+ out_default:
+	fbxserialinfo_use_default(out);
+	bundle_fixup(out);
+	return 1;
+}
+
+void
+fbxserialinfo_get_random(unsigned char *data, unsigned int len)
+{
+	const struct fbx_serial *s;
+
+	s = arch_get_fbxserial();
+
+	if (len > sizeof (s->random_data))
+		len = sizeof (s->random_data);
+
+	memcpy(data, s->random_data, len);
+}
+EXPORT_SYMBOL(fbxserialinfo_get_random);
+
+void
+fbxserialinfo_get_mac_addr(unsigned char *data)
+{
+	const struct fbx_serial *s;
+
+	s = arch_get_fbxserial();
+	memcpy(data, s->mac_addr_base, MAC_ADDR_SIZE);
+}
+EXPORT_SYMBOL(fbxserialinfo_get_mac_addr);
diff -Nruw linux-3.11.10-fbx/net/fbxatm./crc10.c linux-3.11.10-fbx/net/fbxatm/crc10.c
--- linux-3.11.10-fbx/net/fbxatm./crc10.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxatm/crc10.c	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,48 @@
+#include <linux/types.h>
+
+static const u16 crc10_table[256] = {
+	0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
+	0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
+	0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
+	0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
+	0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
+	0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
+	0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
+	0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
+	0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
+	0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
+	0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
+	0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
+	0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
+	0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
+	0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
+	0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
+	0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
+	0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
+	0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
+	0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
+	0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
+	0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
+	0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
+	0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
+	0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
+	0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
+	0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
+	0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
+	0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
+	0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
+	0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
+	0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1,
+};
+
+static u16 crc10_byte(u16 crc, const u8 c)
+{
+	return ((crc << 8) & 0x3ff) ^ crc10_table[(crc >> 2) & 0xff] ^ c;
+}
+
+u16 crc10(u16 crc, const u8 *buffer, size_t len)
+{
+	while (len--)
+		crc = crc10_byte(crc, *buffer++);
+	return crc;
+}
diff -Nruw linux-3.11.10-fbx/net/fbxatm./fbxatm_2684.c linux-3.11.10-fbx/net/fbxatm/fbxatm_2684.c
--- linux-3.11.10-fbx/net/fbxatm./fbxatm_2684.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxatm/fbxatm_2684.c	2015-09-29 11:15:09.001756505 +0200
@@ -0,0 +1,837 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
+#include <linux/pkt_sched.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/icmp.h>
+#include <linux/proc_fs.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <linux/fbxatm_dev.h>
+
+#include "fbxatm_priv.h"
+
+#define PFX	"fbxatm_2684: "
+
+static LIST_HEAD(fbxatm_2684_dev_list);
+static DEFINE_MUTEX(fbxatm_2684_mutex);
+
+#define LLC_NEEDED_HEADROOM		10
+#define VCMUX_BRIDGED_NEEDED_HEADROOM	2
+
+#define LLC			0xaa, 0xaa, 0x03
+#define SNAP_BRIDGED		0x00, 0x80, 0xc2
+#define SNAP_ROUTED		0x00, 0x00, 0x00
+#define PID_ETHERNET_NOFCS	0x00, 0x07
+
+static u8 llc_bridged_802d3_pad[] = { LLC, SNAP_BRIDGED, PID_ETHERNET_NOFCS,
+				      0, 0 };
+static u8 llc_snap_routed[] = { LLC, SNAP_ROUTED };
+
+/*
+ * private data for 2684 vcc
+ */
+struct fbxatm_2684_vcc;
+
+struct fbxatm_2684_queue {
+	struct fbxatm_vcc		*vcc;
+	unsigned int			queue_idx;
+	struct fbxatm_2684_vcc		*priv;
+};
+
+struct fbxatm_2684_vcc {
+	struct fbxatm_2684_queue	queues[FBXATM_2684_MAX_VCC];
+	size_t				queue_count;
+
+	struct net_device		*dev;
+	struct fbxatm_2684_vcc_params	params;
+
+	spinlock_t			tx_lock;
+
+	struct rtnl_link_stats64	stats;
+
+	struct list_head		next;
+};
+
+static uint32_t tel_last_ip;
+
+static void warn_if_tel(struct fbxatm_2684_vcc *vcc, struct sk_buff *skb)
+{
+	struct iphdr *iph;
+	struct udphdr *udph = NULL;
+
+	iph = (struct iphdr *)skb->data;
+
+	if (iph->protocol != IPPROTO_UDP)
+		return;
+
+	if (skb_headlen(skb) < (iph->ihl * 4) + sizeof (struct udphdr))
+		return;
+
+	udph = (struct udphdr *)((unsigned char *)iph + (iph->ihl * 4));
+	if (ntohs(udph->dest) >= 5004 && ntohs(udph->dest) <= 5020) {
+		static u32 last_ip;
+		static unsigned long last_time;
+		unsigned long now;
+
+		now = jiffies;
+		if ((last_ip == iph->saddr &&
+		     (!last_time || time_before(now, last_time + 2 * HZ)))) {
+			static unsigned int consecutive;
+			consecutive++;
+			if (consecutive > 5) {
+				tel_last_ip = iph->saddr;
+				consecutive = 0;
+			}
+		}
+
+		last_time = now;
+		last_ip = iph->saddr;
+	}
+}
+
+/*
+ * procfs read callback
+ */
+static int tel_proc_show(struct seq_file *m, void *v)
+{
+	seq_printf(m, "%pI4\n", &tel_last_ip);
+	return 0;
+}
+
+static int tel_proc_write(struct file *file, const char __user *ubuf,
+			  size_t len, loff_t *off)
+{
+	tel_last_ip = 0;
+	return len;
+}
+
+static int tel_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, tel_proc_show, PDE_DATA(inode));
+}
+
+static const struct file_operations tel_proc_fops = {
+	.owner          = THIS_MODULE,
+	.open           = tel_proc_open,
+	.read           = seq_read,
+	.write		= tel_proc_write,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
+/*
+ * fbxatm stack receive callback, called from softirq
+ */
+static void vcc_rx_callback(struct sk_buff *skb, void *data)
+{
+	struct fbxatm_2684_queue *queue;
+	struct fbxatm_2684_vcc *priv;
+
+	queue = (struct fbxatm_2684_queue *)data;
+	priv = queue->priv;
+
+	switch (priv->params.encap) {
+	case FBXATM_E2684_VCMUX:
+		switch (priv->params.payload) {
+		case FBXATM_P2684_BRIDGE:
+			/* assume 802.3, need to remove 2 bytes zero
+			 * padding */
+			if (skb->len < 2 || memcmp(skb->data, "\0\0", 2))
+				goto drop;
+			skb_pull(skb, 2);
+			skb->protocol = eth_type_trans(skb, priv->dev);
+			memset(skb->data, 0, 2);
+			break;
+
+		case FBXATM_P2684_ROUTED:
+			/* assume ipv4 */
+			skb->protocol = htons(ETH_P_IP);
+			break;
+		}
+		break;
+
+	case FBXATM_E2684_LLC:
+		switch (priv->params.payload) {
+		case FBXATM_P2684_BRIDGE:
+		{
+			/* recognize only 802.3 */
+			if (skb->len < sizeof(llc_bridged_802d3_pad))
+				goto drop;
+
+			if (memcmp(skb->data, llc_bridged_802d3_pad, 7))
+				goto drop;
+
+			/* don't check the last bytes of pid, it can
+			 * be 1 or 7 depending on the presence of
+			 * FCS */
+			skb_pull(skb, sizeof(llc_bridged_802d3_pad));
+			skb->protocol = eth_type_trans(skb, priv->dev);
+			break;
+		}
+
+		case FBXATM_P2684_ROUTED:
+		{
+			u16 proto;
+			unsigned int offset;
+
+			if (skb->len < sizeof(llc_snap_routed) + 2)
+				goto drop;
+
+			offset = sizeof (llc_snap_routed);
+			proto = skb->data[offset] << 8;
+			proto |= skb->data[offset + 1];
+
+			skb->protocol = proto;
+			skb_pull(skb, sizeof(llc_snap_routed) + 2);
+			break;
+		}
+		}
+		break;
+	}
+
+	skb->dev = priv->dev;
+	skb->pkt_type = PACKET_HOST;
+	priv->stats.rx_bytes += skb->len;
+	priv->stats.rx_packets++;
+
+	if (priv->params.encap == FBXATM_E2684_VCMUX &&
+	    priv->params.payload == FBXATM_P2684_ROUTED &&
+	    queue->vcc->vpi == 8 && queue->vcc->vci == 35)
+		warn_if_tel(priv, skb);
+
+	netif_rx(skb);
+	return;
+
+drop:
+	priv->stats.rx_errors++;
+	dev_kfree_skb(skb);
+}
+
+/*
+ * fbxatm stack tx done callback, called from softirq
+ */
+static void vcc_tx_done_callback(void *data)
+{
+	struct fbxatm_2684_queue *queue;
+	struct fbxatm_2684_vcc *priv;
+
+	queue = (struct fbxatm_2684_queue *)data;
+	priv = queue->priv;
+
+	spin_lock(&priv->tx_lock);
+	if (__netif_subqueue_stopped(priv->dev, queue->queue_idx))
+		netif_wake_subqueue(priv->dev, queue->queue_idx);
+	spin_unlock(&priv->tx_lock);
+}
+
+/*
+ * fbxatm stack callback when vcc link changes
+ */
+static void vcc_link_change(void *data, int link,
+			    unsigned int rx_cell_rate,
+			    unsigned int tx_cell_rate)
+{
+	struct fbxatm_2684_queue *queue;
+	struct fbxatm_2684_vcc *priv;
+
+	queue = (struct fbxatm_2684_queue *)data;
+	priv = queue->priv;
+
+	if (link)
+		netif_carrier_on(priv->dev);
+	else
+		netif_carrier_off(priv->dev);
+}
+
+/*
+ * vcc user ops, callback from fbxatm stack
+ */
+static const struct fbxatm_vcc_uops fbxatm_2684_uops = {
+	.link_change	= vcc_link_change,
+	.rx_pkt		= vcc_rx_callback,
+	.tx_done	= vcc_tx_done_callback,
+};
+
+/*
+ * netdevice ->ndo_select_queue() callback
+ */
+static u16 fbxatm_2684_netdev_select_queue(struct net_device *dev,
+					   struct sk_buff *skb)
+{
+	/* force lower band to avoid kernel doing round robin */
+	return 0;
+}
+
+/*
+ * netdevice xmit callback
+ */
+static int fbxatm_2684_netdev_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct fbxatm_2684_vcc *priv;
+	int ret, queue_idx;
+	unsigned int needed_headroom;
+	struct fbxatm_2684_queue *queue;
+
+	priv = netdev_priv(dev);
+	queue_idx = skb_get_queue_mapping(skb);
+	queue = &priv->queues[queue_idx];
+
+	/*
+	 * check if we have to expand skb head
+	 */
+	needed_headroom = 0;
+	if (priv->params.encap == FBXATM_E2684_VCMUX) {
+		if (priv->params.payload == FBXATM_P2684_BRIDGE)
+			needed_headroom = VCMUX_BRIDGED_NEEDED_HEADROOM;
+	} else
+		needed_headroom = LLC_NEEDED_HEADROOM;
+
+	if (skb_headroom(skb) < needed_headroom) {
+		struct sk_buff *nskb;
+		unsigned int new_head;
+
+		new_head = skb_headroom(skb) + needed_headroom;
+		nskb = skb_realloc_headroom(skb, new_head);
+		dev_kfree_skb(skb);
+		if (!nskb)
+			goto dropped;
+		skb = nskb;
+	}
+
+	switch (priv->params.encap) {
+	case FBXATM_E2684_VCMUX:
+		switch (priv->params.payload) {
+		case FBXATM_P2684_BRIDGE:
+			skb_push(skb, 2);
+			memset(skb->data, 0, 2);
+			break;
+		case FBXATM_P2684_ROUTED:
+			/* nothing to do */
+			break;
+		}
+		break;
+
+	case FBXATM_E2684_LLC:
+		switch (priv->params.payload) {
+		case FBXATM_P2684_BRIDGE:
+			skb_push(skb, sizeof(llc_bridged_802d3_pad));
+			memcpy(skb->data, llc_bridged_802d3_pad,
+			       sizeof(llc_bridged_802d3_pad));
+			break;
+
+		case FBXATM_P2684_ROUTED:
+		{
+			unsigned int offset;
+
+			skb_push(skb, sizeof(llc_snap_routed));
+			memcpy(skb->data, llc_snap_routed,
+			       sizeof(llc_snap_routed));
+
+			offset = sizeof (llc_snap_routed);
+			skb->data[offset] = (skb->protocol >> 8) & 0xff;
+			skb->data[offset + 1] = skb->protocol & 0xff;
+			break;
+		}
+		}
+		break;
+	}
+
+	spin_lock(&priv->tx_lock);
+
+	ret = fbxatm_send(queue->vcc, skb);
+	if (ret) {
+		/* packet was not sent, queue is full */
+		netif_stop_subqueue(dev, queue_idx);
+		spin_unlock(&priv->tx_lock);
+		return NETDEV_TX_BUSY;
+	}
+
+	/* check if queue is full */
+	priv->stats.tx_bytes += skb->len;
+	priv->stats.tx_packets++;
+
+	if (fbxatm_vcc_queue_full(queue->vcc))
+		netif_stop_subqueue(dev, queue_idx);
+	spin_unlock(&priv->tx_lock);
+
+	return NETDEV_TX_OK;
+
+dropped:
+	priv->stats.tx_errors++;
+	return NETDEV_TX_OK;
+}
+
+/*
+ * netdevice get_stats callback
+ */
+static struct rtnl_link_stats64 *
+fbxatm_2684_netdev_get_stats64(struct net_device *dev,
+			       struct rtnl_link_stats64 *stats)
+{
+	struct fbxatm_2684_vcc *priv;
+	priv = netdev_priv(dev);
+	memcpy(stats, &priv->stats, sizeof (*stats));
+	return stats;
+}
+
+/*
+ * netdevice setup callback for bridge encap
+ */
+static void setup_bridged(struct net_device *dev)
+{
+	ether_setup(dev);
+}
+
+/*
+ * netdevice setup callback for routed encap
+ */
+static void setup_routed(struct net_device *dev)
+{
+	dev->type		= ARPHRD_PPP;
+	dev->hard_header_len	= 0;
+	dev->mtu		= 1500;
+	dev->addr_len		= 0;
+	dev->tx_queue_len	= 128;
+	dev->flags		= IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+}
+
+static const struct net_device_ops fbxatm_2684_ops = {
+	.ndo_start_xmit		= fbxatm_2684_netdev_xmit,
+	.ndo_get_stats64	= fbxatm_2684_netdev_get_stats64,
+	.ndo_select_queue	= fbxatm_2684_netdev_select_queue,
+};
+
+/*
+ * sysfs callback, show encapsulation
+ */
+static ssize_t show_encap(struct device *d,
+			  struct device_attribute *attr, char *buf)
+{
+	struct fbxatm_2684_vcc *priv = netdev_priv(to_net_dev(d));
+
+	switch (priv->params.encap) {
+	case FBXATM_E2684_LLC:
+		return sprintf(buf, "llc\n");
+	case FBXATM_E2684_VCMUX:
+	default:
+		return sprintf(buf, "vcmux\n");
+	}
+}
+
+static DEVICE_ATTR(encap, S_IRUGO, show_encap, NULL);
+
+/*
+ * sysfs callback, show payload
+ */
+static ssize_t show_payload(struct device *d,
+			    struct device_attribute *attr, char *buf)
+{
+	struct fbxatm_2684_vcc *priv = netdev_priv(to_net_dev(d));
+
+	switch (priv->params.payload) {
+	case FBXATM_P2684_BRIDGE:
+		return sprintf(buf, "bridge\n");
+	case FBXATM_P2684_ROUTED:
+	default:
+		return sprintf(buf, "routed\n");
+	}
+}
+
+static DEVICE_ATTR(payload, S_IRUGO, show_payload, NULL);
+
+/*
+ * sysfs callback, show vcc id
+ */
+static ssize_t show_vcc(struct device *d,
+			struct device_attribute *attr, char *buf)
+{
+	struct fbxatm_2684_vcc *priv = netdev_priv(to_net_dev(d));
+
+	return sprintf(buf, "%u.%u.%u\n",
+		       priv->queues[0].vcc->adev->ifindex,
+		       priv->queues[0].vcc->vpi, priv->queues[0].vcc->vci);
+}
+
+static DEVICE_ATTR(vcc, S_IRUGO, show_vcc, NULL);
+
+static struct attribute *fbxatm2684_attrs[] = {
+	&dev_attr_encap.attr,
+	&dev_attr_payload.attr,
+	&dev_attr_vcc.attr,
+	NULL
+};
+
+static struct attribute_group fbxatm2684_group = {
+	.name = "fbxatm2684",
+	.attrs = fbxatm2684_attrs,
+};
+
+/*
+ * create sysfs files for 2684 device
+ */
+static int vcc2684_sysfs_register(struct fbxatm_2684_vcc *priv,
+				  struct net_device *dev)
+{
+	int ret;
+
+	ret = sysfs_create_group(&dev->dev.kobj, &fbxatm2684_group);
+	if (ret)
+		goto out1;
+
+	ret = sysfs_create_link(&dev->dev.kobj,
+				&priv->queues[0].vcc->adev->dev.kobj,
+				"fbxatm_dev");
+	if (ret)
+		goto out2;
+
+	return 0;
+
+out2:
+	sysfs_remove_group(&dev->dev.kobj, &fbxatm2684_group);
+out1:
+	return ret;
+}
+
+/*
+ * remove sysfs files for 2684 device
+ */
+static void vcc2684_sysfs_unregister(struct fbxatm_2684_vcc *priv,
+				     struct net_device *dev)
+{
+	sysfs_remove_group(&dev->dev.kobj, &fbxatm2684_group);
+	sysfs_remove_link(&dev->dev.kobj, "fbxatm_dev");
+}
+
+/*
+ * register netdevice & sysfs attribute
+ */
+static int register_2684_netdev(struct fbxatm_2684_vcc *priv,
+				struct net_device *dev)
+{
+	int ret;
+
+	/* hold rtnl while registering netdevice and creating sysfs
+	 * files to avoid race */
+	rtnl_lock();
+
+	if (strchr(dev->name, '%')) {
+		ret = dev_alloc_name(dev, dev->name);
+		if (ret < 0)
+			goto out;
+	}
+
+	ret = register_netdevice(dev);
+	if (ret)
+		goto out;
+
+	ret = vcc2684_sysfs_register(priv, dev);
+	if (ret)
+		goto out_unregister;
+
+	rtnl_unlock();
+	return 0;
+
+out_unregister:
+	unregister_netdevice(dev);
+
+out:
+	rtnl_unlock();
+	return ret;
+}
+
+/*
+ * create a RFC2684 encapsulation on given vcc
+ */
+static int __create_2684_vcc(const struct fbxatm_2684_vcc_params *params)
+{
+	struct fbxatm_2684_vcc *priv;
+	struct fbxatm_vcc *vccs[FBXATM_2684_MAX_VCC];
+	struct net_device *dev = NULL;
+	void (*netdev_setup_cb)(struct net_device *dev);
+	unsigned int headroom;
+	size_t i;
+	int ret;
+
+	/* sanity check */
+	switch (params->encap) {
+	case FBXATM_E2684_VCMUX:
+	case FBXATM_E2684_LLC:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (params->payload) {
+	case FBXATM_P2684_BRIDGE:
+		netdev_setup_cb = setup_bridged;
+		break;
+	case FBXATM_P2684_ROUTED:
+		netdev_setup_cb = setup_routed;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (!params->dev_name[0])
+		return -EINVAL;
+
+	/* bind to vcc */
+	memset(vccs, 0, sizeof (vccs));
+	for (i = 0; i < params->id_count; i++) {
+		struct fbxatm_vcc *vcc;
+
+		vcc = fbxatm_bind_to_vcc(&params->id_list[i],
+					 FBXATM_VCC_USER_2684);
+		if (IS_ERR(vcc)) {
+			ret = PTR_ERR(vcc);
+			goto fail;
+		}
+		vccs[i] = vcc;
+	}
+
+	/* create netdevice */
+	dev = alloc_netdev_mqs(sizeof(*priv), params->dev_name,
+			       netdev_setup_cb, params->id_count, 1);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	netif_set_real_num_tx_queues(dev, params->id_count);
+	netif_set_real_num_rx_queues(dev, 1);
+
+	priv = netdev_priv(dev);
+	memset(priv, 0, sizeof (priv));
+	memcpy(&priv->params, params, sizeof (*params));
+	memcpy(dev->name, priv->params.dev_name, IFNAMSIZ);
+
+	spin_lock_init(&priv->tx_lock);
+	priv->dev = dev;
+	for (i = 0; i < params->id_count; i++) {
+		priv->queues[i].vcc = vccs[i];
+		priv->queues[i].queue_idx = i;
+		priv->queues[i].priv = priv;
+	}
+	priv->queue_count = params->id_count;
+
+	dev->netdev_ops = &fbxatm_2684_ops;
+
+	/* make sure kernel generated packet have correct headroom for
+	 * encapsulation/payload */
+	headroom = 0;
+	for (i = 0; i < params->id_count; i++)
+		headroom = max_t(int, headroom, vccs[i]->adev->tx_headroom);
+	dev->hard_header_len += headroom;
+
+	switch (params->encap) {
+	case FBXATM_E2684_VCMUX:
+	default:
+		if (params->payload == FBXATM_P2684_BRIDGE)
+			dev->hard_header_len += VCMUX_BRIDGED_NEEDED_HEADROOM;
+		break;
+	case FBXATM_E2684_LLC:
+		dev->hard_header_len += LLC_NEEDED_HEADROOM;
+		break;
+	}
+
+	ret = register_2684_netdev(priv, dev);
+	if (ret)
+		goto fail;
+
+	if (fbxatm_vcc_link_is_up(vccs[0])) {
+		netif_carrier_on(dev);
+		netif_tx_start_all_queues(dev);
+	} else
+		netif_carrier_off(dev);
+	list_add_tail(&priv->next, &fbxatm_2684_dev_list);
+
+	for (i = 0; i < params->id_count; i++)
+		fbxatm_set_uops(vccs[i], &fbxatm_2684_uops, &priv->queues[i]);
+
+	return 0;
+
+fail:
+	for (i = 0; i < ARRAY_SIZE(vccs); i++) {
+		if (vccs[i])
+			fbxatm_unbind_vcc(vccs[i]);
+	}
+	if (dev)
+		free_netdev(dev);
+	return ret;
+}
+
+/*
+ * find 2684 vcc from id list
+ */
+static struct fbxatm_2684_vcc *__find_2684_vcc(const struct fbxatm_vcc_id *id,
+					       size_t count)
+{
+	struct fbxatm_2684_vcc *priv;
+	size_t i;
+
+	/* find it */
+	list_for_each_entry(priv, &fbxatm_2684_dev_list, next) {
+		for (i = 0; i < priv->queue_count; i++) {
+			struct fbxatm_2684_queue *q;
+			size_t j;
+
+			q = &priv->queues[i];
+
+			for (j = 0; j < count; j++) {
+				if (q->vcc->adev->ifindex == id[j].dev_idx &&
+				    q->vcc->vpi == id[0].vpi &&
+				    q->vcc->vci == id[0].vci)
+					return priv;
+			}
+		}
+	}
+	return NULL;
+}
+
+/*
+ * create a RFC2684 encapsulation on given vcc
+ */
+static int create_2684_vcc(const struct fbxatm_2684_vcc_params *params)
+{
+	int ret;
+
+	mutex_lock(&fbxatm_2684_mutex);
+	ret = __create_2684_vcc(params);
+	mutex_unlock(&fbxatm_2684_mutex);
+	return ret;
+}
+
+/*
+ * remove RFC2684 encapsulation from given vcc
+ */
+static int __remove_2684_vcc(const struct fbxatm_2684_vcc_params *params)
+{
+	struct fbxatm_2684_vcc *priv;
+	size_t i;
+
+	priv = __find_2684_vcc(params->id_list, params->id_count);
+	if (!priv)
+		return -ENOENT;
+
+	/* close netdevice, fbxatm_2684_netdev_xmit cannot be called
+	 * again */
+	rtnl_lock();
+	dev_close(priv->dev);
+	rtnl_unlock();
+
+	for (i = 0; i < priv->queue_count; i++)
+		fbxatm_unbind_vcc(priv->queues[i].vcc);
+	vcc2684_sysfs_unregister(priv, priv->dev);
+	unregister_netdev(priv->dev);
+	list_del(&priv->next);
+	free_netdev(priv->dev);
+	return 0;
+}
+
+/*
+ * remove RFC2684 encapsulation from given vcc
+ */
+static int remove_2684_vcc(const struct fbxatm_2684_vcc_params *params)
+{
+	int ret;
+
+	mutex_lock(&fbxatm_2684_mutex);
+	ret = __remove_2684_vcc(params);
+	mutex_unlock(&fbxatm_2684_mutex);
+	return ret;
+}
+
+/*
+ * 2684 related ioctl handler
+ */
+static int fbxatm_2684_ioctl(struct socket *sock,
+			     unsigned int cmd, void __user *useraddr)
+{
+	int ret;
+
+	ret = 0;
+
+	switch (cmd) {
+	case FBXATM_2684_IOCADD:
+	case FBXATM_2684_IOCDEL:
+	{
+		struct fbxatm_2684_vcc_params params;
+
+		if (copy_from_user(&params, useraddr, sizeof(params)))
+			return -EFAULT;
+
+		if (cmd == FBXATM_2684_IOCADD)
+			ret = create_2684_vcc(&params);
+		else
+			ret = remove_2684_vcc(&params);
+		break;
+	}
+
+	case FBXATM_2684_IOCGET:
+	{
+		struct fbxatm_2684_vcc_params params;
+		struct fbxatm_2684_vcc *priv;
+
+		if (copy_from_user(&params, useraddr, sizeof(params)))
+			return -EFAULT;
+
+		mutex_lock(&fbxatm_2684_mutex);
+		priv = __find_2684_vcc(params.id_list, params.id_count);
+		if (!priv)
+			ret = -ENOENT;
+		else {
+			memcpy(&params, &priv->params, sizeof (params));
+			memcpy(params.dev_name, priv->dev->name, IFNAMSIZ);
+		}
+		mutex_unlock(&fbxatm_2684_mutex);
+
+		if (ret)
+			return ret;
+
+		if (copy_to_user(useraddr, &params, sizeof(params)))
+			return -EFAULT;
+		break;
+	}
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return ret;
+}
+
+static struct fbxatm_ioctl fbxatm_2684_ioctl_ops = {
+	.handler	= fbxatm_2684_ioctl,
+	.owner		= THIS_MODULE,
+};
+
+int __init fbxatm_2684_init(void)
+{
+	struct proc_dir_entry *root, *proc;
+
+	root = fbxatm_proc_misc_register("tel");
+	if (!root)
+		return -ENOMEM;
+
+	/* tel debug crap */
+	proc = proc_create_data("bad_ip", 0666, root, &tel_proc_fops, NULL);
+	if (!proc)
+		return -ENOMEM;
+
+	fbxatm_register_ioctl(&fbxatm_2684_ioctl_ops);
+	return 0;
+}
+
+void fbxatm_2684_exit(void)
+{
+	fbxatm_unregister_ioctl(&fbxatm_2684_ioctl_ops);
+}
diff -Nruw linux-3.11.10-fbx/net/fbxatm./fbxatm_core.c linux-3.11.10-fbx/net/fbxatm/fbxatm_core.c
--- linux-3.11.10-fbx/net/fbxatm./fbxatm_core.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxatm/fbxatm_core.c	2015-09-29 11:15:09.001756505 +0200
@@ -0,0 +1,207 @@
+#include <linux/init.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/fbxatm.h>
+#include <linux/fbxatm_dev.h>
+#include <linux/module.h>
+#include <net/sock.h>
+#include "fbxatm_priv.h"
+
+static DEFINE_MUTEX(ioctl_mutex);
+static LIST_HEAD(ioctl_list);
+
+void fbxatm_register_ioctl(struct fbxatm_ioctl *ioctl)
+{
+	mutex_lock(&ioctl_mutex);
+	list_add_tail(&ioctl->next, &ioctl_list);
+	mutex_unlock(&ioctl_mutex);
+}
+
+void fbxatm_unregister_ioctl(struct fbxatm_ioctl *ioctl)
+{
+	mutex_lock(&ioctl_mutex);
+	list_del(&ioctl->next);
+	mutex_unlock(&ioctl_mutex);
+}
+
+static int fbxatm_sock_ioctl(struct socket *sock, unsigned int cmd,
+			     unsigned long arg)
+{
+	struct fbxatm_ioctl *ioctl;
+	void __user *useraddr;
+	int ret;
+
+	/* sanity check */
+	useraddr = (void __user *)arg;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	ret = -ENOIOCTLCMD;
+	mutex_lock(&ioctl_mutex);
+
+	list_for_each_entry(ioctl, &ioctl_list, next) {
+		if (!ioctl->handler)
+			continue;
+
+		if (!try_module_get(ioctl->owner))
+			continue;
+
+		ret = ioctl->handler(sock, cmd, useraddr);
+		module_put(ioctl->owner);
+		if (ret != -ENOIOCTLCMD)
+			break;
+	}
+	mutex_unlock(&ioctl_mutex);
+
+	return ret;
+}
+
+static int fbxatm_sock_release(struct socket *sock)
+{
+	struct fbxatm_ioctl *ioctl;
+	struct sock *sk = sock->sk;
+
+	mutex_lock(&ioctl_mutex);
+
+	list_for_each_entry(ioctl, &ioctl_list, next) {
+		if (!ioctl->release)
+			continue;
+
+		if (!try_module_get(ioctl->owner))
+			continue;
+
+		ioctl->release(sock);
+		module_put(ioctl->owner);
+	}
+	mutex_unlock(&ioctl_mutex);
+
+	if (sk)
+		sock_put(sk);
+
+	return 0;
+}
+
+static const struct proto_ops fbxatm_proto_ops = {
+	.family		= PF_FBXATM,
+
+	.release =	fbxatm_sock_release,
+	.ioctl =	fbxatm_sock_ioctl,
+
+	.bind =		sock_no_bind,
+	.connect =	sock_no_connect,
+	.socketpair =	sock_no_socketpair,
+	.accept =	sock_no_accept,
+	.getname =	sock_no_getname,
+	.poll =		sock_no_poll,
+	.listen =	sock_no_listen,
+	.shutdown =	sock_no_shutdown,
+	.setsockopt =	sock_no_setsockopt,
+	.getsockopt =	sock_no_getsockopt,
+	.sendmsg =	sock_no_sendmsg,
+	.recvmsg =	sock_no_recvmsg,
+	.mmap =		sock_no_mmap,
+	.sendpage =	sock_no_sendpage,
+	.owner		= THIS_MODULE,
+};
+
+static struct proto fbxatm_proto = {
+        .name           = "fbxatm",
+        .owner          =  THIS_MODULE,
+        .obj_size       = sizeof (struct sock),
+};
+
+static int fbxatm_sock_create(struct net *net, struct socket *sock,
+			      int protocol, int kern)
+{
+	struct sock *sk;
+
+        sk = sk_alloc(net, PF_FBXATM, GFP_KERNEL, &fbxatm_proto);
+	if (!sk)
+		return -ENOMEM;
+
+        sock_init_data(sock, sk);
+        sock->state = SS_UNCONNECTED;
+        sock->ops = &fbxatm_proto_ops;
+	return 0;
+}
+
+static struct net_proto_family fbxatm_family_ops = {
+	.family = PF_FBXATM,
+	.create = fbxatm_sock_create,
+	.owner = THIS_MODULE,
+};
+
+
+static int __init fbxatm_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "Freebox ATM stack\n");
+	ret = fbxatm_sysfs_init();
+	if (ret)
+		return ret;
+
+	ret = fbxatm_procfs_init();
+	if (ret)
+		goto fail_sysfs;
+
+	ret = fbxatm_vcc_init();
+	if (ret)
+		goto fail_procfs;
+
+	ret = fbxatm_2684_init();
+	if (ret)
+		goto fail_vcc;
+
+	ret = fbxatm_pppoa_init();
+	if (ret)
+		goto fail_2684;
+
+	ret = proto_register(&fbxatm_proto, 0);
+	if (ret)
+		goto fail_pppoa;
+
+	ret = sock_register(&fbxatm_family_ops);
+	if (ret)
+		goto fail_proto;
+
+	return 0;
+
+fail_proto:
+	proto_unregister(&fbxatm_proto);
+
+fail_pppoa:
+	fbxatm_pppoa_exit();
+
+fail_2684:
+	fbxatm_2684_exit();
+
+fail_vcc:
+	fbxatm_vcc_exit();
+
+fail_procfs:
+	fbxatm_procfs_exit();
+
+fail_sysfs:
+	fbxatm_sysfs_exit();
+	printk(KERN_ERR "failed to initialize Freebox ATM stack\n");
+	return ret;
+}
+
+static void __exit fbxatm_exit(void)
+{
+	sock_unregister(PF_FBXATM);
+	proto_unregister(&fbxatm_proto);
+	fbxatm_pppoa_exit();
+	fbxatm_2684_exit();
+	fbxatm_vcc_exit();
+	fbxatm_procfs_exit();
+	fbxatm_sysfs_exit();
+}
+
+subsys_initcall(fbxatm_init);
+module_exit(fbxatm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(PF_FBXATM);
diff -Nruw linux-3.11.10-fbx/net/fbxatm./fbxatm_dev.c linux-3.11.10-fbx/net/fbxatm/fbxatm_dev.c
--- linux-3.11.10-fbx/net/fbxatm./fbxatm_dev.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxatm/fbxatm_dev.c	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,957 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/bitops.h>
+#include <linux/fbxatm_dev.h>
+#include "fbxatm_priv.h"
+
+/*
+ * list of registered device & lock
+ */
+LIST_HEAD(fbxatm_dev_list);
+
+/*
+ * big "rtnl" lock
+ */
+DEFINE_MUTEX(fbxatm_mutex);
+static int fbxatm_ifindex = -1;
+
+/*
+ * find device by index
+ */
+static struct fbxatm_dev *__fbxatm_dev_get_by_index(int ifindex)
+{
+	struct fbxatm_dev *pdev;
+
+	list_for_each_entry(pdev, &fbxatm_dev_list, next) {
+		if (pdev->ifindex == ifindex)
+			return pdev;
+	}
+	return NULL;
+}
+
+/*
+ * find vcc by id
+ */
+static struct fbxatm_vcc *
+__fbxatm_vcc_get_by_id(const struct fbxatm_vcc_id *id)
+{
+	struct fbxatm_dev *adev;
+	struct fbxatm_vcc *vcc;
+	int found;
+
+	adev = __fbxatm_dev_get_by_index(id->dev_idx);
+	if (!adev)
+		return ERR_PTR(-ENODEV);
+
+	found = 0;
+	list_for_each_entry(vcc, &adev->vcc_list, next) {
+		if (vcc->vpi != id->vpi || vcc->vci != id->vci)
+			continue;
+		found = 1;
+		break;
+	}
+
+	if (found)
+		return vcc;
+	return ERR_PTR(-ENOENT);
+}
+
+/*
+ * allocate device
+ */
+struct fbxatm_dev *fbxatm_alloc_device(int sizeof_priv)
+{
+	unsigned int size;
+
+	size = sizeof(struct fbxatm_dev) + sizeof_priv + FBXATMDEV_ALIGN;
+	return kzalloc(size, GFP_KERNEL);
+}
+
+EXPORT_SYMBOL(fbxatm_alloc_device);
+
+/*
+ * calculate crc10 of oam cell
+ */
+static void compute_oam_crc10(struct fbxatm_oam_cell_payload *cell)
+{
+	u8 *pdu;
+	u16 crc;
+
+	/* crc10 does not cover header */
+	pdu = (u8 *)&cell->cell_type;
+	memset(cell->crc10, 0, 2);
+
+	crc = crc10(0, pdu, sizeof (*cell) - sizeof (cell->cell_hdr));
+	cell->crc10[0] = crc >> 8;
+	cell->crc10[1] = crc & 0xff;
+}
+
+/*
+ * check crc10 of oam cell
+ */
+static int check_oam_crc10(struct fbxatm_oam_cell_payload *cell)
+{
+	u8 *pdu;
+	u16 crc;
+
+	pdu = (u8 *)&cell->cell_type;
+
+	crc = (cell->crc10[0] << 8) | cell->crc10[1];
+	memset(cell->crc10, 0, 2);
+
+	if (crc != crc10(0, pdu, sizeof (*cell) - sizeof (cell->cell_hdr)))
+		return 1;
+
+	return 0;
+}
+
+/*
+ * send an oam ping and wait for answer
+ */
+static int do_oam_ping(struct fbxatm_oam_ping *ping)
+{
+	struct fbxatm_dev *adev;
+	struct fbxatm_oam_cell *oam_cell;
+	struct fbxatm_oam_cell_payload *cell;
+	u8 *hdr;
+	int ret;
+
+	switch (ping->req.type) {
+	case FBXATM_OAM_PING_SEG_F4:
+	case FBXATM_OAM_PING_E2E_F4:
+		return -ENOTSUPP;
+	case FBXATM_OAM_PING_SEG_F5:
+	case FBXATM_OAM_PING_E2E_F5:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* find device */
+	mutex_lock(&fbxatm_mutex);
+	adev = __fbxatm_dev_get_by_index(ping->req.id.dev_idx);
+	if (!adev) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	if (!test_bit(FBXATM_DEV_F_LINK_UP, &adev->dev_flags)) {
+		ret = -ENETDOWN;
+		goto out_unlock;
+	}
+
+	/* if f5, vcc need to be opened */
+	switch (ping->req.type) {
+	case FBXATM_OAM_PING_SEG_F5:
+	case FBXATM_OAM_PING_E2E_F5:
+	{
+		struct fbxatm_vcc *vcc;
+
+		vcc = __fbxatm_vcc_get_by_id(&ping->req.id);
+		if (IS_ERR(vcc)) {
+			ret = -ENETDOWN;
+			goto out_unlock;
+		}
+		break;
+	}
+
+	default:
+		break;
+	}
+
+	ping->correlation_id = ++adev->oam_correlation_id;
+
+	/* prepare atm oam cell and send it */
+	oam_cell = kmalloc(sizeof (*oam_cell), GFP_KERNEL);
+	if (!oam_cell) {
+		ret = -ENOMEM;
+		goto out_unlock;
+	}
+	cell = &oam_cell->payload;
+
+	hdr = cell->cell_hdr;
+	ATM_SET_GFC(hdr, 0);
+
+	ATM_SET_VPI(hdr, ping->req.id.vpi);
+	ATM_SET_VCI(hdr, ping->req.id.vci);
+	if (ping->req.type == FBXATM_OAM_PING_E2E_F5)
+		ATM_SET_PT(hdr, OAM_PTI_END2END_F5);
+	else
+		ATM_SET_PT(hdr, OAM_PTI_SEG_F5);
+	ATM_SET_CLP(hdr, 0);
+	ATM_SET_HEC(hdr, 0);
+
+	cell->cell_type = (OAM_TYPE_FAULT_MANAGEMENT << OAM_TYPE_SHIFT) |
+		(FUNC_TYPE_OAM_LOOPBACK << FUNC_TYPE_SHIFT);
+	cell->loopback_indication = 1;
+
+	memcpy(cell->correlation_tag, &ping->correlation_id,
+	       sizeof (cell->correlation_tag));
+	memcpy(cell->loopback_id, ping->req.llid, sizeof (ping->req.llid));
+	memset(cell->source_id, 0x6a, sizeof (cell->source_id));
+	memset(cell->reserved, 0x6a, sizeof (cell->reserved));
+
+	compute_oam_crc10(cell);
+
+	ret = adev->ops->send_oam(adev, oam_cell);
+	if (ret)
+		goto out_unlock;
+
+	/* wait for an answer */
+	list_add(&ping->next, &adev->oam_pending_ping);
+	ping->replied = 0;
+	init_waitqueue_head(&ping->wq);
+	mutex_unlock(&fbxatm_mutex);
+
+	ret = wait_event_interruptible_timeout(ping->wq, ping->replied,
+					       HZ * 5);
+	list_del(&ping->next);
+
+	if (ret == -ERESTARTSYS)
+		return ret;
+
+	if (ping->replied < 0) {
+		/* ping failed */
+		return ping->replied;
+	}
+
+	if (!ping->replied) {
+		/* timeout */
+		return -ETIME;
+	}
+
+	return 0;
+
+
+out_unlock:
+	mutex_unlock(&fbxatm_mutex);
+	return ret;
+}
+
+/*
+ * special llid values
+ */
+static const u8 llid_all1[16] = { 0xff, 0xff, 0xff, 0xff,
+				  0xff, 0xff, 0xff, 0xff,
+				  0xff, 0xff, 0xff, 0xff,
+				  0xff, 0xff, 0xff, 0xff };
+
+static const u8 llid_all0[16] = { 0 };
+
+/*
+ * handle incoming oam cell
+ */
+static void handle_oam_cell(struct fbxatm_dev *adev,
+			    struct fbxatm_oam_cell *oam_cell)
+{
+	struct fbxatm_oam_cell_payload *cell;
+	u16 vci;
+	u8 *hdr, pt, oam, func;
+
+	/* check CRC10 */
+	cell = &oam_cell->payload;
+	if (check_oam_crc10(cell)) {
+		adev->stats.rx_bad_oam++;
+		goto out;
+	}
+
+	/* drop f4 cells */
+	hdr = cell->cell_hdr;
+	vci = ATM_GET_VCI(hdr);
+
+	if (vci == OAM_VCI_SEG_F4 || vci == OAM_VCI_END2END_F4) {
+		adev->stats.rx_f4_oam++;
+		goto out;
+	}
+
+	/* keep f5 cells only */
+	pt = ATM_GET_PT(hdr);
+	if (pt != OAM_PTI_SEG_F5 && pt != OAM_PTI_END2END_F5) {
+		adev->stats.rx_other_oam++;
+		goto out;
+	}
+
+	adev->stats.rx_f5_oam++;
+
+	/* keep oam loopback type only */
+	oam = (cell->cell_type & OAM_TYPE_MASK) >> OAM_TYPE_SHIFT;
+	func = (cell->cell_type & FUNC_TYPE_MASK) >> FUNC_TYPE_SHIFT;
+
+	if (oam != OAM_TYPE_FAULT_MANAGEMENT ||
+	    func != FUNC_TYPE_OAM_LOOPBACK) {
+		adev->stats.rx_other_oam++;
+		goto out;
+	}
+
+	if (cell->loopback_indication & 1) {
+		int match, ret;
+
+		/* request, check for llid match */
+		match = 0;
+		switch (pt) {
+		case OAM_PTI_SEG_F5:
+			/* 0x0 or 0xffffffff */
+			if (!memcmp(cell->loopback_id, llid_all0,
+				    sizeof (llid_all0)))
+				match = 1;
+			/* fallthrough */
+
+		case OAM_PTI_END2END_F5:
+			/* 0xffffffff only */
+			if (!memcmp(cell->loopback_id, llid_all1,
+				    sizeof (llid_all1)))
+				match = 1;
+			break;
+		}
+
+		if (!match) {
+			adev->stats.rx_bad_llid_oam++;
+			goto out;
+		}
+
+		/* ok, update llid and answer */
+		cell->loopback_indication = 0;
+		memcpy(cell->loopback_id, llid_all1, sizeof (llid_all1));
+		compute_oam_crc10(cell);
+
+		mutex_lock(&fbxatm_mutex);
+		ret = adev->ops->send_oam(adev, oam_cell);
+		mutex_unlock(&fbxatm_mutex);
+
+		if (!ret) {
+			/* send successful, don't free cell */
+			return;
+		}
+
+	} else {
+		struct fbxatm_oam_ping *ping;
+
+		/* reply, find a matching sender */
+		mutex_lock(&fbxatm_mutex);
+		list_for_each_entry(ping, &adev->oam_pending_ping, next) {
+
+			/* compare correlation id */
+			if (memcmp(&ping->correlation_id,
+				   cell->correlation_tag,
+				   sizeof (cell->correlation_tag)))
+				continue;
+
+			/* compare ping type */
+			switch (ping->req.type) {
+			case FBXATM_OAM_PING_SEG_F5:
+				if (pt != OAM_PTI_SEG_F5)
+					continue;
+				break;
+			case FBXATM_OAM_PING_E2E_F5:
+				if (pt != OAM_PTI_END2END_F5)
+					continue;
+				break;
+			default:
+				break;
+			}
+
+			/* seems we have a match */
+			ping->replied = 1;
+			wake_up(&ping->wq);
+		}
+		mutex_unlock(&fbxatm_mutex);
+	}
+
+out:
+	kfree(oam_cell);
+}
+
+/*
+ * oam rx processing workqueue
+ */
+static void fbxatm_oam_work(struct work_struct *work)
+{
+	struct fbxatm_dev *adev;
+	struct fbxatm_oam_cell *cell;
+
+	adev = container_of(work, struct fbxatm_dev, oam_work);
+
+	do {
+		cell = NULL;
+		spin_lock_bh(&adev->oam_lock);
+		if (!list_empty(&adev->rx_oam_cells)) {
+			cell = list_first_entry(&adev->rx_oam_cells,
+						struct fbxatm_oam_cell, next);
+			list_del(&cell->next);
+			adev->rx_oam_cells_count--;
+		}
+		spin_unlock_bh(&adev->oam_lock);
+
+		if (cell)
+			handle_oam_cell(adev, cell);
+
+	} while (cell);
+}
+
+/*
+ * register given device
+ */
+static int __fbxatm_register_device(struct fbxatm_dev *adev,
+				    const char *base_name,
+				    const struct fbxatm_dev_ops *ops)
+{
+	struct fbxatm_dev *pdev;
+	int name_len, count, ret;
+	long *inuse;
+
+	adev->ops = ops;
+	INIT_LIST_HEAD(&adev->vcc_list);
+	INIT_LIST_HEAD(&adev->next);
+	spin_lock_init(&adev->stats_lock);
+	spin_lock_init(&adev->oam_lock);
+	INIT_LIST_HEAD(&adev->rx_oam_cells);
+	INIT_WORK(&adev->oam_work, fbxatm_oam_work);
+	INIT_LIST_HEAD(&adev->oam_pending_ping);
+	get_random_bytes(&adev->oam_correlation_id, 4);
+
+	name_len = strlen(base_name);
+	adev->name = kmalloc(name_len + 10, GFP_KERNEL);
+	if (!adev->name) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	/* allocate ifindex */
+	while (1) {
+		if (++fbxatm_ifindex < 0)
+			fbxatm_ifindex = 0;
+		if (__fbxatm_dev_get_by_index(fbxatm_ifindex))
+			continue;
+		adev->ifindex = fbxatm_ifindex;
+		break;
+	}
+
+	/* allocate device name */
+	inuse = (long *)get_zeroed_page(GFP_ATOMIC);
+	if (!inuse) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	list_for_each_entry(pdev, &fbxatm_dev_list, next) {
+		unsigned long val;
+		char *end;
+
+		/* look for common prefix */
+		if (strncmp(base_name, pdev->name, name_len))
+			continue;
+
+		/* make sure name is the same, not just a prefix */
+		val = simple_strtoul(pdev->name + name_len, &end, 10);
+		if (!*end)
+			continue;
+
+		set_bit(val, inuse);
+	}
+
+	count = find_first_zero_bit(inuse, PAGE_SIZE * 8);
+	free_page((unsigned long)inuse);
+
+	snprintf(adev->name, name_len + 10, "%s%d", base_name, count);
+	list_add_tail(&adev->next, &fbxatm_dev_list);
+
+	/* create procfs entries */
+	ret = fbxatm_proc_dev_register(adev);
+	if (ret)
+		goto fail;
+
+	/* call device procfs init if any */
+	if (adev->ops->init_procfs) {
+		ret = adev->ops->init_procfs(adev);
+		if (ret)
+			goto fail_procfs;
+	}
+
+	/* create sysfs entries */
+	ret = fbxatm_register_dev_sysfs(adev);
+	if (ret)
+		goto fail_procfs;
+
+	return 0;
+
+fail_procfs:
+	fbxatm_proc_dev_deregister(adev);
+
+fail:
+	list_del(&adev->next);
+	kfree(adev->name);
+	return ret;
+}
+
+/*
+ * take lock and register device
+ */
+int fbxatm_register_device(struct fbxatm_dev *adev,
+			   const char *base_name,
+			   const struct fbxatm_dev_ops *ops)
+{
+	int ret;
+
+	mutex_lock(&fbxatm_mutex);
+	ret = __fbxatm_register_device(adev, base_name, ops);
+	mutex_unlock(&fbxatm_mutex);
+	return ret;
+}
+
+EXPORT_SYMBOL(fbxatm_register_device);
+
+/*
+ * change device "link" state
+ */
+static void fbxatm_dev_set_link(struct fbxatm_dev *adev, int link)
+{
+	struct fbxatm_vcc *vcc;
+
+	/* prevent new vcc creation and oam ping */
+	mutex_lock(&fbxatm_mutex);
+
+	if (link) {
+		memset(&adev->stats, 0, sizeof (adev->stats));
+		list_for_each_entry(vcc, &adev->vcc_list, next)
+			memset(&vcc->stats, 0, sizeof (vcc->stats));
+		wmb();
+		set_bit(FBXATM_DEV_F_LINK_UP, &adev->dev_flags);
+		list_for_each_entry(vcc, &adev->vcc_list, next) {
+			set_bit(FBXATM_VCC_F_LINK_UP, &vcc->vcc_flags);
+			if (!vcc->user_ops || !vcc->user_ops->link_change)
+				continue;
+			vcc->user_ops->link_change(vcc->user_cb_data, 1,
+						   adev->link_cell_rate_ds,
+						   adev->link_cell_rate_us);
+		}
+	} else {
+		/* prevent further oam cells input */
+		spin_lock_bh(&adev->oam_lock);
+		clear_bit(FBXATM_DEV_F_LINK_UP, &adev->dev_flags);
+		spin_unlock_bh(&adev->oam_lock);
+
+		/* flush rx oam work */
+		cancel_work_sync(&adev->oam_work);
+
+		/* now disable tx on all vcc */
+		list_for_each_entry(vcc, &adev->vcc_list, next) {
+			spin_lock_bh(&vcc->tx_lock);
+			clear_bit(FBXATM_VCC_F_LINK_UP, &vcc->vcc_flags);
+			spin_unlock_bh(&vcc->tx_lock);
+			if (!vcc->user_ops || !vcc->user_ops->link_change)
+				continue;
+			vcc->user_ops->link_change(vcc->user_cb_data, 0, 0, 0);
+		}
+	}
+
+	fbxatm_dev_change_sysfs(adev);
+	mutex_unlock(&fbxatm_mutex);
+}
+
+/*
+ * set device "link" to up, allowing vcc/device send ops to be called,
+ * this function sleeps
+ */
+void fbxatm_dev_set_link_up(struct fbxatm_dev *adev)
+{
+	if (!test_bit(FBXATM_DEV_F_LINK_UP, &adev->dev_flags))
+		printk(KERN_INFO "%s: link UP - "
+		       "down: %u kbit/s - up: %u kbit/s\n", adev->name,
+		       adev->link_rate_ds / 1000, adev->link_rate_us / 1000);
+	return fbxatm_dev_set_link(adev, 1);
+}
+
+EXPORT_SYMBOL(fbxatm_dev_set_link_up);
+
+/*
+ * set device link to down, disallowing any vcc/device send ops to be
+ * called, this function sleeps
+ */
+void fbxatm_dev_set_link_down(struct fbxatm_dev *adev)
+{
+	if (test_bit(FBXATM_DEV_F_LINK_UP, &adev->dev_flags))
+		printk(KERN_INFO "%s: link DOWN\n", adev->name);
+	return fbxatm_dev_set_link(adev, 0);
+}
+
+EXPORT_SYMBOL(fbxatm_dev_set_link_down);
+
+/*
+ * take lock and unregister device
+ */
+int fbxatm_unregister_device(struct fbxatm_dev *adev)
+{
+	int ret;
+
+	ret = 0;
+	mutex_lock(&fbxatm_mutex);
+
+	if (!list_empty(&adev->vcc_list)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	list_del(&adev->next);
+
+	if (adev->ops->release_procfs)
+		adev->ops->release_procfs(adev);
+	fbxatm_proc_dev_deregister(adev);
+
+	fbxatm_unregister_dev_sysfs(adev);
+out:
+	mutex_unlock(&fbxatm_mutex);
+	return ret;
+}
+
+EXPORT_SYMBOL(fbxatm_unregister_device);
+
+/*
+ * actually free device memory
+ */
+void __fbxatm_free_device(struct fbxatm_dev *adev)
+{
+	kfree(adev->name);
+	kfree(adev);
+}
+
+/*
+ * free device memory
+ */
+void fbxatm_free_device(struct fbxatm_dev *adev)
+{
+	/* actual free is done in sysfs release */
+//	class_device_put(&adev->class_dev);
+}
+
+EXPORT_SYMBOL(fbxatm_free_device);
+
+/*
+ * device callback when oam cell comes in
+ */
+void fbxatm_netifrx_oam(struct fbxatm_dev *adev, struct fbxatm_oam_cell *cell)
+{
+	spin_lock_bh(&adev->oam_lock);
+	if (!test_bit(FBXATM_DEV_F_LINK_UP, &adev->dev_flags) ||
+	    adev->rx_oam_cells_count > 8) {
+		kfree(cell);
+		spin_unlock_bh(&adev->oam_lock);
+		return;
+	}
+	adev->rx_oam_cells_count++;
+	list_add_tail(&cell->next, &adev->rx_oam_cells);
+	spin_unlock_bh(&adev->oam_lock);
+	schedule_work(&adev->oam_work);
+}
+
+EXPORT_SYMBOL(fbxatm_netifrx_oam);
+
+/*
+ * set user ops on vcc
+ */
+void fbxatm_set_uops(struct fbxatm_vcc *vcc,
+		     const struct fbxatm_vcc_uops *user_ops,
+		     void *user_cb_data)
+{
+	spin_lock_bh(&vcc->user_ops_lock);
+	vcc->user_ops = user_ops;
+	vcc->user_cb_data = user_cb_data;
+	spin_unlock_bh(&vcc->user_ops_lock);
+}
+
+/*
+ * bind to given vcc
+ */
+static struct fbxatm_vcc *
+__fbxatm_bind_to_vcc(const struct fbxatm_vcc_id *id,
+		     enum fbxatm_vcc_user user)
+{
+	struct fbxatm_vcc *vcc;
+
+	vcc = __fbxatm_vcc_get_by_id(id);
+	if (IS_ERR(vcc))
+		return vcc;
+
+	if (vcc->user != FBXATM_VCC_USER_NONE)
+		return ERR_PTR(-EBUSY);
+
+	vcc->user = user;
+	return vcc;
+}
+
+/*
+ * bind to given vcc
+ */
+struct fbxatm_vcc *
+fbxatm_bind_to_vcc(const struct fbxatm_vcc_id *id,
+		   enum fbxatm_vcc_user user)
+{
+	struct fbxatm_vcc *vcc;
+
+	mutex_lock(&fbxatm_mutex);
+	vcc = __fbxatm_bind_to_vcc(id, user);
+	mutex_unlock(&fbxatm_mutex);
+	return vcc;
+}
+
+/*
+ * unbind from given vcc
+ */
+void fbxatm_unbind_vcc(struct fbxatm_vcc *vcc)
+{
+	spin_lock_bh(&vcc->user_ops_lock);
+	vcc->user_ops = NULL;
+	vcc->user_cb_data = NULL;
+	vcc->user = FBXATM_VCC_USER_NONE;
+	spin_unlock_bh(&vcc->user_ops_lock);
+}
+
+/*
+ * open vcc on given device
+ */
+static int __fbxatm_dev_open_vcc(const struct fbxatm_vcc_id *id,
+				 const struct fbxatm_vcc_qos *qos)
+{
+	struct fbxatm_vcc *vcc;
+	struct fbxatm_dev *adev;
+	int ret, count;
+
+	/* check vpi/vci unicity  */
+	vcc = __fbxatm_vcc_get_by_id(id);
+	if (!IS_ERR(vcc))
+		return -EBUSY;
+
+	/* sanity check */
+	switch (qos->traffic_class) {
+	case FBXATM_VCC_TC_UBR_NO_PCR:
+	case FBXATM_VCC_TC_UBR:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (qos->max_sdu > 4096)
+		return -EINVAL;
+
+	if (!qos->max_buffered_pkt || qos->max_buffered_pkt > 128)
+		return -EINVAL;
+
+	adev = __fbxatm_dev_get_by_index(id->dev_idx);
+	if (!adev)
+		return -ENODEV;
+
+	/* make sure device accept requested priorities */
+	if (qos->priority > adev->max_priority)
+		return -EINVAL;
+
+	if (qos->rx_priority > adev->max_rx_priority)
+		return -EINVAL;
+
+	/* don't open more vcc than device can handle */
+	count = 0;
+	list_for_each_entry(vcc, &adev->vcc_list, next)
+		count++;
+	if (count + 1 > adev->max_vcc)
+		return -ENOSPC;
+
+	/* make sure vpi/vci is valid for this device */
+	if ((~adev->vpi_mask & id->vpi) || (~adev->vci_mask & id->vci))
+		return -EINVAL;
+
+	if (!try_module_get(adev->ops->owner))
+		return -ENODEV;
+
+	/* ok, create vcc */
+	vcc = kzalloc(sizeof (*vcc), GFP_KERNEL);
+	if (!vcc)
+		return -ENOMEM;
+
+	spin_lock_init(&vcc->user_ops_lock);
+	spin_lock_init(&vcc->tx_lock);
+	vcc->vpi = id->vpi;
+	vcc->vci = id->vci;
+	vcc->adev = adev;
+	vcc->to_drop_pkt = 0;
+	memcpy(&vcc->qos, qos, sizeof (*qos));
+
+	ret = adev->ops->open(vcc);
+	if (ret) {
+		kfree(vcc);
+		return ret;
+	}
+
+	/* inherit vcc link state from device */
+	if (test_bit(FBXATM_DEV_F_LINK_UP, &adev->dev_flags))
+		set_bit(FBXATM_VCC_F_LINK_UP, &vcc->vcc_flags);
+
+	list_add_tail(&vcc->next, &adev->vcc_list);
+	return ret;
+}
+
+/*
+ * find device & open vcc on it
+ */
+static int fbxatm_dev_open_vcc(const struct fbxatm_vcc_id *id,
+			       const struct fbxatm_vcc_qos *qos)
+{
+	int ret;
+
+	mutex_lock(&fbxatm_mutex);
+	ret = __fbxatm_dev_open_vcc(id, qos);
+	mutex_unlock(&fbxatm_mutex);
+	return ret;
+}
+
+/*
+ * close vcc on device
+ */
+static int __fbxatm_dev_close_vcc(struct fbxatm_vcc *vcc)
+{
+	struct fbxatm_dev *adev;
+
+	if (vcc->user != FBXATM_VCC_USER_NONE)
+		return -EBUSY;
+	adev = vcc->adev;
+	module_put(adev->ops->owner);
+	adev->ops->close(vcc);
+	list_del(&vcc->next);
+	kfree(vcc);
+	return 0;
+}
+
+/*
+ * find device & vcc and close it
+ */
+static int fbxatm_dev_close_vcc(const struct fbxatm_vcc_id *id)
+{
+	struct fbxatm_vcc *vcc;
+	int ret;
+
+	mutex_lock(&fbxatm_mutex);
+	vcc = __fbxatm_vcc_get_by_id(id);
+	if (IS_ERR(vcc))
+		ret = PTR_ERR(vcc);
+	else
+		ret = __fbxatm_dev_close_vcc(vcc);
+	mutex_unlock(&fbxatm_mutex);
+	return ret;
+}
+
+/*
+ * ioctl handler
+ */
+static int fbxatm_vcc_ioctl(struct socket *sock,
+			    unsigned int cmd, void __user *useraddr)
+{
+	int ret;
+
+	ret = 0;
+
+	switch (cmd) {
+	case FBXATM_IOCADD:
+	case FBXATM_IOCDEL:
+	{
+		struct fbxatm_vcc_params params;
+
+		if (copy_from_user(&params, useraddr, sizeof(params)))
+			return -EFAULT;
+
+		if (cmd == FBXATM_IOCADD)
+			ret = fbxatm_dev_open_vcc(&params.id, &params.qos);
+		else
+			ret = fbxatm_dev_close_vcc(&params.id);
+		break;
+	}
+
+	case FBXATM_IOCGET:
+	{
+		struct fbxatm_vcc_params params;
+		struct fbxatm_vcc *vcc;
+
+		if (copy_from_user(&params, useraddr, sizeof(params)))
+			return -EFAULT;
+
+		mutex_lock(&fbxatm_mutex);
+		vcc = __fbxatm_vcc_get_by_id(&params.id);
+		if (IS_ERR(vcc))
+			ret = PTR_ERR(vcc);
+		else {
+			memcpy(&params.qos, &vcc->qos, sizeof (vcc->qos));
+			params.user = vcc->user;
+		}
+		mutex_unlock(&fbxatm_mutex);
+
+		if (ret)
+			return ret;
+
+		if (copy_to_user(useraddr, &params, sizeof(params)))
+			return -EFAULT;
+		break;
+	}
+
+	case FBXATM_IOCOAMPING:
+	{
+		struct fbxatm_oam_ping ping;
+
+		if (copy_from_user(&ping.req, useraddr, sizeof(ping.req)))
+			return -EFAULT;
+
+		ret = do_oam_ping(&ping);
+		if (ret)
+			return ret;
+
+		if (copy_to_user(useraddr, &ping.req, sizeof(ping.req)))
+			return -EFAULT;
+		break;
+	}
+
+	case FBXATM_IOCDROP:
+	{
+		struct fbxatm_vcc_drop_params params;
+		struct fbxatm_vcc *vcc;
+
+		if (copy_from_user(&params, useraddr, sizeof(params)))
+			return -EFAULT;
+
+		mutex_lock(&fbxatm_mutex);
+		vcc = __fbxatm_vcc_get_by_id(&params.id);
+		if (IS_ERR(vcc))
+			ret = PTR_ERR(vcc);
+		else {
+			spin_lock_bh(&vcc->user_ops_lock);
+			vcc->to_drop_pkt += params.drop_count;
+			spin_unlock_bh(&vcc->user_ops_lock);
+			ret = 0;
+		}
+		mutex_unlock(&fbxatm_mutex);
+
+		if (ret)
+			return ret;
+		break;
+	}
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return ret;
+}
+
+static struct fbxatm_ioctl fbxatm_vcc_ioctl_ops = {
+	.handler	= fbxatm_vcc_ioctl,
+	.owner		= THIS_MODULE,
+};
+
+int __init fbxatm_vcc_init(void)
+{
+	fbxatm_register_ioctl(&fbxatm_vcc_ioctl_ops);
+	return 0;
+}
+
+void fbxatm_vcc_exit(void)
+{
+	fbxatm_unregister_ioctl(&fbxatm_vcc_ioctl_ops);
+}
diff -Nruw linux-3.11.10-fbx/net/fbxatm./fbxatm_pppoa.c linux-3.11.10-fbx/net/fbxatm/fbxatm_pppoa.c
--- linux-3.11.10-fbx/net/fbxatm./fbxatm_pppoa.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxatm/fbxatm_pppoa.c	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,485 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/if_pppox.h>
+#include <linux/ppp_channel.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/fbxatm.h>
+#include <linux/fbxatm_dev.h>
+#include "fbxatm_priv.h"
+
+#define PFX	"fbxatm_pppoa: "
+
+static LIST_HEAD(fbxatm_pppoa_vcc_list);
+static DEFINE_MUTEX(fbxatm_pppoa_mutex);
+
+/*
+ * private data for pppoa vcc
+ */
+struct fbxatm_pppoa_vcc {
+	struct fbxatm_vcc		*vcc;
+	struct fbxatm_pppoa_vcc_params	params;
+	enum fbxatm_pppoa_encap		cur_encap;
+
+	/* used by ppp */
+	int				flags;
+	struct ppp_channel		chan;
+
+	struct socket			*sock;
+	struct list_head		next;
+};
+
+
+#define __LLC_HDR		0xfe, 0xfe, 0x03
+#define __NLPID_PPP		0xcf
+#define __PPP_LCP		0xc0, 0x21
+
+static const u8 llc_ppp[]	= { __LLC_HDR, __NLPID_PPP };
+static const u8 llc_ppp_lcp[]	= { __LLC_HDR, __NLPID_PPP, __PPP_LCP };
+static const u8 lcp[]		= { __PPP_LCP };
+
+
+/*
+ * fbxatm stack receive callback, called from softirq
+ */
+static void vcc_rx_callback(struct sk_buff *skb, void *data)
+{
+	struct fbxatm_pppoa_vcc *priv;
+
+	priv = (struct fbxatm_pppoa_vcc *)data;
+
+	if (priv->chan.ppp == NULL) {
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	switch (priv->cur_encap) {
+	case FBXATM_EPPPOA_VCMUX:
+		/* nothing to do */
+		break;
+
+	case FBXATM_EPPPOA_LLC:
+		/* make sure llc header is present and remove */
+		if (skb->len < sizeof(llc_ppp) ||
+		    memcmp(skb->data, llc_ppp, sizeof(llc_ppp)))
+			goto error;
+		skb_pull(skb, sizeof(llc_ppp));
+		break;
+
+	case FBXATM_EPPPOA_AUTODETECT:
+		/* look for lcp, with an llc header or not */
+		if (skb->len >= sizeof(llc_ppp_lcp) &&
+		    !memcmp(skb->data, llc_ppp_lcp, sizeof(llc_ppp_lcp))) {
+			priv->cur_encap = FBXATM_EPPPOA_LLC;
+			skb_pull(skb, sizeof(llc_ppp));
+			break;
+		}
+
+		if (skb->len >= sizeof(lcp) &&
+		    !memcmp(skb->data, lcp, sizeof (lcp))) {
+			priv->cur_encap = FBXATM_EPPPOA_VCMUX;
+			break;
+		}
+
+		/* no match */
+		goto error;
+	}
+
+	ppp_input(&priv->chan, skb);
+	return;
+
+error:
+	dev_kfree_skb(skb);
+	ppp_input_error(&priv->chan, 0);
+}
+
+/*
+ * fbxatm stack tx done callback, called from softirq
+ */
+static void vcc_tx_done_callback(void *data)
+{
+	struct fbxatm_pppoa_vcc *priv;
+	priv = (struct fbxatm_pppoa_vcc *)data;
+	ppp_output_wakeup(&priv->chan);
+}
+
+/*
+ * vcc user ops, callback from fbxatm stack
+ */
+static const struct fbxatm_vcc_uops fbxatm_pppoa_vcc_uops = {
+	.rx_pkt		= vcc_rx_callback,
+	.tx_done	= vcc_tx_done_callback,
+};
+
+/*
+ * ppp xmit callback
+ */
+static int ppp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+{
+	struct fbxatm_pppoa_vcc *priv;
+	struct sk_buff *to_send_skb, *nskb;
+	int ret;
+
+	priv = (struct fbxatm_pppoa_vcc *)chan->private;
+
+	/* MAYBE FIXME: handle protocol compression ? */
+
+	to_send_skb = skb;
+	nskb = NULL;
+
+	/* send using vcmux encap if not yet known */
+	switch (priv->cur_encap) {
+	case FBXATM_EPPPOA_AUTODETECT:
+	case FBXATM_EPPPOA_VCMUX:
+		break;
+
+	case FBXATM_EPPPOA_LLC:
+	{
+		unsigned int headroom;
+
+		headroom = skb_headroom(skb);
+
+		if (headroom < sizeof(llc_ppp)) {
+			headroom += sizeof(llc_ppp);
+			nskb = skb_realloc_headroom(skb, headroom);
+			if (!nskb) {
+				dev_kfree_skb(skb);
+				return 1;
+			}
+			to_send_skb = nskb;
+		}
+
+		skb_push(to_send_skb, sizeof(llc_ppp));
+		memcpy(to_send_skb->data, llc_ppp, sizeof(llc_ppp));
+		break;
+	}
+	}
+
+	ret = fbxatm_send(priv->vcc, to_send_skb);
+	if (ret) {
+		/* packet was not sent, queue is full, free any newly
+		 * created skb */
+		if (nskb)
+			dev_kfree_skb(nskb);
+		else {
+			/* restore original skb if we altered it */
+			if (priv->cur_encap == FBXATM_EPPPOA_LLC)
+				skb_pull(skb, sizeof(llc_ppp));
+		}
+
+		/* suspend ppp output, will be woken up by
+		 * ppp_output_wakeup, we're called under ppp lock so
+		 * we can't race with tx done */
+		return 0;
+	}
+
+	/* packet was sent, if we sent a copy free the original */
+	if (nskb)
+		dev_kfree_skb(skb);
+
+	if (fbxatm_vcc_queue_full(priv->vcc))
+		ppp_output_stop(chan);
+
+	return 1;
+}
+
+static int ppp_ioctl(struct ppp_channel *chan, unsigned int cmd,
+		     unsigned long arg)
+{
+	struct fbxatm_pppoa_vcc *priv;
+	int ret;
+
+	priv = (struct fbxatm_pppoa_vcc *)chan->private;
+
+	switch (cmd) {
+	case PPPIOCGFLAGS:
+		ret = put_user(priv->flags, (int __user *)arg) ? -EFAULT : 0;
+		break;
+	case PPPIOCSFLAGS:
+		ret = get_user(priv->flags, (int __user *) arg) ? -EFAULT : 0;
+		break;
+	default:
+		ret = -ENOTTY;
+		break;
+	}
+	return ret;
+}
+
+static struct ppp_channel_ops fbxatm_pppoa_ppp_ops = {
+	.start_xmit = ppp_xmit,
+	.ioctl = ppp_ioctl,
+};
+
+/*
+ * find pppoa vcc from id
+ */
+static struct fbxatm_pppoa_vcc *
+__find_pppoa_vcc(const struct fbxatm_vcc_id *id)
+{
+	struct fbxatm_pppoa_vcc *priv;
+	int found;
+
+	/* find it */
+	found = 0;
+	list_for_each_entry(priv, &fbxatm_pppoa_vcc_list, next) {
+		if (priv->vcc->adev->ifindex != id->dev_idx ||
+		    priv->vcc->vpi != id->vpi ||
+		    priv->vcc->vci != id->vci)
+			continue;
+
+		found = 1;
+		break;
+	}
+
+	if (found)
+		return priv;
+	return NULL;
+}
+
+/*
+ * find pppoa vcc from socket
+ */
+static struct fbxatm_pppoa_vcc *
+__find_pppoa_vcc_from_socket(const struct socket *sock)
+{
+	struct fbxatm_pppoa_vcc *priv;
+	int found;
+
+	/* find it */
+	found = 0;
+	list_for_each_entry(priv, &fbxatm_pppoa_vcc_list, next) {
+		if (priv->sock != sock)
+			continue;
+
+		found = 1;
+		break;
+	}
+
+	if (found)
+		return priv;
+	return NULL;
+}
+
+/*
+ * bind to given vcc
+ */
+static int __bind_pppoa_vcc(const struct fbxatm_pppoa_vcc_params *params,
+			    struct socket *sock)
+{
+	struct fbxatm_pppoa_vcc *priv;
+	int ret;
+
+	/* sanity check */
+	switch (params->encap) {
+	case FBXATM_EPPPOA_AUTODETECT:
+	case FBXATM_EPPPOA_VCMUX:
+	case FBXATM_EPPPOA_LLC:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	memcpy(&priv->params, params, sizeof (*params));
+	priv->cur_encap = params->encap;
+
+	/* bind to vcc */
+	priv->vcc = fbxatm_bind_to_vcc(&params->id, FBXATM_VCC_USER_PPPOA);
+	if (IS_ERR(priv->vcc)) {
+		ret = PTR_ERR(priv->vcc);
+		goto fail;
+	}
+
+	fbxatm_set_uops(priv->vcc, &fbxatm_pppoa_vcc_uops, priv);
+	priv->chan.private = priv;
+	priv->chan.ops = &fbxatm_pppoa_ppp_ops;
+	priv->chan.mtu = priv->vcc->qos.max_sdu - PPP_HDRLEN;
+	priv->chan.hdrlen = 0;
+	priv->sock = sock;
+
+	if (priv->cur_encap != FBXATM_EPPPOA_VCMUX) {
+		/* assume worst case if vcmux is not forced */
+		priv->chan.mtu -= sizeof(llc_ppp);
+		priv->chan.hdrlen += sizeof(llc_ppp);
+	}
+
+	priv->chan.mtu -= priv->vcc->adev->tx_headroom;
+	priv->chan.hdrlen += priv->vcc->adev->tx_headroom;
+
+	ret = ppp_register_channel(&priv->chan);
+	if (ret)
+		goto fail_unbind;
+	list_add_tail(&priv->next, &fbxatm_pppoa_vcc_list);
+	return 0;
+
+fail_unbind:
+	fbxatm_unbind_vcc(priv->vcc);
+
+fail:
+	kfree(priv);
+	return ret;
+}
+
+/*
+ * bind to given vcc
+ */
+static int bind_pppoa_vcc(const struct fbxatm_pppoa_vcc_params *params,
+			  struct socket *sock)
+{
+	int ret;
+
+	mutex_lock(&fbxatm_pppoa_mutex);
+	ret = __bind_pppoa_vcc(params, sock);
+	mutex_unlock(&fbxatm_pppoa_mutex);
+	return ret;
+}
+
+/*
+ * unbind from given vcc
+ */
+static void __unbind_pppoa_vcc(struct fbxatm_pppoa_vcc *priv)
+{
+	ppp_unregister_channel(&priv->chan);
+	fbxatm_unbind_vcc(priv->vcc);
+	list_del(&priv->next);
+	kfree(priv);
+}
+
+/*
+ * unbind from given vcc
+ */
+static int unbind_pppoa_vcc(const struct fbxatm_pppoa_vcc_params *params)
+{
+	struct fbxatm_pppoa_vcc *priv;
+	int ret;
+
+	ret = 0;
+	mutex_lock(&fbxatm_pppoa_mutex);
+	priv = __find_pppoa_vcc(&params->id);
+	if (!priv)
+		ret = -ENOENT;
+	else
+		__unbind_pppoa_vcc(priv);
+	mutex_unlock(&fbxatm_pppoa_mutex);
+	return ret;
+}
+
+/*
+ * pppoa related ioctl handler
+ */
+static int fbxatm_pppoa_ioctl(struct socket *sock,
+			      unsigned int cmd, void __user *useraddr)
+{
+	int ret;
+
+	ret = 0;
+
+	switch (cmd) {
+	case FBXATM_PPPOA_IOCADD:
+	case FBXATM_PPPOA_IOCDEL:
+	{
+		struct fbxatm_pppoa_vcc_params params;
+
+		if (copy_from_user(&params, useraddr, sizeof(params)))
+			return -EFAULT;
+
+		if (cmd == FBXATM_PPPOA_IOCADD)
+			ret = bind_pppoa_vcc(&params, sock);
+		else
+			ret = unbind_pppoa_vcc(&params);
+		break;
+	}
+
+	case FBXATM_PPPOA_IOCGET:
+	{
+		struct fbxatm_pppoa_vcc_params params;
+		struct fbxatm_pppoa_vcc *priv;
+
+		if (copy_from_user(&params, useraddr, sizeof(params)))
+			return -EFAULT;
+
+		mutex_lock(&fbxatm_pppoa_mutex);
+		priv = __find_pppoa_vcc(&params.id);
+		if (!priv)
+			ret = -ENOENT;
+		else
+			memcpy(&params, &priv->params, sizeof (params));
+		mutex_unlock(&fbxatm_pppoa_mutex);
+
+		if (ret)
+			return ret;
+
+		if (copy_to_user(useraddr, &params, sizeof(params)))
+			return -EFAULT;
+		break;
+	}
+
+	case PPPIOCGCHAN:
+	case PPPIOCGUNIT:
+	{
+		struct fbxatm_pppoa_vcc *priv;
+		int value;
+
+		value = 0;
+
+		mutex_lock(&fbxatm_pppoa_mutex);
+		priv = __find_pppoa_vcc_from_socket(sock);
+		if (!priv)
+			ret = -ENOENT;
+		else {
+			if (cmd == PPPIOCGCHAN)
+				value = ppp_channel_index(&priv->chan);
+			else
+				value = ppp_unit_number(&priv->chan);
+		}
+		mutex_unlock(&fbxatm_pppoa_mutex);
+
+		if (ret)
+			return ret;
+
+		if (copy_to_user(useraddr, &value, sizeof(value)))
+			ret = -EFAULT;
+		break;
+	}
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return ret;
+}
+
+/*
+ * pppoa related release handler
+ */
+static void fbxatm_pppoa_release(struct socket *sock)
+{
+	struct fbxatm_pppoa_vcc *priv;
+
+	mutex_lock(&fbxatm_pppoa_mutex);
+	priv = __find_pppoa_vcc_from_socket(sock);
+	if (priv)
+		__unbind_pppoa_vcc(priv);
+	mutex_unlock(&fbxatm_pppoa_mutex);
+}
+
+static struct fbxatm_ioctl fbxatm_pppoa_ioctl_ops = {
+	.handler	= fbxatm_pppoa_ioctl,
+	.release	= fbxatm_pppoa_release,
+	.owner		= THIS_MODULE,
+};
+
+int __init fbxatm_pppoa_init(void)
+{
+	fbxatm_register_ioctl(&fbxatm_pppoa_ioctl_ops);
+	return 0;
+}
+
+void fbxatm_pppoa_exit(void)
+{
+	fbxatm_unregister_ioctl(&fbxatm_pppoa_ioctl_ops);
+}
diff -Nruw linux-3.11.10-fbx/net/fbxatm./fbxatm_priv.h linux-3.11.10-fbx/net/fbxatm/fbxatm_priv.h
--- linux-3.11.10-fbx/net/fbxatm./fbxatm_priv.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxatm/fbxatm_priv.h	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,67 @@
+#ifndef FBXATM_PRIV_H_
+#define FBXATM_PRIV_H_
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+extern struct list_head fbxatm_dev_list;
+extern struct mutex fbxatm_mutex;
+
+int __init fbxatm_vcc_init(void);
+
+void fbxatm_vcc_exit(void);
+
+void __fbxatm_free_device(struct fbxatm_dev *adev);
+
+int __init fbxatm_2684_init(void);
+
+void fbxatm_2684_exit(void);
+
+/*
+ * pppoa
+ */
+#ifdef CONFIG_PPP
+int __init fbxatm_pppoa_init(void);
+
+void fbxatm_pppoa_exit(void);
+#else
+static inline int fbxatm_pppoa_init(void) { return 0; };
+static inline void fbxatm_pppoa_exit(void) { };
+#endif
+
+/*
+ * procfs stuff
+ */
+int fbxatm_proc_dev_register(struct fbxatm_dev *dev);
+
+void fbxatm_proc_dev_deregister(struct fbxatm_dev *dev);
+
+struct proc_dir_entry *fbxatm_proc_misc_register(const char *path);
+
+void fbxatm_proc_misc_deregister(const char *path);
+
+int __init fbxatm_procfs_init(void);
+
+void fbxatm_procfs_exit(void);
+
+
+/*
+ * sysfs stuff
+ */
+int __init fbxatm_sysfs_init(void);
+
+void fbxatm_sysfs_exit(void);
+
+void fbxatm_dev_change_sysfs(struct fbxatm_dev *adev);
+
+int fbxatm_register_dev_sysfs(struct fbxatm_dev *adev);
+
+void fbxatm_unregister_dev_sysfs(struct fbxatm_dev *adev);
+
+
+/*
+ * crc10
+ */
+u16 crc10(u16 crc, const u8 *buffer, size_t len);
+
+#endif /* !FBXATM_PRIV_H_ */
diff -Nruw linux-3.11.10-fbx/net/fbxatm./fbxatm_procfs.c linux-3.11.10-fbx/net/fbxatm/fbxatm_procfs.c
--- linux-3.11.10-fbx/net/fbxatm./fbxatm_procfs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxatm/fbxatm_procfs.c	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,340 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/fbxatm_dev.h>
+#include <net/net_namespace.h>
+#include "fbxatm_priv.h"
+
+static struct proc_dir_entry *fbxatm_proc_root;
+
+/*
+ * /proc/net/atm/vcc
+ */
+static int vcc_seq_show(struct seq_file *seq, void *v)
+{
+	struct fbxatm_vcc *vcc;
+
+	if (v == (void *)SEQ_START_TOKEN) {
+		seq_printf(seq, "%s",
+			   "Itf.VPI.VCI USER TC MaxSDU  RX TX  RXAAL5 "
+			   "TXAAL5\n");
+		return 0;
+	}
+
+	vcc = (struct fbxatm_vcc *)v;
+	seq_printf(seq, "%d.%u.%u %d ", vcc->adev->ifindex,
+		   vcc->vpi, vcc->vci, vcc->user);
+	seq_printf(seq, "%u %u ", vcc->qos.traffic_class, vcc->qos.max_sdu);
+	seq_printf(seq, "%lu %lu  %lu %lu\n",
+		   vcc->stats.rx_bytes,
+		   vcc->stats.tx_bytes,
+		   vcc->stats.rx_aal5,
+		   vcc->stats.tx_aal5);
+	return 0;
+}
+
+static void *vcc_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct fbxatm_dev *adev;
+	struct fbxatm_vcc *tvcc, *vcc;
+	int count;
+
+	mutex_lock(&fbxatm_mutex);
+
+	if (!*pos)
+		return SEQ_START_TOKEN;
+
+	count = 1;
+	tvcc = NULL;
+	list_for_each_entry(adev, &fbxatm_dev_list, next) {
+		list_for_each_entry(vcc, &adev->vcc_list, next) {
+			if (count == *pos) {
+				tvcc = vcc;
+				break;
+			}
+			count++;
+		}
+	}
+
+	return tvcc;
+}
+
+static void *vcc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct fbxatm_dev *adev;
+	struct fbxatm_vcc *last_vcc, *vcc, *tvcc;
+
+	if (v == (void *)SEQ_START_TOKEN) {
+		if (list_empty(&fbxatm_dev_list))
+			return NULL;
+		adev = list_entry(fbxatm_dev_list.next, struct fbxatm_dev,
+				  next);
+		last_vcc = NULL;
+	} else {
+		last_vcc = (struct fbxatm_vcc *)v;
+		adev = last_vcc->adev;
+	}
+
+	tvcc = NULL;
+	list_for_each_entry_continue(adev, &fbxatm_dev_list, next) {
+
+		if (last_vcc && last_vcc->adev == adev) {
+			vcc = last_vcc;
+			list_for_each_entry_continue(vcc, &adev->vcc_list,
+						     next) {
+				tvcc = vcc;
+				break;
+			}
+		} else {
+			list_for_each_entry(vcc, &adev->vcc_list, next) {
+				tvcc = vcc;
+				break;
+			}
+		}
+	}
+
+	if (tvcc)
+		(*pos)++;
+	return tvcc;
+}
+
+static void vcc_seq_stop(struct seq_file *seq, void *v)
+{
+	mutex_unlock(&fbxatm_mutex);
+}
+
+static const struct seq_operations vcc_seq_ops = {
+	.start		= vcc_seq_start,
+	.next		= vcc_seq_next,
+	.stop		= vcc_seq_stop,
+	.show		= vcc_seq_show,
+};
+
+static int vcc_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &vcc_seq_ops);
+}
+
+static const struct file_operations vcc_seq_fops = {
+	.open		= vcc_seq_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+/*
+ * /proc/net/atm/dev
+ */
+static int adev_seq_show(struct seq_file *seq, void *v)
+{
+	struct fbxatm_dev *adev;
+
+	if (v == (void *)SEQ_START_TOKEN) {
+		seq_printf(seq, "%s",
+			   "Itf  RX TX  RXAAL5 TXAAL5  RXF4OAM TXF4OAM  "
+			   "RXF5OAM TXF5OAM  RXBADOAM RXBADLLIDOAM "
+			   "RXOTHEROAM RXDROPPED TXDROPNOLINK\n");
+		return 0;
+	}
+
+	adev = (struct fbxatm_dev *)v;
+	seq_printf(seq, "%d  %lu %lu  %lu %lu  ",
+		   adev->ifindex,
+		   adev->stats.rx_bytes,
+		   adev->stats.tx_bytes,
+		   adev->stats.rx_aal5,
+		   adev->stats.tx_aal5);
+
+	seq_printf(seq, "%lu %lu  %lu %lu  %lu %lu %lu %lu %lu\n",
+		   adev->stats.rx_f4_oam,
+		   adev->stats.tx_f4_oam,
+
+		   adev->stats.rx_f5_oam,
+		   adev->stats.tx_f5_oam,
+
+		   adev->stats.rx_bad_oam,
+		   adev->stats.rx_bad_llid_oam,
+		   adev->stats.rx_other_oam,
+		   adev->stats.rx_dropped,
+		   adev->stats.tx_drop_nolink);
+	return 0;
+}
+
+static void *adev_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct fbxatm_dev *adev, *tadev;
+	int count;
+
+	mutex_lock(&fbxatm_mutex);
+
+	if (!*pos)
+		return SEQ_START_TOKEN;
+
+	count = 1;
+	tadev = NULL;
+	list_for_each_entry(adev, &fbxatm_dev_list, next) {
+		if (count == *pos) {
+			tadev = adev;
+			break;
+		}
+		count++;
+	}
+
+	return tadev;
+}
+
+static void *adev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct fbxatm_dev *adev, *tadev;
+
+	if (v == (void *)SEQ_START_TOKEN) {
+		if (list_empty(&fbxatm_dev_list))
+			return NULL;
+		adev = list_entry(fbxatm_dev_list.next, struct fbxatm_dev,
+				  next);
+	} else
+		adev = (struct fbxatm_dev *)v;
+
+	tadev = NULL;
+	list_for_each_entry_continue(adev, &fbxatm_dev_list, next) {
+		tadev = adev;
+		break;
+	}
+
+	if (tadev)
+		(*pos)++;
+	return tadev;
+}
+
+static void adev_seq_stop(struct seq_file *seq, void *v)
+{
+	mutex_unlock(&fbxatm_mutex);
+}
+
+static const struct seq_operations adev_seq_ops = {
+	.start		= adev_seq_start,
+	.next		= adev_seq_next,
+	.stop		= adev_seq_stop,
+	.show		= adev_seq_show,
+};
+
+static int adev_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &adev_seq_ops);
+}
+
+static const struct file_operations adev_seq_fops = {
+	.open		= adev_seq_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+
+/*
+ * create device private entry in proc
+ */
+int fbxatm_proc_dev_register(struct fbxatm_dev *adev)
+{
+	adev->dev_proc_entry = proc_mkdir(adev->name, fbxatm_proc_root);
+	if (!adev->dev_proc_entry)
+		return 1;
+	return 0;
+}
+
+
+void fbxatm_proc_dev_deregister(struct fbxatm_dev *adev)
+{
+	remove_proc_entry(adev->name, fbxatm_proc_root);
+}
+
+/*
+ * create misc private entry in proc
+ */
+struct proc_dir_entry *fbxatm_proc_misc_register(const char *path)
+{
+	return proc_mkdir(path, fbxatm_proc_root);
+}
+
+void fbxatm_proc_misc_deregister(const char *path)
+{
+	remove_proc_entry(path, fbxatm_proc_root);
+}
+
+/*
+ * list of proc entries for fbxatm
+ */
+static struct fbxatm_proc_entry {
+	char *name;
+	const struct file_operations *proc_fops;
+	struct proc_dir_entry *dirent;
+
+} fbxatm_proc_entries[] = {
+	{
+		.name = "dev",
+		.proc_fops = &adev_seq_fops,
+	},
+	{
+		.name = "vcc",
+		.proc_fops = &vcc_seq_fops,
+	},
+};
+
+static void fbxatm_remove_proc(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(fbxatm_proc_entries); i++) {
+		struct fbxatm_proc_entry *e;
+
+		e = &fbxatm_proc_entries[i];
+
+		if (!e->dirent)
+			continue;
+		remove_proc_entry(e->name, fbxatm_proc_root);
+		e->dirent = NULL;
+	}
+
+	remove_proc_entry("fbxatm", init_net.proc_net);
+}
+
+int __init fbxatm_procfs_init(void)
+{
+	unsigned int i;
+	int ret;
+
+	fbxatm_proc_root = proc_net_mkdir(&init_net, "fbxatm",
+					  init_net.proc_net);
+	if (!fbxatm_proc_root) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(fbxatm_proc_entries); i++) {
+		struct proc_dir_entry *dirent;
+		struct fbxatm_proc_entry *e;
+
+		e = &fbxatm_proc_entries[i];
+
+		dirent = proc_create_data(e->name, S_IRUGO, fbxatm_proc_root,
+					  e->proc_fops, NULL);
+		if (!dirent) {
+			ret = -ENOMEM;
+			goto err;
+		}
+		e->dirent = dirent;
+	}
+
+	return 0;
+
+err:
+	if (fbxatm_proc_root)
+		fbxatm_remove_proc();
+	return ret;
+}
+
+void fbxatm_procfs_exit(void)
+{
+	fbxatm_remove_proc();
+}
diff -Nruw linux-3.11.10-fbx/net/fbxatm./fbxatm_remote.c linux-3.11.10-fbx/net/fbxatm/fbxatm_remote.c
--- linux-3.11.10-fbx/net/fbxatm./fbxatm_remote.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxatm/fbxatm_remote.c	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,810 @@
+#include <linux/module.h>
+#include <linux/fbxatm.h>
+#include <linux/fbxatm_dev.h>
+#include <linux/fbxatm_remote.h>
+#include <asm/unaligned.h>
+
+#define PFX		"fbxatm_remote: "
+#define MAX_PORTS	64
+#define MAX_RETRANS	10
+#define RETRANS_TIMER	(HZ / 3)
+
+struct fbxatm_remote_ctx;
+
+struct fbxatm_remote_sock {
+	spinlock_t			lock;
+	struct fbxatm_remote_sockaddr	addr;
+
+	struct timer_list		retrans_timer;
+	unsigned int			retrans_count;
+
+	struct sk_buff			*pending;
+
+	/* 1 for output */
+	int				direction;
+
+	/* wait ack for output, send ack for input */
+	int				has_ack;
+
+	u8				seq;
+
+	struct fbxatm_remote_ctx	*ctx;
+	struct list_head		next;
+};
+
+struct fbxatm_remote_ctx {
+	spinlock_t			lock;
+	int				dead;
+
+	struct net_device		*netdev;
+	u8				remote_mac[ETH_ALEN];
+	u32				session_id;
+
+	struct fbxatm_remote_sock	*socks_per_lport[MAX_PORTS];
+	struct sk_buff			*pending_zero_ack;
+
+	void				(*timeout_cb)(void *priv);
+	void				*priv;
+
+	struct list_head		next;
+};
+
+static struct list_head ctx_list;
+static DEFINE_SPINLOCK(ctx_list_lock);
+static void (*unknown_cb)(struct net_device *,
+			  struct sk_buff *);
+
+unsigned int fbxatm_remote_headroom(struct fbxatm_remote_ctx *ctx)
+{
+	return sizeof (struct fbxatm_remote_hdr) +
+		ctx->netdev->hard_header_len + ctx->netdev->needed_headroom;
+}
+
+EXPORT_SYMBOL(fbxatm_remote_headroom);
+
+/*
+ * allocate skb with enough headroom for header
+ */
+struct sk_buff *fbxatm_remote_alloc_skb(struct fbxatm_remote_ctx *ctx,
+					unsigned int size)
+{
+	struct sk_buff *skb;
+	unsigned int hroom_size;
+
+	hroom_size = fbxatm_remote_headroom(ctx);
+	skb = dev_alloc_skb(hroom_size + size);
+	if (!skb)
+		return NULL;
+	skb_reserve(skb, hroom_size);
+	return skb;
+}
+
+EXPORT_SYMBOL(fbxatm_remote_alloc_skb);
+
+/*
+ * return sock addr
+ */
+void fbxatm_remote_sock_getaddr(struct fbxatm_remote_sock *sock,
+				struct fbxatm_remote_sockaddr *addr)
+{
+	memcpy(addr, &sock->addr, sizeof (*addr));
+}
+
+EXPORT_SYMBOL(fbxatm_remote_sock_getaddr);
+
+/*
+ * socket retrans timer callback
+ */
+static void sock_timer(unsigned long data)
+{
+	struct fbxatm_remote_sock *sock;
+	struct sk_buff *skb;
+
+	sock = (struct fbxatm_remote_sock *)data;
+
+	spin_lock_bh(&sock->ctx->lock);
+	spin_lock(&sock->lock);
+
+	if (!sock->addr.infinite_retry && sock->retrans_count >= MAX_RETRANS) {
+		printk(KERN_ERR PFX "retrans max reached\n");
+		sock->ctx->dead = 1;
+		dev_kfree_skb(sock->pending);
+		sock->pending = NULL;
+		if (sock->ctx->timeout_cb)
+			sock->ctx->timeout_cb(sock->ctx->priv);
+		spin_unlock(&sock->lock);
+		spin_unlock_bh(&sock->ctx->lock);
+		return;
+	}
+
+	sock->retrans_count++;
+	sock->retrans_timer.expires = jiffies + RETRANS_TIMER;
+
+	skb = skb_clone(sock->pending, GFP_ATOMIC);
+	if (skb)
+		dev_queue_xmit(skb);
+	add_timer(&sock->retrans_timer);
+
+	spin_unlock(&sock->lock);
+	spin_unlock_bh(&sock->ctx->lock);
+}
+
+/*
+ * append header for given socket
+ */
+static int append_tx_header(struct fbxatm_remote_sock *sock,
+			    struct sk_buff *skb)
+{
+	struct fbxatm_remote_hdr *hdr;
+	unsigned int needed;
+
+	needed = skb->dev->hard_header_len + skb->dev->needed_headroom +
+		sizeof (*hdr);
+
+	if (unlikely(skb_headroom(skb) < needed)) {
+		if (net_ratelimit())
+			printk(KERN_WARNING PFX "headroom too small %d < %d\n",
+			       skb_headroom(skb), needed);
+	}
+
+	if (skb_cow_head(skb, needed))
+		return 1;
+
+	hdr = (struct fbxatm_remote_hdr *)skb_push(skb, sizeof (*hdr));
+	skb_set_network_header(skb, 0);
+
+	put_unaligned(htonl(FBXATM_REMOTE_MAGIC), &hdr->magic);
+	if (sock->direction == 1) {
+		/* output */
+		hdr->flags = 0;
+	} else {
+		/* input */
+		hdr->flags = FBXATM_RFLAGS_ACK;
+	}
+	hdr->seq = sock->seq;
+	put_unaligned(htons(skb->len), &hdr->len);
+	put_unaligned(sock->addr.lport, &hdr->sport);
+	put_unaligned(sock->addr.dport, &hdr->dport);
+
+	put_unaligned(sock->addr.mtype, &hdr->mtype);
+	put_unaligned(sock->ctx->session_id, &hdr->session_id);
+
+	skb->protocol = htons(ETH_P_FBXATM_REMOTE);
+	if (dev_hard_header(skb, skb->dev, ETH_P_FBXATM_REMOTE,
+			    sock->ctx->remote_mac, NULL, skb->len) < 0)
+		return 1;
+
+	return 0;
+}
+
+/*
+ * purge socket send queue, advance next sequence
+ */
+void fbxatm_remote_sock_purge(struct fbxatm_remote_sock *sock)
+{
+	spin_lock_bh(&sock->lock);
+	if (sock->pending) {
+		del_timer_sync(&sock->retrans_timer);
+		dev_kfree_skb(sock->pending);
+		sock->pending = NULL;
+		sock->seq++;
+	}
+	spin_unlock_bh(&sock->lock);
+}
+
+EXPORT_SYMBOL(fbxatm_remote_sock_purge);
+
+/*
+ * check if tx is pending on socket
+ */
+int fbxatm_remote_sock_pending(struct fbxatm_remote_sock *sock)
+{
+	int ret;
+
+	spin_lock_bh(&sock->lock);
+	ret = sock->pending ? 1 : 0;
+	spin_unlock_bh(&sock->lock);
+	return ret;
+}
+
+EXPORT_SYMBOL(fbxatm_remote_sock_pending);
+
+/*
+ * send skb on socket
+ */
+int fbxatm_remote_sock_send(struct fbxatm_remote_sock *sock,
+			    struct sk_buff *skb)
+{
+	BUG_ON(sock->direction == 0);
+
+	spin_lock_bh(&sock->lock);
+	skb->dev = sock->ctx->netdev;
+
+	if (append_tx_header(sock, skb)) {
+		spin_unlock_bh(&sock->lock);
+		dev_kfree_skb(skb);
+		return 1;
+	}
+
+	if (unlikely(sock->ctx->dead)) {
+		spin_unlock_bh(&sock->lock);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	/* start retrans timer if needed */
+	if (sock->has_ack) {
+		if (sock->pending) {
+			printk(KERN_ERR PFX "sock already has tx pending\n");
+			spin_unlock_bh(&sock->lock);
+			dev_kfree_skb(skb);
+			return 1;
+		}
+
+		sock->pending = skb_clone(skb, GFP_ATOMIC);
+		if (sock->pending) {
+			sock->retrans_count = 0;
+			sock->retrans_timer.expires = jiffies + RETRANS_TIMER;
+			add_timer(&sock->retrans_timer);
+		}
+	}
+
+	spin_unlock_bh(&sock->lock);
+	dev_queue_xmit(skb);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(fbxatm_remote_sock_send);
+
+/*
+ * send ack skb on socket
+ */
+int fbxatm_remote_sock_send_ack(struct fbxatm_remote_sock *sock,
+				struct sk_buff *skb)
+{
+	BUG_ON(sock->direction == 1);
+
+	spin_lock_bh(&sock->lock);
+
+	skb->dev = sock->ctx->netdev;
+
+	if (append_tx_header(sock, skb)) {
+		spin_unlock_bh(&sock->lock);
+		dev_kfree_skb(skb);
+		return 1;
+	}
+
+	if (unlikely(sock->ctx->dead)) {
+		spin_unlock_bh(&sock->lock);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	skb->dev = sock->ctx->netdev;
+	sock->pending = skb_clone(skb, GFP_ATOMIC);
+	spin_unlock_bh(&sock->lock);
+	dev_queue_xmit(skb);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(fbxatm_remote_sock_send_ack);
+
+/*
+ * send raw ack
+ */
+int fbxatm_remote_sock_send_raw_ack(struct fbxatm_remote_ctx *ctx,
+				    struct net_device *dev,
+				    u8 *remote_mac,
+				    struct fbxatm_remote_hdr *hdr,
+				    struct sk_buff *ack)
+{
+	struct fbxatm_remote_hdr *ack_hdr;
+
+	if (skb_cow_head(ack, sizeof (*ack_hdr))) {
+		dev_kfree_skb(ack);
+		return 1;
+	}
+
+	ack_hdr = (struct fbxatm_remote_hdr *)skb_push(ack, sizeof (*hdr));
+	skb_set_network_header(ack, 0);
+
+	put_unaligned(htonl(FBXATM_REMOTE_MAGIC), &ack_hdr->magic);
+	ack_hdr->flags = FBXATM_RFLAGS_ACK;
+	ack_hdr->seq = hdr->seq;
+
+	put_unaligned(htons(ack->len), &ack_hdr->len);
+	put_unaligned(hdr->dport, &ack_hdr->sport);
+	put_unaligned(hdr->sport, &ack_hdr->dport);
+	put_unaligned(hdr->mtype, &ack_hdr->mtype);
+	put_unaligned(hdr->session_id, &ack_hdr->session_id);
+
+	ack->dev = dev;
+
+	if (dev_hard_header(ack, dev, ETH_P_FBXATM_REMOTE,
+			    remote_mac, NULL, ack->len) < 0) {
+		dev_kfree_skb(ack);
+		return 1;
+	}
+
+	if (hdr->dport == 0) {
+		kfree(ctx->pending_zero_ack);
+		ctx->pending_zero_ack = skb_clone(ack, GFP_ATOMIC);
+	}
+
+	if (dev_queue_xmit(ack))
+		return 1;
+
+	return 0;
+}
+
+EXPORT_SYMBOL(fbxatm_remote_sock_send_raw_ack);
+
+/*
+ * handle input data on 'in' direction socket
+ */
+static void __in_sock_rcv(struct fbxatm_remote_sock *sock,
+			  struct sk_buff *skb,
+			  struct fbxatm_remote_hdr *hdr)
+{
+	struct sk_buff *ack;
+	int ret;
+
+	spin_lock(&sock->lock);
+
+	if (sock->has_ack) {
+		u8 expected_seq;
+
+		/* check for duplicate seq  */
+		if (hdr->seq == sock->seq) {
+
+			/* got last packet again, ack has been
+			 * lost, send it again if we have it */
+			if (sock->pending) {
+				ack = skb_clone(sock->pending, GFP_ATOMIC);
+				if (ack)
+					dev_queue_xmit(ack);
+			}
+
+			spin_unlock(&sock->lock);
+			dev_kfree_skb(skb);
+			return;
+		}
+
+		expected_seq = sock->seq + 1;
+		if (hdr->seq != expected_seq) {
+			/* lost sync */
+			spin_unlock(&sock->lock);
+			dev_kfree_skb(skb);
+			return;
+		}
+
+		/* about to accept new packet, free any pending ack */
+		dev_kfree_skb(sock->pending);
+		sock->pending = NULL;
+
+		sock->seq = hdr->seq;
+
+		/* set sock dport to last receive packet to send
+		 * correct ack */
+		sock->addr.dport = hdr->sport;
+	}
+
+	/* deliver packet to socket */
+	ret = sock->addr.deliver(sock->addr.priv, skb, &ack);
+
+	if (!sock->has_ack || !ret) {
+		/* don't send ack now */
+		spin_unlock(&sock->lock);
+		return;
+	}
+
+	if (!ack) {
+		/* generate empty ack */
+		ack = fbxatm_remote_alloc_skb(sock->ctx, 0);
+		if (!ack) {
+			spin_unlock(&sock->lock);
+			return;
+		}
+	}
+
+	ack->dev = sock->ctx->netdev;
+
+	if (append_tx_header(sock, ack)) {
+		spin_unlock(&sock->lock);
+		dev_kfree_skb(ack);
+		return;
+	}
+
+	sock->pending = ack;
+
+	/* send ack now */
+	ack = skb_clone(sock->pending, GFP_ATOMIC);
+	spin_unlock(&sock->lock);
+
+	if (ack)
+		dev_queue_xmit(ack);
+}
+
+/*
+ * handle data on 'out' direction socket
+ */
+static void __out_sock_rcv(struct fbxatm_remote_sock *sock,
+			   struct sk_buff *skb,
+			   struct fbxatm_remote_hdr *hdr)
+{
+	if (!sock->has_ack) {
+		dev_kfree_skb(skb);
+		printk(KERN_ERR PFX "ack for non ack sock\n");
+		return;
+	}
+
+	spin_lock(&sock->lock);
+
+	/* check if ack if for last sent seq */
+	if (hdr->seq != sock->seq) {
+		spin_unlock(&sock->lock);
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	/* make sure we're expecting it */
+	if (!sock->pending) {
+		spin_unlock(&sock->lock);
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	del_timer_sync(&sock->retrans_timer);
+	dev_kfree_skb(sock->pending);
+	sock->pending = NULL;
+	sock->seq++;
+
+	if (sock->addr.response)
+		sock->addr.response(sock->addr.priv, skb);
+	else
+		dev_kfree_skb(skb);
+
+	spin_unlock(&sock->lock);
+}
+
+/*
+ * fbxatm ethertype rx callback
+ */
+static int fbxatm_rcv(struct sk_buff *skb, struct net_device *dev,
+		      struct packet_type *pt, struct net_device *orig_dev)
+{
+	struct fbxatm_remote_hdr *hdr;
+	struct fbxatm_remote_ctx *ctx;
+	int found;
+	unsigned int len;
+	u16 port;
+
+	if (!netif_running(dev)) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	skb = skb_unshare(skb, GFP_ATOMIC);
+	if (!skb)
+		return 0;
+
+	/* decode fbxatm ethertype */
+	if (!pskb_may_pull(skb, sizeof (*hdr))) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	hdr = (struct fbxatm_remote_hdr *)skb_network_header(skb);
+	if (ntohl(hdr->magic) != FBXATM_REMOTE_MAGIC) {
+		if (net_ratelimit())
+			printk(KERN_ERR PFX "bad fbxatm remote magic: %08x\n",
+			       ntohl(hdr->magic));
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	/* check len */
+	len = ntohs(hdr->len);
+	if (skb->len < len) {
+		if (net_ratelimit())
+			printk(KERN_ERR PFX "short packet\n");
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	/* trim skb to correct size */
+	if (pskb_trim(skb, len)) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	port = ntohs(hdr->dport);
+	if (port >= MAX_PORTS) {
+		dev_kfree_skb(skb);
+		printk(KERN_ERR PFX "bad port %u\n", port);
+		return 0;
+	}
+
+	/* remove header */
+	skb_set_network_header(skb, 0);
+	__skb_pull(skb, sizeof (*hdr));
+	skb_set_transport_header(skb, 0);
+
+	/* find context by mac/session id */
+	found = 0;
+	spin_lock_bh(&ctx_list_lock);
+	list_for_each_entry(ctx, &ctx_list, next) {
+		struct ethhdr *eth;
+		struct fbxatm_remote_sock *sock;
+		int is_ack;
+
+		eth = eth_hdr(skb);
+		if (memcmp(eth->h_source, ctx->remote_mac, ETH_ALEN))
+			continue;
+
+		if (hdr->session_id != ctx->session_id)
+			continue;
+
+		spin_lock(&ctx->lock);
+
+		if (unlikely(ctx->dead)) {
+			spin_unlock(&ctx->lock);
+			continue;
+		}
+
+		/* found context, find socket by port */
+		found = 1;
+
+		/* special case for port 0, in case ack is lost */
+		if (port == 0 && ctx->pending_zero_ack) {
+			struct sk_buff *ack;
+			ack = skb_clone(ctx->pending_zero_ack, GFP_ATOMIC);
+			if (ack)
+				dev_queue_xmit(ack);
+			spin_unlock(&ctx->lock);
+			break;
+		}
+
+		sock = ctx->socks_per_lport[port];
+		if (!sock) {
+			printk(KERN_ERR PFX "context but no socket for "
+			       "port: %u\n", port);
+			spin_unlock(&ctx->lock);
+			break;
+		}
+
+		if (hdr->mtype != sock->addr.mtype) {
+			printk(KERN_ERR PFX "incorrect mtype for sock\n");
+			spin_unlock(&ctx->lock);
+			break;
+		}
+
+		/* check direction, we should only get ack for output
+		 * socket */
+		is_ack = (hdr->flags & FBXATM_RFLAGS_ACK) ? 1 : 0;
+		if (sock->direction ^ is_ack) {
+			printk(KERN_ERR PFX "incorrect ack value for sock\n");
+			spin_unlock(&ctx->lock);
+			break;
+		}
+
+		/* ok deliver */
+		if (sock->direction)
+			__out_sock_rcv(sock, skb, hdr);
+		else
+			__in_sock_rcv(sock, skb, hdr);
+
+		spin_unlock(&ctx->lock);
+		spin_unlock_bh(&ctx_list_lock);
+		return 0;
+	}
+
+	spin_unlock_bh(&ctx_list_lock);
+
+	if (!found && unknown_cb)
+		unknown_cb(dev, skb);
+	else
+		dev_kfree_skb(skb);
+
+	return 0;
+}
+
+void fbxatm_remote_set_unknown_cb(void (*cb)(struct net_device *,
+					     struct sk_buff *))
+{
+	unknown_cb = cb;
+}
+
+EXPORT_SYMBOL(fbxatm_remote_set_unknown_cb);
+
+/*
+ * allocate local port for socket
+ */
+static int __alloc_lport(struct fbxatm_remote_ctx *ctx,
+			 struct fbxatm_remote_sock *sock)
+{
+	int i;
+
+	for (i = 1; i < ARRAY_SIZE(ctx->socks_per_lport); i++) {
+		if (ctx->socks_per_lport[i])
+			continue;
+		sock->addr.lport = htons(i);
+		ctx->socks_per_lport[i] = sock;
+		return 0;
+	}
+	return -EADDRINUSE;
+}
+
+static struct fbxatm_remote_sock *sock_new(struct fbxatm_remote_sockaddr *addr)
+{
+	struct fbxatm_remote_sock *sock;
+
+	sock = kzalloc(sizeof (*sock), GFP_KERNEL);
+	if (!sock)
+		return NULL;
+	memcpy(&sock->addr, addr, sizeof (*addr));
+	init_timer(&sock->retrans_timer);
+	spin_lock_init(&sock->lock);
+	sock->retrans_timer.data = (unsigned long)sock;
+	sock->retrans_timer.function = sock_timer;
+	return sock;
+}
+
+struct fbxatm_remote_sock *
+fbxatm_remote_sock_connect(struct fbxatm_remote_ctx *ctx,
+			   struct fbxatm_remote_sockaddr *addr,
+			   int need_ack)
+{
+	struct fbxatm_remote_sock *sock;
+
+	sock = sock_new(addr);
+	if (!sock)
+		return NULL;
+
+	spin_lock_bh(&ctx->lock);
+	sock->ctx = ctx;
+	if (__alloc_lport(ctx, sock)) {
+		spin_unlock_bh(&ctx->lock);
+		kfree(sock);
+		return NULL;
+	}
+
+	sock->direction = 1;
+	sock->seq = 0;
+	sock->has_ack = need_ack;
+	spin_unlock_bh(&ctx->lock);
+
+	return sock;
+}
+
+EXPORT_SYMBOL(fbxatm_remote_sock_connect);
+
+struct fbxatm_remote_sock *
+fbxatm_remote_sock_bind(struct fbxatm_remote_ctx *ctx,
+			struct fbxatm_remote_sockaddr *addr,
+			int send_ack)
+{
+	struct fbxatm_remote_sock *sock;
+
+	sock = sock_new(addr);
+	if (!sock)
+		return NULL;
+
+	spin_lock_bh(&ctx->lock);
+	sock->ctx = ctx;
+	if (__alloc_lport(ctx, sock)) {
+		spin_unlock_bh(&ctx->lock);
+		kfree(sock);
+		return NULL;
+	}
+
+	sock->direction = 0;
+	sock->seq = ~0;
+	sock->has_ack = send_ack;
+	spin_unlock_bh(&ctx->lock);
+
+	return sock;
+}
+
+EXPORT_SYMBOL(fbxatm_remote_sock_bind);
+
+void fbxatm_remote_sock_close(struct fbxatm_remote_sock *sock)
+{
+	spin_lock_bh(&sock->ctx->lock);
+	if (sock->addr.lport)
+		sock->ctx->socks_per_lport[ntohs(sock->addr.lport)] = NULL;
+
+	spin_lock(&sock->lock);
+	del_timer_sync(&sock->retrans_timer);
+	dev_kfree_skb(sock->pending);
+	spin_unlock(&sock->lock);
+	spin_unlock_bh(&sock->ctx->lock);
+	kfree(sock);
+}
+
+EXPORT_SYMBOL(fbxatm_remote_sock_close);
+
+struct fbxatm_remote_ctx *fbxatm_remote_alloc_ctx(struct net_device *netdev,
+						  u8 *remote_mac,
+						  u32 session_id,
+						  void (*timeout)(void *priv),
+						  void *priv)
+{
+	struct fbxatm_remote_ctx *ctx;
+
+	ctx = kzalloc(sizeof (*ctx), GFP_KERNEL);
+	if (!ctx)
+		return NULL;
+	ctx->netdev = netdev;
+	spin_lock_init(&ctx->lock);
+	memcpy(ctx->remote_mac, remote_mac, ETH_ALEN);
+	ctx->session_id = session_id;
+	ctx->timeout_cb = timeout;
+	ctx->priv = priv;
+
+	spin_lock_bh(&ctx_list_lock);
+	list_add_tail(&ctx->next, &ctx_list);
+	spin_unlock_bh(&ctx_list_lock);
+
+	return ctx;
+}
+
+EXPORT_SYMBOL(fbxatm_remote_alloc_ctx);
+
+void fbxatm_remote_ctx_set_dead(struct fbxatm_remote_ctx *ctx)
+{
+	spin_lock_bh(&ctx->lock);
+	ctx->dead = 1;
+	spin_unlock_bh(&ctx->lock);
+}
+
+EXPORT_SYMBOL(fbxatm_remote_ctx_set_dead);
+
+void fbxatm_remote_free_ctx(struct fbxatm_remote_ctx *ctx)
+{
+	int i;
+
+	spin_lock_bh(&ctx_list_lock);
+	spin_lock(&ctx->lock);
+
+	for (i = 1; i < ARRAY_SIZE(ctx->socks_per_lport); i++) {
+		if (!ctx->socks_per_lport[i])
+			continue;
+		printk(KERN_ERR PFX "socket count is not 0\n");
+		spin_unlock(&ctx->lock);
+		spin_unlock_bh(&ctx_list_lock);
+		return;
+	}
+
+	kfree(ctx->pending_zero_ack);
+	list_del(&ctx->next);
+	spin_unlock(&ctx->lock);
+	spin_unlock_bh(&ctx_list_lock);
+	kfree(ctx);
+}
+
+EXPORT_SYMBOL(fbxatm_remote_free_ctx);
+
+static struct packet_type fbxatm_packet_type = {
+	.type	= __constant_htons(ETH_P_FBXATM_REMOTE),
+	.func	= fbxatm_rcv,
+};
+
+int fbxatm_remote_init(void)
+{
+	spin_lock_init(&ctx_list_lock);
+	INIT_LIST_HEAD(&ctx_list);
+	dev_add_pack(&fbxatm_packet_type);
+	return 0;
+}
+
+EXPORT_SYMBOL(fbxatm_remote_init);
+
+void fbxatm_remote_exit(void)
+{
+	dev_remove_pack(&fbxatm_packet_type);
+}
+
+EXPORT_SYMBOL(fbxatm_remote_exit);
diff -Nruw linux-3.11.10-fbx/net/fbxatm./fbxatm_remote_driver.c linux-3.11.10-fbx/net/fbxatm/fbxatm_remote_driver.c
--- linux-3.11.10-fbx/net/fbxatm./fbxatm_remote_driver.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxatm/fbxatm_remote_driver.c	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,1123 @@
+/*
+ * "remote" driver for fbxatm, "connect" to a remote fbxatm stack in
+ * net stub mode and control its PHYs
+ *
+ * Copyright (C) 2009 Maxime Bizon <mbizon@freebox.fr>
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+
+#include "fbxatm_remote_driver.h"
+
+#define PFX	"fbxatm_remote_driver: "
+
+static struct list_head remote_dev_list;
+static DEFINE_SPINLOCK(remote_lock);
+
+/*
+ * request moving to dead state and schedule
+ */
+static void set_dying(struct driver_remote *priv)
+{
+	priv->want_die = 1;
+	schedule_delayed_work(&priv->fsm_work, 0);
+}
+
+/*
+ * socket deliver callback for vcc_rx socket
+ */
+static int vcc_rx_deliver(void *data, struct sk_buff *skb,
+			  struct sk_buff **ack)
+{
+	struct driver_remote_vcc *pvcc;
+
+	pvcc = (struct driver_remote_vcc *)data;
+	fbxatm_netifrx(pvcc->vcc, skb);
+	return 0;
+}
+
+/*
+ * socket deliver callback for vcc_qempty socket
+ */
+static int vcc_qempty_deliver(void *data, struct sk_buff *skb,
+			      struct sk_buff **ack)
+{
+	struct driver_remote_vcc *pvcc;
+	struct driver_remote *priv;
+
+	pvcc = (struct driver_remote_vcc *)data;
+	priv = pvcc->priv;
+
+	spin_lock(&priv->tx_lock);
+
+	if (pvcc->tx_pending) {
+		/* wait until we get tx ack */
+		pvcc->tx_got_qempty = 1;
+		goto send_ack;
+	}
+
+	/* report tx done event */
+	clear_bit(FBXATM_VCC_F_FULL, &pvcc->vcc->vcc_flags);
+	fbxatm_tx_done(pvcc->vcc);
+
+send_ack:
+	dev_kfree_skb(skb);
+	*ack = NULL;
+	spin_unlock(&priv->tx_lock);
+	/* send ack now */
+	return 1;
+}
+
+/*
+ * socket response callback for vcc_send socket, called with bh
+ * disabled
+ */
+static void vcc_send_response(void *data, struct sk_buff *skb)
+{
+	struct fbxatm_remote_vcc_send_ack *pkt_ack;
+	struct driver_remote_vcc *pvcc;
+	struct driver_remote *priv;
+
+	pvcc = (struct driver_remote_vcc *)data;
+	priv = pvcc->priv;
+
+	spin_lock(&priv->tx_lock);
+
+	if (unlikely(!pvcc->tx_pending)) {
+		printk(KERN_ERR PFX "send response while no tx pending\n");
+		goto out;
+	}
+
+	if (!pskb_may_pull(skb, sizeof (*pkt_ack))) {
+		printk(KERN_ERR PFX "bad vcc send ack\n");
+		goto out;
+	}
+
+	pvcc->tx_pending = 0;
+
+	pkt_ack = (struct fbxatm_remote_vcc_send_ack *)skb->data;
+	if (pkt_ack->full) {
+		/* qempty will wake us up later, but maybe we got it
+		 * already ? */
+		if (!pvcc->tx_got_qempty)
+			goto out;
+	}
+
+	/* wake up queue */
+	clear_bit(FBXATM_VCC_F_FULL, &pvcc->vcc->vcc_flags);
+	fbxatm_tx_done(pvcc->vcc);
+
+out:
+	spin_unlock(&priv->tx_lock);
+	dev_kfree_skb(skb);
+}
+
+/*
+ * fbxatm request to send aal5 on given vcc, called with bh disabled
+ */
+static int remote_send(struct fbxatm_vcc *vcc, struct sk_buff *skb)
+{
+	struct driver_remote_vcc *pvcc;
+	struct driver_remote *priv;
+
+	pvcc = vcc->dev_priv;
+	priv = pvcc->priv;
+
+	spin_lock(&priv->tx_lock);
+	if (priv->state != RSTATE_S_ACTIVE)
+		goto drop;
+
+	if (test_bit(FBXATM_VCC_F_FULL, &vcc->vcc_flags)) {
+		spin_unlock(&priv->tx_lock);
+		/* return queue full */
+		return 1;
+	}
+
+	if (fbxatm_remote_sock_send(pvcc->vcc_send_sock, skb)) {
+		/* packet has been dropped */
+		spin_unlock(&priv->tx_lock);
+		return 0;
+	}
+
+	set_bit(FBXATM_VCC_F_FULL, &vcc->vcc_flags);
+	pvcc->tx_pending = 1;
+	pvcc->tx_got_qempty = 0;
+
+	spin_unlock(&priv->tx_lock);
+	return 0;
+
+drop:
+	spin_unlock(&priv->tx_lock);
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+/*
+ * sleep until we get an ack for sockets using wq
+ */
+static int __wait_ack(struct driver_remote *priv)
+{
+	wait_event(priv->wq, priv->wq_res != 0 ||
+		   priv->state != RSTATE_S_ACTIVE);
+
+	if (priv->wq_res == 2 || priv->state != RSTATE_S_ACTIVE) {
+		/* timeout or device will die soon */
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * fbxatm callback to send oam cell
+ */
+static int remote_send_oam(struct fbxatm_dev *adev,
+			   struct fbxatm_oam_cell *cell)
+{
+	struct driver_remote *priv;
+	struct sk_buff *skb;
+	int ret;
+
+	priv = fbxatm_dev_priv(adev);
+	mutex_lock(&priv->mutex);
+
+	if (priv->state != RSTATE_S_ACTIVE) {
+		mutex_unlock(&priv->mutex);
+		return -ENODEV;
+	}
+
+	/* prepare outgoing packet */
+	skb = fbxatm_remote_alloc_skb(priv->remote_ctx,
+				      sizeof (cell->payload));
+	if (!skb) {
+		mutex_unlock(&priv->mutex);
+		kfree(cell);
+		return -ENOMEM;
+	}
+
+	memcpy(skb_put(skb, sizeof (cell->payload)), &cell->payload,
+	       sizeof (cell->payload));
+	kfree(cell);
+
+	/* send & wait for ack */
+	skb_queue_purge(&priv->wq_acks);
+	priv->wq_res = 0;
+
+	if (fbxatm_remote_sock_send(priv->dev_send_oam_sock, skb))
+		return -ENOMEM;
+
+	ret = __wait_ack(priv);
+	mutex_unlock(&priv->mutex);
+
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * fbxatm callback to open given vcc
+ */
+static int remote_vcc_open(struct fbxatm_vcc *vcc)
+{
+	struct fbxatm_remote_vcc_action *pkt;
+	struct fbxatm_remote_vcc_action_ack *pkt_ack;
+	struct sk_buff *skb, *ack;
+	struct fbxatm_remote_sockaddr addr;
+	struct driver_remote_vcc *pvcc;
+	struct driver_remote *priv;
+	int ret;
+
+	priv = fbxatm_dev_priv(vcc->adev);
+	mutex_lock(&priv->mutex);
+
+	if (priv->state != RSTATE_S_ACTIVE) {
+		mutex_unlock(&priv->mutex);
+		return -EIO;
+	}
+
+	/* allocate private vcc context */
+	pvcc = kzalloc(sizeof (*pvcc), GFP_KERNEL);
+	if (!pvcc) {
+		mutex_unlock(&priv->mutex);
+		return -ENOMEM;
+	}
+
+	pvcc->priv = priv;
+	pvcc->vcc = vcc;
+	vcc->dev_priv = pvcc;
+
+	/* open input sockets */
+	memset(&addr, 0, sizeof (addr));
+	addr.mtype = htonl(FBXATM_RMT_VCC_RX);
+	addr.priv = pvcc;
+	addr.deliver = vcc_rx_deliver;
+	pvcc->vcc_rx_sock = fbxatm_remote_sock_bind(priv->remote_ctx,
+						    &addr, 0);
+	if (!pvcc->vcc_rx_sock) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	memset(&addr, 0, sizeof (addr));
+	addr.mtype = htonl(FBXATM_RMT_VCC_QEMPTY);
+	addr.priv = pvcc;
+	addr.deliver = vcc_qempty_deliver;
+	pvcc->vcc_qempty_sock = fbxatm_remote_sock_bind(priv->remote_ctx,
+							&addr, 1);
+	if (!pvcc->vcc_qempty_sock) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	/* prepare outgoing packet */
+	skb = fbxatm_remote_alloc_skb(priv->remote_ctx,
+				      sizeof (*pkt));
+	if (!skb) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	pkt = (struct fbxatm_remote_vcc_action *)skb_put(skb, sizeof (*pkt));
+	pkt->action = htonl(1);
+
+	fbxatm_remote_sock_getaddr(pvcc->vcc_rx_sock, &addr);
+	pkt->vcc_rx_port = addr.lport;
+
+	fbxatm_remote_sock_getaddr(pvcc->vcc_qempty_sock, &addr);
+	pkt->vcc_qempty_port = addr.lport;
+
+	pkt->vpi = htonl(vcc->vpi);
+	pkt->vci = htonl(vcc->vci);
+	pkt->traffic_class = htonl(vcc->qos.traffic_class);
+	pkt->max_sdu = htonl(vcc->qos.max_sdu);
+	pkt->max_buffered_pkt = htonl(vcc->qos.max_buffered_pkt);
+	pkt->priority = htonl(vcc->qos.priority);
+	pkt->rx_priority = htonl(vcc->qos.rx_priority);
+
+	/* send & wait for ack */
+	skb_queue_purge(&priv->wq_acks);
+	priv->wq_res = 0;
+
+	if (fbxatm_remote_sock_send(priv->vcc_action_sock, skb)) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	ret = __wait_ack(priv);
+
+	if (ret) {
+		/* timeout, device will die soon */
+		goto fail;
+	}
+
+	/* check ack */
+	ack = skb_dequeue(&priv->wq_acks);
+	if (!pskb_may_pull(ack, sizeof (*pkt_ack))) {
+		printk(KERN_ERR PFX "bad vcc action ack\n");
+		dev_kfree_skb(ack);
+		set_dying(priv);
+		ret = -EIO;
+		goto fail;
+	}
+
+	pkt_ack = (struct fbxatm_remote_vcc_action_ack *)ack->data;
+	if (pkt_ack->ret) {
+		printk(KERN_ERR PFX "remote vcc open failed\n");
+		dev_kfree_skb(ack);
+		set_dying(priv);
+		ret = -EIO;
+		goto fail;
+	}
+
+	pvcc->remote_id = pkt_ack->vcc_remote_id;
+
+	memset(&addr, 0, sizeof (addr));
+	addr.mtype = htonl(FBXATM_RMT_VCC_SEND);
+	addr.priv = pvcc;
+	addr.dport = pkt_ack->vcc_send_port;
+	addr.response = vcc_send_response;
+	pvcc->vcc_send_sock = fbxatm_remote_sock_connect(priv->remote_ctx,
+							 &addr, 1);
+	if (!pvcc->vcc_send_sock) {
+		dev_kfree_skb(ack);
+		set_dying(priv);
+		ret = -EIO;
+		goto fail;
+	}
+
+	dev_kfree_skb(ack);
+	list_add(&pvcc->next, &priv->pvcc_list);
+	mutex_unlock(&priv->mutex);
+	return 0;
+
+fail:
+	if (pvcc->vcc_qempty_sock)
+		fbxatm_remote_sock_close(pvcc->vcc_qempty_sock);
+	if (pvcc->vcc_rx_sock)
+		fbxatm_remote_sock_close(pvcc->vcc_rx_sock);
+	kfree(pvcc);
+	mutex_unlock(&priv->mutex);
+	return ret;
+}
+
+/*
+ * fbxatm callback to close & flush given vcc
+ */
+static void remote_vcc_close(struct fbxatm_vcc *vcc)
+{
+	struct driver_remote *priv;
+	struct driver_remote_vcc *pvcc;
+	struct sk_buff *skb, *ack;
+	struct fbxatm_remote_vcc_action *pkt;
+	struct fbxatm_remote_vcc_action_ack *pkt_ack;
+	int ret;
+
+	priv = fbxatm_dev_priv(vcc->adev);
+	mutex_lock(&priv->mutex);
+
+	pvcc = vcc->dev_priv;
+
+	if (priv->state != RSTATE_S_ACTIVE) {
+		/* just close vcc, don't tell remote */
+		list_del(&pvcc->next);
+		kfree(pvcc);
+
+		/* warn fsm so it can restart */
+		schedule_delayed_work(&priv->fsm_work, 0);
+		goto out_unlock;
+	}
+
+	/* prepare outgoing packet */
+	skb = fbxatm_remote_alloc_skb(priv->remote_ctx,
+				      sizeof (*pkt));
+	if (!skb) {
+		set_dying(priv);
+		goto out_unlock;
+	}
+
+	pkt = (struct fbxatm_remote_vcc_action *)skb_put(skb, sizeof (*pkt));
+	pkt->action = htonl(0);
+	pkt->vcc_remote_id = pvcc->remote_id;
+
+	/* free vcc now, failure will trigger dead state */
+	fbxatm_remote_sock_close(pvcc->vcc_send_sock);
+	fbxatm_remote_sock_close(pvcc->vcc_rx_sock);
+	fbxatm_remote_sock_close(pvcc->vcc_qempty_sock);
+	list_del(&pvcc->next);
+	kfree(pvcc);
+
+	/* send & wait for ack */
+	skb_queue_purge(&priv->wq_acks);
+	priv->wq_res = 0;
+
+	if (fbxatm_remote_sock_send(priv->vcc_action_sock, skb)) {
+		set_dying(priv);
+		goto out_unlock;
+	}
+
+	ret = __wait_ack(priv);
+
+	if (ret) {
+		/* timeout, device will die soon */
+		mutex_unlock(&priv->mutex);
+		return;
+	}
+
+	/* check ack */
+	ack = skb_dequeue(&priv->wq_acks);
+	if (!pskb_may_pull(ack, sizeof (*pkt_ack))) {
+		printk(KERN_ERR PFX "bad vcc action close ack\n");
+		dev_kfree_skb(ack);
+		set_dying(priv);
+		goto out_unlock;
+	}
+
+	pkt_ack = (struct fbxatm_remote_vcc_action_ack *)ack->data;
+	if (pkt_ack->ret) {
+		printk(KERN_ERR PFX "remote vcc close failed\n");
+		dev_kfree_skb(ack);
+		set_dying(priv);
+		goto out_unlock;
+	}
+
+out_unlock:
+	mutex_unlock(&priv->mutex);
+}
+
+/*
+ * common response for vcc action & send oam socket
+ */
+static void common_response(void *data, struct sk_buff *skb)
+{
+	struct driver_remote *priv;
+
+	priv = (struct driver_remote *)data;
+	skb_queue_tail(&priv->wq_acks, skb);
+	priv->wq_res = 1;
+	wake_up(&priv->wq);
+}
+
+/*
+ * create device procfs entries
+ */
+static int remote_init_procfs(struct fbxatm_dev *adev)
+{
+	return 0;
+}
+
+/*
+ * release device procfs entries
+ */
+static void remote_release_procfs(struct fbxatm_dev *adev)
+{
+}
+
+/*
+ * local fake fbxatm device callbacks
+ */
+static const struct fbxatm_dev_ops remote_fbxatm_ops = {
+	.open		= remote_vcc_open,
+	.close		= remote_vcc_close,
+	.send		= remote_send,
+	.send_oam	= remote_send_oam,
+	.init_procfs	= remote_init_procfs,
+	.release_procfs	= remote_release_procfs,
+	.owner		= THIS_MODULE,
+};
+
+/*
+ * dev_rx_oam socket rx callback
+ */
+static int dev_rx_oam_deliver(void *data, struct sk_buff *skb,
+			      struct sk_buff **ack)
+{
+	struct driver_remote *priv;
+
+	priv = (struct driver_remote *)data;
+	skb_queue_tail(&priv->dev_oam_reqs, skb);
+	schedule_delayed_work(&priv->fsm_work, 0);
+
+	return 0;
+}
+
+/*
+ * dev_link socket rx callback
+ */
+static int dev_link_deliver(void *data, struct sk_buff *skb,
+			    struct sk_buff **ack)
+{
+	struct driver_remote *priv;
+
+	priv = (struct driver_remote *)data;
+	skb_queue_tail(&priv->dev_link_reqs, skb);
+	schedule_delayed_work(&priv->fsm_work, 0);
+
+	/* send ack later */
+	return 0;
+}
+
+/*
+ * dev_connect socket tx ack callback
+ */
+static void dev_connect_response(void *data, struct sk_buff *skb)
+{
+	struct driver_remote *priv;
+
+	priv = (struct driver_remote *)data;
+	skb_queue_tail(&priv->connect_acks, skb);
+	schedule_delayed_work(&priv->fsm_work, 0);
+}
+
+/*
+ * handle link change queue
+ */
+static void handle_dev_link_queue(struct driver_remote *priv)
+{
+	struct sk_buff *skb;
+
+	do {
+		struct fbxatm_remote_dev_link *pkt;
+		struct fbxatm_dev *adev;
+		struct sk_buff *ack;
+
+		skb = skb_dequeue(&priv->dev_link_reqs);
+		if (!skb)
+			break;
+
+		if (!pskb_may_pull(skb, sizeof (*pkt))) {
+			printk(KERN_ERR PFX "bad rx dev link\n");
+			dev_kfree_skb(skb);
+			continue;
+		}
+
+		pkt = (struct fbxatm_remote_dev_link *)skb->data;
+
+		adev = priv->fbxatm_dev;
+		adev->link_rate_ds = ntohl(pkt->link_rate_ds);
+		adev->link_rate_us = ntohl(pkt->link_rate_us);
+		adev->link_cell_rate_ds = ntohl(pkt->link_cell_rate_ds);
+		adev->link_cell_rate_us = ntohl(pkt->link_cell_rate_us);
+
+		if (pkt->link)
+			fbxatm_dev_set_link_up(adev);
+		else
+			fbxatm_dev_set_link_down(adev);
+
+		/* send ack */
+		ack = fbxatm_remote_alloc_skb(priv->remote_ctx, 0);
+		if (!ack)
+			continue;
+		fbxatm_remote_sock_send_ack(priv->dev_link_sock, ack);
+
+	} while (1);
+}
+
+/*
+ * handle oam rx queue
+ */
+static void handle_oam_rx_queue(struct driver_remote *priv)
+{
+	struct fbxatm_oam_cell *cell;
+	struct sk_buff *skb;
+
+	do {
+		skb = skb_dequeue(&priv->dev_oam_reqs);
+		if (!skb)
+			break;
+
+		cell = kmalloc(sizeof (*cell), GFP_KERNEL);
+		if (!cell) {
+			dev_kfree_skb(skb);
+			continue;
+		}
+
+		if (!pskb_may_pull(skb, sizeof (cell->payload))) {
+			printk(KERN_ERR PFX "bad rx oam\n");
+			kfree(cell);
+			dev_kfree_skb(skb);
+			continue;
+		}
+
+		memcpy(&cell->payload, skb->data, sizeof (cell->payload));
+		dev_kfree_skb(skb);
+		fbxatm_netifrx_oam(priv->fbxatm_dev, cell);
+
+	} while (1);
+}
+
+/*
+ * free all and set to dead state
+ */
+static void remote_free(struct driver_remote *priv)
+{
+	struct driver_remote_vcc *pvcc;
+
+	if (priv->state == RSTATE_S_DEAD)
+		return;
+
+	priv->want_die = 0;
+
+	/* make sure remote_send device operation doesn't use sockets
+	 * any more */
+	spin_lock_bh(&priv->tx_lock);
+	priv->state = RSTATE_S_DEAD;
+	spin_unlock_bh(&priv->tx_lock);
+
+	/* same goes for open_vcc, close_vcc and send_oam ops,
+	 * RSTATE_S_DEAD will prevent function from being entered, and
+	 * any sleeper will be woken up */
+	wake_up(&priv->wq);
+
+	mutex_lock(&priv->mutex);
+
+	/* no vcc can be created, close all vcc sockets */
+	list_for_each_entry(pvcc, &priv->pvcc_list, next) {
+
+		if (pvcc->vcc_send_sock) {
+			fbxatm_remote_sock_close(pvcc->vcc_send_sock);
+			pvcc->vcc_send_sock = NULL;
+		}
+
+		if (pvcc->vcc_rx_sock) {
+			fbxatm_remote_sock_close(pvcc->vcc_rx_sock);
+			pvcc->vcc_rx_sock = NULL;
+		}
+
+		if (pvcc->vcc_qempty_sock) {
+			fbxatm_remote_sock_close(pvcc->vcc_qempty_sock);
+			pvcc->vcc_qempty_sock = NULL;
+		}
+	}
+	mutex_unlock(&priv->mutex);
+
+	/* no external callback from fbxatm can use sockets now */
+
+	/* close all sockets */
+	if (priv->dev_connect_sock) {
+		fbxatm_remote_sock_close(priv->dev_connect_sock);
+		priv->dev_connect_sock = NULL;
+	}
+
+	if (priv->keepalive_sock) {
+		fbxatm_remote_sock_close(priv->keepalive_sock);
+		priv->keepalive_sock = NULL;
+	}
+
+	if (priv->dev_link_sock) {
+		fbxatm_remote_sock_close(priv->dev_link_sock);
+		priv->dev_link_sock = NULL;
+	}
+
+	if (priv->dev_rx_oam_sock) {
+		fbxatm_remote_sock_close(priv->dev_rx_oam_sock);
+		priv->dev_rx_oam_sock = NULL;
+	}
+
+	if (priv->vcc_action_sock) {
+		fbxatm_remote_sock_close(priv->vcc_action_sock);
+		priv->vcc_action_sock = NULL;
+	}
+
+	if (priv->dev_send_oam_sock) {
+		fbxatm_remote_sock_close(priv->dev_send_oam_sock);
+		priv->dev_send_oam_sock = NULL;
+	}
+
+	if (priv->netdev) {
+		dev_put(priv->netdev);
+		priv->netdev = NULL;
+	}
+
+	if (priv->remote_ctx) {
+		fbxatm_remote_free_ctx(priv->remote_ctx);
+		priv->remote_ctx = NULL;
+	}
+
+	skb_queue_purge(&priv->connect_acks);
+	skb_queue_purge(&priv->dev_link_reqs);
+	skb_queue_purge(&priv->dev_oam_reqs);
+	skb_queue_purge(&priv->wq_acks);
+
+	if (priv->fbxatm_dev_registered) {
+		fbxatm_dev_set_link_down(priv->fbxatm_dev);
+		printk(KERN_WARNING "%s: marking dead\n",
+		       priv->fbxatm_dev->name);
+	}
+}
+
+/*
+ * remote context timeout on any socket
+ */
+static void remote_sock_timeout(void *data)
+{
+	set_dying((struct driver_remote *)data);
+}
+
+/*
+ * main workqueue to handle device fsm
+ */
+static void remote_fsm(struct work_struct *t)
+{
+	struct delayed_work *dwork;
+	struct driver_remote *priv;
+	struct net_device *netdev;
+	struct fbxatm_remote_sockaddr addr;
+
+	dwork = container_of(t, struct delayed_work, work);
+	priv = container_of(dwork, struct driver_remote, fsm_work);
+
+	if (priv->want_die) {
+		remote_free(priv);
+		/* let FSM restart if needed */
+	}
+
+	switch (priv->state) {
+	case RSTATE_S_WAIT_NETDEV:
+	{
+		struct fbxatm_remote_connect *pkt;
+		struct fbxatm_remote_ctx *ctx;
+		struct sk_buff *skb;
+		u32 session_id;
+
+		netdev = dev_get_by_name(&init_net, priv->pd->netdev_name);
+		if (!netdev) {
+			/* netdev notifier will reschedule */
+			return;
+		}
+		priv->netdev = netdev;
+
+		/* got netdev, open remote context */
+		get_random_bytes(&session_id, sizeof (session_id));
+		ctx = fbxatm_remote_alloc_ctx(netdev, priv->pd->remote_mac,
+					      session_id,
+					      remote_sock_timeout,
+					      priv);
+		if (!ctx) {
+			printk(KERN_ERR PFX "unable to allocate remote ctx\n");
+			remote_free(priv);
+			return;
+		}
+		priv->remote_ctx = ctx;
+
+		/* open rx sockets */
+		memset(&addr, 0, sizeof (addr));
+		addr.mtype = htonl(FBXATM_RMT_DEV_LINK);
+		addr.deliver = dev_link_deliver;
+		addr.priv = priv;
+		priv->dev_link_sock = fbxatm_remote_sock_bind(ctx, &addr, 1);
+		if (!priv->dev_link_sock) {
+			remote_free(priv);
+			return;
+		}
+
+		memset(&addr, 0, sizeof (addr));
+		addr.mtype = htonl(FBXATM_RMT_DEV_RX_OAM);
+		addr.deliver = dev_rx_oam_deliver;
+		addr.priv = priv;
+		priv->dev_rx_oam_sock = fbxatm_remote_sock_bind(ctx, &addr, 0);
+		if (!priv->dev_rx_oam_sock) {
+			remote_free(priv);
+			return;
+		}
+
+		/* create socket for initial connexion */
+		memset(&addr, 0, sizeof (addr));
+		addr.mtype = htonl(FBXATM_RMT_CONNECT);
+		addr.response = dev_connect_response;
+		addr.priv = priv;
+		addr.infinite_retry = 1;
+		priv->dev_connect_sock = fbxatm_remote_sock_connect(ctx,
+								    &addr, 1);
+		if (!priv->dev_connect_sock) {
+			remote_free(priv);
+			return;
+		}
+
+		/* send the connect packet */
+		skb = fbxatm_remote_alloc_skb(priv->remote_ctx,
+					      sizeof (*pkt));
+		if (!skb) {
+			remote_free(priv);
+			return;
+		}
+
+		pkt = (struct fbxatm_remote_connect *)
+			skb_put(skb, sizeof (*pkt));
+
+		memcpy(pkt->name, priv->pd->remote_name, sizeof (pkt->name));
+
+		fbxatm_remote_sock_getaddr(priv->dev_link_sock, &addr);
+		pkt->dev_link_port = addr.lport;
+
+		fbxatm_remote_sock_getaddr(priv->dev_rx_oam_sock, &addr);
+		pkt->dev_rx_oam_port = addr.lport;
+
+		if (fbxatm_remote_sock_send(priv->dev_connect_sock, skb)) {
+			dev_kfree_skb(skb);
+			remote_free(priv);
+			return;
+		}
+
+		/* wait for connect ack... */
+		priv->state = RSTATE_S_WAIT_REMOTE;
+		break;
+	}
+
+	case RSTATE_S_WAIT_REMOTE:
+	{
+		struct fbxatm_remote_connect_ack *pkt_ack;
+		struct fbxatm_remote_ctx *ctx;
+		struct fbxatm_dev *adev;
+		struct sk_buff *skb;
+		int ret;
+
+		skb = skb_dequeue(&priv->connect_acks);
+		if (!skb)
+			return;
+
+		if (!pskb_may_pull(skb, sizeof (*pkt_ack))) {
+			printk(KERN_ERR PFX "bad connect ack\n");
+			dev_kfree_skb(skb);
+			return;
+		}
+
+		pkt_ack = (struct fbxatm_remote_connect_ack *)skb->data;
+		ctx = priv->remote_ctx;
+
+		/* open sockets */
+		memset(&addr, 0, sizeof (addr));
+		addr.mtype = htonl(FBXATM_RMT_VCC_ACTION);
+		addr.response = common_response;
+		addr.dport = pkt_ack->vcc_action_port;
+		addr.priv = priv;
+		priv->vcc_action_sock = fbxatm_remote_sock_connect(ctx,
+								   &addr, 1);
+		if (!priv->vcc_action_sock) {
+			remote_free(priv);
+			return;
+		}
+
+		memset(&addr, 0, sizeof (addr));
+		addr.mtype = htonl(FBXATM_RMT_DEV_SEND_OAM);
+		addr.response = common_response;
+		addr.dport = pkt_ack->dev_send_oam_port;
+		addr.priv = priv;
+		priv->dev_send_oam_sock = fbxatm_remote_sock_connect(ctx,
+								     &addr, 1);
+		if (!priv->dev_send_oam_sock) {
+			remote_free(priv);
+			return;
+		}
+
+		memset(&addr, 0, sizeof (addr));
+		addr.mtype = htonl(FBXATM_RMT_KEEPALIVE);
+		addr.priv = priv;
+		addr.dport = pkt_ack->keepalive_port;
+		priv->keepalive_sock = fbxatm_remote_sock_connect(ctx,
+								  &addr, 1);
+		if (!priv->keepalive_sock) {
+			remote_free(priv);
+			return;
+		}
+
+		/* all set, register fbxatm device */
+		adev = priv->fbxatm_dev;
+		adev->max_vcc = ntohl(pkt_ack->max_vcc);
+		adev->vci_mask = ntohl(pkt_ack->vci_mask);
+		adev->vpi_mask = ntohl(pkt_ack->vpi_mask);
+		adev->max_priority = ntohl(pkt_ack->max_priority);
+		adev->max_rx_priority = ntohl(pkt_ack->max_rx_priority);
+		adev->link_rate_ds = ntohl(pkt_ack->link_rate_ds);
+		adev->link_rate_us = ntohl(pkt_ack->link_rate_us);
+		adev->link_cell_rate_ds = ntohl(pkt_ack->link_cell_rate_ds);
+		adev->link_cell_rate_us = ntohl(pkt_ack->link_cell_rate_us);
+		adev->tx_headroom = fbxatm_remote_headroom(ctx);
+
+		/* register atm device */
+		priv->state = RSTATE_S_ACTIVE;
+
+		if (!priv->fbxatm_dev_registered) {
+			ret = fbxatm_register_device(adev, "remote_fbxatm",
+						     &remote_fbxatm_ops);
+			if (ret) {
+				remote_free(priv);
+				return;
+			}
+		}
+
+		priv->fbxatm_dev_registered = 1;
+		printk(KERN_INFO "%s: connected to %s - %pM6/%s\n",
+		       adev->name, priv->pd->remote_name,
+		       priv->pd->remote_mac, priv->netdev->name);
+
+		if (pkt_ack->link)
+			fbxatm_dev_set_link_up(adev);
+		else
+			fbxatm_dev_set_link_down(adev);
+
+		schedule_delayed_work(&priv->fsm_work, HZ);
+		break;
+	}
+
+	case RSTATE_S_ACTIVE:
+	{
+		/* process link change event if any */
+		handle_dev_link_queue(priv);
+
+		/* process rx oam if any */
+		handle_oam_rx_queue(priv);
+
+		/* send keepalive */
+		if (!fbxatm_remote_sock_pending(priv->keepalive_sock)) {
+			struct sk_buff *skb;
+
+			skb = fbxatm_remote_alloc_skb(priv->remote_ctx, 0);
+			if (skb)
+				fbxatm_remote_sock_send(priv->keepalive_sock,
+							skb);
+		}
+
+		schedule_delayed_work(&priv->fsm_work, HZ);
+		break;
+	}
+
+	case RSTATE_S_DEAD:
+		/* wait until open vcc list is empty */
+		if (!list_empty(&priv->pvcc_list))
+			break;
+
+		priv->state = RSTATE_S_WAIT_NETDEV;
+		if (priv->fbxatm_dev_registered)
+			printk(KERN_INFO "%s: reconnecting\n",
+			       priv->fbxatm_dev->name);
+		schedule_delayed_work(&priv->fsm_work, HZ);
+		break;
+	}
+}
+
+/*
+ * netdevice notifier callback
+ */
+static int remote_device_event(struct notifier_block *this,
+			       unsigned long event, void *ptr)
+{
+	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+	struct driver_remote *priv;
+
+	spin_lock_bh(&remote_lock);
+
+	/* go through remote list to check if device matches one */
+	list_for_each_entry(priv, &remote_dev_list, next) {
+
+		switch (event) {
+		case NETDEV_REGISTER:
+			if (strcmp(dev->name, priv->pd->netdev_name))
+				continue;
+			break;
+
+		case NETDEV_UNREGISTER:
+			if (dev != priv->netdev)
+				continue;
+			priv->want_die = 1;
+			break;
+
+		default:
+			continue;
+		}
+		schedule_delayed_work(&priv->fsm_work, 0);
+	}
+
+	spin_unlock_bh(&remote_lock);
+
+	return 0;
+}
+
+static struct notifier_block remote_notifier = {
+	.notifier_call = remote_device_event,
+};
+
+/*
+ * platform data probe callback
+ */
+static int remote_probe(struct platform_device *pdev)
+{
+	struct fbxatm_remote_pdata *pd;
+	struct driver_remote *priv;
+	struct fbxatm_dev *adev;
+
+	pd = pdev->dev.platform_data;
+	if (!pd || !pd->netdev_name[0])
+		return -EINVAL;
+
+	/* allocate fbxatm device */
+	adev = fbxatm_alloc_device(sizeof (*priv));
+	if (!adev)
+		return -ENOMEM;
+
+	priv = fbxatm_dev_priv(adev);
+
+	priv->fbxatm_dev = adev;
+	INIT_LIST_HEAD(&priv->pvcc_list);
+	spin_lock_init(&priv->tx_lock);
+	mutex_init(&priv->mutex);
+
+	priv->state = RSTATE_S_WAIT_NETDEV;
+	INIT_DELAYED_WORK(&priv->fsm_work, remote_fsm);
+
+	skb_queue_head_init(&priv->connect_acks);
+	skb_queue_head_init(&priv->dev_link_reqs);
+	skb_queue_head_init(&priv->dev_oam_reqs);
+	init_waitqueue_head(&priv->wq);
+	skb_queue_head_init(&priv->wq_acks);
+	priv->pd = pd;
+
+	spin_lock_bh(&remote_lock);
+	list_add_tail(&priv->next, &remote_dev_list);
+	spin_unlock_bh(&remote_lock);
+
+	platform_set_drvdata(pdev, priv);
+
+	printk(KERN_INFO PFX "connecting to %s - %pM6/%s\n",
+	       priv->pd->remote_name,
+	       priv->pd->remote_mac,
+	       priv->pd->netdev_name);
+
+	/* kick fsm  */
+	schedule_delayed_work(&priv->fsm_work, 0);
+	return 0;
+}
+
+static int remote_remove(struct platform_device *pdev)
+{
+	struct driver_remote *priv;
+	struct fbxatm_dev *adev;
+
+	priv = platform_get_drvdata(pdev);
+	adev = priv->fbxatm_dev;
+
+	/* remove from global list so network notifier can't find us */
+	spin_lock_bh(&remote_lock);
+	list_del(&priv->next);
+	spin_unlock_bh(&remote_lock);
+
+	/* cancel any pending fsm */
+	cancel_delayed_work_sync(&priv->fsm_work);
+
+	/* force dead state */
+	remote_free(priv);
+
+	if (priv->fbxatm_dev_registered)
+		fbxatm_unregister_device(adev);
+	fbxatm_free_device(adev);
+	return 0;
+}
+
+struct platform_driver fbxatm_remote_driver = {
+	.probe	= remote_probe,
+	.remove	= remote_remove,
+	.driver	= {
+		.name	= "fbxatm_remote",
+		.owner  = THIS_MODULE,
+	},
+};
+
+static int __init fbxatm_remote_driver_init(void)
+{
+	int ret;
+
+	INIT_LIST_HEAD(&remote_dev_list);
+	ret = fbxatm_remote_init();
+	if (ret)
+		goto fail;
+	ret = register_netdevice_notifier(&remote_notifier);
+	if (ret)
+		goto fail_remote;
+	ret = platform_driver_register(&fbxatm_remote_driver);
+	if (ret)
+		goto fail_notifier;
+	return 0;
+
+fail_notifier:
+	unregister_netdevice_notifier(&remote_notifier);
+fail_remote:
+	fbxatm_remote_exit();
+fail:
+	return ret;
+}
+
+static void __exit fbxatm_remote_driver_exit(void)
+{
+	fbxatm_remote_exit();
+	unregister_netdevice_notifier(&remote_notifier);
+	platform_driver_unregister(&fbxatm_remote_driver);
+}
+
+module_init(fbxatm_remote_driver_init);
+module_exit(fbxatm_remote_driver_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");
diff -Nruw linux-3.11.10-fbx/net/fbxatm./fbxatm_remote_driver.h linux-3.11.10-fbx/net/fbxatm/fbxatm_remote_driver.h
--- linux-3.11.10-fbx/net/fbxatm./fbxatm_remote_driver.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxatm/fbxatm_remote_driver.h	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,79 @@
+#ifndef FBXATM_REMOTE_DRIVER_H_
+#define FBXATM_REMOTE_DRIVER_H_
+
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/fbxatm.h>
+#include <linux/fbxatm_dev.h>
+#include <linux/fbxatm_remote.h>
+
+enum remote_state {
+	RSTATE_S_WAIT_NETDEV = 0,
+	RSTATE_S_WAIT_REMOTE,
+	RSTATE_S_ACTIVE,
+	RSTATE_S_DEAD,
+};
+
+struct driver_remote;
+
+struct driver_remote_vcc {
+
+	struct fbxatm_vcc		*vcc;
+	struct driver_remote		*priv;
+
+	unsigned int			remote_id;
+
+	int				tx_pending;
+	int				tx_got_qempty;
+
+	/* output */
+	struct fbxatm_remote_sock	*vcc_send_sock;
+
+	/* input */
+	struct fbxatm_remote_sock	*vcc_rx_sock;
+	struct fbxatm_remote_sock	*vcc_qempty_sock;
+
+	struct list_head		next;
+};
+
+struct driver_remote {
+	struct fbxatm_dev		*fbxatm_dev;
+	int				fbxatm_dev_registered;
+
+	struct list_head		pvcc_list;
+
+	spinlock_t			tx_lock;
+	struct mutex			mutex;
+
+	struct fbxatm_remote_ctx	*remote_ctx;
+	struct net_device		*netdev;
+
+	enum remote_state		state;
+	struct delayed_work		fsm_work;
+	int				want_die;
+
+	/* output */
+	struct fbxatm_remote_sock	*dev_connect_sock;
+	struct sk_buff_head		connect_acks;
+
+	struct fbxatm_remote_sock	*keepalive_sock;
+
+	/* input */
+	struct fbxatm_remote_sock	*dev_link_sock;
+	struct sk_buff_head		dev_link_reqs;
+
+	struct fbxatm_remote_sock	*dev_rx_oam_sock;
+	struct sk_buff_head		dev_oam_reqs;
+
+	/* used to wait for send_oam & vcc_action */
+	struct fbxatm_remote_sock	*vcc_action_sock;
+	struct fbxatm_remote_sock	*dev_send_oam_sock;
+	wait_queue_head_t		wq;
+	int				wq_res;
+	struct sk_buff_head		wq_acks;
+
+	struct fbxatm_remote_pdata	*pd;
+	struct list_head		next;
+};
+
+#endif /* ! FBXATM_REMOTE_DRIVER_H_ */
diff -Nruw linux-3.11.10-fbx/net/fbxatm./fbxatm_sysfs.c linux-3.11.10-fbx/net/fbxatm/fbxatm_sysfs.c
--- linux-3.11.10-fbx/net/fbxatm./fbxatm_sysfs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxatm/fbxatm_sysfs.c	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,154 @@
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/kobject.h>
+#include <linux/stat.h>
+#include <linux/fbxatm_dev.h>
+#include "fbxatm_priv.h"
+
+#define to_fbxatm_dev(cldev) container_of(cldev, struct fbxatm_dev, dev)
+
+static ssize_t show_ifindex(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	struct fbxatm_dev *adev = to_fbxatm_dev(dev);
+	return sprintf(buf, "%d\n", adev->ifindex);
+}
+
+static ssize_t show_link_state(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct fbxatm_dev *adev = to_fbxatm_dev(dev);
+	return sprintf(buf, "%d\n",
+		       test_bit(FBXATM_DEV_F_LINK_UP, &adev->dev_flags) ?
+		       1 : 0);
+}
+
+static ssize_t show_link_rate_us(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct fbxatm_dev *adev = to_fbxatm_dev(dev);
+	return sprintf(buf, "%d\n", adev->link_rate_us);
+}
+
+static ssize_t show_link_rate_ds(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct fbxatm_dev *adev = to_fbxatm_dev(dev);
+	return sprintf(buf, "%d\n", adev->link_rate_ds);
+}
+
+static ssize_t show_max_priority(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct fbxatm_dev *adev = to_fbxatm_dev(dev);
+	return sprintf(buf, "%d\n", adev->max_priority);
+}
+
+static ssize_t show_max_rx_priority(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct fbxatm_dev *adev = to_fbxatm_dev(dev);
+	return sprintf(buf, "%d\n", adev->max_rx_priority);
+}
+
+static DEVICE_ATTR(ifindex, S_IRUGO, show_ifindex, NULL);
+static DEVICE_ATTR(link_state, S_IRUGO, show_link_state, NULL);
+static DEVICE_ATTR(link_rate_us, S_IRUGO, show_link_rate_us, NULL);
+static DEVICE_ATTR(link_rate_ds, S_IRUGO, show_link_rate_ds, NULL);
+static DEVICE_ATTR(max_priority, S_IRUGO, show_max_priority, NULL);
+static DEVICE_ATTR(max_rx_priority, S_IRUGO, show_max_rx_priority, NULL);
+
+static struct device_attribute *fbxatm_attrs[] = {
+	&dev_attr_ifindex,
+	&dev_attr_link_state,
+	&dev_attr_link_rate_us,
+	&dev_attr_link_rate_ds,
+	&dev_attr_max_priority,
+	&dev_attr_max_rx_priority,
+};
+
+static int fbxatm_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct fbxatm_dev *adev;
+
+	if (!dev)
+		return -ENODEV;
+
+	adev = to_fbxatm_dev(dev);
+	if (!adev)
+		return -ENODEV;
+
+	if (add_uevent_var(env, "NAME=%s", adev->name))
+		return -ENOMEM;
+
+	if (add_uevent_var(env, "IFINDEX=%u", adev->ifindex))
+		return -ENOMEM;
+
+	if (add_uevent_var(env, "LINK=%u",
+			   test_bit(FBXATM_DEV_F_LINK_UP, &adev->dev_flags) ?
+			   1 : 0))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void fbxatm_release(struct device *dev)
+{
+	struct fbxatm_dev *adev = to_fbxatm_dev(dev);
+	__fbxatm_free_device(adev);
+}
+
+static struct class fbxatm_class = {
+	.name		= "fbxatm",
+	.dev_release	= fbxatm_release,
+	.dev_uevent	= fbxatm_uevent,
+};
+
+void fbxatm_dev_change_sysfs(struct fbxatm_dev *adev)
+{
+	struct device *dev = &adev->dev;
+
+	kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, NULL);
+}
+
+int fbxatm_register_dev_sysfs(struct fbxatm_dev *adev)
+{
+	struct device *dev = &adev->dev;
+	int i, j, ret;
+
+	dev->class = &fbxatm_class;
+	dev_set_name(dev, "%s", adev->name);
+	ret = device_register(dev);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(fbxatm_attrs); i++) {
+		ret = device_create_file(dev, fbxatm_attrs[i]);
+		if (ret)
+			goto err;
+	}
+	return 0;
+
+err:
+	for (j = 0; j < i; j++)
+		device_remove_file(dev, fbxatm_attrs[j]);
+	device_del(dev);
+	return ret;
+}
+
+void fbxatm_unregister_dev_sysfs(struct fbxatm_dev *adev)
+{
+	struct device *dev = &adev->dev;
+	device_del(dev);
+}
+
+int __init fbxatm_sysfs_init(void)
+{
+	return class_register(&fbxatm_class);
+}
+
+void fbxatm_sysfs_exit(void)
+{
+	class_unregister(&fbxatm_class);
+}
diff -Nruw linux-3.11.10-fbx/net/fbxatm./Kconfig linux-3.11.10-fbx/net/fbxatm/Kconfig
--- linux-3.11.10-fbx/net/fbxatm./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxatm/Kconfig	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,28 @@
+menuconfig FBXATM
+	tristate "Freebox Asynchronous Transfer Mode (ATM)"
+
+if FBXATM
+
+config FBXATM_REMOTE
+	bool
+
+choice
+	prompt "mode"
+	default FBXATM_STACK
+
+config FBXATM_STACK
+	bool "standard"
+
+config FBXATM_REMOTE_STUB
+	bool "remote stub"
+	select FBXATM_REMOTE
+
+endchoice
+
+config FBXATM_REMOTE_DRIVER
+	tristate "remote fbxatm driver"
+	depends on FBXATM_STACK
+	select FBXATM_REMOTE
+	default n
+
+endif
diff -Nruw linux-3.11.10-fbx/net/fbxatm./Makefile linux-3.11.10-fbx/net/fbxatm/Makefile
--- linux-3.11.10-fbx/net/fbxatm./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxatm/Makefile	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,18 @@
+obj-$(CONFIG_FBXATM) += fbxatm.o
+obj-$(CONFIG_FBXATM_REMOTE) += fbxatm_remote.o
+
+fbxatm-y := fbxatm_procfs.o fbxatm_sysfs.o
+
+ifeq ($(CONFIG_FBXATM_STACK),y)
+fbxatm-y += 	fbxatm_core.o	\
+		fbxatm_2684.o	\
+		fbxatm_dev.o	\
+		crc10.o
+fbxatm-$(CONFIG_PPP) += fbxatm_pppoa.o
+endif
+
+ifeq ($(CONFIG_FBXATM_REMOTE_STUB),y)
+fbxatm-y += fbxatm_remote_stub.o
+endif
+
+obj-$(CONFIG_FBXATM_REMOTE_DRIVER) += fbxatm_remote_driver.o
diff -Nruw linux-3.11.10-fbx/net/fbxbridge./fbxbridge_dev.c linux-3.11.10-fbx/net/fbxbridge/fbxbridge_dev.c
--- linux-3.11.10-fbx/net/fbxbridge./fbxbridge_dev.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxbridge/fbxbridge_dev.c	2015-09-29 11:15:09.001756505 +0200
@@ -0,0 +1,846 @@
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/sockios.h>
+
+#include <linux/notifier.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+
+#include <linux/fbxbridge.h>
+#include <linux/if_vlan.h>
+#include <net/neighbour.h>
+#include <net/netevent.h>
+
+struct fbxbridge *fbxbridge_list = NULL;
+extern struct sk_buff *(*fbxbridge_handle_frame_hook)(struct fbxbridge *br,
+						      struct sk_buff *skb);
+extern void fbxbridge_set(int (*hook)(struct net *net,
+				      unsigned int, void __user *));
+
+/*
+ * bridge network function
+ */
+static int bridge_net_open(struct net_device *dev)
+{
+	return 0;
+}
+
+static int bridge_net_stop(struct net_device *dev)
+{
+	return 0;
+}
+
+
+#define	DEFAULT_RENEWAL_TIME	60
+#define	DEFAULT_REBIND_TIME	300
+#define	DEFAULT_LEASE_TIME	600
+
+
+static int bridge_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct fbxbridge *br = (struct fbxbridge *)netdev_priv(dev);
+
+	br->dev->stats.tx_packets++;
+	br->dev->stats.tx_bytes += skb->len;
+
+	if (handle_local_output_frame(br, skb))
+		br->dev->stats.tx_dropped++;
+
+	return 0;
+}
+
+static const struct net_device_ops fbxbridge_net_ops = {
+	.ndo_open		= bridge_net_open,
+	.ndo_stop		= bridge_net_stop,
+	.ndo_start_xmit		= bridge_net_start_xmit,
+};
+
+static void bridge_net_setup(struct net_device *dev)
+{
+	dev->netdev_ops = &fbxbridge_net_ops;
+	dev->flags = IFF_NOARP;
+	dev->tx_queue_len = 0;	/* we use bridge devices queues */
+	dev->type = ARPHRD_PPP;
+	dev->mtu = 1500;
+	dev->features = 0;
+	dev->hard_header_len = 16;
+}
+
+/*
+ * helper to access the bridge list
+ */
+static void add_to_bridge_list(struct fbxbridge *br)
+{
+	br->next = fbxbridge_list;
+	fbxbridge_list = br;
+}
+
+static void remove_from_bridge_list(struct fbxbridge *br)
+{
+	struct fbxbridge *p, *pprev;
+
+	pprev = p = fbxbridge_list;
+	while (p) {
+		if (p == br)
+			break;
+		pprev = p;
+	}
+
+	if (p) {
+		if (p == fbxbridge_list)
+			fbxbridge_list = fbxbridge_list->next;
+		else
+			pprev->next = br->next;
+	}
+}
+
+static struct fbxbridge *get_bridge_by_name(const char *name)
+{
+	struct fbxbridge *p;
+
+	p = fbxbridge_list;
+	while (p) {
+		if (!strcmp(name, p->name))
+			return p;
+		p = p->next;
+	}
+
+	return NULL;
+}
+
+static struct fbxbridge *alloc_bridge(const char *name)
+{
+	struct net_device *dev;
+	struct fbxbridge *br;
+
+	dev = alloc_netdev(sizeof(*br), name, bridge_net_setup);
+	if (!dev)
+		return NULL;
+	br = netdev_priv(dev);
+
+	memset(br, 0, sizeof (*br));
+	strncpy(br->name, name, IFNAMSIZ);
+	br->refcount = 1;
+	br->last_arp_send = jiffies;
+	br->dev = dev;
+
+	br->dhcpd_renew_time = DEFAULT_RENEWAL_TIME;
+	br->dhcpd_rebind_time = DEFAULT_REBIND_TIME;
+	br->dhcpd_lease_time = DEFAULT_LEASE_TIME;
+
+	fbxbridge_fp_init(br);
+
+	return br;
+}
+
+static struct net_device *get_vlan_master(struct net_device *dev,
+					  u16 *id, u16 *id2)
+{
+	struct net_device *sub_dev, *sub2_dev;
+
+	if (!is_vlan_dev(dev)) {
+		*id = 0;
+		*id2 = 0;
+		return dev;
+
+	}
+
+	sub_dev = vlan_dev_upper_dev(dev);
+	if (!is_vlan_dev(sub_dev)) {
+		*id = vlan_dev_vlan_id(dev);
+		*id2 = 0;
+		return sub_dev;
+	}
+
+	/* subdevice is a vlan too */
+	sub2_dev = vlan_dev_upper_dev(sub_dev);
+	*id = vlan_dev_vlan_id(sub_dev);
+	*id2 = vlan_dev_vlan_id(dev);
+
+	return sub2_dev;
+}
+
+static void
+__grab_bridge_device(struct fbxbridge *br,
+		     struct net_device *dev,
+		     bool is_wan)
+{
+	struct fbxbridge_port *bport;
+	u16 id, id2;
+
+	bport = &br->ports[is_wan ? FBXBR_PORT_WAN : FBXBR_PORT_LAN];
+	if (bport->dev)
+		return;
+
+	dev_hold(dev);
+	dev->fbx_bridge_port = br;
+	bport->dev = dev;
+	bport->master_dev = get_vlan_master(dev, &id, &id2);
+	bport->master_dev->fbx_bridge_maybe_port++;
+	bport->vlan1 = id;
+	bport->vlan2 = id2;
+
+	printk(KERN_INFO "%s: %s device %s grabbed - master %s\n",
+	       br->name, is_wan ? "wan" : "lan", dev->name,
+	       bport->master_dev->name);
+
+	__fbxbridge_fp_check(br);
+}
+
+static void
+__ungrab_bridge_device(struct fbxbridge *br, bool is_wan)
+{
+	struct net_device *dev;
+	struct fbxbridge_port *bport;
+
+	bport = &br->ports[is_wan ? FBXBR_PORT_WAN : FBXBR_PORT_LAN];
+	if (!bport->dev)
+		return;
+
+	dev = bport->dev;
+	bport->dev = NULL;
+	bport->master_dev->fbx_bridge_maybe_port--;
+	bport->master_dev = NULL;
+
+	if (!is_wan)
+		br->have_hw_addr = 0;
+
+	dev->fbx_bridge_port = NULL;
+	dev_put(dev);
+
+	printk(KERN_INFO "%s: %s device %s released\n",
+	       br->name, is_wan ? "wan" : "lan", dev->name);
+
+	__fbxbridge_fp_check(br);
+}
+
+static int release_bridge(struct fbxbridge *br)
+{
+	if (--br->refcount > 0)
+		return 0;
+
+	/*
+	 * allow removal of bridge even if there are devices present
+	 * in it.
+	 */
+	if (br->ports[FBXBR_PORT_WAN].dev)
+		__ungrab_bridge_device(br, true);
+	if (br->ports[FBXBR_PORT_LAN].dev)
+		__ungrab_bridge_device(br, false);
+
+	printk(KERN_INFO FBXBRIDGE_PFX "unregistering bridge %s\n", br->name);
+
+	br->dev->fbx_bridge = NULL;
+	wmb();
+	unregister_netdev(br->dev);
+	remove_from_bridge_list(br);
+	free_netdev(br->dev);
+
+	module_put(THIS_MODULE);
+
+	return 0;
+}
+
+static int remove_bridge(const char *name)
+{
+	struct fbxbridge *br;
+
+	if (!(br = get_bridge_by_name(name)))
+		return -ENODEV;
+
+	return release_bridge(br);
+}
+
+static int create_bridge(const char *name)
+{
+	struct fbxbridge *br;
+
+	if (get_bridge_by_name(name))
+		return -EEXIST;
+
+	/* allocate this bridge */
+	if (!(br = alloc_bridge(name)))
+		return -ENOMEM;
+
+	if (register_netdev(br->dev)) {
+		free_netdev(br->dev);
+		return -ENODEV;
+	}
+
+	br->dev->fbx_bridge = br;
+	add_to_bridge_list(br);
+
+	/* can not fail, the ioctl hook is protected by a mutex */
+	try_module_get(THIS_MODULE);
+
+	printk(KERN_INFO FBXBRIDGE_PFX "registered bridge %s\n", name);
+
+	return 0;
+}
+
+static int set_bridge_info(const char *name, unsigned int flags,
+			   unsigned int dns1, unsigned int dns2,
+			   unsigned long *ip_aliases,
+			   unsigned long dhcpd_renew_time,
+			   unsigned long dhcpd_rebind_time,
+			   unsigned long dhcpd_lease_time,
+			   unsigned int inputmark)
+{
+	struct fbxbridge *br;
+
+	if (!(br = get_bridge_by_name(name)))
+		return -ENODEV;
+
+	local_bh_disable();
+	br->flags = flags;
+	br->dns1_ipaddr = dns1;
+	br->dns2_ipaddr = dns2;
+	memcpy(br->ip_aliases, ip_aliases, sizeof (br->ip_aliases));
+	br->dhcpd_renew_time = dhcpd_renew_time;
+	br->dhcpd_rebind_time = dhcpd_rebind_time;
+	br->dhcpd_lease_time = dhcpd_lease_time;
+	br->inputmark = inputmark;
+	local_bh_enable();
+
+	return 0;
+}
+
+
+
+static int find_bridge_interface(const char *dev_name, struct fbxbridge **br)
+{
+	struct fbxbridge *p;
+
+	p = fbxbridge_list;
+	while (p) {
+
+		if (strcmp(p->name, dev_name)) {
+			p = p->next;
+			continue;
+		}
+
+		if (br)
+			*br = p;
+		return 1;
+	}
+
+	return 0;
+}
+
+static int find_bridge_device(const char *dev_name, struct fbxbridge **br,
+			      bool *is_wan)
+{
+	struct fbxbridge *p;
+
+	p = fbxbridge_list;
+	while (p) {
+		if (!strcmp(p->lan_dev_name, dev_name)) {
+			if (br)
+				*br = p;
+			if (is_wan)
+				*is_wan = 0;
+			return 1;
+		}
+		if (!strcmp(p->wan_dev_name, dev_name)) {
+			if (br)
+				*br = p;
+			if (is_wan)
+				*is_wan = 1;
+			return 1;
+		}
+		p = p->next;
+	}
+
+	return 0;
+}
+
+static inline __be32 gen_wan_gw(__be32 be_ipaddr, __be32 be_netmask)
+{
+	u32 ipaddr, netmask;
+	u32 gw, mask;
+
+	ipaddr = __be32_to_cpu(be_ipaddr);
+	netmask = __be32_to_cpu(be_netmask);
+
+	gw = ipaddr & netmask;
+	mask = ~netmask;
+
+	gw |= (mask - 1);
+	if (gw == ipaddr) {
+		/*
+		 * that's unfortunate, but when in PPP mode, the
+		 * computed gateway can be the public ip address of
+		 * the customer. nevermind, pick the .253 address,
+		 * gateway ip is just a polite jocke in PPP anyway.
+		 */
+		gw &= netmask;
+		gw |= mask - 2;
+	}
+	return __cpu_to_be32(gw);
+}
+
+/*
+ * assume it is running with bh disabled
+ */
+static void __fetch_wan_addr(struct fbxbridge *br, struct in_ifaddr *ifa)
+{
+	struct net_device *dev;
+	int changed;
+
+	if (ifa) {
+		changed = 0;
+		dev = ifa->ifa_dev->dev;
+
+		if (br->wan_ipaddr != ifa->ifa_local ||
+		    br->wan_netmask != ifa->ifa_mask)
+			changed = 1;
+
+		br->wan_ipaddr = ifa->ifa_local;
+		br->wan_netmask = ifa->ifa_mask;
+		if (br->wan_netmask != 0xffffffff) {
+			br->wan_gw = gen_wan_gw(br->wan_ipaddr, br->wan_netmask);
+			br->lan_netmask = br->wan_netmask;
+		} else {
+			br->wan_gw = ifa->ifa_address;
+			br->lan_netmask = htonl(0xffffff00);
+		}
+
+		if (changed) {
+			printk(KERN_NOTICE "%s: wan inet device %s address "
+			       "changed to [%pI4]\n", br->name,
+ 			       dev->name, &br->wan_ipaddr);
+			printk("%s: %s: wan netmask: %pI4\n",
+			       br->name, dev->name, &br->wan_netmask);
+			printk("%s: %s: wan gw: %pI4\n",
+			       br->name, dev->name, &br->wan_gw);
+			__fbxbridge_fp_check(br);
+		}
+	}
+}
+
+void __fbxbridge_keep_hw_addr(struct fbxbridge *br, unsigned char *hwaddr)
+{
+	if (br->have_hw_addr && !memcmp(br->lan_hwaddr, hwaddr, ETH_ALEN))
+		return;
+
+	if (br->have_hw_addr)
+		__fbxbridge_fp_flush(br);
+
+	memcpy(br->lan_hwaddr, hwaddr, ETH_ALEN);
+	br->have_hw_addr = 1;
+	printk(KERN_NOTICE "%s: new lan hw address is now %pM\n",
+	       br->name, hwaddr);
+	__fbxbridge_fp_check(br);
+}
+
+static int add_bridge_device(const char *bridge_name, const char *dev_name,
+			     bool is_wan)
+{
+	struct net_device *dev;
+	struct fbxbridge *br;
+
+	/* check no bridge already use this device */
+	if (find_bridge_device(dev_name, NULL, NULL))
+		return -EEXIST;
+
+	if (!(br = get_bridge_by_name(bridge_name)))
+		return -ENODEV;
+
+	/* any room for a new device */
+	if (is_wan) {
+		if (br->wan_dev_name[0])
+			return -EBUSY;
+
+		strcpy(br->wan_dev_name, dev_name);
+	} else {
+		if (br->lan_dev_name[0])
+			return -EBUSY;
+
+		strcpy(br->lan_dev_name, dev_name);
+	}
+
+	/* try to resolve device */
+	if (!(dev = dev_get_by_name(&init_net, dev_name))) {
+		/* device does  not exists  yet, will wait  for device
+		 * events */
+		return 0;
+	}
+
+	local_bh_disable();
+	__grab_bridge_device(br, dev, is_wan);
+	if (is_wan) {
+		struct in_device *in_dev;
+
+		rcu_read_lock();
+
+		in_dev = __in_dev_get_rcu(dev);
+		if (in_dev)
+			__fetch_wan_addr(br, in_dev->ifa_list);
+
+		rcu_read_unlock();
+	}
+	local_bh_enable();
+	dev_put(dev);
+
+	return 0;
+}
+
+static int remove_bridge_device(const char *bridge_name, const char *dev_name)
+{
+	struct fbxbridge *br;
+	bool is_wan;
+
+	/* check bridge use this device */
+	if (!find_bridge_device(dev_name, &br, &is_wan))
+		return -ENODEV;
+
+	/* is this the one ? */
+	if (strcmp(bridge_name, br->name))
+		return -ENODEV;
+
+	local_bh_disable();
+	__ungrab_bridge_device(br, is_wan);
+	if (is_wan)
+		br->wan_dev_name[0] = 0;
+	else
+		br->lan_dev_name[0] = 0;
+	local_bh_enable();
+
+	return 0;
+}
+
+
+
+static int bridge_device_event(struct notifier_block *this,
+			       unsigned long event, void *ptr)
+{
+	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+	struct fbxbridge *br;
+	bool is_wan;
+
+	/* only interested by device that belong to a bridge */
+	if (!find_bridge_device(dev->name, &br, &is_wan))
+		return NOTIFY_DONE;
+
+	local_bh_disable();
+
+	switch (event) {
+	case NETDEV_UP:
+		__grab_bridge_device(br, dev, is_wan);
+		break;
+
+	case NETDEV_GOING_DOWN:
+	case NETDEV_DOWN:
+	case NETDEV_UNREGISTER:
+		__ungrab_bridge_device(br, is_wan);
+		break;
+
+	default:
+		break;
+	};
+
+	local_bh_enable();
+
+	return NOTIFY_DONE;
+}
+
+/*
+ * handle inet configuration event on bridge interface (fbxbr%d)
+ */
+static void bridge_inet_interface_event(struct fbxbridge *br,
+					unsigned long event,
+					struct in_ifaddr *ifa)
+{
+	int changed = 0;
+
+	switch (event) {
+	case NETDEV_UP:
+		local_bh_disable();
+		if (ifa->ifa_address && ifa->ifa_local != ifa->ifa_address) {
+			br->br_ipaddr = ifa->ifa_local;
+			br->br_remote_ipaddr = ifa->ifa_address;
+			changed = 1;
+			__fbxbridge_fp_check(br);
+		}
+		local_bh_enable();
+		break;
+
+	case NETDEV_DOWN:
+		local_bh_disable();
+		if (br->br_ipaddr) {
+			br->br_ipaddr = br->br_remote_ipaddr = 0;
+			changed = 1;
+			__fbxbridge_fp_check(br);
+		}
+		local_bh_enable();
+		break;
+
+	default:
+		return;
+	}
+
+	if (!changed)
+		return;
+
+	if (br->br_ipaddr) {
+		printk(KERN_INFO "%s: bridge interface configured: "
+		       "[%pI4 -> %pI4]\n", br->name,
+		       &br->br_ipaddr, &br->br_remote_ipaddr);
+	} else {
+		printk(KERN_INFO "%s: bridge interface unconfigured\n",
+		       br->name);
+	}
+}
+
+/*
+ * handle inet configuration event on bridge wan device
+ */
+static void bridge_inet_device_event(struct fbxbridge *br, unsigned long event,
+				     struct in_ifaddr *ifa)
+{
+	switch (event) {
+	case NETDEV_UP:
+		local_bh_disable();
+		__fetch_wan_addr(br, ifa);
+		local_bh_enable();
+		break;
+
+	case NETDEV_DOWN:
+		/* we never  clear wan address, so we  can continue to
+		 * use the bridge on lan side even if wan is down */
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+static int bridge_inet_event(struct notifier_block *this,
+			     unsigned long event, void *ptr)
+{
+	struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
+	struct net_device *dev = ifa->ifa_dev->dev;
+	struct fbxbridge *br;
+
+	/* is it a bridge or a wan device that belong to a bridge ? */
+	if (find_bridge_interface(dev->name, &br))
+		bridge_inet_interface_event(br, event, ifa);
+	else {
+		bool is_wan;
+
+		if (find_bridge_device(dev->name, &br, &is_wan) && is_wan)
+			bridge_inet_device_event(br, event, ifa);
+	}
+
+	return NOTIFY_DONE;
+}
+
+
+static struct notifier_block fbxbridge_notifier = {
+	notifier_call: bridge_device_event,
+};
+
+static struct notifier_block fbxbridge_inet_notifier = {
+	notifier_call: bridge_inet_event,
+};
+
+
+/* ioctl handling */
+static int fbxbridge_ioctl(struct net *net, unsigned int ign, void __user *arg)
+{
+	struct fbxbridge_ioctl_req	req;
+	struct fbxbridge_ioctl_chg	chg;
+	struct fbxbridge_ioctl_dev_chg	dev_chg;
+	struct fbxbridge_ioctl_params	params;
+	struct fbxbridge		*br;
+	int				ret;
+
+	/* fetch ioctl request */
+	if (access_ok(VERIFY_READ, arg, sizeof(req)) != 1)
+		return -EFAULT;
+
+	if (copy_from_user(&req, arg, sizeof (req)))
+		return -EFAULT;
+
+	switch (req.cmd) {
+	case E_CMD_BR_CHG:
+		if (access_ok(VERIFY_READ, (void *)req.arg, sizeof(chg)) != 1)
+			return -EFAULT;
+
+		if (copy_from_user(&chg, (void *)req.arg, sizeof (chg)))
+			return -EFAULT;
+
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (!chg.action)
+			return create_bridge(chg.brname);
+		return remove_bridge(chg.brname);
+
+	case E_CMD_BR_DEV_CHG:
+		if (access_ok(VERIFY_READ, (void *)req.arg,
+			      sizeof(dev_chg)) != 1)
+			return -EFAULT;
+
+		if (copy_from_user(&dev_chg, (void *)req.arg,
+				   sizeof (dev_chg)))
+			return -EFAULT;
+
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (!dev_chg.action)
+			return add_bridge_device(dev_chg.brname,
+						 dev_chg.devname,
+						 dev_chg.wan ? 1 : 0);
+
+		return remove_bridge_device(dev_chg.brname, dev_chg.devname);
+
+	case E_CMD_BR_PARAMS:
+		if (access_ok(VERIFY_READ, (void *)req.arg,
+			      sizeof(params)) != 1)
+			return -EFAULT;
+
+		if (copy_from_user(&params, (void *)req.arg, sizeof (params)))
+			return -EFAULT;
+
+		if (!params.action) {
+			/* this is a get */
+			if (!(br = get_bridge_by_name(params.brname)))
+				return -ENODEV;
+
+			local_bh_disable();
+
+			params.flags = br->flags;
+			params.dns1_addr = br->dns1_ipaddr;
+			params.dns2_addr = br->dns2_ipaddr;
+			params.have_hw_addr = br->have_hw_addr;
+			memcpy(params.ip_aliases, br->ip_aliases,
+			       sizeof (br->ip_aliases));
+			memcpy(params.lan_hwaddr, br->lan_hwaddr, ETH_ALEN);
+
+			memcpy(params.wan_dev.name, br->wan_dev_name,
+			       IFNAMSIZ);
+			if (br->ports[FBXBR_PORT_WAN].dev)
+				params.wan_dev.present = 1;
+
+			memcpy(params.lan_dev.name, br->lan_dev_name,
+			       IFNAMSIZ);
+			if (br->ports[FBXBR_PORT_LAN].dev)
+				params.lan_dev.present = 1;
+
+			params.dhcpd_renew_time = br->dhcpd_renew_time;
+			params.dhcpd_rebind_time = br->dhcpd_rebind_time;
+			params.dhcpd_lease_time = br->dhcpd_lease_time;
+			params.inputmark = br->inputmark;
+			local_bh_enable();
+
+			if (access_ok(VERIFY_WRITE, (void *)req.arg,
+				      sizeof(params)) != 1)
+				return -EFAULT;
+
+			if (copy_to_user((void *)req.arg, &params,
+					 sizeof (params)))
+				return -EFAULT;
+			return 0;
+		}
+
+		/* this is a set */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		local_bh_disable();
+		ret = set_bridge_info(params.brname, params.flags,
+				      params.dns1_addr, params.dns2_addr,
+				      params.ip_aliases,
+				      params.dhcpd_renew_time,
+				      params.dhcpd_rebind_time,
+				      params.dhcpd_lease_time,
+				      params.inputmark);
+		local_bh_enable();
+
+		return ret;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int fbxbridge_netevent_callback(struct notifier_block *nb,
+				       unsigned long event,
+				       void *data)
+{
+	struct neighbour *n;
+	struct fbxbridge *p;
+	u32 dip;
+
+	if (event != NETEVENT_NEIGH_UPDATE)
+		return 0;
+
+	n = (struct neighbour *)data;
+	if (n->nud_state & NUD_VALID)
+		return 0;
+
+	if (n->tbl->family != AF_INET)
+		return 0;
+
+	memcpy(&dip, n->primary_key, 4);
+
+	local_bh_disable();
+	p = fbxbridge_list;
+	while (p) {
+		unsigned int i;
+
+		for (i = 0; i < ARRAY_SIZE(p->ports); i++) {
+			if (p->ports[i].dev != n->dev)
+				continue;
+
+			__fbxbridge_fp_flush_by_dip(&p->ports[i], dip);
+			goto found;
+		}
+
+		p = p->next;
+	}
+
+found:
+	local_bh_enable();
+	return 0;
+}
+
+static struct notifier_block fbxbridge_netevent_nb = {
+        .notifier_call = fbxbridge_netevent_callback,
+};
+
+static int __init fbxbridge_init_module(void)
+{
+	register_netdevice_notifier(&fbxbridge_notifier);
+	register_inetaddr_notifier(&fbxbridge_inet_notifier);
+	register_netevent_notifier(&fbxbridge_netevent_nb);
+	fbxbridge_handle_frame_hook = fbxbridge_handle_frame;
+	fbxbridge_set(fbxbridge_ioctl);
+
+	return 0;
+}
+
+static void __exit fbxbridge_exit_module(void)
+{
+	unregister_netdevice_notifier(&fbxbridge_notifier);
+	unregister_netevent_notifier(&fbxbridge_netevent_nb);
+	unregister_inetaddr_notifier(&fbxbridge_inet_notifier);
+	fbxbridge_set(NULL);
+	fbxbridge_handle_frame_hook = NULL;
+}
+
+module_init(fbxbridge_init_module);
+module_exit(fbxbridge_exit_module);
+
+MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");
+MODULE_DESCRIPTION("Freebox Network Bridge - www.freebox.fr");
+MODULE_LICENSE("GPL");
diff -Nruw linux-3.11.10-fbx/net/fbxbridge./fbxbridge_dhcp.c linux-3.11.10-fbx/net/fbxbridge/fbxbridge_dhcp.c
--- linux-3.11.10-fbx/net/fbxbridge./fbxbridge_dhcp.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxbridge/fbxbridge_dhcp.c	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,461 @@
+/*
+ * fbxbridge_dhcp.c
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include <linux/netdevice.h>
+#include <linux/udp.h>
+#include <linux/netfilter.h>
+#include <net/ip.h>
+#include <asm/checksum.h>
+
+#include <linux/fbxbridge.h>
+#include <linux/fbxserial.h>
+
+#define BOOTP_REQUEST   1
+#define BOOTP_REPLY     2
+
+struct bootp_pkt {              /* BOOTP packet format */
+	struct iphdr iph;       /* IP header */
+	struct udphdr udph;     /* UDP header */
+	u8 op;                  /* 1=request, 2=reply */
+	u8 htype;               /* HW address type */
+	u8 hlen;                /* HW address length */
+	u8 hops;                /* Used only by gateways */
+	u32 xid;                /* Transaction ID */
+	u16 secs;               /* Seconds since we started */
+	u16 flags;              /* Just what it says */
+	u32 client_ip;          /* Client's IP address if known */
+	u32 your_ip;            /* Assigned IP address */
+	u32 server_ip;          /* (Next, e.g. NFS) Server's IP address */
+	u32 relay_ip;           /* IP address of BOOTP relay */
+	u8 hw_addr[16];         /* Client's HW address */
+	u8 serv_name[64];       /* Server host name */
+	u8 boot_file[128];      /* Name of boot file */
+	u8 exten[312];          /* DHCP options / BOOTP vendor extensions */
+};
+
+
+#define DHCPDISCOVER	1
+#define DHCPOFFER	2
+#define DHCPREQUEST	3
+#define DHCPDECLINE	4
+#define DHCPACK		5
+#define DHCPNACK	6
+#define DHCPRELEASE	7
+#define DHCPINFORM	8
+
+#define BROADCAST_FLAG	0x8000 /* "I need broadcast replies" */
+
+static const char *dhcp_to_name[] = {
+	"NONE",
+	"DHCPDISCOVER",
+	"DHCPOFFER",
+	"DHCPREQUEST",
+	"DHCPDECLINE",
+	"DHCPACK",
+	"DHCPNACK",
+	"DHCPRELEASE",
+	"DHCPINFORM",
+};
+
+
+#define PARAM_SUBMASK	(1 << 0)
+#define PARAM_ROUTER	(1 << 1)
+#define PARAM_DNS	(1 << 2)
+#define PARAM_BROADCAST	(1 << 3)
+
+struct dhcp_options
+{
+	u8	msg_type;
+	u32	t1;		/* renewal timeout */
+	u32	t2;		/* rebinding timemout */
+	u32	lease_time;	/* lease time */
+	u32	server_id;	/* server identifier */
+	u32	request_param;	/* requested config params (bitfield) */
+
+	u32	netmask;	/* netmask assigne to client */
+	u32	router;
+	u32	bcast;
+	u32	dns1;
+	u32	dns2;
+	u32	requested_ip;
+
+	bool	need_bcast;
+};
+
+
+static const unsigned char dhcp_magic_cookie[] = { 0x63, 0x82, 0x53, 0x63 };
+
+/* parse the dhcp options string to a struct */
+static void parse_dhcp_opts(const u8			*opts_str,
+			    int				maxlen,
+			    struct dhcp_options		*opts)
+{
+	const u8 *p, *end;
+
+	memset(opts, 0, sizeof(*opts));
+
+	/* check magic cookie */
+	if (memcmp(opts_str, dhcp_magic_cookie, sizeof(dhcp_magic_cookie)))
+		return;
+
+	/* now go for options */
+	p = opts_str + 4;
+	end = opts_str + maxlen;
+
+	while (p < end && *p != 0xff) {
+		const u8 *option;
+		size_t len, i;
+
+		option = p++;
+
+                if (*option == 0)
+                        continue;
+
+		/* jump of 'len' + 1 bytes */
+		len = *p;
+		p += len + 1;
+		if (p >= end)
+			break;
+
+		/* search for known parameter */
+		switch (*option) {
+		case 53: /* msg_type */
+			if (len)
+				opts->msg_type = option[2];
+			break;
+
+		case 55: /* param request */
+			for (i = 0; i < len; i++) {
+				switch (option[2 + i]) {
+				case 1: /* subnet */
+					opts->request_param |= PARAM_SUBMASK;
+					break;
+
+				case 3: /* router */
+					opts->request_param |= PARAM_ROUTER;
+					break;
+
+				case 6: /* dns */
+					opts->request_param |= PARAM_DNS;
+					break;
+
+				case 28: /* broadcast */
+					opts->request_param |= PARAM_BROADCAST;
+					break;
+				}
+			}
+			break;
+
+		case 50: /* requested_ip */
+			if (len >= 4)
+				memcpy(&opts->requested_ip, option + 2, 4);
+			break;
+
+		case 54: /* server_id */
+			if (len >= 4)
+				memcpy(&opts->server_id, option + 2, 4);
+			break;
+}
+	}
+}
+
+static void dump_dhcp_message(struct fbxbridge *br, struct sk_buff *skb,
+			      struct bootp_pkt *bpkt, const char *action,
+			      const char *dest)
+
+{
+	struct dhcp_options opts;
+
+	parse_dhcp_opts(bpkt->exten, skb->len - (sizeof(*bpkt) - 312),
+			&opts);
+
+	if (opts.msg_type < 9) {
+		struct iphdr *iph;
+
+		iph = ip_hdr(skb);
+		printk(KERN_DEBUG "%s: %s dhcp %s %s "
+		       "(%pI4 -> %pI4) "
+		       "(caddr: %pI4 - yaddr: %pI4 - "
+		       "saddr: %pI4 - req_addr: %pI4)\n",
+		       br->name,
+		       action,
+		       dhcp_to_name[opts.msg_type],
+		       dest,
+		       &iph->saddr,
+		       &iph->daddr,
+		       &bpkt->client_ip,
+		       &bpkt->your_ip,
+		       &bpkt->server_ip,
+		       &opts.requested_ip);
+	} else {
+		printk(KERN_DEBUG "%s: %s unknown dhcp message %s\n",
+		       br->name, action, dest);
+	}
+}
+
+/* write a the dhcp options string from a struct */
+static void make_dhcp_opts(u8				*opts_str,
+			   const struct dhcp_options	*opts,
+			   int				type)
+{
+	int len = 0;
+
+	memcpy(opts_str, dhcp_magic_cookie, sizeof(dhcp_magic_cookie));
+	len += sizeof(dhcp_magic_cookie);
+
+	/* msg type (REPLY or OFFER) */
+	opts_str[len++] = 53;
+	opts_str[len++] = 1;
+	opts_str[len++] = opts->msg_type;
+
+	/* server id */
+	opts_str[len++] = 54;
+	opts_str[len++] = 4;
+	memcpy(opts_str + len, &opts->server_id, 4);
+	len += 4;
+
+	/* t1 */
+	if (opts->t1) {
+		opts_str[len++] = 58;
+		opts_str[len++] = 4;
+		memcpy(opts_str + len, &opts->t1, 4);
+		len += 4;
+	}
+
+	/* t2 */
+	if (opts->t2) {
+		opts_str[len++] = 59;
+		opts_str[len++] = 4;
+		memcpy(opts_str + len, &opts->t2, 4);
+		len += 4;
+	}
+
+	/* lease time */
+	if (opts->lease_time) {
+		opts_str[len++] = 51;
+		opts_str[len++] = 4;
+		memcpy(opts_str + len, &opts->lease_time, 4);
+		len += 4;
+	}
+
+	/* add requested_param */
+	if (opts->request_param & PARAM_SUBMASK) {
+		opts_str[len++] = 1;
+		opts_str[len++] = 4;
+		memcpy(opts_str + len, &opts->netmask, 4);
+		len += 4;
+	}
+
+	if (opts->request_param & PARAM_ROUTER) {
+		opts_str[len++] = 3;
+		opts_str[len++] = 4;
+		memcpy(opts_str + len, &opts->router, 4);
+		len += 4;
+	}
+
+	if (opts->request_param & PARAM_BROADCAST) {
+		opts_str[len++] = 28;
+		opts_str[len++] = 4;
+		memcpy(opts_str + len, &opts->bcast, 4);
+		len += 4;
+	}
+
+	if (opts->request_param & PARAM_DNS) {
+		opts_str[len++] = 6;
+		opts_str[len++] = (opts->dns2 ? 8 : 4);
+		memcpy(opts_str + len, &opts->dns1, 4);
+		if (opts->dns2)
+			memcpy(opts_str + len + 4, &opts->dns2, 4);
+		len += (opts->dns2 ? 8 : 4);
+	}
+
+	opts_str[len++] = 255;
+}
+
+/* dhcp server */
+static void send_dhcp_reply(struct fbxbridge *br, int type,
+			    const struct bootp_pkt *src_packet,
+			    const struct dhcp_options *src_opts)
+{
+	struct sk_buff		*skb;
+	struct iphdr		*h;
+	struct bootp_pkt	*b;
+	struct dhcp_options	dhcp_opts;
+
+	/* Allocate packet */
+	skb = dev_alloc_skb(sizeof (struct bootp_pkt) + 32);
+	if (!skb)
+		return;
+
+	skb_reserve(skb, 16);
+	b = (struct bootp_pkt *)skb_put(skb, sizeof(struct bootp_pkt));
+	memset(b, 0, sizeof(struct bootp_pkt));
+
+	/* Construct IP header */
+	skb_reset_network_header(skb);
+	h = &b->iph;
+	h->version = 4;
+	h->ihl = 5;
+	h->tot_len = htons(sizeof(struct bootp_pkt));
+	h->frag_off = __constant_htons(IP_DF);
+	h->ttl = 64;
+	h->protocol = IPPROTO_UDP;
+	h->saddr = br->wan_gw;
+
+	switch (type) {
+	case DHCPOFFER:
+	case DHCPACK:
+		if (src_packet->client_ip)
+			h->daddr = src_packet->client_ip;
+                else if (src_opts->need_bcast)
+                        h->daddr = INADDR_BROADCAST;
+		else
+			h->daddr = br->wan_ipaddr;
+		break;
+
+	case DHCPNACK:
+		/* always broadcast NAK */
+		h->daddr = INADDR_BROADCAST;
+		break;
+	}
+
+	h->check = ip_fast_csum((unsigned char *) h, h->ihl);
+
+	/* Construct UDP header */
+	b->udph.source = __constant_htons(67);
+	b->udph.dest = __constant_htons(68);
+	b->udph.len = htons(sizeof(struct bootp_pkt) - sizeof(struct iphdr));
+
+	/* Construct DHCP header */
+	b->op = BOOTP_REPLY;
+	b->htype = ARPHRD_ETHER;
+	b->hlen = ETH_ALEN;
+	b->secs = 0;
+	b->xid = src_packet->xid;
+
+	switch (type) {
+	case DHCPOFFER:
+		b->server_ip = br->wan_gw;
+		b->your_ip = br->wan_ipaddr;
+		break;
+
+	case DHCPACK:
+		b->client_ip = src_packet->client_ip;
+		b->server_ip = br->wan_gw;
+		b->your_ip = br->wan_ipaddr;
+		break;
+
+	case DHCPNACK:
+		break;
+	}
+
+	b->relay_ip = src_packet->relay_ip;
+	memcpy(b->hw_addr, src_packet->hw_addr, sizeof(src_packet->hw_addr));
+
+	/* Construct DHCP options */
+	memset(&dhcp_opts, 0, sizeof (dhcp_opts));
+	dhcp_opts.msg_type = type;
+	dhcp_opts.server_id = br->wan_gw;
+
+	switch (type) {
+	case DHCPOFFER:
+	case DHCPACK:
+		dhcp_opts.t1 = htonl(br->dhcpd_renew_time);
+		dhcp_opts.t2 = htonl(br->dhcpd_rebind_time);
+		dhcp_opts.lease_time = htonl(br->dhcpd_lease_time);
+		dhcp_opts.netmask = br->lan_netmask;
+		dhcp_opts.bcast = (br->lan_netmask & br->wan_gw) |
+			~br->lan_netmask;
+		dhcp_opts.dns1 = br->dns1_ipaddr;
+		dhcp_opts.dns2 = br->dns2_ipaddr ? br->dns2_ipaddr : 0;
+		dhcp_opts.router = br->wan_gw;
+		dhcp_opts.request_param = src_opts->request_param;
+		break;
+	}
+
+	make_dhcp_opts(b->exten, &dhcp_opts, type);
+	dump_dhcp_message(br, skb, b, "sending", "to lan");
+
+	output_lan_frame(br, skb);
+}
+
+void fbxbridge_dhcpd(struct fbxbridge *br, struct sk_buff *skb)
+{
+	struct bootp_pkt *bpkt;
+	struct dhcp_options opts;
+
+	/* code assumes linear skb */
+	if (skb_linearize(skb) < 0)
+		return;
+
+	/* reject short packet */
+	if (skb->len < (sizeof(*bpkt) - 312))
+		return;
+
+	bpkt = (struct bootp_pkt *)skb->data;
+
+	/* select only valid BOOTP Request/Discover */
+	if (bpkt->op != BOOTP_REQUEST || bpkt->hlen != ETH_ALEN)
+		return;
+
+	parse_dhcp_opts(bpkt->exten, skb->len - (sizeof(*bpkt) - 312), &opts);
+        if (ntohs(bpkt->flags) & BROADCAST_FLAG)
+		opts.need_bcast = true;
+
+	dump_dhcp_message(br, skb, bpkt, "received", "from lan");
+
+	/* select DHCPDISCOVER to send a DHCPOFFER */
+	if (opts.msg_type == DHCPDISCOVER) {
+		__fbxbridge_keep_hw_addr(br, bpkt->hw_addr);
+
+		send_dhcp_reply(br, DHCPOFFER, bpkt, &opts);
+
+	} else if (opts.msg_type == DHCPREQUEST) {
+
+		__fbxbridge_keep_hw_addr(br, bpkt->hw_addr);
+
+		/* send ACK or NACK */
+		if (!opts.requested_ip) {
+			/* RENEWING/REBINDING */
+			if (!bpkt->client_ip) {
+				/* invalid packet; ignore */
+				return;
+			}
+
+			if (bpkt->client_ip != br->wan_ipaddr)
+				send_dhcp_reply(br, DHCPNACK, bpkt, &opts);
+			else
+				send_dhcp_reply(br, DHCPACK, bpkt, &opts);
+			return;
+
+		}
+
+		/* INIT-REBOOT or SELECTING */
+		if (bpkt->client_ip) {
+			/* invalid packet; ignore */
+			return;
+		}
+
+		if (!opts.server_id) {
+			/* INIT-REBOOT */
+			if (opts.requested_ip != br->wan_ipaddr)
+				send_dhcp_reply(br, DHCPNACK, bpkt, &opts);
+			else
+				send_dhcp_reply(br, DHCPACK, bpkt, &opts);
+			return;
+		}
+
+		/* SELECTING */
+		if (opts.server_id == br->wan_gw) {
+			/* client selected us */
+			send_dhcp_reply(br, DHCPACK, bpkt, &opts);
+		} else {
+			/* ignore */
+		}
+	}
+}
diff -Nruw linux-3.11.10-fbx/net/fbxbridge./fbxbridge_fastpath.c linux-3.11.10-fbx/net/fbxbridge/fbxbridge_fastpath.c
--- linux-3.11.10-fbx/net/fbxbridge./fbxbridge_fastpath.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxbridge/fbxbridge_fastpath.c	2015-09-29 11:15:09.001756505 +0200
@@ -0,0 +1,316 @@
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/jhash.h>
+#include <linux/if_vlan.h>
+#include <net/ip.h>
+
+#include <linux/fbxbridge.h>
+
+static inline u32 rule_hash(u32 sip, u32 dip, u16 sport, u16 dport)
+{
+	return jhash_3words(sip, dip, sport | dport << 16, 0);
+}
+
+static struct fbxbridge_fp_rule *
+lookup_rule(const struct fbxbridge_fp *fp, u32 hash,
+	    u32 sip, u32 dip, u16 sport, u16 dport)
+{
+	struct fbxbridge_fp_rule *r;
+
+	hlist_for_each_entry(r, &fp->hrules[hash % FBXBR_CACHE_SIZE], hnext) {
+		/* compare entry */
+		if (r->sip == sip && r->dip == dip &&
+		    r->sport == sport && r->dport == dport)
+			return r;
+	}
+	return NULL;
+}
+
+static struct fbxbridge_port *get_iport_svlan(struct net_device *idev,
+					      u16 vlan)
+{
+	struct fbxbridge *br;
+
+	br = fbxbridge_list;
+	do {
+		struct fbxbridge_port *iport;
+
+		if (!br->fast_path_enabled) {
+			br = br->next;
+			continue;
+		}
+
+		iport = &br->ports[FBXBR_PORT_WAN];
+		if (iport->master_dev == idev &&
+		    iport->vlan1 == vlan &&
+		    iport->vlan2 == 0)
+			return iport;
+
+		iport = &br->ports[FBXBR_PORT_LAN];
+		if (iport->master_dev == idev &&
+		    iport->vlan1 == vlan &&
+		    iport->vlan2 == 0)
+			return iport;
+
+		br = br->next;
+
+	} while (br);
+
+	return NULL;
+}
+
+static int __fp_in_ether_vlan(struct net_device *idev,
+			      struct sk_buff *skb,
+			      bool is_tcp)
+{
+	struct fbxbridge_fp_rule *rule;
+	struct fbxbridge_port *iport;
+	struct iphdr *iph;
+	struct vlan_hdr *vhdr;
+	u16 sport, dport;
+	u32 hash;
+
+	vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN);
+	iport = get_iport_svlan(idev, ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK);
+	if (!iport)
+		return 0;
+
+	skb_set_network_header(skb, VLAN_ETH_HLEN);
+	iph = ip_hdr(skb);
+	if (is_tcp) {
+		struct tcphdr *tcph;
+		tcph = (struct tcphdr *)((void *)iph + iph->ihl * 4);
+		sport = tcph->source;
+		dport = tcph->dest;
+	} else {
+		struct udphdr *udph;
+		udph = (struct udphdr *)((void *)iph + iph->ihl * 4);
+		sport = udph->source;
+		dport = udph->dest;
+	}
+
+	hash = rule_hash(iph->saddr, iph->daddr, sport, dport);
+	rule = lookup_rule(&iport->tcp_fp, hash,
+			   iph->saddr, iph->daddr, sport, dport);
+	if (!rule)
+		return 0;
+
+	skb->fbxbridge_state = 2;
+	skb->protocol = htons(ETH_P_8021Q);
+	skb->dev = rule->oport->master_dev;
+
+	memcpy(skb->data, rule->dest_hwaddr, 6);
+	memcpy(skb->data + 6, skb->dev->dev_addr, 6);
+	vhdr->h_vlan_TCI = htons(rule->oport->vlan1);
+	dev_queue_xmit(skb);
+	return 1;
+}
+
+int __fbxbridge_fp_in_vlan_tcp4(struct net_device *idev, struct sk_buff *skb)
+{
+	return __fp_in_ether_vlan(idev, skb, true);
+}
+
+int __fbxbridge_fp_in_vlan_udp4(struct net_device *idev, struct sk_buff *skb)
+{
+	return __fp_in_ether_vlan(idev, skb, false);
+}
+
+static int __fbxbridge_fp_add(struct fbxbridge *br,
+			      const struct sk_buff *skb,
+			      const uint8_t *new_dest_hw_addr,
+			      struct fbxbridge_port *iport,
+			      struct fbxbridge_port *oport)
+{
+	struct fbxbridge_fp_rule *rule;
+	const struct iphdr *iph;
+	struct fbxbridge_fp *fp;
+	u32 hash;
+	u16 sport, dport;
+
+	if (!br->fast_path_enabled)
+		return 0;
+
+	iph = ip_hdr(skb);
+	switch (iph->protocol) {
+	case IPPROTO_UDP:
+	{
+		struct udphdr *udph;
+
+		udph = (struct udphdr *)skb_transport_header(skb);
+		sport = udph->source;
+		dport = udph->dest;
+		fp = &iport->udp_fp;
+		break;
+	}
+
+	case IPPROTO_TCP:
+	{
+		struct tcphdr *tcph;
+
+		tcph = (struct tcphdr *)skb_transport_header(skb);
+		/* ignore unless SYN */
+		if (!tcph->syn)
+			return 0;
+		sport = tcph->source;
+		dport = tcph->dest;
+		fp = &iport->tcp_fp;
+		break;
+	}
+
+	default:
+		return 0;
+	}
+
+	hash = rule_hash(iph->saddr, iph->daddr, sport, dport);
+	rule = lookup_rule(fp, hash, iph->saddr, iph->daddr, sport, dport);
+	if (rule)
+		return 0;
+
+	/* add new entry */
+	if (fp->count < FBXBR_MAX_RULES) {
+		rule = kmalloc(sizeof (*rule), GFP_ATOMIC);
+		if (!rule)
+			return 1;
+		fp->count++;
+	} else {
+		rule = list_first_entry(&fp->rules, struct fbxbridge_fp_rule,
+					next);
+		hlist_del(&rule->hnext);
+		list_del(&rule->next);
+	}
+
+	rule->sip = iph->saddr;
+	rule->dip = iph->daddr;
+	rule->sport = sport;
+	rule->dport = dport;
+	if (new_dest_hw_addr)
+		memcpy(rule->dest_hwaddr, new_dest_hw_addr, 6);
+	rule->oport = oport;
+	hlist_add_head(&rule->hnext, &fp->hrules[hash % FBXBR_CACHE_SIZE]);
+	list_add_tail(&rule->next, &fp->rules);
+
+	return 0;
+}
+
+int __fbxbridge_fp_add_wan_to_lan(struct fbxbridge *br,
+				  const struct sk_buff *skb,
+				  const uint8_t *new_dest_hw_addr)
+{
+	return __fbxbridge_fp_add(br, skb, new_dest_hw_addr,
+				  &br->ports[FBXBR_PORT_WAN],
+				  &br->ports[FBXBR_PORT_LAN]);
+}
+
+int __fbxbridge_fp_add_lan_to_wan(struct fbxbridge *br,
+				  const struct sk_buff *skb,
+				  const uint8_t *new_dest_hw_addr)
+{
+	return __fbxbridge_fp_add(br, skb, new_dest_hw_addr,
+				  &br->ports[FBXBR_PORT_LAN],
+				  &br->ports[FBXBR_PORT_WAN]);
+}
+
+void fbxbridge_fp_init(struct fbxbridge *br)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(br->ports); i++) {
+		struct fbxbridge_port *bport = &br->ports[i];
+		size_t j;
+
+		INIT_LIST_HEAD(&bport->tcp_fp.rules);
+		for (j = 0; j < FBXBR_CACHE_SIZE; j++)
+			INIT_HLIST_HEAD(&bport->tcp_fp.hrules[j]);
+		bport->tcp_fp.count = 0;
+		INIT_LIST_HEAD(&bport->udp_fp.rules);
+		for (j = 0; j < FBXBR_CACHE_SIZE; j++)
+			INIT_HLIST_HEAD(&bport->udp_fp.hrules[j]);
+		bport->udp_fp.count = 0;
+	}
+}
+
+static void __flush_by_dip(struct fbxbridge_fp *fp, uint32_t dip)
+{
+	struct fbxbridge_fp_rule *rule, *tmp;
+
+	list_for_each_entry_safe(rule, tmp, &fp->rules, next) {
+		if (rule->dip != dip)
+			continue;
+
+		hlist_del(&rule->hnext);
+		list_del(&rule->next);
+		kfree(rule);
+		fp->count--;
+	}
+}
+
+void __fbxbridge_fp_flush_by_dip(struct fbxbridge_port *bport, uint32_t dip)
+{
+	__flush_by_dip(&bport->tcp_fp, dip);
+	__flush_by_dip(&bport->udp_fp, dip);
+}
+
+void __fbxbridge_fp_flush(struct fbxbridge *br)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(br->ports); i++) {
+		struct fbxbridge_port *bport = &br->ports[i];
+		struct fbxbridge_fp_rule *rule, *tmp;
+		struct fbxbridge_fp *fp;
+
+		fp = &bport->tcp_fp;
+		list_for_each_entry_safe(rule, tmp, &fp->rules, next)
+			kfree(rule);
+
+		fp = &bport->udp_fp;
+		list_for_each_entry_safe(rule, tmp, &fp->rules, next)
+			kfree(rule);
+	}
+	fbxbridge_fp_init(br);
+}
+
+void fbxbridge_fp_flush_all(void)
+{
+	struct fbxbridge *br;
+
+	local_bh_disable();
+	br = fbxbridge_list;
+	while (br) {
+		if (br->fast_path_enabled)
+			__fbxbridge_fp_flush(br);
+		br = br->next;
+	}
+	local_bh_enable();
+}
+
+void __fbxbridge_fp_check(struct fbxbridge *br)
+{
+	struct fbxbridge_port *lport, *wport;
+	bool enabled;
+
+	lport = &br->ports[FBXBR_PORT_LAN];
+	wport = &br->ports[FBXBR_PORT_WAN];
+
+	enabled = (lport->dev && wport->dev &&
+		   lport->dev->type == wport->dev->type &&
+		   !(!!lport->vlan1 ^ !!wport->vlan1) &&
+		   !(!!lport->vlan2 ^ !!wport->vlan2) &&
+		   br->have_hw_addr &&
+		   br->br_ipaddr &&
+		   br->wan_ipaddr);
+
+	if (!(enabled ^ br->fast_path_enabled)) {
+		if (br->fast_path_enabled)
+			__fbxbridge_fp_flush(br);
+		return;
+	}
+
+	br->fast_path_enabled = enabled;
+	printk(KERN_INFO "%s: fastpath is now %s\n",
+	       br->name,
+	       enabled ? "enabled" : "disabled");
+	if (!br->fast_path_enabled)
+		__fbxbridge_fp_flush(br);
+}
diff -Nruw linux-3.11.10-fbx/net/fbxbridge./fbxbridge_filter.c linux-3.11.10-fbx/net/fbxbridge/fbxbridge_filter.c
--- linux-3.11.10-fbx/net/fbxbridge./fbxbridge_filter.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxbridge/fbxbridge_filter.c	2015-09-29 11:15:09.005756542 +0200
@@ -0,0 +1,204 @@
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <linux/netfilter.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <linux/fbxbridge.h>
+
+#ifdef CONFIG_NETFILTER
+/*
+ * not available in any public header inside include/ :(
+ */
+unsigned int nf_iterate(struct list_head *head,
+			struct sk_buff *skb,
+			unsigned int hook,
+			const struct net_device *indev,
+			const struct net_device *outdev,
+			struct list_head **i,
+			int (*okfn)(struct sk_buff *),
+			int hook_thresh);
+
+static int lolfn(struct sk_buff *skb)
+{
+	return 0;
+}
+
+/*
+ * We can't use NF_HOOK directly here as it will kfree_skb() if it is
+ * to be dropped.
+ */
+int fbxbridge_nf_hook(struct fbxbridge *br, uint8_t pf, unsigned int hook,
+		      struct sk_buff *skb, struct net_device *in,
+		      struct net_device *out)
+{
+	struct list_head *elem;
+	int verdict;
+
+	rcu_read_lock();
+	elem = &nf_hooks[pf][hook];
+	verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, in, out, &elem,
+			     lolfn, INT_MIN);
+	rcu_read_unlock();
+	return verdict;
+}
+
+/*
+ * invoke netfilter FORWARD table for finer grained control
+ */
+static int
+netfilter_lan_to_wan_packet(struct fbxbridge *br, struct sk_buff *skb)
+{
+	if (!br->ports[FBXBR_PORT_WAN].dev || !br->ports[FBXBR_PORT_LAN].dev)
+		return NF_DROP;
+
+	skb->nfct = &nf_ct_untracked_get()->ct_general;
+	skb->nfctinfo = IP_CT_NEW;
+	nf_conntrack_get(skb->nfct);
+
+	return fbxbridge_nf_hook(br, NFPROTO_IPV4,
+				 NF_INET_FORWARD, skb,
+				 br->dev,
+				 br->ports[FBXBR_PORT_WAN].dev);
+}
+
+/*
+ * invoke netfilter FORWARD table for finer grained control
+ */
+static int
+netfilter_wan_to_lan_packet(struct fbxbridge *br, struct sk_buff *skb)
+{
+	if (!br->ports[FBXBR_PORT_WAN].dev || !br->ports[FBXBR_PORT_LAN].dev)
+		return NF_DROP;
+
+	skb->nfct = &nf_ct_untracked_get()->ct_general;
+	skb->nfctinfo = IP_CT_NEW;
+	nf_conntrack_get(skb->nfct);
+
+	return fbxbridge_nf_hook(br, NFPROTO_IPV4,
+				 NF_INET_FORWARD, skb,
+				 br->ports[FBXBR_PORT_WAN].dev,
+				 br->dev);
+}
+
+#else
+static inline int
+netfilter_lan_to_wan_packet(struct fbxbridge *br, struct sk_buff *skb)
+{ return NF_ACCEPT; }
+
+static inline int
+netfilter_wan_to_lan_packet(struct fbxbridge *br, struct sk_buff *skb)
+{ return NF_ACCEPT; }
+#endif
+
+
+static int
+filter_lan_to_wan_packet(struct fbxbridge *br, struct sk_buff *skb)
+{
+	struct iphdr *ip = ip_hdr(skb);
+
+	/* disallow source spoofing */
+	if (ip->saddr != br->wan_ipaddr)
+		return NF_DROP;
+
+	/* disallow all private net destination */
+	if ((ntohl(ip->daddr) & 0xff000000) == 0x0a000000)
+		return NF_DROP;
+
+	if ((ntohl(ip->daddr) & 0xfff00000) == 0xac100000)
+		return NF_DROP;
+
+	if ((ntohl(ip->daddr) & 0xffff0000) == 0xc0a80000)
+		return NF_DROP;
+
+	/* no multicast please */
+	if (IN_MULTICAST(ntohl(ip->daddr)))
+		return NF_DROP;
+
+	/* Don't let IP broadcast go through us */
+	if (ip->daddr == INADDR_ANY)
+		return NF_DROP;
+
+	if (ip->daddr == INADDR_BROADCAST)
+		return NF_DROP;
+
+	return NF_ACCEPT;
+}
+
+/*
+ * note: caller assured that ip header is valid
+ */
+int
+fbxbridge_filter_lan_to_wan_packet(struct fbxbridge *br, struct sk_buff *skb)
+{
+	int ret = NF_ACCEPT;
+
+	if ((br->flags & (FBXBRIDGE_FLAGS_FILTER))) {
+		ret = filter_lan_to_wan_packet(br, skb);
+		if (ret != NF_ACCEPT)
+			return ret;
+	}
+
+	if ((br->flags & FBXBRIDGE_FLAGS_NETFILTER))
+		ret = netfilter_lan_to_wan_packet(br, skb);
+
+	return ret;
+}
+
+static int filter_wan_to_lan_packet(struct fbxbridge *br, struct sk_buff *skb)
+{
+	struct iphdr *ip = ip_hdr(skb);
+
+	/* discard packet with obvious bad source */
+	if (IN_LOOPBACK(ntohl(ip->saddr)))
+		return NF_DROP;
+
+	/* give ipv6 in ip private to freebox back to the
+	 * kernel */
+	if (ip->protocol == IPPROTO_IPV6) {
+		struct ipv6hdr *iph6;
+		unsigned int hlen;
+
+		/* sanity check on header value */
+		hlen = ip->ihl * 4;
+		if (skb->len < hlen + sizeof(struct ipv6hdr))
+			return NF_DROP;
+
+		iph6 = (struct ipv6hdr *)((unsigned char *)ip + hlen);
+		if ((iph6->daddr.s6_addr32[0] & htonl(0xfffffff0)) ==
+		    htonl(0x2a010e30))
+			return NF_STOP;
+	}
+
+	/* keep ETHER_IP packets */
+	if (ip->protocol == 97)
+		return NF_STOP;
+
+	return NF_ACCEPT;
+}
+
+/*
+ * note: caller assured that ip header is valid
+ */
+int
+fbxbridge_filter_wan_to_lan_packet(struct fbxbridge *br, struct sk_buff *skb)
+{
+	int ret = NF_ACCEPT;
+
+	if ((br->flags & (FBXBRIDGE_FLAGS_FILTER))) {
+		ret = filter_wan_to_lan_packet(br, skb);
+		if (ret != NF_ACCEPT) {
+			return ret;
+		}
+	}
+
+	if ((br->flags & FBXBRIDGE_FLAGS_NETFILTER))
+		ret = netfilter_wan_to_lan_packet(br, skb);
+
+	return ret;
+}
diff -Nruw linux-3.11.10-fbx/net/fbxbridge./fbxbridge_forward.c linux-3.11.10-fbx/net/fbxbridge/fbxbridge_forward.c
--- linux-3.11.10-fbx/net/fbxbridge./fbxbridge_forward.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxbridge/fbxbridge_forward.c	2015-09-29 11:15:09.005756542 +0200
@@ -0,0 +1,362 @@
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/udp.h>
+#include <net/arp.h>
+
+#include <linux/pkt_sched.h>
+#include <linux/ip.h>
+#include <linux/netfilter.h>
+#include <net/ip.h>
+#include <net/sock.h>
+#include <net/route.h>
+
+#include <linux/fbxbridge.h>
+
+#ifdef CONFIG_NET_SCH_INGRESS
+#include <net/pkt_sched.h>
+#endif
+
+#ifdef CONFIG_NET_CLS_ACT
+static int ing_filter(struct sk_buff *skb)
+{
+        struct netdev_queue *rxq = rcu_dereference(skb->dev->ingress_queue);
+	struct net_device *dev = skb->dev;
+        u32 ttl = G_TC_RTTL(skb->tc_verd);
+	int result = TC_ACT_OK;
+	struct Qdisc *q;
+
+	if (!rxq || rxq->qdisc == &noop_qdisc)
+		goto out;
+
+	if (unlikely(MAX_RED_LOOP < ttl++)) {
+		if (net_ratelimit())
+			pr_warning( "Redir loop detected Dropping packet (%d->%d)\n",
+			       skb->skb_iif, dev->ifindex);
+		return TC_ACT_SHOT;
+	}
+
+	skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd);
+	skb->tc_verd = SET_TC_RTTL(skb->tc_verd, 0);
+	skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_INGRESS);
+
+	q = rxq->qdisc;
+	if (q != &noop_qdisc) {
+		spin_lock(qdisc_lock(q));
+		if (likely(!test_bit(__QDISC_STATE_DEACTIVATED, &q->state)))
+			result = qdisc_enqueue_root(skb, q);
+		spin_unlock(qdisc_lock(q));
+	}
+
+out:
+	return result;
+}
+#endif
+
+#ifdef CONFIG_NETFILTER
+static int fbxbridge_skb_set_inputmark(struct fbxbridge *br, struct sk_buff *skb)
+{
+	if (skb->mark & br->inputmark) {
+		if (net_ratelimit()) {
+			printk(KERN_WARNING "fbxbridge: %s: input mark "
+			       "already set on skb %p\n", br->name, skb);
+		}
+		return 0;
+	}
+	skb->mark |= br->inputmark;
+	return 1;
+}
+
+static inline void fbxbridge_skb_clear_inputmark(struct fbxbridge *br,
+						 struct sk_buff *skb)
+{
+	skb->mark &= ~br->inputmark;
+}
+#endif
+
+static int handle_wan_frame(struct fbxbridge *br, struct sk_buff *skb)
+{
+	struct iphdr *iph;
+	int ret;
+	int verdict;
+
+	/*
+	 * filter only valid ip packet;
+	 */
+	if (skb->protocol != __constant_htons(ETH_P_IP))
+		return NF_STOP;
+
+	if (fbxbridge_check_ip_packet(skb)) {
+		kfree_skb(skb);
+		return NF_DROP;
+	}
+
+	iph = ip_hdr(skb);
+	if (iph->frag_off & htons(IP_OFFSET)) {
+		/* don't filter frags */
+		goto done;
+	}
+
+	if (fbxbridge_check_udp_tcp_packet(skb)) {
+		kfree_skb(skb);
+		return NF_DROP;
+	}
+
+#ifdef CONFIG_NETFILTER
+	if (br->flags & FBXBRIDGE_FLAGS_NETFILTER) {
+		int changed = fbxbridge_skb_set_inputmark(br, skb);
+		verdict = fbxbridge_nf_hook(br, NFPROTO_IPV4, NF_INET_LOCAL_IN,
+					    skb, skb->dev, NULL);
+		if (changed)
+			fbxbridge_skb_clear_inputmark(br, skb);
+
+		if (verdict == NF_ACCEPT || verdict == NF_STOP) {
+			/*
+			 * let it enter via the WAN interface.
+			 */
+			return NF_STOP;
+		}
+	}
+#endif
+
+	ret = fbxbridge_filter_wan_to_lan_packet(br, skb);
+	if (ret != NF_ACCEPT) {
+		if (ret == NF_DROP)
+			kfree_skb(skb);
+		return ret;
+	}
+	skb->fbxbridge_state = 1;
+
+	/* don't handle packet unless wan is up */
+	if (!br->wan_ipaddr) {
+		kfree_skb(skb);
+		return NF_DROP;
+	}
+
+done:
+#ifdef CONFIG_NET_CLS_ACT
+	/* pass it in ingress policer if frame is to be bridged */
+	switch (ing_filter(skb)) {
+	case TC_ACT_SHOT:
+	case TC_ACT_STOLEN:
+		kfree_skb(skb);
+		return NF_DROP;
+	}
+
+	skb->tc_verd = 0;
+#endif
+
+	output_lan_frame(br, skb);
+	return ret;
+}
+
+static void handle_arp_frame(struct fbxbridge *br, struct sk_buff *skb)
+{
+	unsigned int sender_ipaddr, target_ipaddr;
+	unsigned char *sender_hwaddr, *req;
+	struct arphdr *arp;
+
+	/* sanity check on packet */
+	if (skb->len < ARP_ETHER_SIZE)
+		return;
+
+	arp = (struct arphdr *)skb->data;
+
+	if (arp->ar_hrd != __constant_htons(ARPHRD_ETHER) &&
+	    arp->ar_hrd != __constant_htons(ARPHRD_IEEE802))
+		return;
+
+	if (arp->ar_pro != __constant_htons(ETH_P_IP))
+		return;
+
+	if (arp->ar_hln != ETH_ALEN)
+		return;
+
+	if (arp->ar_pln != 4)
+		return;
+
+	if (arp->ar_op != __constant_htons(ARPOP_REQUEST) &&
+	    arp->ar_op != __constant_htons(ARPOP_REPLY))
+		return;
+
+	/* fetch subfields */
+	req = (unsigned char *)(arp + 1);
+
+	sender_hwaddr = req;
+	req += ETH_ALEN;
+
+	memcpy(&sender_ipaddr, req, 4);
+	req += 4;
+
+	/* skip target_hwaddr */
+	req += ETH_ALEN;
+
+	memcpy(&target_ipaddr, req, 4);
+
+	/* ignore gratuitous ARP */
+	if (!sender_ipaddr)
+		return;
+
+	if (arp->ar_op == __constant_htons(ARPOP_REQUEST)) {
+
+		/* client is sending an arp request */
+		if (!br->wan_ipaddr) {
+			/* wan is down, our address is not known,
+			 * answer to every arp requests */
+
+			/* ignore what looks like gratuitous ARP */
+			if (sender_ipaddr == target_ipaddr)
+				return;
+
+		} else {
+			/* wan is up, filter our arp reply to match
+			 * WAN */
+
+			/* accept only arp from remote client */
+			if (sender_ipaddr != br->wan_ipaddr)
+				return;
+
+			/* accept only arp request for wan network */
+			if ((target_ipaddr & br->lan_netmask) !=
+			    (br->wan_ipaddr & br->lan_netmask))
+				return;
+
+			/* request is for the client's address, keep quiet */
+			if (target_ipaddr == br->wan_ipaddr)
+				return;
+		}
+
+		/* ok I can answer */
+		output_arp_frame(br, skb->dev, ARPOP_REPLY,
+				 target_ipaddr,
+				 skb->dev->dev_addr,
+				 br->wan_ipaddr,
+				 sender_hwaddr);
+
+		/* keep the client address */
+		__fbxbridge_keep_hw_addr(br, sender_hwaddr);
+
+	} else {
+
+		/* accept only arp from remote client */
+		if (sender_ipaddr != br->wan_ipaddr)
+			return;
+
+		/* we received  an arp reply,  if it was  addressed to
+		 * us, then keep the client mac address  */
+		if (target_ipaddr != br->wan_gw)
+			return;
+
+		__fbxbridge_keep_hw_addr(br, sender_hwaddr);
+	}
+}
+
+static inline int is_local_ip(struct fbxbridge *br, unsigned long ipaddr)
+{
+	int i;
+
+	if (ipaddr == br->br_ipaddr || ipv4_is_multicast(ipaddr))
+		return 1;
+
+	for (i = 0; i < MAX_ALIASES; i++) {
+		if (br->ip_aliases[i] && br->ip_aliases[i] == ipaddr)
+			return 1;
+	}
+
+	return 0;
+}
+
+static int handle_lan_frame(struct fbxbridge *br, struct sk_buff *skb)
+{
+	struct iphdr *iph;
+	int ret;
+
+	/* handle non ip frame (arp) now */
+	if (skb->protocol == __constant_htons(ETH_P_ARP)) {
+		handle_arp_frame(br, skb);
+		kfree_skb(skb);
+		return NF_DROP;
+	}
+
+	/*
+	 * filter only valid ip packet;
+	 */
+	if (skb->protocol != __constant_htons(ETH_P_IP))
+		return NF_STOP;
+
+	if (fbxbridge_check_ip_packet(skb)) {
+		kfree_skb(skb);
+		return NF_DROP;
+	}
+
+	iph = ip_hdr(skb);
+
+	/* look  the destination  address, if  talking to  our private
+	 * address or alias, then frame is local */
+	if (is_local_ip(br, iph->daddr)) {
+		handle_local_input_lan_frame(br, skb);
+		return NF_ACCEPT;
+	}
+
+	/* don't handle packet unless wan is up */
+	if (!br->wan_ipaddr) {
+		kfree_skb(skb);
+		return NF_DROP;
+	}
+
+	if (iph->frag_off & htons(IP_OFFSET)) {
+		/* don't filter frags */
+		goto done;
+	}
+
+	if (fbxbridge_check_udp_tcp_packet(skb)) {
+		kfree_skb(skb);
+		return NF_DROP;
+	}
+	iph = ip_hdr(skb);
+
+	if ((br->flags & FBXBRIDGE_FLAGS_DHCPD) &&
+	    iph->protocol == IPPROTO_UDP) {
+		struct udphdr *udp;
+
+		udp = (struct udphdr *)skb_transport_header(skb);
+		if (udp->dest == htons(67)) {
+			fbxbridge_dhcpd(br, skb);
+			kfree_skb(skb);
+			return NF_DROP;
+		}
+	}
+
+	ret = fbxbridge_filter_lan_to_wan_packet(br, skb);
+	if (ret != NF_ACCEPT) {
+		if (ret == NF_DROP)
+			kfree_skb(skb);
+		return ret;
+	}
+	skb->fbxbridge_state = 1;
+
+done:
+	output_wan_frame(br, skb);
+	return ret;
+}
+
+struct sk_buff *fbxbridge_handle_frame(struct fbxbridge *br,
+				       struct sk_buff *skb)
+{
+	int ret;
+
+	/* if bridge interface is down, do nothing */
+	if (!(br->dev->flags & IFF_UP))
+		return skb;
+
+	/* check if frame is coming from lan or wan */
+	if (skb->dev == br->ports[FBXBR_PORT_WAN].dev)
+		ret = handle_wan_frame(br, skb);
+	else
+		ret = handle_lan_frame(br, skb);
+
+	/* tell kernel if the packet has been consumed or not */
+	return (ret != NF_STOP ? NULL : skb);
+}
diff -Nruw linux-3.11.10-fbx/net/fbxbridge./fbxbridge_local.c linux-3.11.10-fbx/net/fbxbridge/fbxbridge_local.c
--- linux-3.11.10-fbx/net/fbxbridge./fbxbridge_local.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxbridge/fbxbridge_local.c	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,65 @@
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/pkt_sched.h>
+
+#include <linux/ip.h>
+
+#include <linux/fbxbridge.h>
+
+void handle_local_input_lan_frame(struct fbxbridge *br, struct sk_buff *skb)
+{
+	if (!br->br_remote_ipaddr) {
+		kfree_skb(skb);
+		return;
+	}
+
+	/* packet comes from lan, snat it and make it local */
+	fbxbridge_snat_packet(skb, br->br_remote_ipaddr);
+	skb->dev = br->dev;
+	skb->pkt_type = PACKET_HOST;
+	br->dev->stats.rx_packets++;
+	br->dev->stats.rx_bytes += skb->len;
+	netif_rx(skb);
+}
+
+int handle_local_output_frame(struct fbxbridge *br, struct sk_buff *skb)
+{
+	struct iphdr *ip;
+
+	/* if no wan addr, we can't do anything */
+	if (!br->wan_ipaddr) {
+		kfree_skb(skb);
+		return 1;
+	}
+
+	/*
+	 * filter only valid packets
+	 */
+	if (skb->protocol != __constant_htons(ETH_P_IP)) {
+		kfree_skb(skb);
+		return 1;
+	}
+
+        if (fbxbridge_check_ip_packet(skb)) {
+                kfree_skb(skb);
+                return 1;
+	}
+	ip = ip_hdr(skb);
+
+	if (ipv4_is_multicast(ip->daddr)) {
+		output_lan_mcast_frame(br, skb);
+		return 0;
+	}
+
+	if (ip->daddr != br->br_remote_ipaddr) {
+		kfree_skb(skb);
+		return 1;
+	}
+
+	fbxbridge_dnat_packet(skb, br->wan_ipaddr);
+	output_lan_frame(br, skb);
+	return 0;
+}
diff -Nruw linux-3.11.10-fbx/net/fbxbridge./fbxbridge_output.c linux-3.11.10-fbx/net/fbxbridge/fbxbridge_output.c
--- linux-3.11.10-fbx/net/fbxbridge./fbxbridge_output.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxbridge/fbxbridge_output.c	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,217 @@
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+
+#include <linux/timer.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <net/ip.h>
+#include <net/arp.h>
+
+#include <linux/fbxbridge.h>
+
+static unsigned char hw_bcast[ETH_ALEN] = { 0xff, 0xff, 0xff,
+					    0xff, 0xff, 0xff };
+
+static unsigned char hw_zero[ETH_ALEN] = { 0 };
+
+void output_arp_frame(struct fbxbridge *br, struct net_device *dev,
+		      unsigned short type,
+		      unsigned long src_ip, unsigned char *src_hw,
+		      unsigned long target_ip, unsigned char *target_hw)
+{
+	struct arphdr *arp;
+	struct sk_buff *skb;
+	unsigned char *arp_ptr;
+
+	/* prepare arp packet */
+	skb = dev_alloc_skb(ARP_ETHER_SIZE + dev->hard_header_len + 15);
+	if (unlikely (!skb))
+		return;
+
+	skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+	skb_reset_network_header(skb);
+	arp = (struct arphdr *)skb_network_header(skb);
+	skb_put(skb, ARP_ETHER_SIZE);
+
+	skb->dev = dev;
+	skb->protocol = htons(ETH_P_ARP);
+
+	arp->ar_hrd = htons(dev->type);
+	arp->ar_pro = htons(ETH_P_IP);
+	arp->ar_hln = dev->addr_len;
+	arp->ar_pln = 4;
+	arp->ar_op = htons(type);
+
+	arp_ptr = (unsigned char *)(arp + 1);
+
+	memcpy(arp_ptr, src_hw, dev->addr_len);
+	arp_ptr += dev->addr_len;
+	memcpy(arp_ptr, &src_ip, 4);
+	arp_ptr += 4;
+	memcpy(arp_ptr, target_hw, dev->addr_len);
+	arp_ptr += dev->addr_len;
+	memcpy(arp_ptr, &target_ip, 4);
+
+	/* handle promiscous mode on bridge */
+	if (unlikely(br->dev->flags & IFF_PROMISC)) {
+		struct sk_buff *skb2;
+
+		if ((skb2 = skb_clone(skb, GFP_ATOMIC))) {
+			skb2->dev = br->dev;
+			skb2->pkt_type = PACKET_HOST;
+			netif_rx(skb2);
+		}
+	}
+
+	if (dev_hard_header(skb, dev, ETH_P_ARP,
+			    target_hw == hw_zero ? hw_bcast : target_hw,
+			    src_hw, skb->len) < 0) {
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	dev_queue_xmit(skb);
+}
+
+void output_lan_mcast_frame(struct fbxbridge *br, struct sk_buff *skb)
+{
+	struct iphdr *ip;
+	char mcast_hwaddr[6];
+	uint32_t daddr;
+
+	/* only send mcast if we have an active device */
+	if (!br->ports[FBXBR_PORT_LAN].dev) {
+		kfree(skb);
+		return;
+	}
+
+	ip = ip_hdr(skb);
+
+	/* compute mcast hwaddr */
+	mcast_hwaddr[0] = 0x1;
+	mcast_hwaddr[1] = 0x0;
+	mcast_hwaddr[2] = 0x5e;
+	daddr = ntohl(ip->daddr);
+	mcast_hwaddr[3] = (daddr & 0x7f0000) >> 16;
+	mcast_hwaddr[4] = (daddr & 0xff00) >> 8;
+	mcast_hwaddr[5] = (daddr & 0xff);
+
+	skb->dev = br->ports[FBXBR_PORT_LAN].dev;
+	dev_hard_header(skb, skb->dev, ETH_P_802_3,
+			mcast_hwaddr, skb->dev->dev_addr, ETH_P_IP);
+
+	dev_queue_xmit(skb);
+}
+
+void output_lan_frame(struct fbxbridge *br, struct sk_buff *skb)
+{
+	struct iphdr *iph;
+
+	if (!br->ports[FBXBR_PORT_LAN].dev) {
+		kfree_skb(skb);
+		return;
+	}
+
+	iph = ip_hdr(skb);
+
+	if ((!br->have_hw_addr && iph->daddr != INADDR_BROADCAST)) {
+
+		/* (fixme: try to queue instead of dropping ?) */
+		kfree_skb(skb);
+
+		/* rate limit arp sending to ARP_RATE_LIMIT  */
+		if (time_before(jiffies, br->last_arp_send + ARP_RATE_LIMIT))
+			return;
+		br->last_arp_send = jiffies;
+
+		output_arp_frame(br, br->ports[FBXBR_PORT_LAN].dev,
+				 ARPOP_REQUEST,
+				 br->wan_gw,
+				 br->ports[FBXBR_PORT_LAN].dev->dev_addr,
+				 br->wan_ipaddr,
+				 hw_zero);
+		return;
+	}
+
+	/* we have  an active device, send  to the hw addr  if we have
+	 * it, or to  the bcast hw addr if we don't  or the packet is
+	 * an ip broadcast */
+	skb->dev = br->ports[FBXBR_PORT_LAN].dev;
+	dev_hard_header(skb, skb->dev, ETH_P_802_3,
+			(br->have_hw_addr &&
+			 iph->daddr != INADDR_BROADCAST) ?
+			br->lan_hwaddr : hw_bcast,
+			skb->dev->dev_addr, ETH_P_IP);
+
+	if (skb->fbxbridge_state == 1)
+		__fbxbridge_fp_add_wan_to_lan(br, skb, br->lan_hwaddr);
+
+	dev_queue_xmit(skb);
+}
+
+/*
+ * queue the packet on the wan device
+ */
+void output_wan_frame(struct fbxbridge *br, struct sk_buff *skb)
+{
+	if (!br->ports[FBXBR_PORT_WAN].dev) {
+		kfree_skb(skb);
+		return;
+	}
+
+	skb->dev = br->ports[FBXBR_PORT_WAN].dev;
+	if (skb->dev->type == ARPHRD_ETHER) {
+		struct neighbour *n;
+		struct iphdr *iph;
+		__be32 nexthop;
+
+		/*
+		 * on FTTH (wan dev->type == ARPHRD_ETHER is our cue
+		 * in this case) get nexthop address, if nexthop is
+		 * outside local wan, it is the wan_gw.
+		 */
+		iph = ip_hdr(skb);
+		nexthop = iph->daddr;
+		if ((nexthop & br->wan_netmask) !=
+		    (br->wan_ipaddr & br->wan_netmask))
+				nexthop = br->wan_gw;
+
+		/*
+		 * get a neighbour, possibly creating it.
+		 */
+		n = __neigh_lookup(&arp_tbl, &nexthop, skb->dev, 1);
+		if (!n)
+			return ;
+
+		if ((n->nud_state & NUD_VALID) == 0) {
+			/*
+			 * no MAC address for this neighbour (yet),
+			 * trigger ARP state machine via
+			 * neigh_event_send() and drop skb.
+			 */
+
+			if (net_ratelimit())
+				printk("%pI4 is invalid (state 0x%x).\n",
+				       &nexthop, n->nud_state);
+			neigh_event_send(n, NULL);
+			neigh_release(n);
+			kfree_skb(skb);
+			return ;
+		} else
+			neigh_event_send(n, NULL);
+
+		if (skb->fbxbridge_state == 1)
+			__fbxbridge_fp_add_lan_to_wan(br, skb, n->ha);
+
+		dev_hard_header(skb, skb->dev, ETH_P_802_3, n->ha,
+				skb->dev->dev_addr, ETH_P_IP);
+		neigh_release(n);
+	} else {
+		if (skb->fbxbridge_state == 1)
+			__fbxbridge_fp_add_lan_to_wan(br, skb, NULL);
+	}
+	dev_queue_xmit(skb);
+}
diff -Nruw linux-3.11.10-fbx/net/fbxbridge./fbxbridge_utils.c linux-3.11.10-fbx/net/fbxbridge/fbxbridge_utils.c
--- linux-3.11.10-fbx/net/fbxbridge./fbxbridge_utils.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxbridge/fbxbridge_utils.c	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,142 @@
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <linux/netfilter_ipv4.h>
+#include <net/ip.h>
+#include <net/tcp.h>
+#include <asm/checksum.h>
+
+#include <linux/fbxbridge.h>
+
+int fbxbridge_check_ip_packet(struct sk_buff *skb)
+{
+	const struct iphdr *iph;
+
+	if (!pskb_may_pull(skb, sizeof (*iph)))
+		return 1;
+
+	iph = (struct iphdr *)skb->data;
+
+	if (iph->ihl < 5 || iph->version != 4)
+		return 1;
+
+	if (!pskb_may_pull(skb, iph->ihl * 4))
+		return 1;
+
+	iph = (struct iphdr *)skb->data;
+
+	if (ntohs(iph->tot_len) > skb->len)
+		return 1;
+
+	skb_reset_network_header(skb);
+	skb->transport_header = skb->network_header + iph->ihl * 4;
+
+	return 0;
+}
+
+int fbxbridge_check_udp_tcp_packet(struct sk_buff *skb)
+{
+	const struct iphdr *iph;
+
+	iph = ip_hdr(skb);
+
+	switch (iph->protocol) {
+	case IPPROTO_UDP:
+		if (!pskb_may_pull(skb, skb_transport_offset(skb) +
+				   sizeof (struct udphdr)))
+			return 1;
+		break;
+	case IPPROTO_TCP:
+		if (!pskb_may_pull(skb, skb_transport_offset(skb) +
+				   sizeof (struct tcphdr)))
+			return 1;
+		break;
+	}
+	return 0;
+}
+
+/*
+ * do source or destination nat
+ */
+static void recalculate_checksum(struct sk_buff *skb, u32 osaddr, u32 odaddr)
+{
+	struct iphdr *iph;
+	u16 check;
+
+	iph = ip_hdr(skb);
+	if (iph->frag_off & htons(IP_OFFSET)) {
+		printk("frag => no checksum\n");
+		return;
+	}
+
+	if (fbxbridge_check_udp_tcp_packet(skb))
+		return;
+
+	iph = ip_hdr(skb);
+
+	switch (iph->protocol) {
+	case IPPROTO_TCP:
+	{
+		struct tcphdr *tcph;
+
+		tcph = (struct tcphdr *)skb_transport_header(skb);
+		check = tcph->check;
+		if (skb->ip_summed != CHECKSUM_COMPLETE)
+			check = ~check;
+		check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, check);
+		check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
+		if (skb->ip_summed == CHECKSUM_COMPLETE)
+			check = ~check;
+		tcph->check = check;
+		break;
+	}
+
+	case IPPROTO_UDP:
+	{
+		struct udphdr *udph;
+
+		udph = (struct udphdr *)skb_transport_header(skb);
+		check = udph->check;
+		if (check != 0) {
+			check = csum_tcpudp_magic(iph->saddr, iph->daddr,
+						  0, 0, ~check);
+			check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0,
+						  ~check);
+			udph->check = check ? : 0xFFFF;
+		}
+		break;
+	}
+	}
+}
+
+void fbxbridge_snat_packet(struct sk_buff *skb, unsigned long new_addr)
+{
+	struct iphdr	*ip;
+	unsigned long	oaddr;
+
+	ip = ip_hdr(skb);
+	oaddr = ip->saddr;
+	ip->saddr = new_addr;
+	ip->check = 0;
+	ip->check = ip_fast_csum((unsigned char *) ip, ip->ihl);
+	recalculate_checksum(skb, oaddr, ip->daddr);
+}
+
+void fbxbridge_dnat_packet(struct sk_buff *skb, unsigned long new_addr)
+{
+	struct iphdr	*ip;
+	unsigned long	oaddr;
+
+	ip = ip_hdr(skb);
+	oaddr = ip->daddr;
+	ip->daddr = new_addr;
+	ip->check = 0;
+	ip->check = ip_fast_csum((unsigned char *) ip, ip->ihl);
+	recalculate_checksum(skb, ip->saddr, oaddr);
+}
diff -Nruw linux-3.11.10-fbx/net/fbxbridge./Kconfig linux-3.11.10-fbx/net/fbxbridge/Kconfig
--- linux-3.11.10-fbx/net/fbxbridge./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxbridge/Kconfig	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,6 @@
+
+#
+# Freebox bridge
+#
+config FBXBRIDGE
+	bool "Freebox Bridge"
diff -Nruw linux-3.11.10-fbx/net/fbxbridge./Makefile linux-3.11.10-fbx/net/fbxbridge/Makefile
--- linux-3.11.10-fbx/net/fbxbridge./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/net/fbxbridge/Makefile	2015-06-12 14:02:27.822997480 +0200
@@ -0,0 +1,8 @@
+
+obj-$(CONFIG_FBXBRIDGE)	+= fbxbridge.o
+
+fbxbridge-objs := 	fbxbridge_dev.o fbxbridge_forward.o	\
+			fbxbridge_local.o fbxbridge_output.o	\
+			fbxbridge_utils.o fbxbridge_filter.o	\
+			fbxbridge_dhcp.o fbxbridge_fastpath.o
+
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/net/ipv4/ip_ffn.c	2015-09-29 11:15:09.013756597 +0200
@@ -0,0 +1,677 @@
+/*
+ * IP fast forwarding and NAT
+ *
+ * Very restrictive code, that only cope non fragmented UDP and TCP
+ * packets, that are routed and NATed with no other modification.
+ *
+ * Provide a fast path for established conntrack entries so that
+ * packets go out ASAP.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/jhash.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+
+#define FFN_CACHE_SIZE		128
+#define MAX_FFN_ENTRY		512
+
+struct ffn_data {
+	u8 new_tos;
+	u32 new_sip;
+	u32 new_dip;
+	u16 new_sport;
+	u16 new_dport;
+	int force_skb_prio : 1;
+	int alter : 1;
+	int tos_change : 1;
+	unsigned int new_skb_prio;
+	u32 new_mark;
+	struct dst_entry *dst;
+	struct nf_conn *ct;
+	enum ip_conntrack_info ctinfo;
+};
+
+struct ffn_lookup_entry {
+	int added_when;
+	u32 sip;
+	u32 dip;
+	u16 sport;
+	u16 dport;
+	u8 protocol;
+#ifdef CONFIG_IP_FFN_PROCFS
+	uint64_t forwarded_bytes;
+	uint32_t forwarded_packets;
+#endif
+	struct list_head next;
+	struct ffn_data manip;
+	struct list_head all_next;
+};
+
+static DEFINE_SPINLOCK(ffn_lock);
+static struct list_head ffn_cache[FFN_CACHE_SIZE];
+static struct list_head ffn_all;
+static unsigned int ffn_entry_count;
+
+/*
+ * hash on five parameter
+ */
+static inline unsigned int ffn_hash(u32 sip, u32 dip, u16 sport, u16 dport,
+				    int is_tcp)
+{
+	return jhash_3words(sip, is_tcp ? dip : ~dip, sport | dport << 16, 0);
+}
+
+/*
+ * attempt to find entry with given value in cache
+ */
+static struct ffn_lookup_entry *__ffn_find(u32 sip, u32 dip,
+					   u16 sport, u16 dport,
+					   u8 protocol,
+					   unsigned int hash)
+{
+	struct ffn_lookup_entry *tmp;
+
+	list_for_each_entry(tmp, &ffn_cache[hash % FFN_CACHE_SIZE], next) {
+
+		/* compare entry */
+		if (tmp->sip == sip && tmp->dip == dip &&
+		    tmp->sport == sport && tmp->dport == dport &&
+		    tmp->protocol == protocol)
+			return tmp;
+	}
+	return NULL;
+}
+
+static struct ffn_lookup_entry *__ffn_get(u32 sip, u32 dip,
+					  u16 sport, u16 dport,
+					  int is_tcp)
+{
+	unsigned int hash;
+	u8 protocol;
+
+	/* lookup entry in cache */
+	protocol = (is_tcp) ? IPPROTO_TCP : IPPROTO_UDP;
+	hash = ffn_hash(sip, dip, sport, dport, is_tcp);
+	return __ffn_find(sip, dip, sport, dport, protocol, hash);
+}
+
+static void __ffn_remove_entry(struct ffn_lookup_entry *e)
+{
+	list_del(&e->next);
+	list_del(&e->all_next);
+	ffn_entry_count--;
+	dst_release(e->manip.dst);
+	kfree(e);
+}
+
+static int __ffn_add_entry(struct ffn_lookup_entry *e,
+			   u8 proto, unsigned int hash)
+{
+	/* make sure it's not present */
+	if (__ffn_find(e->sip, e->dip, e->sport, e->dport, proto, hash))
+		return 1;
+
+	if (ffn_entry_count >= MAX_FFN_ENTRY)
+		return 1;
+
+	/* add new entry */
+	list_add_tail(&e->next, &ffn_cache[hash % FFN_CACHE_SIZE]);
+	list_add_tail(&e->all_next, &ffn_all);
+	ffn_entry_count++;
+	return 0;
+}
+
+/*
+ * helper to recompute checksum
+ */
+static inline u16 recheck4(u32 oldvalinv, u32 newval,
+			   u32 oldvalinv2, u32 newval2,
+			   u32 oldvalinv3, u32 newval3,
+			   u32 oldvalinv4, u32 newval4,
+			   u16 oldcheck)
+{
+	u32 diffs[] = { oldvalinv, newval, oldvalinv2, newval2,
+			oldvalinv3, newval3,
+			oldvalinv4, newval4 };
+
+	return csum_fold(csum_partial((char *)diffs, sizeof (diffs),
+				      oldcheck ^ 0xFFFF));
+}
+
+static inline __sum16 tcp_v4_check(int len, __be32 saddr,
+				   __be32 daddr, __wsum base)
+{
+	return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_TCP, base);
+}
+
+/*
+ * two hooks into netfilter code
+ */
+extern int external_tcpv4_packet(struct nf_conn *ct,
+				 const struct sk_buff *skb,
+				 unsigned int dataoff,
+				 enum ip_conntrack_info ctinfo);
+
+extern int external_udpv4_packet(struct nf_conn *ct,
+				 const struct sk_buff *skb,
+				 unsigned int dataoff,
+				 enum ip_conntrack_info ctinfo);
+
+extern int ip_local_deliver_finish(struct sk_buff *skb);
+
+/*
+ * check if packet is in ffn cache, or mark it if it can be added
+ * later
+ */
+int ip_ffn_process(struct sk_buff *skb)
+{
+	struct ffn_lookup_entry *e;
+	struct nf_conntrack *nfct;
+	struct iphdr *iph;
+	struct tcphdr *tcph = NULL;
+	struct udphdr *udph = NULL;
+	u16 tcheck;
+	u8 proto;
+	int res;
+
+	if (!net_eq(dev_net(skb->dev), &init_net))
+		goto not_ffnable;
+
+	iph = ip_hdr(skb);
+
+	/* refuse fragmented IP packet, or packets with IP options */
+	if (iph->ihl > 5 || (iph->frag_off & htons(IP_MF | IP_OFFSET)))
+		goto not_ffnable;
+
+	/* refuse fragmented IP packet */
+	if (iph->frag_off & htons(IP_MF | IP_OFFSET))
+		goto not_ffnable;
+
+	/* check encapsulated protocol is udp or tcp */
+	if (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP)
+		goto not_ffnable;
+
+	if (iph->ttl <= 1)
+		goto not_ffnable;
+
+	proto = iph->protocol;
+	if (proto == IPPROTO_TCP) {
+		if (skb_headlen(skb) < (iph->ihl * 4) + sizeof (struct tcphdr))
+			goto not_ffnable;
+		tcph = (struct tcphdr *)((unsigned char *)iph +
+					 (iph->ihl * 4));
+
+		if (tcph->doff * 4 < sizeof (struct tcphdr) ||
+		    skb_headlen(skb) < iph->ihl * 4 + tcph->doff * 4)
+			goto not_ffnable;
+
+		spin_lock_bh(&ffn_lock);
+		e = __ffn_get(iph->saddr, iph->daddr, tcph->source,
+			      tcph->dest, 1);
+	} else {
+
+		if (skb_headlen(skb) < (iph->ihl * 4) + sizeof (struct udphdr))
+			goto not_ffnable;
+		udph = (struct udphdr *)((unsigned char *)iph +
+					 (iph->ihl * 4));
+		spin_lock_bh(&ffn_lock);
+		e = __ffn_get(iph->saddr, iph->daddr, udph->source,
+			      udph->dest, 0);
+	}
+
+	if (!e) {
+		spin_unlock_bh(&ffn_lock);
+		goto ffnable;
+	}
+
+	if (e->manip.dst->obsolete > 0) {
+		__ffn_remove_entry(e);
+		spin_unlock_bh(&ffn_lock);
+		goto ffnable;
+	}
+
+	nfct = &e->manip.ct->ct_general;
+	nf_conntrack_get(nfct);
+
+	if (proto == IPPROTO_TCP) {
+		/* do sequence number checking and update
+		 * conntrack info */
+		res = external_tcpv4_packet(e->manip.ct, skb, iph->ihl * 4,
+					    e->manip.ctinfo);
+		tcheck = tcph->check;
+	} else {
+		res = external_udpv4_packet(e->manip.ct, skb, iph->ihl * 4,
+					    e->manip.ctinfo);
+		tcheck = udph->check;
+	}
+
+	if (unlikely(res != NF_ACCEPT)) {
+		/* packet rejected by conntrack, unless asked to drop,
+		 * send it back into kernel */
+		spin_unlock_bh(&ffn_lock);
+		nf_conntrack_put(nfct);
+
+		if (res == NF_DROP) {
+			dev_kfree_skb(skb);
+			return 0;
+		}
+
+		goto ffnable;
+	}
+
+	if (!e->manip.alter)
+		goto fix_ip_hdr;
+
+	if (skb->ip_summed != CHECKSUM_PARTIAL) {
+		/* fix ports & transport protocol checksum */
+		if (proto == IPPROTO_TCP) {
+			tcheck = recheck4(~iph->saddr, e->manip.new_sip,
+					  ~iph->daddr, e->manip.new_dip,
+					  tcph->source ^ 0xFFFF,
+					  e->manip.new_sport,
+					  tcph->dest ^ 0xFFFF,
+					  e->manip.new_dport,
+					  tcheck);
+			tcph->source = e->manip.new_sport;
+			tcph->dest = e->manip.new_dport;
+			tcph->check = tcheck;
+		} else {
+			tcheck = recheck4(~iph->saddr, e->manip.new_sip,
+					  ~iph->daddr, e->manip.new_dip,
+					  udph->source ^ 0xFFFF,
+					  e->manip.new_sport,
+					  udph->dest ^ 0xFFFF,
+					  e->manip.new_dport,
+					  tcheck);
+			udph->source = e->manip.new_sport;
+			udph->dest = e->manip.new_dport;
+			if (udph->check)
+				udph->check = tcheck ? tcheck : 0xffff;
+		}
+
+	} else {
+		unsigned int len;
+
+		/*
+		 * assume tcph->check only covers ip pseudo header, so
+		 * don't update checksum wrt port change
+		 *
+		 * we might check skb->csum_offset to confirm that
+		 * this is a valid assertion
+		 */
+		if (proto == IPPROTO_TCP) {
+			len = skb->len - ((void *)tcph - (void *)iph);
+			tcheck = ~csum_tcpudp_magic(e->manip.new_sip,
+						    e->manip.new_dip,
+						    len, IPPROTO_TCP, 0);
+			tcph->check = tcheck;
+			tcph->source = e->manip.new_sport;
+			tcph->dest = e->manip.new_dport;
+		} else {
+			len = skb->len - ((void *)udph - (void *)iph);
+			if (udph->check) {
+				tcheck = ~csum_tcpudp_magic(e->manip.new_sip,
+							    e->manip.new_dip,
+							    len,
+							    IPPROTO_UDP, 0);
+				udph->check = tcheck ? tcheck : 0xffff;
+			}
+			udph->source = e->manip.new_sport;
+			udph->dest = e->manip.new_dport;
+		}
+	}
+
+fix_ip_hdr:
+	/* update IP header field */
+	iph->ttl--;
+	iph->saddr = e->manip.new_sip;
+	iph->daddr = e->manip.new_dip;
+
+	if (e->manip.tos_change)
+		iph->tos = e->manip.new_tos;
+
+	iph->check = 0;
+	iph->check = ip_fast_csum((u8 *)iph, 5);
+
+	/* forward skb */
+	if (e->manip.force_skb_prio)
+		skb->priority = e->manip.new_skb_prio;
+	else
+		skb->priority = rt_tos2priority(iph->tos);
+
+	skb->mark = e->manip.new_mark;
+
+#ifdef CONFIG_IP_FFN_PROCFS
+	e->forwarded_packets++;
+	e->forwarded_bytes += skb->len;
+#endif
+
+	skb_dst_set(skb, dst_clone(e->manip.dst));
+	spin_unlock_bh(&ffn_lock);
+
+	if (nfct == skb->nfct) {
+		/*
+		 * skbs to/from localhost will have the conntrack
+		 * already set, don't leak references here.
+		 */
+		nf_conntrack_put(nfct);
+	} else {
+		if (unlikely(skb->nfct != NULL)) {
+			/*
+			 * conntrack is not NULL here and it is not
+			 * the same as the one we have in the
+			 * ffn_entry, this shoud not happen, warn once
+			 * and switch to slow path.
+			 */
+			WARN_ONCE(skb->nfct != NULL,
+				  "weird skb->nfct %p, NULL was expected\n",
+				  skb->nfct);
+			printk_once(KERN_WARNING "ffn entry:\n"
+				    " added_when: %i\n"
+				    " sip: %pI4 -> %pI4\n"
+				    " dip: %pI4 -> %pI4\n"
+				    " sport: %u -> %u\n"
+				    " dport: %u -> %u\n",
+				    e->added_when,
+				    &e->sip, &e->manip.new_sip,
+				    &e->dip, &e->manip.new_dip,
+				    htons(e->sport), htons(e->manip.new_sport),
+				    htons(e->dport), htons(e->manip.new_dport));
+			goto not_ffnable;
+		}
+		skb->nfct = nfct;
+		skb->nfctinfo = e->manip.ctinfo;
+	}
+
+	skb->ffn_state = FFN_STATE_FAST_FORWARDED;
+	if (e->added_when == IP_FFN_FINISH_OUT)
+		dst_output(skb);
+	else
+		ip_local_deliver_finish(skb);
+
+	return 0;
+
+ffnable:
+	skb->ffn_state = FFN_STATE_FORWARDABLE;
+	skb->ffn_orig_tos = iph->tos;
+	return 1;
+
+not_ffnable:
+	skb->ffn_state = FFN_STATE_INCOMPATIBLE;
+	return 1;
+}
+
+/*
+ * check if skb is candidate for ffn, and if so add it to ffn cache
+ *
+ * called after post routing
+ */
+void ip_ffn_add(struct sk_buff *skb, int when)
+{
+	struct nf_conn *ct;
+	struct nf_conntrack_tuple *tuple, *rtuple;
+	enum ip_conntrack_info ctinfo;
+	struct ffn_lookup_entry *e;
+	struct iphdr *iph;
+	unsigned int hash;
+	int dir;
+	u8 proto;
+
+	if (!net_eq(dev_net(skb->dev), &init_net))
+		return;
+
+	if (ffn_entry_count >= MAX_FFN_ENTRY)
+		return;
+
+	iph = ip_hdr(skb);
+
+	if (!skb->nfct || (skb_dst(skb)->output != ip_output &&
+			   when == IP_FFN_FINISH_OUT))
+		return;
+
+	ct = nf_ct_get(skb, &ctinfo);
+	if ((ctinfo != IP_CT_ESTABLISHED) &&
+	    (ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY))
+		return;
+
+	if (nfct_help(ct))
+		return;
+
+	dir = (ctinfo == IP_CT_ESTABLISHED) ?
+		IP_CT_DIR_ORIGINAL : IP_CT_DIR_REPLY;
+	tuple = &ct->tuplehash[dir].tuple;
+
+	if (tuple->dst.protonum != IPPROTO_TCP &&
+	    tuple->dst.protonum != IPPROTO_UDP)
+		return;
+
+	rtuple = &ct->tuplehash[1 - dir].tuple;
+
+	e = kmalloc(sizeof (*e), GFP_ATOMIC);
+	if (!e)
+		return;
+
+	e->added_when = when;
+	e->sip = tuple->src.u3.ip;
+	e->dip = tuple->dst.u3.ip;
+	e->sport = tuple->src.u.all;
+	e->dport = tuple->dst.u.all;
+	e->protocol = tuple->dst.protonum;
+
+#ifdef CONFIG_IP_FFN_PROCFS
+	e->forwarded_packets = 0;
+	e->forwarded_bytes = 0;
+#endif
+
+	e->manip.new_sip = rtuple->dst.u3.ip;
+	e->manip.new_dip = rtuple->src.u3.ip;
+	e->manip.new_sport = rtuple->dst.u.all;
+	e->manip.new_dport = rtuple->src.u.all;
+
+	if (e->manip.new_sip == e->sip &&
+	    e->manip.new_dip == e->dip &&
+	    e->manip.new_sport == e->sport &&
+	    e->manip.new_dport == e->dport)
+		e->manip.alter = 0;
+	else
+		e->manip.alter = 1;
+
+	if (skb->ffn_orig_tos != iph->tos) {
+		e->manip.tos_change = 1;
+		e->manip.new_tos = iph->tos;
+	} else
+		e->manip.tos_change = 0;
+
+	if (skb->priority != rt_tos2priority(iph->tos)) {
+		e->manip.force_skb_prio = 1;
+		e->manip.new_skb_prio = skb->priority;
+	} else
+		e->manip.force_skb_prio = 0;
+
+	e->manip.new_mark = skb->mark;
+	e->manip.dst = skb_dst(skb);
+	dst_hold(e->manip.dst);
+	e->manip.ct = ct;
+	e->manip.ctinfo = ctinfo;
+
+	hash = ffn_hash(e->sip, e->dip, e->sport, e->dport,
+			e->protocol == IPPROTO_TCP);
+	proto = (e->protocol == IPPROTO_TCP) ? IPPROTO_TCP : IPPROTO_UDP;
+
+	spin_lock_bh(&ffn_lock);
+	if (__ffn_add_entry(e, proto, hash)) {
+		spin_unlock_bh(&ffn_lock);
+		dst_release(e->manip.dst);
+		kfree(e);
+		return;
+	}
+	spin_unlock_bh(&ffn_lock);
+}
+
+/*
+ * netfilter callback when conntrack is about to be destroyed
+ */
+void ip_ffn_ct_destroy(struct nf_conn *ct)
+{
+	struct nf_conntrack_tuple *tuple;
+	struct ffn_lookup_entry *e;
+	int dir;
+
+	/* locate all entry that use this conntrack */
+	for (dir = 0; dir < 2; dir++) {
+		tuple = &ct->tuplehash[dir].tuple;
+
+		if (tuple->dst.protonum != IPPROTO_TCP &&
+		    tuple->dst.protonum != IPPROTO_UDP)
+			return;
+
+		spin_lock_bh(&ffn_lock);
+		e = __ffn_get(tuple->src.u3.ip, tuple->dst.u3.ip,
+			      tuple->src.u.all, tuple->dst.u.all,
+			      tuple->dst.protonum == IPPROTO_TCP);
+		if (e)
+			__ffn_remove_entry(e);
+		spin_unlock_bh(&ffn_lock);
+	}
+}
+
+/*
+ * initialize ffn cache data
+ */
+static void __ip_ffn_init_cache(void)
+{
+	int i;
+
+	for (i = 0; i < FFN_CACHE_SIZE; i++)
+		INIT_LIST_HEAD(&ffn_cache[i]);
+	INIT_LIST_HEAD(&ffn_all);
+	ffn_entry_count = 0;
+}
+
+/*
+ * flush all ffn cache
+ */
+void ip_ffn_flush_all(void)
+{
+	struct ffn_lookup_entry *e, *tmp;
+
+	spin_lock_bh(&ffn_lock);
+	list_for_each_entry_safe(e, tmp, &ffn_all, all_next)
+		__ffn_remove_entry(e);
+	__ip_ffn_init_cache();
+	spin_unlock_bh(&ffn_lock);
+}
+
+#ifdef CONFIG_IP_FFN_PROCFS
+struct proc_dir_entry *proc_net_ip_ffn;
+
+static int ip_ffn_entries_show(struct seq_file *m, void *v)
+{
+	int i;
+
+	spin_lock_bh(&ffn_lock);
+
+	for (i = 0; i < FFN_CACHE_SIZE; ++i) {
+		struct ffn_lookup_entry *e;
+
+		if (list_empty(&ffn_cache[i]))
+			continue;
+
+		seq_printf(m, "Bucket %i:\n", i);
+		list_for_each_entry (e, &ffn_cache[i], next) {
+			seq_printf(m, " Protocol: ");
+			switch (e->protocol) {
+			case IPPROTO_TCP:
+				seq_printf(m, "TCPv4\n");
+				break;
+			case IPPROTO_UDP:
+				seq_printf(m, "UDPv4\n");
+				break;
+			default:
+				seq_printf(m, "ipproto_%i\n", e->protocol);
+				break;
+			}
+			seq_printf(m, " Original flow: %pI4:%u -> %pI4:%u\n",
+				   &e->sip,
+				   ntohs(e->sport),
+				   &e->dip,
+				   ntohs(e->dport));
+
+			if (e->sip != e->manip.new_sip ||
+			    e->dip != e->manip.new_dip ||
+			    e->sport != e->manip.new_sport ||
+			    e->dport != e->manip.new_dport) {
+				seq_printf(m,
+					   " Modified flow: %pI4:%u -> "
+					   "%pI4:%u\n",
+					   &e->manip.new_sip,
+					   ntohs(e->manip.new_sport),
+					   &e->manip.new_dip,
+					   ntohs(e->manip.new_dport));
+			}
+
+			seq_printf(m, "  Forwarded packets: %u\n",
+				   e->forwarded_packets);
+			seq_printf(m, "  Forwarded bytes: %llu\n",
+				   e->forwarded_bytes);
+			seq_printf(m, "\n");
+		}
+	}
+
+	spin_unlock_bh(&ffn_lock);
+	return 0;
+}
+
+static int ip_ffn_entries_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ip_ffn_entries_show, NULL);
+}
+
+static const struct file_operations ip_ffn_entries_fops = {
+	.owner = THIS_MODULE,
+	.open	= ip_ffn_entries_open,
+	.release = single_release,
+	.read = seq_read,
+	.llseek = seq_lseek,
+};
+
+
+static int __init __ip_ffn_init_procfs(void)
+{
+	proc_net_ip_ffn = proc_net_mkdir(&init_net, "ip_ffn",
+					 init_net.proc_net);
+	if (!proc_net_ip_ffn) {
+		printk(KERN_ERR "proc_mkdir() has failed for 'net/ip_ffn'.\n");
+		return -1;
+	}
+
+	if (proc_create("entries", 0400, proc_net_ip_ffn,
+			&ip_ffn_entries_fops) == NULL) {
+		printk(KERN_ERR "proc_create() has failed for "
+		       "'net/ip_ffn/entries'.\n");
+		return -1;
+	}
+	return 0;
+}
+#endif
+
+/*
+ * initialize ffn
+ */
+void __init ip_ffn_init(void)
+{
+	printk("IP Fast Forward and NAT enabled\n");
+	__ip_ffn_init_cache();
+
+#ifdef CONFIG_IP_FFN_PROCFS
+	if (__ip_ffn_init_procfs() < 0)
+		printk(KERN_WARNING "IP FFN: unable to create proc entries.\n");
+#endif
+}
diff -Nruw linux-3.11.10-fbx/security/fbxlsmjail./Kconfig linux-3.11.10-fbx/security/fbxlsmjail/Kconfig
--- linux-3.11.10-fbx/security/fbxlsmjail./Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/security/fbxlsmjail/Kconfig	2015-09-29 11:15:09.201758030 +0200
@@ -0,0 +1,7 @@
+config SECURITY_FBXLSMJAIL
+	bool "Freebox jail Support"
+	depends on SECURITY
+	default n
+	help
+	  This selects Freebox jailing LSM.
+
diff -Nruw linux-3.11.10-fbx/security/fbxlsmjail./Makefile linux-3.11.10-fbx/security/fbxlsmjail/Makefile
--- linux-3.11.10-fbx/security/fbxlsmjail./Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-3.11.10-fbx/security/fbxlsmjail/Makefile	2015-09-29 11:15:09.201758030 +0200
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_SECURITY_FBXLSMJAIL)	+= fbxlsmjail.o
--- /dev/null	2014-07-28 16:39:28.978429694 +0200
+++ linux-3.11.10-fbx/sound/soc/kirkwood/kirkwood-fbxgw2r.c	2015-09-29 11:15:09.389759460 +0200
@@ -0,0 +1,243 @@
+/*
+ * kirkwood-fbxgw2r.c
+ *
+ * Modified-from: kirkwood-rd88f6282a.c, which was before
+ * Modified-from: kirkwood-openrd.c
+ * Which is:
+ * (c) 2010 Arnaud Patard <apatard@mandriva.com>
+ * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <mach/kirkwood.h>
+#include <asm/mach-types.h>
+#include "../codecs/cs42l52.h"
+
+#if 0
+static void dump_registers(struct snd_soc_codec *codec)
+{
+	int i;
+
+	for (i = 0; i < 0x40; ++i) {
+		printk("%02x: %02x\n", i, codec->read(codec, i));
+	}
+}
+#endif
+
+static int fbxgw2r_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+	u8 reg;
+
+#if 0
+	printk("Default register configuration:\n");
+	dump_registers(codec);
+#endif
+
+	/*
+	 * make sure we correctly transition from speaker to headphone
+	 * and vice&versa.
+	 */
+	codec->write(codec, 0x4, 0x05);
+
+	/*
+	 * select input4a/input4b for capture
+	 */
+	reg = codec->read(codec, 0x8);
+	reg &= ~(7 << 5);
+	reg |= (3 << 5);
+	codec->write(codec, 0x8, reg);
+
+	reg = codec->read(codec, 0x9);
+	reg &= ~(7 << 5);
+	reg |= (3 << 5);
+	codec->write(codec, 0x9, reg);
+
+	/*
+	 * set headphone analog gain to 1.000
+	 */
+	reg = codec->read(codec, 0x0d);
+	reg &= ~0xe0;
+	reg |= 0xc0;
+	codec->write(codec, 0x0d, reg);
+
+	return 0;
+}
+
+static int fbxgw2r_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int ret;
+	unsigned int fmt;
+	int freq = 0;
+
+	fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS;
+	ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+	if (ret < 0)
+		return ret;
+	ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+	if (ret < 0)
+		return ret;
+
+
+	switch (params_rate(params)) {
+	default:
+	case 44100:
+		freq = 11289600;
+		break;
+	case 96000:
+	case 48000:
+		freq = 12288000;
+		break;
+	}
+
+	return snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN);
+}
+
+static int fbxgw2r_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec *codec = rtd->codec_dai->codec;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		u8 reg;
+
+		/*
+		 * power up ADC A & B
+		 */
+		reg = snd_soc_read(codec, 0x2);
+		reg &= ~(3 << 1);
+		snd_soc_write(codec, 0x2, reg);
+
+		/*
+		 * unmute ADC A mixer volume
+		 */
+		reg = snd_soc_read(codec, 0x18);
+		reg &= ~(1 << 7);
+		snd_soc_write(codec, 0x18, reg);
+
+		/*
+		 * unmute ADC B mixer volume
+		 */
+		reg = snd_soc_read(codec, 0x19);
+		reg &= ~(1 << 7);
+		snd_soc_write(codec, 0x19, reg);
+	}
+
+#if 0
+	dump_registers(codec);
+#endif
+	return 0;
+}
+
+static void fbxgw2r_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec *codec = rtd->codec_dai->codec;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		u8 reg;
+
+		/*
+		 * power down ADC A & B
+		 */
+		reg = snd_soc_read(codec, 0x2);
+		reg |= (3 << 1);
+		snd_soc_write(codec, 0x2, reg);
+
+		/*
+		 * mute ADC A mixer volume
+		 */
+		reg = snd_soc_read(codec, 0x18);
+		reg |= (1 << 7);
+		snd_soc_write(codec, 0x18, reg);
+
+		/*
+		 * mute ADC B mixer volume
+		 */
+		reg = snd_soc_read(codec, 0x19);
+		reg |= (1 << 7);
+		snd_soc_write(codec, 0x19, reg);
+	}
+}
+
+static struct snd_soc_ops fbxgw2r_ops = {
+	.hw_params = fbxgw2r_hw_params,
+	.startup = fbxgw2r_startup,
+	.shutdown = fbxgw2r_shutdown,
+};
+
+static struct snd_soc_dai_link fbxgw2r_dai[] = {
+	{
+		.name = "CS42L52",
+		.stream_name = "CS42L52 HiFi",
+		.cpu_dai_name = "kirkwood-i2s",
+		.platform_name = "kirkwood-pcm-audio",
+		.codec_dai_name = "cs42l52",
+		.codec_name = "cs42l52.1-004a",
+		.ops = &fbxgw2r_ops,
+		.init = fbxgw2r_dai_init,
+	},
+};
+
+
+static struct snd_soc_card fbxgw2r = {
+	.name = "FBXGW2R",
+	.dai_link = fbxgw2r_dai,
+	.num_links = ARRAY_SIZE(fbxgw2r_dai),
+};
+
+static int fbxgw2r_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &fbxgw2r;
+
+	card->dev = &pdev->dev;
+	return snd_soc_register_card(card);
+}
+
+static int fbxgw2r_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	return snd_soc_unregister_card(card);
+}
+
+struct platform_driver fbxgw2r_driver = {
+	.driver = {
+		.name = "fbxgw2r-audio",
+		.owner = THIS_MODULE,
+	},
+	.probe = fbxgw2r_probe,
+	.remove = fbxgw2r_remove,
+};
+
+static int __init fbxgw2r_init(void)
+{
+	return platform_driver_register(&fbxgw2r_driver);
+}
+
+static void __exit fbxgw2r_exit(void)
+{
+	platform_driver_unregister(&fbxgw2r_driver);
+}
+
+module_init(fbxgw2r_init);
+module_exit(fbxgw2r_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("ALSA SoC FBXGW2R Client");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:soc-audio");
