Index: abi/include/abi/proc/uarg.h
===================================================================
--- abi/include/abi/proc/uarg.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ abi/include/abi/proc/uarg.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -42,5 +42,5 @@
 	size_t uspace_stack_size;
 	
-	void (* uspace_thread_function)();
+	void (* uspace_thread_function)(void *);
 	void *uspace_thread_arg;
 	
Index: boot/Makefile
===================================================================
--- boot/Makefile	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ boot/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -115,4 +115,9 @@
 		cp "$(USPACE_PATH)/$(DRVS_PATH)/$$file_dir/$$file_name/$$file_name.dev" "$(DIST_PATH)/$(DRVS_PATH)/$$file_name/" ; \
 	done
+	for file in $(RD_DRVS_FW) ; do \
+		file_dir="`dirname "$$file"`" ; \
+		file_name="`basename "$$file"`" ; \
+		cp "$(USPACE_PATH)/$(DRVS_PATH)/$$file_dir/$$file_name/$$file_name.fw" "$(DIST_PATH)/$(DRVS_PATH)/$$file_name/" ; \
+	done
 	if ls $(DIST_OVERLAY_PATH)/* >/dev/null; then \
 		cp -r -L $(DIST_OVERLAY_PATH)/* "$(DIST_PATH)"; \
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ boot/Makefile.common	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -139,7 +139,11 @@
 	nic/rtl8139 \
 	nic/rtl8169 \
+	nic/ar9271 \
 	block/ahci
 
 RD_DRV_CFG =
+
+RD_DRVS_FW_NON_ESSENTIAL = \
+	nic/ar9271
 
 RD_LIBS =
@@ -223,5 +227,6 @@
 	$(USPACE_PATH)/app/viewer/viewer \
 	$(USPACE_PATH)/app/df/df \
-	$(USPACE_PATH)/app/fontviewer/fontviewer
+	$(USPACE_PATH)/app/fontviewer/fontviewer \
+	$(USPACE_PATH)/app/wifi_supplicant/wifi_supplicant
 
 RD_TESTS = \
@@ -247,8 +252,10 @@
 RD_APPS = $(RD_APPS_ESSENTIAL)
 RD_DRVS = $(RD_DRVS_ESSENTIAL)
+RD_DRVS_FW = $(RD_DRVS_FW_ESSENTIAL)
 else
 RD_SRVS = $(RD_SRVS_ESSENTIAL) $(RD_SRVS_NON_ESSENTIAL)
 RD_APPS = $(RD_APPS_ESSENTIAL) $(RD_APPS_NON_ESSENTIAL)
 RD_DRVS = $(RD_DRVS_ESSENTIAL) $(RD_DRVS_NON_ESSENTIAL)
+RD_DRVS_FW = $(RD_DRVS_FW_ESSENTIAL) $(RD_DRVS_FW_NON_ESSENTIAL)
 endif
 
Index: boot/arch/arm32/src/mm.c
===================================================================
--- boot/arch/arm32/src/mm.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ boot/arch/arm32/src/mm.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -193,7 +193,8 @@
 }
 
-static void enable_paging()
-{
-	/* c3   - each two bits controls access to the one of domains (16)
+static void enable_paging(void)
+{
+	/*
+	 * c3   - each two bits controls access to the one of domains (16)
 	 * 0b01 - behave as a client (user) of a domain
 	 */
@@ -232,5 +233,6 @@
 
 /** Start the MMU - initialize page table and enable paging. */
-void mmu_start() {
+void mmu_start(void)
+{
 	disable_paging();
 #ifdef PROCESSOR_ARCH_armv7_a
Index: boot/arch/mips32/Makefile.inc
===================================================================
--- boot/arch/mips32/Makefile.inc	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ boot/arch/mips32/Makefile.inc	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -64,4 +64,10 @@
 endif
 
+ifeq ($(MACHINE),msim)
+	RD_DRVS_ESSENTIAL += \
+		platform/msim \
+		block/ddisk
+endif
+
 SOURCES = \
 	arch/$(BARCH)/src/asm.S \
Index: boot/arch/ppc32/include/asm.h
===================================================================
--- boot/arch/ppc32/include/asm.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ boot/arch/ppc32/include/asm.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -35,5 +35,5 @@
 extern void jump_to_kernel(void *, void *, size_t, void *)
     __attribute__((noreturn));
-extern void real_mode();
+extern void real_mode(void);
 
 #endif
Index: boot/arch/sparc32/src/ambapp.c
===================================================================
--- boot/arch/sparc32/src/ambapp.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ boot/arch/sparc32/src/ambapp.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -58,5 +58,5 @@
 static void ambapp_scan_area(uintptr_t, unsigned int);
 
-void ambapp_scan()
+void ambapp_scan(void)
 {
 	amba_fake = false;
@@ -104,5 +104,5 @@
 }
 
-void ambapp_qemu_fake_scan()
+void ambapp_qemu_fake_scan(void)
 {
 	/* UART */
@@ -134,10 +134,10 @@
 }
 
-bool ambapp_fake()
+bool ambapp_fake(void)
 {
 	return amba_fake;
 }
 
-void ambapp_print_devices()
+void ambapp_print_devices(void)
 {
 	printf("ABMA devices:\n");
Index: boot/arch/sparc32/src/mm.c
===================================================================
--- boot/arch/sparc32/src/mm.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ boot/arch/sparc32/src/mm.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -76,5 +76,5 @@
 }
 
-static void mmu_disable()
+static void mmu_disable(void)
 {
 	uint32_t cr = asi_u32_read(ASI_MMUREGS, 0x000);
Index: boot/generic/include/memstr.h
===================================================================
--- boot/generic/include/memstr.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ boot/generic/include/memstr.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -36,8 +36,11 @@
 
 extern void *memcpy(void *, const void *, size_t)
-    __attribute__ ((optimize("-fno-tree-loop-distribute-patterns")));
+    __attribute__((nonnull(1, 2)))
+    __attribute__((optimize("-fno-tree-loop-distribute-patterns")));
 extern void *memset(void *, int, size_t)
-    __attribute__ ((optimize("-fno-tree-loop-distribute-patterns")));
-extern void *memmove(void *, const void *, size_t);
+    __attribute__((nonnull(1)))
+    __attribute__((optimize("-fno-tree-loop-distribute-patterns")));
+extern void *memmove(void *, const void *, size_t)
+    __attribute__((nonnull(1, 2)));
 
 #endif
Index: contrib/bazaar/bzreml/__init__.py
===================================================================
--- contrib/bazaar/bzreml/__init__.py	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ contrib/bazaar/bzreml/__init__.py	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -70,5 +70,5 @@
 	
 	sender_user, sender_email = parseaddr(sender)
-	payload = MIMEText(body.encode("utf-8"), "plain", "utf-8")
+	payload = MIMEText(body.decode("utf-8", 'ignore').encode("utf-8", 'ignore'), "plain", "utf-8")
 	
 	msg = MIMEMultipart()
Index: contrib/bazaar/mbprotect/__init__.py
===================================================================
--- contrib/bazaar/mbprotect/__init__.py	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ contrib/bazaar/mbprotect/__init__.py	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -60,9 +60,10 @@
 		return
 	
-	# First permitted case is appending changesets to main branch.Look for
+	# First permitted case is appending changesets to main branch. Look for
 	# old tip in new main branch.
 	for revision_id in iter_reverse_revision_history(repo, params.new_revid):
 		if revision_id == params.old_revid:
-			return	# Found old tip
+			# Old tip found
+			return
 	
 	# Another permitted case is backing out changesets. Look for new tip
@@ -70,5 +71,6 @@
 	for revision_id in iter_reverse_revision_history(repo, params.old_revid):
 		if revision_id == params.new_revid:
-			return	# Found new tip
+			# New tip found
+			return
 	
 	# Trying to do something else. Reject the change.
Index: contrib/conf/msim.conf
===================================================================
--- contrib/conf/msim.conf	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ contrib/conf/msim.conf	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -17,2 +17,6 @@
 add dkeyboard keyboard 0x10000000 2
 add dorder order 0x10000100 5
+
+add ddisk disk 0x10000200 6
+disk fmap "hdisk.img"
+
Index: contrib/qfs/qfs.sh
===================================================================
--- contrib/qfs/qfs.sh	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ contrib/qfs/qfs.sh	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -29,5 +29,5 @@
 #
 
-VERSION=2.2.0
+VERSION=2.3.0
 BASENAME=qemu-${VERSION}
 BASENAME_MASTER=qemu-master
@@ -36,5 +36,5 @@
 URL=http://wiki.qemu-project.org/download/${TARBALL}
 REPO=git://git.qemu.org/qemu.git
-MD5="f7a5e2da22d057eb838a91da7aff43c8"
+MD5="2fab3ea4460de9b57192e5b8b311f221"
 
 if [ "$1" == "--master" ]; then
Index: defaults/arm32/integratorcp/Makefile.config
===================================================================
--- defaults/arm32/integratorcp/Makefile.config	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ defaults/arm32/integratorcp/Makefile.config	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -1,2 +1,6 @@
 # Machine type
 MACHINE = integratorcp
+
+# Barebone build with essential binaries only
+CONFIG_BAREBONE = y
+
Index: kernel/arch/abs32le/src/abs32le.c
===================================================================
--- kernel/arch/abs32le/src/abs32le.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/abs32le/src/abs32le.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -69,5 +69,5 @@
 }
 
-void arch_post_cpu_init()
+void arch_post_cpu_init(void)
 {
 }
Index: kernel/arch/amd64/include/arch/asm.h
===================================================================
--- kernel/arch/amd64/include/arch/asm.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/amd64/include/arch/asm.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -339,5 +339,5 @@
  *
  */
-NO_TRACE static inline void enable_l_apic_in_msr()
+NO_TRACE static inline void enable_l_apic_in_msr(void)
 {
 	asm volatile (
Index: kernel/arch/amd64/src/fpu_context.c
===================================================================
--- kernel/arch/amd64/src/fpu_context.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/amd64/src/fpu_context.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -54,5 +54,5 @@
 }
 
-void fpu_init()
+void fpu_init(void)
 {
 	/* TODO: Zero all SSE, MMX etc. registers */
Index: kernel/arch/arm32/include/arch/cp15.h
===================================================================
--- kernel/arch/arm32/include/arch/cp15.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/arm32/include/arch/cp15.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -47,5 +47,5 @@
 
 #define CONTROL_REG_GEN_READ(name, crn, opc1, crm, opc2) \
-static inline uint32_t name##_read() \
+static inline uint32_t name##_read(void) \
 { \
 	uint32_t val; \
Index: kernel/arch/arm32/include/arch/security_ext.h
===================================================================
--- kernel/arch/arm32/include/arch/security_ext.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/arm32/include/arch/security_ext.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -46,5 +46,5 @@
  * older archs.
  */
-static inline bool sec_ext_is_implemented()
+static inline bool sec_ext_is_implemented(void)
 {
 #ifdef PROCESSOR_ARCH_armv7_a
@@ -60,5 +60,5 @@
  * mode.
  */
-static inline bool sec_ext_is_monitor_mode()
+static inline bool sec_ext_is_monitor_mode(void)
 {
 	return (current_status_reg_read() & MODE_MASK) == MONITOR_MODE;
@@ -75,5 +75,5 @@
  * Look for 'secureworld_exit' in arch/arm/cpu/armv7/omap3/board.c.
  */
-static inline bool sec_ext_is_secure()
+static inline bool sec_ext_is_secure(void)
 {
 	return sec_ext_is_implemented()
Index: kernel/arch/arm32/src/arm32.c
===================================================================
--- kernel/arch/arm32/src/arm32.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/arm32/src/arm32.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -158,5 +158,5 @@
 
 /** Reboot. */
-void arch_reboot()
+void arch_reboot(void)
 {
 	/* not implemented */
Index: kernel/arch/arm32/src/fpu_context.c
===================================================================
--- kernel/arch/arm32/src/fpu_context.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/arm32/src/fpu_context.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -101,4 +101,5 @@
 	FPSCR_EN_ALL = FPSCR_DENORMAL_EN_FLAG | FPSCR_INEXACT_EN_FLAG | FPSCR_UNDERFLOW_EN_FLAG | FPSCR_OVERFLOW_EN_FLAG | FPSCR_ZERO_DIV_EN_FLAG | FPSCR_INVALID_OP_EN_FLAG,
 };
+
 extern uint32_t fpscr_read(void);
 extern void fpscr_write(uint32_t);
@@ -114,7 +115,8 @@
 static void (*restore_context)(fpu_context_t *ctx);
 
-static int fpu_have_coprocessor_access()
-{
-/* The register containing the information (CPACR) is not available on armv6-
+static int fpu_have_coprocessor_access(void)
+{
+/*
+ * The register containing the information (CPACR) is not available on armv6-
  * rely on user decision to use CONFIG_FPU.
  */
@@ -143,7 +145,8 @@
  * @note do we need to call secure monitor here?
  */
-static void fpu_enable_coprocessor_access()
-{
-/* The register containing the information (CPACR) is not available on armv6-
+static void fpu_enable_coprocessor_access(void)
+{
+/*
+ * The register containing the information (CPACR) is not available on armv6-
  * rely on user decision to use CONFIG_FPU.
  */
Index: kernel/arch/ia32/src/fpu_context.c
===================================================================
--- kernel/arch/ia32/src/fpu_context.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/ia32/src/fpu_context.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -125,5 +125,5 @@
 
 /** Initialize x87 FPU. Mask all exceptions. */
-void fpu_init()
+void fpu_init(void)
 {
 	uint32_t help0 = 0;
Index: kernel/arch/ia64/src/mm/vhpt.c
===================================================================
--- kernel/arch/ia64/src/mm/vhpt.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/ia64/src/mm/vhpt.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -82,5 +82,5 @@
 }
 
-void vhpt_invalidate_all()
+void vhpt_invalidate_all(void)
 {
 	memsetb(vhpt_base, VHPT_SIZE, 0);
Index: kernel/arch/mips32/include/arch/drivers/msim.h
===================================================================
--- kernel/arch/mips32/include/arch/drivers/msim.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/mips32/include/arch/drivers/msim.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -39,5 +39,7 @@
 #define MSIM_VIDEORAM     0x90000000
 #define MSIM_KBD_ADDRESS  0x90000000
+
 #define MSIM_KBD_IRQ      2
+#define MSIM_DDISK_IRQ    6
 
 #endif
Index: kernel/arch/mips32/src/debugger.c
===================================================================
--- kernel/arch/mips32/src/debugger.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/mips32/src/debugger.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -288,5 +288,5 @@
  *
  */
-void debugger_init()
+void debugger_init(void)
 {
 	unsigned int i;
Index: kernel/arch/mips32/src/fpu_context.c
===================================================================
--- kernel/arch/mips32/src/fpu_context.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/mips32/src/fpu_context.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -53,5 +53,5 @@
 }
 
-void fpu_init()
+void fpu_init(void)
 {
 	/* TODO: Zero all registers */
Index: kernel/arch/mips32/src/mach/msim/msim.c
===================================================================
--- kernel/arch/mips32/src/mach/msim/msim.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/mips32/src/mach/msim/msim.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -62,4 +62,5 @@
 void msim_init(void)
 {
+	cp0_unmask_int(MSIM_DDISK_IRQ);
 }
 
Index: kernel/arch/sparc32/src/sparc32.c
===================================================================
--- kernel/arch/sparc32/src/sparc32.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/arch/sparc32/src/sparc32.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -96,5 +96,5 @@
 
 
-void arch_post_cpu_init()
+void arch_post_cpu_init(void)
 {
 }
Index: kernel/generic/include/lib/memfnc.h
===================================================================
--- kernel/generic/include/lib/memfnc.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/generic/include/lib/memfnc.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -46,6 +46,8 @@
 
 extern void *memset(void *, int, size_t)
+    __attribute__((nonnull(1)))
     ATTRIBUTE_OPTIMIZE("-fno-tree-loop-distribute-patterns") DO_NOT_DISCARD;
 extern void *memcpy(void *, const void *, size_t)
+    __attribute__((nonnull(1, 2)))
     ATTRIBUTE_OPTIMIZE("-fno-tree-loop-distribute-patterns") DO_NOT_DISCARD;
 
Index: kernel/generic/include/memstr.h
===================================================================
--- kernel/generic/include/memstr.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/generic/include/memstr.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -48,7 +48,10 @@
 #define memcpy(dst, src, cnt)  __builtin_memcpy((dst), (src), (cnt))
 
-extern void memsetb(void *, size_t, uint8_t);
-extern void memsetw(void *, size_t, uint16_t);
-extern void *memmove(void *, const void *, size_t) DO_NOT_DISCARD;
+extern void memsetb(void *, size_t, uint8_t)
+    __attribute__((nonnull(1)));
+extern void memsetw(void *, size_t, uint16_t)
+    __attribute__((nonnull(1)));
+extern void *memmove(void *, const void *, size_t)
+    __attribute__((nonnull(1, 2))) DO_NOT_DISCARD;
 
 #endif
Index: kernel/generic/include/mm/slab.h
===================================================================
--- kernel/generic/include/mm/slab.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/generic/include/mm/slab.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -142,5 +142,6 @@
 extern void *malloc(size_t, unsigned int)
     __attribute__((malloc));
-extern void *realloc(void *, size_t, unsigned int);
+extern void *realloc(void *, size_t, unsigned int)
+    __attribute__((warn_unused_result));
 extern void free(void *);
 
Index: kernel/generic/include/typedefs.h
===================================================================
--- kernel/generic/include/typedefs.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/generic/include/typedefs.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -50,5 +50,5 @@
 } atomic_t;
 
-typedef void (* function)();
+typedef void (* function)(void);
 
 typedef uint32_t container_id_t;
Index: kernel/generic/src/ipc/ops/conctmeto.c
===================================================================
--- kernel/generic/src/ipc/ops/conctmeto.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/generic/src/ipc/ops/conctmeto.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -44,4 +44,6 @@
 	int newphid = phone_alloc(TASK);
 
+	/* Remember the phoneid or the error. */
+	call->priv = newphid;
 	if (newphid < 0)
 		return ELIMIT;
@@ -49,5 +51,4 @@
 	/* Set arg5 for server */
 	IPC_SET_ARG5(call->data, (sysarg_t) &TASK->phones[newphid]);
-	call->priv = newphid;
 
 	return EOK;
@@ -73,8 +74,17 @@
 static int answer_process(call_t *answer)
 {
-	if (IPC_GET_RETVAL(answer->data))
-		phone_dealloc(answer->priv);
-	else
-		IPC_SET_ARG5(answer->data, answer->priv);
+	int newphid = (int) answer->priv;
+
+	if (IPC_GET_RETVAL(answer->data)) {
+		if (newphid >= 0) {
+			/*
+			 * The phone was indeed allocated and now needs
+			 * to be deallocated.
+			 */
+			phone_dealloc(newphid);
+		}
+	} else {
+		IPC_SET_ARG5(answer->data, newphid);
+	}
 	
 	return EOK;
Index: kernel/generic/src/ipc/sysipc.c
===================================================================
--- kernel/generic/src/ipc/sysipc.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/generic/src/ipc/sysipc.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -388,15 +388,15 @@
 {
 	call_t *call = get_call(callid);
-	phone_t *phone;
-	bool need_old = answer_need_old(call);
-	bool after_forward = false;
-	ipc_data_t old;
-	int rc;
-
 	if (!call)
 		return ENOENT;
-
+	
+	ipc_data_t old;
+	bool need_old = answer_need_old(call);
 	if (need_old)
 		old = call->data;
+	
+	bool after_forward = false;
+	int rc;
+	phone_t *phone;
 	
 	if (phone_get(phoneid, &phone) != EOK) {
@@ -409,5 +409,5 @@
 		goto error;
 	}
-
+	
 	call->flags |= IPC_CALL_FORWARDED;
 	
Index: kernel/generic/src/lib/func.c
===================================================================
--- kernel/generic/src/lib/func.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/generic/src/lib/func.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -51,5 +51,5 @@
  *
  */
-void halt()
+void halt(void)
 {
 #if (defined(CONFIG_DEBUG)) && (defined(CONFIG_KCONSOLE))
Index: kernel/generic/src/mm/as.c
===================================================================
--- kernel/generic/src/mm/as.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/generic/src/mm/as.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -834,12 +834,12 @@
 			if ((cond = (bool) node->keys)) {
 				uintptr_t ptr = node->key[node->keys - 1];
-				size_t size =
+				size_t node_size =
 				    (size_t) node->value[node->keys - 1];
 				size_t i = 0;
 				
-				if (overlaps(ptr, P2SZ(size), area->base,
+				if (overlaps(ptr, P2SZ(node_size), area->base,
 				    P2SZ(pages))) {
 					
-					if (ptr + P2SZ(size) <= start_free) {
+					if (ptr + P2SZ(node_size) <= start_free) {
 						/*
 						 * The whole interval fits
@@ -860,5 +860,5 @@
 					i = (start_free - ptr) >> PAGE_WIDTH;
 					if (!used_space_remove(area, start_free,
-					    size - i))
+					    node_size - i))
 						panic("Cannot remove used space.");
 				} else {
@@ -867,5 +867,5 @@
 					 * completely removed.
 					 */
-					if (!used_space_remove(area, ptr, size))
+					if (!used_space_remove(area, ptr, node_size))
 						panic("Cannot remove used space.");
 				}
@@ -887,5 +887,5 @@
 				    area->pages - pages);
 		
-				for (; i < size; i++) {
+				for (; i < node_size; i++) {
 					pte_t *pte = page_mapping_find(as,
 					    ptr + P2SZ(i), false);
Index: kernel/generic/src/mm/slab.c
===================================================================
--- kernel/generic/src/mm/slab.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/generic/src/mm/slab.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -610,4 +610,6 @@
     unsigned int kmflag), size_t (*destructor)(void *obj), unsigned int flags)
 {
+	ASSERT(size > 0);
+	
 	memsetb(cache, sizeof(*cache), 0);
 	cache->name = name;
@@ -888,5 +890,5 @@
 	/* Initialize magazine cache */
 	_slab_cache_create(&mag_cache, "slab_magazine_t",
-	    sizeof(slab_magazine_t) + SLAB_MAG_SIZE * sizeof(void*),
+	    sizeof(slab_magazine_t) + SLAB_MAG_SIZE * sizeof(void *),
 	    sizeof(uintptr_t), NULL, NULL, SLAB_CACHE_NOMAGAZINE |
 	    SLAB_CACHE_SLINSIDE);
Index: kernel/generic/src/udebug/udebug_ops.c
===================================================================
--- kernel/generic/src/udebug/udebug_ops.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ kernel/generic/src/udebug/udebug_ops.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -375,4 +375,5 @@
 	if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
 		mutex_unlock(&TASK->udebug.lock);
+		free(id_buffer);
 		return EINVAL;
 	}
@@ -456,7 +457,4 @@
 int udebug_args_read(thread_t *thread, void **buffer)
 {
-	/* Prepare a buffer to hold the arguments. */
-	sysarg_t *arg_buffer = malloc(6 * sizeof(sysarg_t), 0);
-	
 	/* On success, this will lock t->udebug.lock. */
 	int rc = _thread_op_begin(thread, false);
@@ -471,4 +469,7 @@
 	}
 	
+	/* Prepare a buffer to hold the arguments. */
+	sysarg_t *arg_buffer = malloc(6 * sizeof(sysarg_t), 0);
+	
 	/* Copy to a local buffer before releasing the lock. */
 	memcpy(arg_buffer, thread->udebug.syscall_args, 6 * sizeof(sysarg_t));
@@ -499,7 +500,4 @@
 int udebug_regs_read(thread_t *thread, void **buffer)
 {
-	/* Prepare a buffer to hold the data. */
-	istate_t *state_buf = malloc(sizeof(istate_t), 0);
-	
 	/* On success, this will lock t->udebug.lock */
 	int rc = _thread_op_begin(thread, false);
@@ -512,4 +510,7 @@
 		return EBUSY;
 	}
+	
+	/* Prepare a buffer to hold the data. */
+	istate_t *state_buf = malloc(sizeof(istate_t), 0);
 	
 	/* Copy to the allocated buffer */
Index: tools/toolchain.sh
===================================================================
--- tools/toolchain.sh	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ tools/toolchain.sh	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -513,5 +513,6 @@
 	PATH="$PATH:${INSTALL_DIR}/${PREFIX}/bin" ./configure \
 		"--target=${TARGET}" \
-		"--prefix=${PREFIX}" "--program-prefix=${TARGET}-"
+		"--prefix=${PREFIX}" "--program-prefix=${TARGET}-" \
+		--enable-werror=no
 	check_error $? "Error configuring GDB."
 	
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -89,4 +89,5 @@
 	app/wavplay \
 	app/websrv \
+	app/wifi_supplicant \
 	srv/audio/hound \
 	srv/clipboard \
@@ -136,4 +137,5 @@
 	drv/block/ahci \
 	drv/block/ata_bd \
+	drv/block/ddisk \
 	drv/char/i8042 \
 	drv/char/pl050 \
@@ -158,4 +160,5 @@
 	drv/nic/rtl8139 \
 	drv/nic/rtl8169 \
+	drv/nic/ar9271 \
 	drv/platform/icp
 
@@ -175,8 +178,13 @@
 
 ifeq ($(UARCH), $(filter $(UARCH),mips32 mips32eb))
+ifeq ($(MACHINE),msim)
+	DIRS += \
+		drv/platform/msim
+else
 	DIRS += \
 		drv/platform/malta \
 		drv/bus/pci/pciintel \
 		drv/bus/isa
+endif
 endif
 
@@ -207,4 +215,5 @@
 	lib/fs \
 	lib/block \
+	lib/crypto \
 	lib/clui \
 	lib/fmtutil \
@@ -237,5 +246,6 @@
 	lib/posix \
 	lib/mbr \
-	lib/gpt
+	lib/gpt \
+	lib/ieee80211
 
 LIBC_BUILD = $(addsuffix .build,$(LIBC))
Index: uspace/Makefile.common
===================================================================
--- uspace/Makefile.common	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/Makefile.common	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -120,4 +120,5 @@
 LIBPOSIX_PREFIX = $(LIB_PREFIX)/posix
 
+LIBCRYPTO_PREFIX = $(LIB_PREFIX)/crypto
 LIBBLOCK_PREFIX = $(LIB_PREFIX)/block
 LIBFS_PREFIX = $(LIB_PREFIX)/fs
@@ -142,4 +143,5 @@
 LIBPCM_PREFIX = $(LIB_PREFIX)/pcm
 LIBNIC_PREFIX = $(LIB_PREFIX)/nic
+LIBIEEE80211_PREFIX = $(LIB_PREFIX)/ieee80211
 LIBMINIX_PREFIX = $(LIB_PREFIX)/minix
 LIBCOMPRESS_PREFIX = $(LIB_PREFIX)/compress
Index: uspace/app/bdsh/cmds/modules/cat/cat.c
===================================================================
--- uspace/app/bdsh/cmds/modules/cat/cat.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/app/bdsh/cmds/modules/cat/cat.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -108,5 +108,5 @@
 }
 
-static void waitprompt()
+static void waitprompt(void)
 {
 	console_set_pos(console, 0, console_rows-1);
@@ -120,5 +120,5 @@
 }
 
-static void waitkey()
+static void waitkey(void)
 {
 	cons_event_t ev;
@@ -149,5 +149,5 @@
 }
 
-static void newpage()
+static void newpage(void)
 {
 	console_clear(console);
@@ -324,5 +324,5 @@
 	argc = cli_count_args(argv);
 
-	for (c = 0, optind = 0, opt_ind = 0; c != -1;) {
+	for (c = 0, optreset = 1, optind = 0, opt_ind = 0; c != -1;) {
 		c = getopt_long(argc, argv, "xhvmH:t:b:s:n", long_options, &opt_ind);
 		switch (c) {
Index: uspace/app/bdsh/cmds/modules/cmp/cmp.c
===================================================================
--- uspace/app/bdsh/cmds/modules/cmp/cmp.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/app/bdsh/cmds/modules/cmp/cmp.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -130,5 +130,5 @@
 	argc = cli_count_args(argv);
 
-	for (c = 0, optind = 0, opt_ind = 0; c != -1;) {
+	for (c = 0, optreset = 1, optind = 0, opt_ind = 0; c != -1;) {
 		c = getopt_long(argc, argv, "hv", long_options, &opt_ind);
 		switch (c) {
Index: uspace/app/bdsh/cmds/modules/cp/cp.c
===================================================================
--- uspace/app/bdsh/cmds/modules/cp/cp.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/app/bdsh/cmds/modules/cp/cp.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -457,5 +457,5 @@
 	argc = cli_count_args(argv);
 
-	for (c = 0, optind = 0, opt_ind = 0; c != -1;) {
+	for (c = 0, optreset = 1, optind = 0, opt_ind = 0; c != -1;) {
 		c = getopt_long(argc, argv, "hvVfirb:", long_options, &opt_ind);
 		switch (c) { 
Index: uspace/app/bdsh/cmds/modules/ls/ls.c
===================================================================
--- uspace/app/bdsh/cmds/modules/ls/ls.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/app/bdsh/cmds/modules/ls/ls.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -362,5 +362,5 @@
 	argc = cli_count_args(argv);
 	
-	for (c = 0, optind = 0, opt_ind = 0; c != -1;) {
+	for (c = 0, optreset = 1, optind = 0, opt_ind = 0; c != -1;) {
 		c = getopt_long(argc, argv, "hur", long_options, &opt_ind);
 		switch (c) {
Index: uspace/app/bdsh/cmds/modules/mkdir/mkdir.c
===================================================================
--- uspace/app/bdsh/cmds/modules/mkdir/mkdir.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/app/bdsh/cmds/modules/mkdir/mkdir.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -173,5 +173,5 @@
 	argc = cli_count_args(argv);
 
-	for (c = 0, optind = 0, opt_ind = 0; c != -1;) {
+	for (c = 0, optreset = 1, optind = 0, opt_ind = 0; c != -1;) {
 		c = getopt_long(argc, argv, "pvhVfm:", long_options, &opt_ind);
 		switch (c) {
Index: uspace/app/bdsh/cmds/modules/mkfile/mkfile.c
===================================================================
--- uspace/app/bdsh/cmds/modules/mkfile/mkfile.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/app/bdsh/cmds/modules/mkfile/mkfile.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -126,5 +126,5 @@
 	argc = cli_count_args(argv);
 
-	for (c = 0, optind = 0, opt_ind = 0; c != -1;) {
+	for (c = 0, optreset = 1, optind = 0, opt_ind = 0; c != -1;) {
 		c = getopt_long(argc, argv, "ps:h", long_options, &opt_ind);
 		switch (c) {
Index: uspace/app/bdsh/cmds/modules/mount/mount.c
===================================================================
--- uspace/app/bdsh/cmds/modules/mount/mount.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/app/bdsh/cmds/modules/mount/mount.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -117,5 +117,5 @@
 	argc = cli_count_args(argv);
 
-	for (c = 0, optind = 0, opt_ind = 0; c != -1;) {
+	for (c = 0, optreset = 1, optind = 0, opt_ind = 0; c != -1;) {
 		c = getopt_long(argc, argv, "i:h", long_options, &opt_ind);
 		switch (c) {
Index: uspace/app/bdsh/cmds/modules/rm/rm.c
===================================================================
--- uspace/app/bdsh/cmds/modules/rm/rm.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/app/bdsh/cmds/modules/rm/rm.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -261,5 +261,5 @@
 	}
 
-	for (c = 0, optind = 0, opt_ind = 0; c != -1;) {
+	for (c = 0, optreset = 1, optind = 0, opt_ind = 0; c != -1;) {
 		c = getopt_long(argc, argv, "hvrfs", long_options, &opt_ind);
 		switch (c) {
Index: uspace/app/bdsh/cmds/modules/touch/touch.c
===================================================================
--- uspace/app/bdsh/cmds/modules/touch/touch.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/app/bdsh/cmds/modules/touch/touch.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -90,5 +90,5 @@
 	DIR *dirp;
 	
-	for (c = 0, optind = 0, longind = 0; c != -1; ) {
+	for (c = 0, optreset = 1, optind = 0, longind = 0; c != -1; ) {
 		c = getopt_long(argc, argv, "c", long_options, &longind);
 		switch (c) {
Index: uspace/app/bdsh/input.c
===================================================================
--- uspace/app/bdsh/input.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/app/bdsh/input.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -219,5 +219,5 @@
 }
 
-void print_pipe_usage()
+void print_pipe_usage(void)
 {
 	printf("Invalid syntax!\n");
Index: uspace/app/trace/trace.c
===================================================================
--- uspace/app/trace/trace.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/app/trace/trace.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -730,5 +730,5 @@
 }
 
-static void print_syntax()
+static void print_syntax(void)
 {
 	printf("Syntax:\n");
Index: uspace/app/wifi_supplicant/Makefile
===================================================================
--- uspace/app/wifi_supplicant/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/app/wifi_supplicant/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2015 Jan Kolarik
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+BINARY = wifi_supplicant
+EXTRA_CFLAGS = -I$(LIBDRV_PREFIX)/include
+LIBS = $(LIBDRV_PREFIX)/libdrv.a
+
+SOURCES = \
+	wifi_supplicant.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/wifi_supplicant/wifi_supplicant.c
===================================================================
--- uspace/app/wifi_supplicant/wifi_supplicant.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/app/wifi_supplicant/wifi_supplicant.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup nic
+ * @{
+ */
+/** @file WiFi device configuration utility.
+ *
+ */
+
+#include <ieee80211_iface.h>
+
+#include <inet/inetcfg.h>
+#include <inet/dhcp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <loc.h>
+
+#define NAME  "wifi_supplicant"
+
+#define enum_name(name_arr, i) \
+	((i < 0) ? "NA" : name_arr[i])
+
+static const char* ieee80211_security_type_strs[] = {
+	"OPEN", "WEP", "WPA", "WPA2"
+};
+
+static const char* ieee80211_security_alg_strs[] = {
+	"WEP40", "WEP104", "CCMP", "TKIP"
+};
+
+static const char* ieee80211_security_auth_strs[] = {
+	"PSK", "8021X"
+};
+
+static void print_syntax(void)
+{
+	printf("syntax:\n");
+	printf("\t" NAME " [<cmd> [<args...>]]\n");
+	printf("\t<cmd> is:\n");
+	printf("\tlist - list wifi devices in <index>: <name> format\n");
+	printf("\tscan <index> [-n] - output scan results (force scan "
+	    "immediately)\n");
+	printf("\tconnect <index> <ssid_prefix> [<password>] - connect to "
+	    "network\n");
+	printf("\tdisconnect <index> - disconnect from network\n");
+}
+
+static char *nic_addr_format(nic_address_t *addr)
+{
+	char *str;
+	int rc = asprintf(&str, "%02x:%02x:%02x:%02x:%02x:%02x",
+	    addr->address[0], addr->address[1], addr->address[2],
+	    addr->address[3], addr->address[4], addr->address[5]);
+	
+	if (rc < 0)
+		return NULL;
+	
+	return str;
+}
+
+static int get_wifi_list(service_id_t **wifis, size_t *count)
+{
+	category_id_t wifi_cat;
+	int rc = loc_category_get_id("ieee80211", &wifi_cat, 0);
+	if (rc != EOK) {
+		printf("Error resolving category 'ieee80211'.\n");
+		return rc;
+	}
+	
+	rc = loc_category_get_svcs(wifi_cat, wifis, count);
+	if (rc != EOK) {
+		printf("Error getting list of WIFIs.\n");
+		return rc;
+	}
+	
+	return EOK;
+}
+
+static async_sess_t *get_wifi_by_index(size_t i)
+{
+	service_id_t *wifis = NULL;
+	size_t count;
+	
+	int rc = get_wifi_list(&wifis, &count);
+	if (rc != EOK) {
+		printf("Error fetching wifi list.\n");
+		return NULL;
+	}
+	
+	if (i >= count) {
+		printf("Invalid wifi index.\n");
+		free(wifis);
+		return NULL;
+	}
+	
+	async_sess_t *sess =
+	    loc_service_connect(EXCHANGE_SERIALIZE, wifis[i], 0);
+	if (sess == NULL) {
+		printf("Error connecting to service.\n");
+		free(wifis);
+		return NULL;
+	}
+	
+	return sess;
+}
+
+static int wifi_list(void)
+{
+	service_id_t *wifis = NULL;
+	size_t count;
+	
+	int rc = get_wifi_list(&wifis, &count);
+	if (rc != EOK) {
+		printf("Error fetching wifi list.\n");
+		return EINVAL;
+	}
+	
+	printf("[Index]: [Service Name]\n");
+	for (size_t i = 0; i < count; i++) {
+		char *svc_name;
+		rc = loc_service_get_name(wifis[i], &svc_name);
+		if (rc != EOK) {
+			printf("Error getting service name.\n");
+			free(wifis);
+			return rc;
+		}
+		
+		printf("%zu: %s\n", i, svc_name);
+		
+		free(svc_name);
+	}
+	
+	return EOK;
+}
+
+static int wifi_connect(uint32_t index, char *ssid_start, char *password)
+{
+	assert(ssid_start);
+	
+	async_sess_t *sess = get_wifi_by_index(index);
+	if (sess == NULL) {
+		printf("Specified WIFI doesn't exist or cannot connect to "
+		    "it.\n");
+		return EINVAL;
+	}
+	
+	int rc = ieee80211_disconnect(sess);
+	if(rc != EOK) {
+		if (rc == EREFUSED)
+			printf("Device is not ready yet.\n");
+		else
+			printf("Error when disconnecting device. "
+			    "Error: %d\n", rc);
+		
+		return rc;
+	}
+	
+	rc = ieee80211_connect(sess, ssid_start, password);
+	if(rc != EOK) {
+		if (rc == EREFUSED)
+			printf("Device is not ready yet.\n");
+		else if (rc == ETIMEOUT)
+			printf("Timeout when authenticating to network.\n");
+		else if (rc == ENOENT)
+			printf("Given SSID not in scan results.\n");
+		else
+			printf("Error when connecting to network. "
+			    "Error: %d\n", rc);
+		
+		return rc;
+	}
+	
+	// TODO: Wait for DHCP address?
+	
+	printf("Successfully connected to network!\n");
+	
+	return EOK;
+}
+
+static int wifi_disconnect(uint32_t index)
+{
+	async_sess_t *sess = get_wifi_by_index(index);
+	if (sess == NULL) {
+		printf("Specified WIFI doesn't exist or cannot connect to "
+		    "it.\n");
+		return EINVAL;
+	}
+	
+	int rc = ieee80211_disconnect(sess);
+	if (rc != EOK) {
+		if (rc == EREFUSED)
+			printf("Device is not ready yet.\n");
+		else if (rc == EINVAL)
+			printf("Not connected to any WiFi network.\n");
+		else
+			printf("Error when disconnecting from network. "
+			    "Error: %d\n", rc);
+		
+		return rc;
+	}
+	
+	printf("Successfully disconnected.\n");
+	
+	return EOK;
+}
+
+static int wifi_scan(uint32_t index, bool now)
+{
+	async_sess_t *sess = get_wifi_by_index(index);
+	if (sess == NULL) {
+		printf("Specified WIFI doesn't exist or cannot connect to "
+		    "it.\n");
+		return EINVAL;
+	}
+	
+	ieee80211_scan_results_t scan_results;
+	int rc = ieee80211_get_scan_results(sess, &scan_results, now);
+	if (rc != EOK) {
+		if (rc == EREFUSED)
+			printf("Device is not ready yet.\n");
+		else
+			printf("Failed to fetch scan results. Error: %d\n", rc);
+		
+		return rc;
+	}
+	
+	if (scan_results.length == 0)
+		return EOK;
+	
+	printf("%16.16s %17s %4s %5s %5s %7s %7s\n",
+	    "SSID", "MAC", "CHAN", "TYPE", "AUTH", "UNI-ALG", "GRP-ALG");
+	
+	for (uint8_t i = 0; i < scan_results.length; i++) {
+		ieee80211_scan_result_t result = scan_results.results[i];
+		
+		printf("%16.16s %17s %4d %5s %5s %7s %7s\n",
+		    result.ssid, nic_addr_format(&result.bssid),
+		    result.channel,
+		    enum_name(ieee80211_security_type_strs, result.security.type),
+		    enum_name(ieee80211_security_auth_strs, result.security.auth),
+		    enum_name(ieee80211_security_alg_strs, result.security.pair_alg),
+		    enum_name(ieee80211_security_alg_strs, result.security.group_alg));
+	}
+	
+	return EOK;
+}
+
+int main(int argc, char *argv[])
+{
+	int rc = inetcfg_init();
+	if (rc != EOK) {
+		printf("%s: Failed connecting to inetcfg service (%d).\n",
+		    NAME, rc);
+		return 1;
+	}
+	
+	rc = dhcp_init();
+	if (rc != EOK) {
+		printf("%s: Failed connecting to dhcp service (%d).\n",
+		    NAME, rc);
+		return 1;
+	}
+	
+	if (argc == 2) {
+		if (!str_cmp(argv[1], "list"))
+			return wifi_list();
+	} else if (argc > 2) {
+		uint32_t index;
+		rc = str_uint32_t(argv[2], NULL, 10, false, &index);
+		if (rc != EOK) {
+			printf("%s: Invalid argument.\n", NAME);
+			print_syntax();
+			return EINVAL;
+		}
+		
+		if (!str_cmp(argv[1], "scan")) {
+			bool now = false;
+			if (argc > 3)
+				if (!str_cmp(argv[3], "-n"))
+					now = true;
+			
+			return wifi_scan(index, now);
+		} else if (!str_cmp(argv[1], "connect")) {
+			char *pass = NULL;
+			if (argc > 3) {
+				if (argc > 4)
+					pass = argv[4];
+				
+				return wifi_connect(index, argv[3], pass);
+			}
+		} else if (!str_cmp(argv[1], "disconnect"))
+			return wifi_disconnect(index);
+	}
+	
+	print_syntax();
+	
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/drv/block/ata_bd/main.c
===================================================================
--- uspace/drv/block/ata_bd/main.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/drv/block/ata_bd/main.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -81,5 +81,4 @@
 		rc = EINVAL;
 		goto error;
-		return EINVAL;
 	}
 
Index: uspace/drv/block/ddisk/Makefile
===================================================================
--- uspace/drv/block/ddisk/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/block/ddisk/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2013 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+LIBS = $(LIBDRV_PREFIX)/libdrv.a $(LIBSCSI_PREFIX)/libscsi.a
+EXTRA_CFLAGS += -I$(LIBDRV_PREFIX)/include -I$(LIBSCSI_PREFIX)/include
+BINARY = ddisk
+
+SOURCES = \
+	ddisk.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/block/ddisk/ddisk.c
===================================================================
--- uspace/drv/block/ddisk/ddisk.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/block/ddisk/ddisk.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,599 @@
+/*
+ * Copyright (c) 2015 Jakub Jermar
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file
+ * MSIM ddisk block device driver
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+#include <ddf/driver.h>
+#include <ddf/interrupt.h>
+#include <ddf/log.h>
+#include <device/hw_res_parsed.h>
+#include <ddi.h>
+#include <bd_srv.h>
+#include <fibril_synch.h>
+#include <as.h>
+
+#define NAME	"ddisk"
+
+#define DDISK_FUN_NAME	"a"
+
+#define DDISK_BLOCK_SIZE	512
+
+#define DDISK_STAT_IRQ_PENDING	0x4
+#define DDISK_CMD_READ		0x1
+#define DDISK_CMD_WRITE		0x2
+#define DDISK_CMD_IRQ_DEASSERT	0x4
+
+static int ddisk_dev_add(ddf_dev_t *);
+static int ddisk_dev_remove(ddf_dev_t *);
+static int ddisk_dev_gone(ddf_dev_t *);
+static int ddisk_fun_online(ddf_fun_t *);
+static int ddisk_fun_offline(ddf_fun_t *);
+
+static void ddisk_bd_connection(ipc_callid_t, ipc_call_t *, void *);
+
+static void ddisk_irq_handler(ipc_callid_t, ipc_call_t *, ddf_dev_t *);
+
+static driver_ops_t driver_ops = {
+	.dev_add = ddisk_dev_add,
+	.dev_remove = ddisk_dev_remove,
+	.dev_gone = ddisk_dev_gone,
+	.fun_online = ddisk_fun_online,
+	.fun_offline = ddisk_fun_offline
+};
+
+static driver_t ddisk_driver = {
+	.name = NAME,
+	.driver_ops = &driver_ops
+};
+
+typedef struct {
+	int irq;
+	uintptr_t base;
+} ddisk_res_t;
+
+typedef struct {
+	ioport32_t dma_buffer;
+	ioport32_t block;
+	union {
+		ioport32_t status;
+		ioport32_t command;
+	};
+	ioport32_t size;
+} ddisk_regs_t;
+
+typedef struct {
+	fibril_mutex_t lock;
+
+	fibril_condvar_t io_cv;
+	bool io_busy;
+
+	ssize_t size;
+	size_t blocks;
+
+	uintptr_t dma_buffer_phys;
+	void *dma_buffer;
+
+	ddf_dev_t *dev;
+	ddf_fun_t *fun;
+
+	ddisk_res_t ddisk_res;
+	ddisk_regs_t *ddisk_regs;
+
+	bd_srvs_t bds;
+} ddisk_t;
+
+static int ddisk_bd_open(bd_srvs_t *, bd_srv_t *);
+static int ddisk_bd_close(bd_srv_t *);
+static int ddisk_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
+static int ddisk_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *,
+    size_t);
+static int ddisk_bd_get_block_size(bd_srv_t *, size_t *);
+static int ddisk_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
+
+bd_ops_t ddisk_bd_ops = {
+	.open = ddisk_bd_open,
+	.close = ddisk_bd_close,
+	.read_blocks = ddisk_bd_read_blocks,
+	.write_blocks = ddisk_bd_write_blocks,
+	.get_block_size = ddisk_bd_get_block_size,
+	.get_num_blocks = ddisk_bd_get_num_blocks,
+};
+
+irq_pio_range_t ddisk_irq_pio_ranges[] = {
+	{
+		.base = 0,
+		.size = sizeof(ddisk_regs_t)
+	}
+};
+
+irq_cmd_t ddisk_irq_commands[] = {
+	{
+		.cmd = CMD_PIO_READ_32,
+		.addr = NULL,
+		.dstarg = 1
+	},
+	{
+		.cmd = CMD_AND,
+		.srcarg = 1,
+		.value = DDISK_STAT_IRQ_PENDING,
+		.dstarg = 2
+	},
+	{
+		.cmd = CMD_PREDICATE,
+		.srcarg = 2,
+		.value = 2 
+	},
+	{
+		/* Deassert the DMA interrupt. */
+		.cmd = CMD_PIO_WRITE_32,
+		.value = DDISK_CMD_IRQ_DEASSERT,
+		.addr = NULL 
+	},
+	{
+		.cmd = CMD_ACCEPT
+	}
+};
+
+irq_code_t ddisk_irq_code = {
+	.rangecount = 1,
+	.ranges = ddisk_irq_pio_ranges,
+	.cmdcount = sizeof(ddisk_irq_commands) / sizeof(irq_cmd_t),
+	.cmds = ddisk_irq_commands,
+};
+
+void ddisk_irq_handler(ipc_callid_t iid, ipc_call_t *icall, ddf_dev_t *dev)
+{
+	ddf_msg(LVL_DEBUG, "ddisk_irq_handler(), status=%" PRIx32,
+	    (uint32_t) IPC_GET_ARG1(*icall));
+
+	ddisk_t *ddisk = (ddisk_t *) ddf_dev_data_get(dev);
+	
+	fibril_mutex_lock(&ddisk->lock);
+	fibril_condvar_broadcast(&ddisk->io_cv);
+	fibril_mutex_unlock(&ddisk->lock);
+}
+
+int ddisk_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
+{
+	return EOK;
+}
+
+int ddisk_bd_close(bd_srv_t *bd)
+{
+	return EOK;
+}
+
+static
+int ddisk_rw_block(ddisk_t *ddisk, bool read, aoff64_t ba, void *buf)
+{
+	fibril_mutex_lock(&ddisk->lock);
+
+	ddf_msg(LVL_DEBUG, "ddisk_rw_block(): read=%d, ba=%" PRId64 ", buf=%p",
+	    read, ba, buf);
+
+	if (ba >= ddisk->blocks)
+		return ELIMIT;
+
+	while (ddisk->io_busy)
+		fibril_condvar_wait(&ddisk->io_cv, &ddisk->lock);
+
+	ddisk->io_busy = true;
+
+	if (!read)
+		memcpy(ddisk->dma_buffer, buf, DDISK_BLOCK_SIZE);
+	
+	pio_write_32(&ddisk->ddisk_regs->dma_buffer,
+	    ddisk->dma_buffer_phys);
+	pio_write_32(&ddisk->ddisk_regs->block, (uint32_t) ba);
+	pio_write_32(&ddisk->ddisk_regs->command,
+	    read ? DDISK_CMD_READ : DDISK_CMD_WRITE);
+
+	fibril_condvar_wait(&ddisk->io_cv, &ddisk->lock);
+
+	if (read)
+		memcpy(buf, ddisk->dma_buffer, DDISK_BLOCK_SIZE);
+
+	ddisk->io_busy = false;
+	fibril_condvar_signal(&ddisk->io_cv);
+	fibril_mutex_unlock(&ddisk->lock);
+
+	return EOK;
+}
+
+static
+int ddisk_bd_rw_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt, void *buf,
+    size_t size, bool is_read)
+{
+	ddisk_t *ddisk = (ddisk_t *) bd->srvs->sarg;
+	aoff64_t i;
+	int rc;
+
+	if (size < cnt * DDISK_BLOCK_SIZE)
+		return EINVAL;		
+
+	for (i = 0; i < cnt; i++) {
+		rc = ddisk_rw_block(ddisk, is_read, ba + i,
+		    buf + i * DDISK_BLOCK_SIZE);
+		if (rc != EOK)
+			return rc;
+	}
+
+	return EOK;
+}
+
+int ddisk_bd_read_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt, void *buf,
+    size_t size)
+{
+	return ddisk_bd_rw_blocks(bd, ba, cnt, buf, size, true);
+}
+
+int ddisk_bd_write_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
+    const void *buf, size_t size)
+{
+	return ddisk_bd_rw_blocks(bd, ba, cnt, (void *) buf, size, false);
+}
+
+int ddisk_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
+{
+	*rsize = DDISK_BLOCK_SIZE; 
+	return EOK;
+}
+
+int ddisk_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
+{
+	ddisk_t *ddisk = (ddisk_t *) bd->srvs->sarg;
+
+	*rnb = ddisk->blocks;
+	return EOK;	
+}
+
+static int ddisk_get_res(ddf_dev_t *dev, ddisk_res_t *ddisk_res)
+{
+	async_sess_t *parent_sess;
+	hw_res_list_parsed_t hw_res;
+	int rc;
+
+	parent_sess = ddf_dev_parent_sess_create(dev, EXCHANGE_SERIALIZE);
+	if (parent_sess == NULL)
+		return ENOMEM;
+
+	hw_res_list_parsed_init(&hw_res);
+	rc = hw_res_get_list_parsed(parent_sess, &hw_res, 0);
+	if (rc != EOK)
+		return rc;
+
+	if ((hw_res.mem_ranges.count != 1) || (hw_res.irqs.count != 1)) {
+		rc = EINVAL;
+		goto error;
+	}
+
+	addr_range_t *regs = &hw_res.mem_ranges.ranges[0];
+	ddisk_res->base = RNGABS(*regs);
+	ddisk_res->irq = hw_res.irqs.irqs[0]; 
+
+	if (RNGSZ(*regs) < sizeof(ddisk_regs_t)) {
+		rc = EINVAL;
+		goto error;
+	}
+
+	rc = EOK;
+error:
+	hw_res_list_parsed_clean(&hw_res);
+	return rc;
+}
+
+static int ddisk_fun_create(ddisk_t *ddisk)
+{
+	int rc;
+	ddf_fun_t *fun = NULL;
+
+	fun = ddf_fun_create(ddisk->dev, fun_exposed, DDISK_FUN_NAME);
+	if (fun == NULL) {
+		ddf_msg(LVL_ERROR, "Failed creating DDF function.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	/* Set up a connection handler. */
+	ddf_fun_set_conn_handler(fun, ddisk_bd_connection);
+
+	rc = ddf_fun_bind(fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed binding DDF function %s: %s",
+		    DDISK_FUN_NAME, str_error(rc));
+		goto error;
+	}
+
+	ddf_fun_add_to_category(fun, "bd");
+	ddisk->fun = fun;
+
+	return EOK;
+error:
+	if (fun != NULL)
+		ddf_fun_destroy(fun);
+
+	return rc;
+}
+
+static int ddisk_fun_remove(ddisk_t *ddisk)
+{
+	int rc;
+
+	if (ddisk->fun == NULL)
+		return EOK;
+
+	ddf_msg(LVL_DEBUG, "ddisk_fun_remove(%p, '%s')", ddisk,
+	    DDISK_FUN_NAME);
+	rc = ddf_fun_offline(ddisk->fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Error offlining function '%s'.",
+		    DDISK_FUN_NAME);
+		goto error;
+	}
+
+	rc = ddf_fun_unbind(ddisk->fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.",
+		    DDISK_FUN_NAME);
+		goto error;
+	}
+
+	ddf_fun_destroy(ddisk->fun);
+	ddisk->fun = NULL;
+	rc = EOK;
+
+error:
+	return rc;
+}
+
+static int ddisk_fun_unbind(ddisk_t *ddisk)
+{
+	int rc;
+
+	if (ddisk->fun == NULL)
+		return EOK;
+
+	ddf_msg(LVL_DEBUG, "ddisk_fun_unbind(%p, '%s')", ddisk,
+	    DDISK_FUN_NAME);
+	rc = ddf_fun_unbind(ddisk->fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.",
+		    DDISK_FUN_NAME);
+		goto error;
+	}
+
+	ddf_fun_destroy(ddisk->fun);
+	ddisk->fun = NULL;
+	rc = EOK;
+
+error:
+	return rc;
+}
+
+/** Add new device
+ *
+ * @param  dev New device
+ * @return     EOK on success or negative error code.
+ */
+static int ddisk_dev_add(ddf_dev_t *dev)
+{
+	ddisk_t *ddisk;
+	ddisk_res_t res;
+	int rc;
+
+	/*
+	 * Get our resources.
+	 */
+	rc = ddisk_get_res(dev, &res);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Invalid HW resource configuration.");
+		return EINVAL;
+	}
+
+	/*
+	 * Allocate soft state.
+	 */
+	ddisk = ddf_dev_data_alloc(dev, sizeof(ddisk_t));
+	if (!ddisk) {
+		ddf_msg(LVL_ERROR, "Failed allocating soft state.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	/*
+	 * Initialize soft state.
+	 */
+	fibril_mutex_initialize(&ddisk->lock);
+	ddisk->dev = dev;
+	ddisk->ddisk_res = res;
+
+	fibril_condvar_initialize(&ddisk->io_cv);
+	ddisk->io_busy = false;
+
+	bd_srvs_init(&ddisk->bds);
+	ddisk->bds.ops = &ddisk_bd_ops;
+	ddisk->bds.sarg = ddisk;
+
+	/*
+	 * Enable access to ddisk's PIO registers.
+	 */
+	void *vaddr;
+	rc = pio_enable((void *) res.base, sizeof(ddisk_regs_t), &vaddr);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
+		goto error;
+	}
+	ddisk->ddisk_regs = vaddr;
+
+	ddisk->size = (int32_t) pio_read_32(&ddisk->ddisk_regs->size);
+	ddisk->blocks = ddisk->size / DDISK_BLOCK_SIZE;
+
+	if (ddisk->size <= 0) {
+		ddf_msg(LVL_WARN, "No disk detected.");
+		rc = EIO;
+		goto error;
+	}
+
+	/*
+	 * Allocate DMA buffer.
+	 */
+	ddisk->dma_buffer = AS_AREA_ANY;
+	rc = dmamem_map_anonymous(DDISK_BLOCK_SIZE, DMAMEM_4GiB,
+	    AS_AREA_READ | AS_AREA_WRITE, 0, &ddisk->dma_buffer_phys,
+	    &ddisk->dma_buffer);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Cannot allocate DMA memory.");
+		goto error;
+	}
+
+	ddf_msg(LVL_NOTE, "Allocated DMA buffer at %p virtual and %p physical.",
+	    ddisk->dma_buffer, (void *) ddisk->dma_buffer_phys);
+
+	/*
+	 * Create an exposed function.
+	 */
+	rc = ddisk_fun_create(ddisk);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed initializing ddisk controller.");
+		rc = EIO;
+		goto error;
+	}
+
+	/*
+ 	 * Register IRQ handler.
+ 	 */
+	ddisk_regs_t *res_phys = (ddisk_regs_t *) res.base;
+	ddisk_irq_pio_ranges[0].base = res.base;
+	ddisk_irq_commands[0].addr = (void *) &res_phys->status;
+	ddisk_irq_commands[3].addr = (void *) &res_phys->command;
+	rc = register_interrupt_handler(dev, ddisk->ddisk_res.irq,
+	    ddisk_irq_handler, &ddisk_irq_code);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed to register interrupt handler.");
+		goto error;
+	}
+
+	/*
+	 * Success, report what we have found.
+	 */
+	ddf_msg(LVL_NOTE,
+	    "Device at %p with %zd blocks (%zuB) using interrupt %d",
+	    (void *) ddisk->ddisk_res.base, ddisk->blocks,
+	    ddisk->size, ddisk->ddisk_res.irq);
+
+	return EOK;
+
+error:
+	if (ddisk->ddisk_regs)
+		pio_disable(ddisk->ddisk_regs, sizeof(ddisk_regs_t));
+	if (ddisk->dma_buffer)
+		dmamem_unmap_anonymous(ddisk->dma_buffer);
+
+	return rc;
+}
+
+
+static int ddisk_dev_remove_common(ddisk_t *ddisk, bool surprise)
+{
+	int rc;
+
+	if (!surprise)
+		rc = ddisk_fun_remove(ddisk);
+	else
+		rc = ddisk_fun_unbind(ddisk);
+
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Unable to cleanup function '%s'.",
+		    DDISK_FUN_NAME);
+		return rc;
+	}
+
+	unregister_interrupt_handler(ddisk->dev, ddisk->ddisk_res.irq);
+	
+	rc = pio_disable(ddisk->ddisk_regs, sizeof(ddisk_regs_t));
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Unable to disable PIO.");
+		return rc;	
+	}
+
+	dmamem_unmap_anonymous(ddisk->dma_buffer);
+
+	return EOK;
+}
+
+static int ddisk_dev_remove(ddf_dev_t *dev)
+{
+	ddisk_t *ddisk = (ddisk_t *) ddf_dev_data_get(dev);
+
+	ddf_msg(LVL_DEBUG, "ddisk_dev_remove(%p)", dev);
+	return ddisk_dev_remove_common(ddisk, false);
+}
+
+static int ddisk_dev_gone(ddf_dev_t *dev)
+{
+	ddisk_t *ddisk = (ddisk_t *) ddf_dev_data_get(dev);
+
+	ddf_msg(LVL_DEBUG, "ddisk_dev_gone(%p)", dev);
+	return ddisk_dev_remove_common(ddisk, true);
+}
+
+static int ddisk_fun_online(ddf_fun_t *fun)
+{
+	ddf_msg(LVL_DEBUG, "ddisk_fun_online()");
+	return ddf_fun_online(fun);
+}
+
+static int ddisk_fun_offline(ddf_fun_t *fun)
+{
+	ddf_msg(LVL_DEBUG, "ddisk_fun_offline()");
+	return ddf_fun_offline(fun);
+}
+
+/** Block device connection handler */
+static void ddisk_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	ddisk_t *ddisk;
+	ddf_fun_t *fun = (ddf_fun_t *) arg;
+
+	ddisk = (ddisk_t *) ddf_dev_data_get(ddf_fun_get_dev(fun));
+	bd_conn(iid, icall, &ddisk->bds);
+}
+
+int main(int argc, char *argv[])
+{
+	printf(NAME ": HelenOS MSIM ddisk device driver\n");
+	ddf_log_init(NAME);
+	return ddf_driver_main(&ddisk_driver);
+}
Index: uspace/drv/block/ddisk/ddisk.ma
===================================================================
--- uspace/drv/block/ddisk/ddisk.ma	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/block/ddisk/ddisk.ma	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,1 @@
+10 msim/ddisk
Index: uspace/drv/bus/isa/isa.c
===================================================================
--- uspace/drv/bus/isa/isa.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/drv/bus/isa/isa.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -245,5 +245,6 @@
 	bool opened = false;
 	int fd;
-	size_t len = 0;
+	size_t len;
+	ssize_t r;
 
 	fd = open(conf_path, O_RDONLY);
@@ -269,5 +270,6 @@
 	}
 
-	if (0 >= read(fd, buf, len)) {
+	r = read_all(fd, buf, len);
+	if (r < 0) {
 		ddf_msg(LVL_ERROR, "Unable to read file '%s'.", conf_path);
 		goto cleanup;
Index: uspace/drv/bus/usb/vhc/devconn.c
===================================================================
--- uspace/drv/bus/usb/vhc/devconn.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/drv/bus/usb/vhc/devconn.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -32,5 +32,5 @@
 
 
-static vhc_virtdev_t *vhc_virtdev_create()
+static vhc_virtdev_t *vhc_virtdev_create(void)
 {
 	vhc_virtdev_t *dev = malloc(sizeof(vhc_virtdev_t));
Index: uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.c
===================================================================
--- uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -282,5 +282,8 @@
 		return ret;
 	}
+	if (dispc->fb_data)
+		dmamem_unmap_anonymous(dispc->fb_data);
 	
+	dispc->fb_data = buffer;
 	amdm37x_dispc_setup_fb(dispc->regs, x, y, bpp *8, (uint32_t)pa);
 	dispc->active_fb.idx = mode.index;
@@ -290,10 +293,7 @@
 	dispc->active_fb.bpp = bpp;
 	dispc->active_fb.pixel2visual = p2v;
-	dispc->fb_data = buffer;
 	dispc->size = size;
 	assert(mode.index < 1);
 
-	if (dispc->fb_data)
-		dmamem_unmap_anonymous(dispc->fb_data);
 	return EOK;
 }
Index: uspace/drv/nic/ar9271/Makefile
===================================================================
--- uspace/drv/nic/ar9271/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/nic/ar9271/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,57 @@
+#
+# Copyright (c) 2014 Jan Kolarik
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+
+LIBS = \
+	$(LIBUSBDEV_PREFIX)/libusbdev.a \
+	$(LIBUSB_PREFIX)/libusb.a \
+	$(LIBDRV_PREFIX)/libdrv.a \
+	$(LIBNIC_PREFIX)/libnic.a \
+	$(LIBIEEE80211_PREFIX)/libieee80211.a \
+	$(LIBCRYPTO_PREFIX)/libcrypto.a
+
+EXTRA_CFLAGS += \
+	-I. \
+	-I$(LIBUSB_PREFIX)/include \
+	-I$(LIBUSBDEV_PREFIX)/include \
+	-I$(LIBDRV_PREFIX)/include \
+	-I$(LIBNIC_PREFIX)/include \
+	-I$(LIBIEEE80211_PREFIX)/include \
+	-I$(LIBCRYPTO_PREFIX)
+
+BINARY = ar9271
+
+SOURCES = \
+	ath_usb.c \
+	hw.c \
+	wmi.c \
+	htc.c \
+	ar9271.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/nic/ar9271/ar9271.c
===================================================================
--- uspace/drv/nic/ar9271/ar9271.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/nic/ar9271/ar9271.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,946 @@
+/*
+ * Copyright (c) 2014 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file ar9271.c
+ *
+ * Driver for AR9271 USB WiFi dongle.
+ *
+ */
+
+#include <ieee80211.h>
+#include <usb/classes/classes.h>
+#include <usb/dev/request.h>
+#include <usb/dev/poll.h>
+#include <usb/debug.h>
+#include <stdio.h>
+#include <ddf/interrupt.h>
+#include <nic.h>
+#include <macros.h>
+#include "ath_usb.h"
+#include "wmi.h"
+#include "hw.h"
+#include "ar9271.h"
+
+#define NAME  "ar9271"
+#define FIRMWARE_FILENAME  "/drv/ar9271/ar9271.fw"
+
+const usb_endpoint_description_t usb_ar9271_out_bulk_endpoint_description = {
+	.transfer_type = USB_TRANSFER_BULK,
+	.direction = USB_DIRECTION_OUT,
+	.interface_class = USB_CLASS_VENDOR_SPECIFIC,
+	.interface_subclass = 0x0,
+	.interface_protocol = 0x0,
+	.flags = 0
+};
+
+const usb_endpoint_description_t usb_ar9271_in_bulk_endpoint_description = {
+	.transfer_type = USB_TRANSFER_BULK,
+	.direction = USB_DIRECTION_IN,
+	.interface_class = USB_CLASS_VENDOR_SPECIFIC,
+	.interface_subclass = 0x0,
+	.interface_protocol = 0x0,
+	.flags = 0
+};
+
+const usb_endpoint_description_t usb_ar9271_in_int_endpoint_description = {
+	.transfer_type = USB_TRANSFER_INTERRUPT,
+	.direction = USB_DIRECTION_IN,
+	.interface_class = USB_CLASS_VENDOR_SPECIFIC,
+	.interface_subclass = 0x0,
+	.interface_protocol = 0x0,
+	.flags = 0
+};
+
+const usb_endpoint_description_t usb_ar9271_out_int_endpoint_description = {
+	.transfer_type = USB_TRANSFER_INTERRUPT,
+	.direction = USB_DIRECTION_OUT,
+	.interface_class = USB_CLASS_VENDOR_SPECIFIC,
+	.interface_subclass = 0x0,
+	.interface_protocol = 0x0,
+	.flags = 0
+};
+
+/* Array of endpoints expected on the device, NULL terminated. */
+const usb_endpoint_description_t *endpoints[] = {
+	&usb_ar9271_out_bulk_endpoint_description,
+	&usb_ar9271_in_bulk_endpoint_description,
+	&usb_ar9271_in_int_endpoint_description,
+	&usb_ar9271_out_int_endpoint_description,
+	NULL
+};
+
+/* Callback when new device is to be controlled by this driver. */
+static int ar9271_add_device(ddf_dev_t *);
+
+/* IEEE 802.11 callbacks */
+static int ar9271_ieee80211_start(ieee80211_dev_t *);
+static int ar9271_ieee80211_tx_handler(ieee80211_dev_t *, void *, size_t);
+static int ar9271_ieee80211_set_freq(ieee80211_dev_t *, uint16_t);
+static int ar9271_ieee80211_bssid_change(ieee80211_dev_t *, bool);
+static int ar9271_ieee80211_key_config(ieee80211_dev_t *, ieee80211_key_config_t *,
+    bool);
+
+static driver_ops_t ar9271_driver_ops = {
+	.dev_add = ar9271_add_device
+};
+
+static driver_t ar9271_driver = {
+	.name = NAME,
+	.driver_ops = &ar9271_driver_ops
+};
+
+static ieee80211_ops_t ar9271_ieee80211_ops = {
+	.start = ar9271_ieee80211_start,
+	.tx_handler = ar9271_ieee80211_tx_handler,
+	.set_freq = ar9271_ieee80211_set_freq,
+	.bssid_change = ar9271_ieee80211_bssid_change,
+	.key_config = ar9271_ieee80211_key_config
+};
+
+static ieee80211_iface_t ar9271_ieee80211_iface;
+
+static int ar9271_get_device_info(ddf_fun_t *, nic_device_info_t *);
+static int ar9271_get_cable_state(ddf_fun_t *, nic_cable_state_t *);
+static int ar9271_get_operation_mode(ddf_fun_t *, int *, nic_channel_mode_t *,
+    nic_role_t *);
+
+static nic_iface_t ar9271_ieee80211_nic_iface = {
+	.get_device_info = &ar9271_get_device_info,
+	.get_cable_state = &ar9271_get_cable_state,
+	.get_operation_mode = &ar9271_get_operation_mode
+};
+
+static ddf_dev_ops_t ar9271_ieee80211_dev_ops;
+
+/** Get device information.
+ *
+ */
+static int ar9271_get_device_info(ddf_fun_t *dev, nic_device_info_t *info)
+{
+	assert(dev);
+	assert(info);
+	
+	memset(info, 0, sizeof(nic_device_info_t));
+	
+	info->vendor_id = 0x0cf3;
+	info->device_id = 0x9271;
+	str_cpy(info->vendor_name, NIC_VENDOR_MAX_LENGTH,
+	    "Atheros Communications, Inc.");
+	str_cpy(info->model_name, NIC_MODEL_MAX_LENGTH,
+	    "AR9271");
+	
+	return EOK;
+}
+
+/** Get cable state.
+ *
+ */
+static int ar9271_get_cable_state(ddf_fun_t *fun, nic_cable_state_t *state)
+{
+	*state = NIC_CS_PLUGGED;
+	
+	return EOK;
+}
+
+/** Get operation mode of the device.
+ *
+ */
+static int ar9271_get_operation_mode(ddf_fun_t *fun, int *speed,
+    nic_channel_mode_t *duplex, nic_role_t *role)
+{
+	*duplex = NIC_CM_FULL_DUPLEX;
+	*speed = 10;
+	*role = NIC_ROLE_UNKNOWN;
+	
+	return EOK;
+}
+
+/** Set multicast frames acceptance mode.
+ *
+ */
+static int ar9271_on_multicast_mode_change(nic_t *nic,
+    nic_multicast_mode_t mode, const nic_address_t *addr, size_t addr_cnt)
+{
+	switch (mode) {
+	case NIC_MULTICAST_BLOCKED:
+		/* TODO */
+		break;
+	case NIC_MULTICAST_LIST:
+		/* TODO */
+		break;
+	case NIC_MULTICAST_PROMISC:
+		/* TODO */
+		break;
+	default:
+		return ENOTSUP;
+	}
+	
+	return EOK;
+}
+
+/** Set unicast frames acceptance mode.
+ *
+ */
+static int ar9271_on_unicast_mode_change(nic_t *nic, nic_unicast_mode_t mode,
+    const nic_address_t *addr, size_t addr_cnt)
+{
+	switch (mode) {
+	case NIC_UNICAST_BLOCKED:
+		/* TODO */
+		break;
+	case NIC_UNICAST_DEFAULT:
+		/* TODO */
+		break;
+	case NIC_UNICAST_LIST:
+		/* TODO */
+		break;
+	case NIC_UNICAST_PROMISC:
+		/* TODO */
+		break;
+	default:
+		return ENOTSUP;
+	}
+	
+	return EOK;
+}
+
+/** Set broadcast frames acceptance mode.
+ *
+ */
+static int ar9271_on_broadcast_mode_change(nic_t *nic,
+    nic_broadcast_mode_t mode)
+{
+	switch (mode) {
+	case NIC_BROADCAST_BLOCKED:
+		/* TODO */
+		break;
+	case NIC_BROADCAST_ACCEPTED:
+		/* TODO */
+		break;
+	default:
+		return ENOTSUP;
+	}
+	
+	return EOK;
+}
+
+static bool ar9271_rx_status_error(uint8_t status)
+{
+	return (status & AR9271_RX_ERROR_PHY) || (status & AR9271_RX_ERROR_CRC);
+}
+
+static int ar9271_data_polling(void *arg)
+{
+	assert(arg);
+	
+	ar9271_t *ar9271 = (ar9271_t *) arg;
+	
+	size_t buffer_size = ar9271->ath_device->data_response_length;
+	void *buffer = malloc(buffer_size);
+	
+	while (true) {
+		size_t transferred_size;
+		if (htc_read_data_message(ar9271->htc_device,
+		    buffer, buffer_size, &transferred_size) == EOK) {
+			size_t strip_length =
+			    sizeof(ath_usb_data_header_t) +
+			    sizeof(htc_frame_header_t) +
+			    sizeof(htc_rx_status_t);
+			
+			if (transferred_size < strip_length)
+				continue;
+			
+			ath_usb_data_header_t *data_header =
+			    (ath_usb_data_header_t *) buffer;
+			
+			/* Invalid packet. */
+			if (data_header->tag != uint16_t_le2host(RX_TAG))
+				continue;
+			
+			htc_rx_status_t *rx_status =
+			    (htc_rx_status_t *) ((void *) buffer +
+			    sizeof(ath_usb_data_header_t) +
+			    sizeof(htc_frame_header_t));
+			
+			uint16_t data_length =
+			    uint16_t_be2host(rx_status->data_length);
+			
+			int16_t payload_length =
+			    transferred_size - strip_length;
+			
+			if (payload_length - data_length < 0)
+				continue;
+			
+			if (ar9271_rx_status_error(rx_status->status))
+				continue;
+			
+			void *strip_buffer = buffer + strip_length;
+			
+			ieee80211_rx_handler(ar9271->ieee80211_dev,
+			    strip_buffer,
+			    payload_length);
+		}
+	}
+	
+	free(buffer);
+	
+	return EOK;
+}
+
+/** IEEE 802.11 handlers.
+ *
+ */
+static int ar9271_ieee80211_set_freq(ieee80211_dev_t *ieee80211_dev,
+    uint16_t freq)
+{
+	assert(ieee80211_dev);
+	
+	ar9271_t *ar9271 = (ar9271_t *) ieee80211_get_specific(ieee80211_dev);
+	
+	wmi_send_command(ar9271->htc_device, WMI_DISABLE_INTR, NULL, 0, NULL);
+	wmi_send_command(ar9271->htc_device, WMI_DRAIN_TXQ_ALL, NULL, 0, NULL);
+	wmi_send_command(ar9271->htc_device, WMI_STOP_RECV, NULL, 0, NULL);
+	
+	int rc = hw_freq_switch(ar9271, freq);
+	if (rc != EOK) {
+		usb_log_error("Failed to HW switch frequency.\n");
+		return rc;
+	}
+	
+	wmi_send_command(ar9271->htc_device, WMI_START_RECV, NULL, 0, NULL);
+	
+	rc = hw_rx_init(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to initialize RX.\n");
+		return rc;
+	}
+	
+	uint16_t htc_mode = host2uint16_t_be(1);
+	wmi_send_command(ar9271->htc_device, WMI_SET_MODE,
+	    (uint8_t *) &htc_mode, sizeof(htc_mode), NULL);
+	wmi_send_command(ar9271->htc_device, WMI_ENABLE_INTR, NULL, 0, NULL);
+	
+	return EOK;
+}
+
+static int ar9271_ieee80211_bssid_change(ieee80211_dev_t *ieee80211_dev,
+    bool connected)
+{
+	assert(ieee80211_dev);
+	
+	ar9271_t *ar9271 = (ar9271_t *) ieee80211_get_specific(ieee80211_dev);
+	
+	if (connected) {
+		nic_address_t bssid;
+		ieee80211_query_bssid(ieee80211_dev, &bssid);
+		
+		htc_sta_msg_t sta_msg;
+		memset(&sta_msg, 0, sizeof(htc_sta_msg_t));
+		sta_msg.is_vif_sta = 0;
+		sta_msg.max_ampdu =
+		    host2uint16_t_be(1 << IEEE80211_MAX_AMPDU_FACTOR);
+		sta_msg.sta_index = 1;
+		sta_msg.vif_index = 0;
+		memcpy(&sta_msg.addr, bssid.address, ETH_ADDR);
+		
+		wmi_send_command(ar9271->htc_device, WMI_NODE_CREATE,
+		    (uint8_t *) &sta_msg, sizeof(sta_msg), NULL);
+		
+		htc_rate_msg_t rate_msg;
+		memset(&rate_msg, 0, sizeof(htc_rate_msg_t));
+		rate_msg.sta_index = 1;
+		rate_msg.is_new = 1;
+		rate_msg.legacy_rates_count = ARRAY_SIZE(ieee80211bg_data_rates);
+		memcpy(&rate_msg.legacy_rates,
+		    ieee80211bg_data_rates, 
+		    ARRAY_SIZE(ieee80211bg_data_rates));
+		
+		wmi_send_command(ar9271->htc_device, WMI_RC_RATE_UPDATE,
+		    (uint8_t *) &rate_msg, sizeof(rate_msg), NULL);
+		
+		hw_set_rx_filter(ar9271, true);
+	} else {
+		uint8_t station_id = 1;
+		wmi_send_command(ar9271->htc_device, WMI_NODE_REMOVE,
+		    &station_id, sizeof(station_id), NULL);
+		
+		hw_set_rx_filter(ar9271, false);
+	}
+	
+	hw_set_bssid(ar9271);
+	
+	return EOK;
+}
+
+static int ar9271_ieee80211_key_config(ieee80211_dev_t *ieee80211_dev,
+    ieee80211_key_config_t *key_conf, bool insert)
+{
+	assert(ieee80211_dev);
+	
+	ar9271_t *ar9271 = (ar9271_t *) ieee80211_get_specific(ieee80211_dev);
+	
+	if(insert) {
+		assert(key_conf);
+		
+		uint32_t key[5];
+		uint32_t key_type;
+		uint32_t reg_ptr, mic_reg_ptr;
+		void *data_start;
+		
+		nic_address_t bssid;
+		ieee80211_query_bssid(ieee80211_dev, &bssid);
+		
+		switch (key_conf->suite) {
+		case IEEE80211_SECURITY_SUITE_WEP40:
+			key_type = AR9271_KEY_TABLE_TYPE_WEP40;
+			break;
+		case IEEE80211_SECURITY_SUITE_WEP104:
+			key_type = AR9271_KEY_TABLE_TYPE_WEP104;
+			break;
+		case IEEE80211_SECURITY_SUITE_TKIP:
+			key_type = AR9271_KEY_TABLE_TYPE_TKIP;
+			break;
+		case IEEE80211_SECURITY_SUITE_CCMP:
+			key_type = AR9271_KEY_TABLE_TYPE_CCMP;
+			break;
+		default:
+			key_type = -1;
+		}
+		
+		uint8_t key_id =
+		    (key_conf->flags & IEEE80211_KEY_FLAG_TYPE_PAIRWISE) ?
+		    AR9271_STA_KEY_INDEX : key_conf->id;
+		
+		reg_ptr = AR9271_KEY_TABLE(key_id);
+		mic_reg_ptr = AR9271_KEY_TABLE(key_id + 64);
+		data_start = (void *) key_conf->data;
+		
+		key[0] = uint32_t_le2host(*((uint32_t *) data_start));
+		key[1] = uint16_t_le2host(*((uint16_t *) (data_start + 4)));
+		key[2] = uint32_t_le2host(*((uint32_t *) (data_start + 6)));
+		key[3] = uint16_t_le2host(*((uint16_t *) (data_start + 10)));
+		key[4] = uint32_t_le2host(*((uint32_t *) (data_start + 12)));
+		
+		if ((key_conf->suite == IEEE80211_SECURITY_SUITE_WEP40) ||
+		    (key_conf->suite == IEEE80211_SECURITY_SUITE_WEP104))
+			key[4] &= 0xFF;
+		
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 0, key[0]);
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 4, key[1]);
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 8, key[2]);
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 12, key[3]);
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 16, key[4]);
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 20, key_type);
+		
+		uint32_t macl;
+		uint32_t mach;
+		if (key_conf->flags & IEEE80211_KEY_FLAG_TYPE_PAIRWISE) {
+			data_start = (void *) bssid.address;
+			macl = uint32_t_le2host(*((uint32_t *) data_start));
+			mach = uint16_t_le2host(*((uint16_t *) (data_start + 4)));
+		} else {
+			macl = 0;
+			mach = 0;
+		}
+		
+		macl >>= 1;
+		macl |= (mach & 1) << 31;
+		mach >>= 1;
+		mach |= 0x8000;
+		
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 24, macl);
+		wmi_reg_write(ar9271->htc_device, reg_ptr + 28, mach);
+		
+		/* Setup MIC keys for TKIP. */
+		if (key_conf->suite == IEEE80211_SECURITY_SUITE_TKIP) {
+			uint32_t mic[5];
+			uint8_t *gen_mic = data_start + IEEE80211_TKIP_RX_MIC_OFFSET;
+			uint8_t *tx_mic;
+			
+			if (key_conf->flags & IEEE80211_KEY_FLAG_TYPE_GROUP)
+				tx_mic = gen_mic;
+			else
+				tx_mic = data_start + IEEE80211_TKIP_TX_MIC_OFFSET;
+			
+			mic[0] = uint32_t_le2host(*((uint32_t *) gen_mic));
+			mic[1] = uint16_t_le2host(*((uint16_t *) (tx_mic + 2))) & 0xFFFF;
+			mic[2] = uint32_t_le2host(*((uint32_t *) (gen_mic + 4)));
+			mic[3] = uint16_t_le2host(*((uint16_t *) tx_mic)) & 0xFFFF;
+			mic[4] = uint32_t_le2host(*((uint32_t *) (tx_mic + 4)));
+			
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 0, mic[0]);
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 4, mic[1]);
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 8, mic[2]);
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 12, mic[3]);
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 16, mic[4]);
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 20,
+			    AR9271_KEY_TABLE_TYPE_CLR);
+			
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 24, 0);
+			wmi_reg_write(ar9271->htc_device, mic_reg_ptr + 28, 0);
+		}
+		
+		if (key_conf->flags & IEEE80211_KEY_FLAG_TYPE_GROUP)
+			ieee80211_setup_key_confirm(ieee80211_dev, true);
+	} else {
+		/* TODO: Delete keys from device */
+		ieee80211_setup_key_confirm(ieee80211_dev, false);
+	}
+	
+	return EOK;
+}
+
+static int ar9271_ieee80211_tx_handler(ieee80211_dev_t *ieee80211_dev,
+    void *buffer, size_t buffer_size)
+{
+	assert(ieee80211_dev);
+	
+	size_t complete_size;
+	size_t offset;
+	void *complete_buffer;
+	int endpoint;
+	
+	ar9271_t *ar9271 = (ar9271_t *) ieee80211_get_specific(ieee80211_dev);
+	
+	uint16_t frame_ctrl = *((uint16_t *) buffer);
+	if (ieee80211_is_data_frame(frame_ctrl)) {
+		offset = sizeof(htc_tx_data_header_t) +
+		    sizeof(htc_frame_header_t);
+		complete_size = buffer_size + offset;
+		complete_buffer = malloc(complete_size);
+		memset(complete_buffer, 0, complete_size);
+		
+		/*
+		 * Because we handle just station mode yet, node ID and VIF ID
+		 * are fixed.
+		 */
+		htc_tx_data_header_t *data_header =
+		    (htc_tx_data_header_t *)
+		    (complete_buffer + sizeof(htc_frame_header_t));
+		data_header->data_type = HTC_DATA_NORMAL;
+		data_header->node_idx = 1;
+		data_header->vif_idx = 0;
+		data_header->cookie = 0;
+		
+		if (ieee80211_query_using_key(ieee80211_dev)) {
+			data_header->keyix = AR9271_STA_KEY_INDEX;
+			
+			int sec_suite =
+			    ieee80211_get_pairwise_security(ieee80211_dev);
+			
+			switch (sec_suite) {
+			case IEEE80211_SECURITY_SUITE_WEP40:
+			case IEEE80211_SECURITY_SUITE_WEP104:
+				data_header->key_type = AR9271_KEY_TYPE_WEP;
+				break;
+			case IEEE80211_SECURITY_SUITE_TKIP:
+				data_header->key_type = AR9271_KEY_TYPE_TKIP;
+				break;
+			case IEEE80211_SECURITY_SUITE_CCMP:
+				data_header->key_type = AR9271_KEY_TYPE_AES;
+				break;
+			}
+		} else {
+			data_header->key_type = 0;
+			data_header->keyix = 0xFF;
+		}
+		
+		endpoint = ar9271->htc_device->endpoints.data_be_endpoint;
+	} else {
+		offset = sizeof(htc_tx_management_header_t) +
+		    sizeof(htc_frame_header_t);
+		complete_size = buffer_size + offset;
+		complete_buffer = malloc(complete_size);
+		memset(complete_buffer, 0, complete_size);
+		
+		/*
+		 * Because we handle just station mode yet, node ID and VIF ID
+		 * are fixed.
+		 */
+		htc_tx_management_header_t *mgmt_header =
+		    (htc_tx_management_header_t *)
+		    (complete_buffer + sizeof(htc_frame_header_t));
+		mgmt_header->node_idx = 0;
+		mgmt_header->vif_idx = 0;
+		mgmt_header->cookie = 0;
+		mgmt_header->keyix = 0xFF;
+		
+		endpoint = ar9271->htc_device->endpoints.mgmt_endpoint;
+	}
+	
+	/* Copy IEEE802.11 data to new allocated buffer with HTC headers. */
+	memcpy(complete_buffer + offset, buffer, buffer_size);
+	
+	htc_send_data_message(ar9271->htc_device, complete_buffer,
+	    complete_size, endpoint);
+	
+	free(complete_buffer);
+	
+	return EOK;
+}
+
+static int ar9271_ieee80211_start(ieee80211_dev_t *ieee80211_dev)
+{
+	assert(ieee80211_dev);
+	
+	ar9271_t *ar9271 = (ar9271_t *) ieee80211_get_specific(ieee80211_dev);
+	
+	wmi_send_command(ar9271->htc_device, WMI_FLUSH_RECV, NULL, 0, NULL);
+	
+	int rc = hw_reset(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to do HW reset.\n");
+		return rc;
+	}
+	
+	uint16_t htc_mode = host2uint16_t_be(1);
+	wmi_send_command(ar9271->htc_device, WMI_SET_MODE,
+	    (uint8_t *) &htc_mode, sizeof(htc_mode), NULL);
+	wmi_send_command(ar9271->htc_device, WMI_ATH_INIT, NULL, 0, NULL);
+	wmi_send_command(ar9271->htc_device, WMI_START_RECV, NULL, 0, NULL);
+	wmi_send_command(ar9271->htc_device, WMI_ENABLE_INTR, NULL, 0, NULL);
+	
+	rc = hw_rx_init(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to initialize RX.\n");
+		return rc;
+	}
+	
+	/* Send capability message to target. */
+	htc_cap_msg_t cap_msg;
+	cap_msg.ampdu_limit = host2uint32_t_be(0xffff);
+	cap_msg.ampdu_subframes = 0xff;
+	cap_msg.enable_coex = 0;
+	cap_msg.tx_chainmask = 0x1;
+	
+	wmi_send_command(ar9271->htc_device, WMI_TARGET_IC_UPDATE,
+	    (uint8_t *) &cap_msg, sizeof(cap_msg), NULL);
+	
+	rc = htc_init_new_vif(ar9271->htc_device);
+	if (rc != EOK) {
+		usb_log_error("Failed to initialize new VIF.\n");
+		return rc;
+	}
+	
+	/* Add data polling fibril. */
+	fid_t fibril = fibril_create(ar9271_data_polling, ar9271);
+	if (fibril == 0)
+		return ENOMEM;
+	
+	fibril_add_ready(fibril);
+	
+	ar9271->starting_up = false;
+	ieee80211_set_ready(ieee80211_dev, true);
+	
+	usb_log_info("Device fully initialized.\n");
+	
+	return EOK;
+}
+
+static int ar9271_init(ar9271_t *ar9271, usb_device_t *usb_device)
+{
+	ar9271->starting_up = true;
+	ar9271->usb_device = usb_device;
+	
+	fibril_mutex_initialize(&ar9271->ar9271_lock);
+	
+	ar9271->ath_device = calloc(1, sizeof(ath_t));
+	if (!ar9271->ath_device) {
+		usb_log_error("Failed to allocate memory for ath device "
+		    "structure.\n");
+		return ENOMEM;
+	}
+	
+	int rc = ath_usb_init(ar9271->ath_device, usb_device);
+	if (rc != EOK) {
+		free(ar9271->ath_device);
+		usb_log_error("Failed to initialize ath device.\n");
+		return rc;
+	}
+	
+	/* IEEE 802.11 framework structure initialization. */
+	ar9271->ieee80211_dev = ieee80211_device_create();
+	if (!ar9271->ieee80211_dev) {
+		free(ar9271->ath_device);
+		usb_log_error("Failed to allocate memory for IEEE80211 device "
+		    "structure.\n");
+		return ENOMEM;
+	}
+	
+	rc = ieee80211_device_init(ar9271->ieee80211_dev, ar9271->ddf_dev);
+	if (rc != EOK) {
+		free(ar9271->ieee80211_dev);
+		free(ar9271->ath_device);
+		usb_log_error("Failed to initialize IEEE80211 device structure."
+		    "\n");
+		return rc;
+	}
+	
+	ieee80211_set_specific(ar9271->ieee80211_dev, ar9271);
+	
+	/* HTC device structure initialization. */
+	ar9271->htc_device = calloc(1, sizeof(htc_device_t));
+	if (!ar9271->htc_device) {
+		free(ar9271->ieee80211_dev);
+		free(ar9271->ath_device);
+		usb_log_error("Failed to allocate memory for HTC device "
+		    "structure.\n");
+		return ENOMEM;
+	}
+	
+	rc = htc_device_init(ar9271->ath_device, ar9271->ieee80211_dev,
+	    ar9271->htc_device);
+	if (rc != EOK) {
+		free(ar9271->htc_device);
+		free(ar9271->ieee80211_dev);
+		free(ar9271->ath_device);
+		usb_log_error("Failed to initialize HTC device structure.\n");
+		return rc;
+	}
+	
+	return EOK;
+}
+
+/** Upload firmware to WiFi device.
+ *
+ * @param ar9271 AR9271 device structure
+ *
+ * @return EOK if succeed, negative error code otherwise
+ *
+ */
+static int ar9271_upload_fw(ar9271_t *ar9271)
+{
+	usb_device_t *usb_device = ar9271->usb_device;
+	
+	/* TODO: Set by maximum packet size in pipe. */
+	static const size_t MAX_TRANSFER_SIZE = 512;
+	
+	/* Load FW from file. */
+	FILE *fw_file = fopen(FIRMWARE_FILENAME, "rb");
+	if (fw_file == NULL) {
+		usb_log_error("Failed opening file with firmware.\n");
+		return ENOENT;
+	}
+	
+	fseek(fw_file, 0, SEEK_END);
+	uint64_t file_size = ftell(fw_file);
+	fseek(fw_file, 0, SEEK_SET);
+	
+	void *fw_data = malloc(file_size);
+	if (fw_data == NULL) {
+		fclose(fw_file);
+		usb_log_error("Failed allocating memory for firmware.\n");
+		return ENOMEM;
+	}
+	
+	fread(fw_data, file_size, 1, fw_file);
+	fclose(fw_file);
+	
+	/* Upload FW to device. */
+	uint64_t remain_size = file_size;
+	uint32_t current_addr = AR9271_FW_ADDRESS;
+	uint8_t *current_data = fw_data;
+	uint8_t *buffer = malloc(MAX_TRANSFER_SIZE);
+	
+	while (remain_size > 0) {
+		size_t chunk_size = min(remain_size, MAX_TRANSFER_SIZE);
+		memcpy(buffer, current_data, chunk_size);
+		int rc = usb_control_request_set(&usb_device->ctrl_pipe,
+		    USB_REQUEST_TYPE_VENDOR,
+		    USB_REQUEST_RECIPIENT_DEVICE,
+		    AR9271_FW_DOWNLOAD,
+		    uint16_host2usb(current_addr >> 8),
+		    0, buffer, chunk_size);
+		if (rc != EOK) {
+			free(fw_data);
+			free(buffer);
+			usb_log_error("Error while uploading firmware. "
+			    "Error: %d\n", rc);
+			return rc;
+		}
+		
+		remain_size -= chunk_size;
+		current_addr += chunk_size;
+		current_data += chunk_size;
+	}
+	
+	free(fw_data);
+	free(buffer);
+	
+	/*
+	 * Send command that firmware is successfully uploaded.
+	 * This should initiate creating confirmation message in
+	 * device side buffer which we will check in htc_check_ready function.
+	*/
+	int rc = usb_control_request_set(&usb_device->ctrl_pipe,
+	    USB_REQUEST_TYPE_VENDOR,
+	    USB_REQUEST_RECIPIENT_DEVICE,
+	    AR9271_FW_DOWNLOAD_COMP,
+	    uint16_host2usb(AR9271_FW_OFFSET >> 8),
+	    0, NULL, 0);
+	
+	if (rc != EOK) {
+		usb_log_error("IO error when sending fw upload confirmation "
+		    "message.\n");
+		return rc;
+	}
+	
+	usb_log_info("Firmware uploaded successfully.\n");
+	
+	/* Wait until firmware is ready - wait for 1 second to be sure. */
+	sleep(1);
+	
+	return rc;
+}
+
+/** Create driver data structure.
+ *
+ *  @param dev The device structure
+ *
+ *  @return Intialized device data structure or NULL if error occured
+ */
+static ar9271_t *ar9271_create_dev_data(ddf_dev_t *dev)
+{
+	/* USB framework initialization. */
+	usb_device_t *usb_device = calloc(1, sizeof(usb_device_t));
+	if (usb_device == NULL) {
+		usb_log_error("USB device structure allocation failed.\n");
+		return NULL;
+	}
+	
+	const char *err_msg = NULL;
+	int rc = usb_device_init(usb_device, dev, endpoints, &err_msg);
+	if (rc != EOK) {
+		free(usb_device);
+		usb_log_error("Failed to create USB device: %s, "
+		    "ERR_NUM = %d\n", err_msg, rc);
+		return NULL;
+	}
+	
+	/* AR9271 structure initialization. */
+	ar9271_t *ar9271 = calloc(1, sizeof(ar9271_t));
+	if (!ar9271) {
+		free(usb_device);
+		usb_log_error("Failed to allocate memory for device "
+		    "structure.\n");
+		return NULL;
+	}
+	
+	ar9271->ddf_dev = dev;
+	
+	rc = ar9271_init(ar9271, usb_device);
+	if (rc != EOK) {
+		free(ar9271);
+		free(usb_device);
+		usb_log_error("Failed to initialize AR9271 structure: %d\n",
+		    rc);
+		return NULL;
+	}
+	
+	return ar9271;
+}
+
+/** Clean up the ar9271 device structure.
+ *
+ * @param dev  The device structure.
+ */
+static void ar9271_delete_dev_data(ar9271_t *ar9271)
+{
+	assert(ar9271);
+	
+	// TODO
+}
+
+/** Probe and initialize the newly added device.
+ *
+ * @param dev The device structure.
+ *
+ * @return EOK if succeed, negative error code otherwise
+ */
+static int ar9271_add_device(ddf_dev_t *dev)
+{
+	assert(dev);
+	
+	/* Allocate driver data for the device. */
+	ar9271_t *ar9271 = ar9271_create_dev_data(dev);
+	if (ar9271 == NULL) {
+		usb_log_error("Unable to allocate device softstate.\n");
+		return ENOMEM;
+	}
+	
+	usb_log_info("HelenOS AR9271 device initialized.\n");
+	
+	/* Upload AR9271 firmware. */
+	ar9271_upload_fw(ar9271);
+	
+	/* Initialize AR9271 HTC services. */
+	int rc = htc_init(ar9271->htc_device);
+	if (rc != EOK) {
+		ar9271_delete_dev_data(ar9271);
+		usb_log_error("HTC initialization failed.\n");
+		return rc;
+	}
+	
+	/* Initialize AR9271 HW. */
+	rc = hw_init(ar9271);
+	if (rc != EOK) {
+		ar9271_delete_dev_data(ar9271);
+		usb_log_error("HW initialization failed.\n");
+		return rc;
+	}
+	
+	/* Initialize AR9271 IEEE 802.11 framework. */
+	rc = ieee80211_init(ar9271->ieee80211_dev, &ar9271_ieee80211_ops,
+	    &ar9271_ieee80211_iface, &ar9271_ieee80211_nic_iface,
+	    &ar9271_ieee80211_dev_ops);
+	if (rc != EOK) {
+		ar9271_delete_dev_data(ar9271);
+		usb_log_error("Failed to initialize IEEE80211 framework.\n");
+		return rc;
+	}
+	
+	nic_set_filtering_change_handlers(nic_get_from_ddf_dev(dev),
+	    ar9271_on_unicast_mode_change, ar9271_on_multicast_mode_change,
+	    ar9271_on_broadcast_mode_change, NULL, NULL);
+	
+	usb_log_info("HelenOS AR9271 added device.\n");
+	
+	return EOK;
+}
+
+int main(void)
+{
+	log_init(NAME);
+	
+	if (nic_driver_init(NAME) != EOK)
+		return 1;
+	
+	usb_log_info("HelenOS AR9271 driver started.\n");
+	
+	return ddf_driver_main(&ar9271_driver);
+}
Index: uspace/drv/nic/ar9271/ar9271.h
===================================================================
--- uspace/drv/nic/ar9271/ar9271.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/nic/ar9271/ar9271.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,907 @@
+/*
+ * Copyright (c) 2014 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file ar9271.h
+ *
+ * Header file for AR9271 USB WiFi dongle.
+ *
+ */
+
+#ifndef AR9271_H_
+#define AR9271_H_
+
+#include <usb/dev/driver.h>
+#include "htc.h"
+
+/** Number of transmission queues */
+#define AR9271_QUEUES_COUNT  10
+
+/** Number of GPIO pin used for handling led light */
+#define AR9271_LED_PIN  15
+
+/** Nominal value for AR9271 noise floor calibration. */
+#define AR9271_CALIB_NOMINAL_VALUE_2GHZ  -118
+
+/** RX errors values. */
+#define AR9271_RX_ERROR_CRC  0x01
+#define AR9271_RX_ERROR_PHY  0x02
+
+/** Key index used for device in station mode. */
+#define AR9271_STA_KEY_INDEX  4
+
+/* HW encryption key indicator. */
+enum ath9k_key_type {
+	AR9271_KEY_TYPE_CLEAR,
+	AR9271_KEY_TYPE_WEP,
+	AR9271_KEY_TYPE_AES,
+	AR9271_KEY_TYPE_TKIP,
+};
+
+/** AR9271 Registers */
+typedef enum {
+	/* ATH command register */
+	AR9271_COMMAND = 0x0008,
+	AR9271_COMMAND_RX_ENABLE = 0x00000004,
+	
+	/* ATH config register */
+	AR9271_CONFIG = 0x0014,
+	AR9271_CONFIG_ADHOC = 0x00000020,
+	
+	AR9271_QUEUE_BASE_MASK = 0x1000,
+	
+	/* EEPROM Addresses */
+	AR9271_EEPROM_BASE = 0x2100,
+	AR9271_EEPROM_MAC_ADDR_START = 0x2118,
+	
+	/* Reset MAC interface */
+	AR9271_RC = 0x4000,
+	AR9271_RC_AHB = 0x00000001,
+	
+	/* GPIO registers */
+	AR9271_GPIO_IN_OUT = 0x4048,       /**< GPIO value read/set  */
+	AR9271_GPIO_OE_OUT = 0x404C,       /**< GPIO set to output  */
+	AR9271_GPIO_OE_OUT_ALWAYS = 0x3,   /**< GPIO always drive output */
+	AR9271_GPIO_OUT_MUX1 = 0x4060,
+	AR9271_GPIO_OUT_MUX2 = 0x4064,
+	AR9271_GPIO_OUT_MUX3 = 0x4068,
+	AR9271_GPIO_OUT_MUX_AS_OUT = 0x0,  /**< GPIO set mux as output */
+	
+	/* RTC related registers */
+	AR9271_RTC_RC = 0x7000,
+	AR9271_RTC_RC_MAC_WARM = 0x00000001,
+	AR9271_RTC_RC_MAC_COLD = 0x00000002,
+	AR9271_RTC_RC_MASK = 0x00000003,
+	AR9271_RTC_PLL_CONTROL = 0x7014,
+	AR9271_RTC_RESET = 0x7040,
+	AR9271_RTC_STATUS = 0x7044,
+	AR9271_RTC_STATUS_MASK = 0x0000000F,
+	AR9271_RTC_STATUS_SHUTDOWN = 0x00000001,
+	AR9271_RTC_STATUS_ON = 0x00000002,
+	AR9271_RTC_SLEEP_CLOCK = 0x7048,
+	AR9271_RTC_SLEEP_CLOCK_FORCE_DERIVED = 0x2,
+	AR9271_RTC_FORCE_WAKE = 0x704C,
+	AR9271_RTC_FORCE_WAKE_ENABLE = 0x00000001,
+	AR9271_RTC_FORCE_WAKE_ON_INT = 0x00000002,
+	
+	/* MAC Registers */
+	AR9271_STATION_ID0 = 0x8000,  /**< STA Address Lower 32 Bits */
+	AR9271_STATION_ID1 = 0x8004,  /**< STA Address Upper 16 Bits */
+	AR9271_BSSID0 = 0x8008,       /**< BSSID Lower 32 Bits */
+	AR9271_BSSID1 = 0x800C,       /**< BSSID Upper 16 Bits */
+	AR9271_BSSID_MASK0 = 0x80E0,  /**< BSSID Mask Lower 32 Bits */
+	AR9271_BSSID_MASK1 = 0x80E4,  /**< BSSID Mask Upper 16 Bits */
+	AR9271_STATION_ID1_MASK = 0x0000FFFF,
+	AR9271_STATION_ID1_POWER_SAVING = 0x00040000,
+	AR9271_MULTICAST_FILTER1 = 0x8040,
+	AR9271_MULTICAST_FILTER2 = 0x8044,
+	AR9271_DIAG = 0x8048,
+	
+	/* RX filtering register */
+	AR9271_RX_FILTER = 0x803C,
+	AR9271_RX_FILTER_UNI = 0x00000001,
+	AR9271_RX_FILTER_MULTI = 0x00000002,
+	AR9271_RX_FILTER_BROAD = 0x00000004,
+	AR9271_RX_FILTER_CONTROL = 0x00000008,
+	AR9271_RX_FILTER_BEACON = 0x00000010,
+	AR9271_RX_FILTER_PROMISCUOUS = 0x00000020,
+	AR9271_RX_FILTER_PROBEREQ = 0x00000080,
+	AR9271_RX_FILTER_MYBEACON = 0x00000200,
+	AR9271_RX_FILTER_MCAST_BCAST_ALL = 0x00008000,
+	
+	/* Key related registers */
+	AR9271_KEY_TABLE_BASE = 0x8800,
+	AR9271_KEY_TABLE_TYPE_WEP40 = 0x0,
+	AR9271_KEY_TABLE_TYPE_WEP104 = 0x1,
+	AR9271_KEY_TABLE_TYPE_TKIP = 0x4,
+	AR9271_KEY_TABLE_TYPE_CCMP = 0x6,
+	AR9271_KEY_TABLE_TYPE_CLR = 0x7,
+	
+	/* Physical layer registers */
+	AR9271_PHY_ACTIVE = 0x981C,
+	AR9271_ADC_CONTROL = 0x982C,
+	AR9271_AGC_CONTROL = 0x9860,
+	AR9271_PHY_CAL = 0x9864,
+	AR9271_PHY_SYNTH_CONTROL = 0x9874, 
+	AR9271_PHY_SPECTRAL_SCAN = 0x9910,
+	AR9271_PHY_RADAR0 = 0x9954,
+	AR9271_PHY_RADAR0_FFT_ENABLED = 0x80000000,
+	AR9271_PHY_RFBUS_KILL = 0x997C,
+	AR9271_PHY_RFBUS_GRANT = 0x9C20,
+	AR9271_PHY_MODE = 0xA200,
+	AR9271_PHY_CCK_TX_CTRL = 0xA204,
+	AR9271_PHY_TPCRG1 = 0xA258, 
+	AR9271_CARRIER_LEAK_CONTROL = 0xA358,
+	AR9271_ADC_CONTROL_OFF_PWDADC = 0x00008000,
+	AR9271_AGC_CONTROL_CALIB = 0x00000001,
+	AR9271_AGC_CONTROL_NF_CALIB = 0x00000002,
+	AR9271_AGC_CONTROL_NF_CALIB_EN = 0x00008000, 
+	AR9271_AGC_CONTROL_TX_CALIB = 0x00010000,
+	AR9271_AGC_CONTROL_NF_NOT_UPDATE = 0x00020000,
+	AR9271_PHY_MODE_DYNAMIC = 0x04,
+	AR9271_PHY_CCK_TX_CTRL_JAPAN = 0x00000010,
+	AR9271_PHY_TPCRG1_PD_CALIB = 0x00400000,
+	AR9271_CARRIER_LEAK_CALIB = 0x00000002,
+	
+	AR9271_OPMODE_STATION_AP_MASK =	0x00010000,
+	AR9271_OPMODE_ADHOC_MASK = 0x00020000,
+	
+	AR9271_CLOCK_CONTROL = 0x50040,
+	AR9271_MAX_CPU_CLOCK = 0x304,
+	
+	AR9271_RESET_POWER_DOWN_CONTROL = 0x50044,
+	AR9271_RADIO_RF_RESET = 0x20,
+	AR9271_GATE_MAC_CONTROL = 0x4000,
+	
+	/* FW Addresses */
+	AR9271_FW_ADDRESS = 0x501000,
+	AR9271_FW_OFFSET = 0x903000,
+} ar9271_registers_t;
+
+/** Compute key table base position for key by its id. */
+#define AR9271_KEY_TABLE(id)  (AR9271_KEY_TABLE_BASE + (id) * 32)
+
+/** AR9271 Requests */
+typedef enum {
+	AR9271_FW_DOWNLOAD = 0x30,
+	AR9271_FW_DOWNLOAD_COMP = 0x31,
+} ar9271_requests_t;
+
+/** AR9271 device data */
+typedef struct {
+	/** Lock for access. */
+	fibril_mutex_t ar9271_lock;
+	
+	/** Whether device is starting up. */
+	bool starting_up;
+	
+	/** Backing DDF device */
+	ddf_dev_t *ddf_dev;
+	
+	/** USB device data */
+	usb_device_t *usb_device;
+	
+	/** IEEE 802.11 device data */
+	ieee80211_dev_t *ieee80211_dev;
+	
+	/** ATH device data */
+	ath_t *ath_device;
+	
+	/** HTC device data */
+	htc_device_t *htc_device;
+} ar9271_t;
+
+/** AR9271 init values for 2GHz mode operation.
+ *
+ * Including settings of noise floor limits.
+ *
+ * Taken from the Linux driver (drivers/net/wireless/ath/ath9k/)
+ * Copyright (c) 2008-2011 Atheros Communications Inc.
+ * Licensed under the terms of ISC
+ *
+ */
+static const uint32_t ar9271_2g_mode_array[][2] = {
+	{0x00001030, 0x00000160},
+	{0x00001070, 0x0000018c},
+	{0x000010b0, 0x00003e38},
+	{0x000010f0, 0x00000000},
+	{0x00008014, 0x08400b00},
+	{0x0000801c, 0x12e0002b},
+	{0x00008318, 0x00003440},
+	{0x00009804, 0x000003c0},  /*< Note: overridden */
+	{0x00009820, 0x02020200},
+	{0x00009824, 0x01000e0e},
+	{0x00009828, 0x0a020001},  /*< Note: overridden */
+	{0x00009834, 0x00000e0e},
+	{0x00009838, 0x00000007},
+	{0x00009840, 0x206a012e},
+	{0x00009844, 0x03721620},
+	{0x00009848, 0x00001053},
+	{0x0000a848, 0x00001053},
+	{0x00009850, 0x6d4000e2},
+	{0x00009858, 0x7ec84d2e},
+	{0x0000985c, 0x3137605e},
+	{0x00009860, 0x00058d18},
+	{0x00009864, 0x0001ce00},
+	{0x00009868, 0x5ac640d0},
+	{0x0000986c, 0x06903881},
+	{0x00009910, 0x30002310},
+	{0x00009914, 0x00000898},
+	{0x00009918, 0x0000000b},
+	{0x00009924, 0xd00a800d},
+	{0x00009944, 0xffbc1020},
+	{0x00009960, 0x00000000},
+	{0x00009964, 0x00000000},
+	{0x000099b8, 0x0000421c},
+	{0x000099bc, 0x00000c00},
+	{0x000099c0, 0x05eea6d4},
+	{0x000099c4, 0x06336f77},
+	{0x000099c8, 0x6af6532f},
+	{0x000099cc, 0x08f186c8},
+	{0x000099d0, 0x00046384},
+	{0x000099d4, 0x00000000},
+	{0x000099d8, 0x00000000},
+	{0x00009a00, 0x00058084},
+	{0x00009a04, 0x00058088},
+	{0x00009a08, 0x0005808c},
+	{0x00009a0c, 0x00058100},
+	{0x00009a10, 0x00058104},
+	{0x00009a14, 0x00058108},
+	{0x00009a18, 0x0005810c},
+	{0x00009a1c, 0x00058110},
+	{0x00009a20, 0x00058114},
+	{0x00009a24, 0x00058180},
+	{0x00009a28, 0x00058184},
+	{0x00009a2c, 0x00058188},
+	{0x00009a30, 0x0005818c},
+	{0x00009a34, 0x00058190},
+	{0x00009a38, 0x00058194},
+	{0x00009a3c, 0x000581a0},
+	{0x00009a40, 0x0005820c},
+	{0x00009a44, 0x000581a8},
+	{0x00009a48, 0x00058284},
+	{0x00009a4c, 0x00058288},
+	{0x00009a50, 0x00058224},
+	{0x00009a54, 0x00058290},
+	{0x00009a58, 0x00058300},
+	{0x00009a5c, 0x00058304},
+	{0x00009a60, 0x00058308},
+	{0x00009a64, 0x0005830c},
+	{0x00009a68, 0x00058380},
+	{0x00009a6c, 0x00058384},
+	{0x00009a70, 0x00068700},
+	{0x00009a74, 0x00068704},
+	{0x00009a78, 0x00068708},
+	{0x00009a7c, 0x0006870c},
+	{0x00009a80, 0x00068780},
+	{0x00009a84, 0x00068784},
+	{0x00009a88, 0x00078b00},
+	{0x00009a8c, 0x00078b04},
+	{0x00009a90, 0x00078b08},
+	{0x00009a94, 0x00078b0c},
+	{0x00009a98, 0x00078b80},
+	{0x00009a9c, 0x00078b84},
+	{0x00009aa0, 0x00078b88},
+	{0x00009aa4, 0x00078b8c},
+	{0x00009aa8, 0x00078b90},
+	{0x00009aac, 0x000caf80},
+	{0x00009ab0, 0x000caf84},
+	{0x00009ab4, 0x000caf88},
+	{0x00009ab8, 0x000caf8c},
+	{0x00009abc, 0x000caf90},
+	{0x00009ac0, 0x000db30c},
+	{0x00009ac4, 0x000db310},
+	{0x00009ac8, 0x000db384},
+	{0x00009acc, 0x000db388},
+	{0x00009ad0, 0x000db324},
+	{0x00009ad4, 0x000eb704},
+	{0x00009ad8, 0x000eb6a4},
+	{0x00009adc, 0x000eb6a8},
+	{0x00009ae0, 0x000eb710},
+	{0x00009ae4, 0x000eb714},
+	{0x00009ae8, 0x000eb720},
+	{0x00009aec, 0x000eb724},
+	{0x00009af0, 0x000eb728},
+	{0x00009af4, 0x000eb72c},
+	{0x00009af8, 0x000eb7a0},
+	{0x00009afc, 0x000eb7a4},
+	{0x00009b00, 0x000eb7a8},
+	{0x00009b04, 0x000eb7b0},
+	{0x00009b08, 0x000eb7b4},
+	{0x00009b0c, 0x000eb7b8},
+	{0x00009b10, 0x000eb7a5},
+	{0x00009b14, 0x000eb7a9},
+	{0x00009b18, 0x000eb7ad},
+	{0x00009b1c, 0x000eb7b1},
+	{0x00009b20, 0x000eb7b5},
+	{0x00009b24, 0x000eb7b9},
+	{0x00009b28, 0x000eb7c5},
+	{0x00009b2c, 0x000eb7c9},
+	{0x00009b30, 0x000eb7d1},
+	{0x00009b34, 0x000eb7d5},
+	{0x00009b38, 0x000eb7d9},
+	{0x00009b3c, 0x000eb7c6},
+	{0x00009b40, 0x000eb7ca},
+	{0x00009b44, 0x000eb7ce},
+	{0x00009b48, 0x000eb7d2},
+	{0x00009b4c, 0x000eb7d6},
+	{0x00009b50, 0x000eb7c3},
+	{0x00009b54, 0x000eb7c7},
+	{0x00009b58, 0x000eb7cb},
+	{0x00009b5c, 0x000eb7cf},
+	{0x00009b60, 0x000eb7d7},
+	{0x00009b64, 0x000eb7db},
+	{0x00009b68, 0x000eb7db},
+	{0x00009b6c, 0x000eb7db},
+	{0x00009b70, 0x000eb7db},
+	{0x00009b74, 0x000eb7db},
+	{0x00009b78, 0x000eb7db},
+	{0x00009b7c, 0x000eb7db},
+	{0x00009b80, 0x000eb7db},
+	{0x00009b84, 0x000eb7db},
+	{0x00009b88, 0x000eb7db},
+	{0x00009b8c, 0x000eb7db},
+	{0x00009b90, 0x000eb7db},
+	{0x00009b94, 0x000eb7db},
+	{0x00009b98, 0x000eb7db},
+	{0x00009b9c, 0x000eb7db},
+	{0x00009ba0, 0x000eb7db},
+	{0x00009ba4, 0x000eb7db},
+	{0x00009ba8, 0x000eb7db},
+	{0x00009bac, 0x000eb7db},
+	{0x00009bb0, 0x000eb7db},
+	{0x00009bb4, 0x000eb7db},
+	{0x00009bb8, 0x000eb7db},
+	{0x00009bbc, 0x000eb7db},
+	{0x00009bc0, 0x000eb7db},
+	{0x00009bc4, 0x000eb7db},
+	{0x00009bc8, 0x000eb7db},
+	{0x00009bcc, 0x000eb7db},
+	{0x00009bd0, 0x000eb7db},
+	{0x00009bd4, 0x000eb7db},
+	{0x00009bd8, 0x000eb7db},
+	{0x00009bdc, 0x000eb7db},
+	{0x00009be0, 0x000eb7db},
+	{0x00009be4, 0x000eb7db},
+	{0x00009be8, 0x000eb7db},
+	{0x00009bec, 0x000eb7db},
+	{0x00009bf0, 0x000eb7db},
+	{0x00009bf4, 0x000eb7db},
+	{0x00009bf8, 0x000eb7db},
+	{0x00009bfc, 0x000eb7db},
+	{0x0000aa00, 0x00058084},
+	{0x0000aa04, 0x00058088},
+	{0x0000aa08, 0x0005808c},
+	{0x0000aa0c, 0x00058100},
+	{0x0000aa10, 0x00058104},
+	{0x0000aa14, 0x00058108},
+	{0x0000aa18, 0x0005810c},
+	{0x0000aa1c, 0x00058110},
+	{0x0000aa20, 0x00058114},
+	{0x0000aa24, 0x00058180},
+	{0x0000aa28, 0x00058184},
+	{0x0000aa2c, 0x00058188},
+	{0x0000aa30, 0x0005818c},
+	{0x0000aa34, 0x00058190},
+	{0x0000aa38, 0x00058194},
+	{0x0000aa3c, 0x000581a0},
+	{0x0000aa40, 0x0005820c},
+	{0x0000aa44, 0x000581a8},
+	{0x0000aa48, 0x00058284},
+	{0x0000aa4c, 0x00058288},
+	{0x0000aa50, 0x00058224},
+	{0x0000aa54, 0x00058290},
+	{0x0000aa58, 0x00058300},
+	{0x0000aa5c, 0x00058304},
+	{0x0000aa60, 0x00058308},
+	{0x0000aa64, 0x0005830c},
+	{0x0000aa68, 0x00058380},
+	{0x0000aa6c, 0x00058384},
+	{0x0000aa70, 0x00068700},
+	{0x0000aa74, 0x00068704},
+	{0x0000aa78, 0x00068708},
+	{0x0000aa7c, 0x0006870c},
+	{0x0000aa80, 0x00068780},
+	{0x0000aa84, 0x00068784},
+	{0x0000aa88, 0x00078b00},
+	{0x0000aa8c, 0x00078b04},
+	{0x0000aa90, 0x00078b08},
+	{0x0000aa94, 0x00078b0c},
+	{0x0000aa98, 0x00078b80},
+	{0x0000aa9c, 0x00078b84},
+	{0x0000aaa0, 0x00078b88},
+	{0x0000aaa4, 0x00078b8c},
+	{0x0000aaa8, 0x00078b90},
+	{0x0000aaac, 0x000caf80},
+	{0x0000aab0, 0x000caf84},
+	{0x0000aab4, 0x000caf88},
+	{0x0000aab8, 0x000caf8c},
+	{0x0000aabc, 0x000caf90},
+	{0x0000aac0, 0x000db30c},
+	{0x0000aac4, 0x000db310},
+	{0x0000aac8, 0x000db384},
+	{0x0000aacc, 0x000db388},
+	{0x0000aad0, 0x000db324},
+	{0x0000aad4, 0x000eb704},
+	{0x0000aad8, 0x000eb6a4},
+	{0x0000aadc, 0x000eb6a8},
+	{0x0000aae0, 0x000eb710},
+	{0x0000aae4, 0x000eb714},
+	{0x0000aae8, 0x000eb720},
+	{0x0000aaec, 0x000eb724},
+	{0x0000aaf0, 0x000eb728},
+	{0x0000aaf4, 0x000eb72c},
+	{0x0000aaf8, 0x000eb7a0},
+	{0x0000aafc, 0x000eb7a4},
+	{0x0000ab00, 0x000eb7a8},
+	{0x0000ab04, 0x000eb7b0},
+	{0x0000ab08, 0x000eb7b4},
+	{0x0000ab0c, 0x000eb7b8},
+	{0x0000ab10, 0x000eb7a5},
+	{0x0000ab14, 0x000eb7a9},
+	{0x0000ab18, 0x000eb7ad},
+	{0x0000ab1c, 0x000eb7b1},
+	{0x0000ab20, 0x000eb7b5},
+	{0x0000ab24, 0x000eb7b9},
+	{0x0000ab28, 0x000eb7c5},
+	{0x0000ab2c, 0x000eb7c9},
+	{0x0000ab30, 0x000eb7d1},
+	{0x0000ab34, 0x000eb7d5},
+	{0x0000ab38, 0x000eb7d9},
+	{0x0000ab3c, 0x000eb7c6},
+	{0x0000ab40, 0x000eb7ca},
+	{0x0000ab44, 0x000eb7ce},
+	{0x0000ab48, 0x000eb7d2},
+	{0x0000ab4c, 0x000eb7d6},
+	{0x0000ab50, 0x000eb7c3},
+	{0x0000ab54, 0x000eb7c7},
+	{0x0000ab58, 0x000eb7cb},
+	{0x0000ab5c, 0x000eb7cf},
+	{0x0000ab60, 0x000eb7d7},
+	{0x0000ab64, 0x000eb7db},
+	{0x0000ab68, 0x000eb7db},
+	{0x0000ab6c, 0x000eb7db},
+	{0x0000ab70, 0x000eb7db},
+	{0x0000ab74, 0x000eb7db},
+	{0x0000ab78, 0x000eb7db},
+	{0x0000ab7c, 0x000eb7db},
+	{0x0000ab80, 0x000eb7db},
+	{0x0000ab84, 0x000eb7db},
+	{0x0000ab88, 0x000eb7db},
+	{0x0000ab8c, 0x000eb7db},
+	{0x0000ab90, 0x000eb7db},
+	{0x0000ab94, 0x000eb7db},
+	{0x0000ab98, 0x000eb7db},
+	{0x0000ab9c, 0x000eb7db},
+	{0x0000aba0, 0x000eb7db},
+	{0x0000aba4, 0x000eb7db},
+	{0x0000aba8, 0x000eb7db},
+	{0x0000abac, 0x000eb7db},
+	{0x0000abb0, 0x000eb7db},
+	{0x0000abb4, 0x000eb7db},
+	{0x0000abb8, 0x000eb7db},
+	{0x0000abbc, 0x000eb7db},
+	{0x0000abc0, 0x000eb7db},
+	{0x0000abc4, 0x000eb7db},
+	{0x0000abc8, 0x000eb7db},
+	{0x0000abcc, 0x000eb7db},
+	{0x0000abd0, 0x000eb7db},
+	{0x0000abd4, 0x000eb7db},
+	{0x0000abd8, 0x000eb7db},
+	{0x0000abdc, 0x000eb7db},
+	{0x0000abe0, 0x000eb7db},
+	{0x0000abe4, 0x000eb7db},
+	{0x0000abe8, 0x000eb7db},
+	{0x0000abec, 0x000eb7db},
+	{0x0000abf0, 0x000eb7db},
+	{0x0000abf4, 0x000eb7db},
+	{0x0000abf8, 0x000eb7db},
+	{0x0000abfc, 0x000eb7db},
+	{0x0000a204, 0x00000004},
+	{0x0000a20c, 0x0001f000},
+	{0x0000b20c, 0x0001f000},
+	{0x0000a21c, 0x1883800a},
+	{0x0000a230, 0x00000108},
+	{0x0000a250, 0x0004a000},
+	{0x0000a358, 0x7999aa0e}
+};
+
+/** AR9271 TX init values for 2GHz mode operation.
+ *
+ * Taken from the Linux driver (drivers/net/wireless/ath/ath9k/)
+ * Copyright (c) 2008-2011 Atheros Communications Inc.
+ * Licensed under the terms of ISC
+ *
+ */
+static const uint32_t ar9271_2g_tx_array[][2] = {
+	{0x0000a300, 0x00010000},
+	{0x0000a304, 0x00016200},
+	{0x0000a308, 0x00018201},
+	{0x0000a30c, 0x0001b240},
+	{0x0000a310, 0x0001d241},
+	{0x0000a314, 0x0001f600},
+	{0x0000a318, 0x00022800},
+	{0x0000a31c, 0x00026802},
+	{0x0000a320, 0x0002b805},
+	{0x0000a324, 0x0002ea41},
+	{0x0000a328, 0x00038b00},
+	{0x0000a32c, 0x0003ab40},
+	{0x0000a330, 0x0003cd80},
+	{0x0000a334, 0x000368de},
+	{0x0000a338, 0x0003891e},
+	{0x0000a33c, 0x0003a95e},
+	{0x0000a340, 0x0003e9df},
+	{0x0000a344, 0x0003e9df},
+	{0x0000a348, 0x0003e9df},
+	{0x0000a34c, 0x0003e9df},
+	{0x0000a350, 0x0003e9df},
+	{0x0000a354, 0x0003e9df},
+	{0x00007838, 0x0000002b},
+	{0x00007824, 0x00d8a7ff},
+	{0x0000786c, 0x08609eba},
+	{0x00007820, 0x00000c00},
+	{0x0000a274, 0x0a214652},
+	{0x0000a278, 0x0e739ce7},
+	{0x0000a27c, 0x05018063},
+	{0x0000a394, 0x06318c63},
+	{0x0000a398, 0x00000063},
+	{0x0000a3dc, 0x06318c63},
+	{0x0000a3e0, 0x00000063}
+};
+
+/** AR9271 hardware init values.
+ *
+ * Taken from the Linux driver (drivers/net/wireless/ath/ath9k/)
+ * Copyright (c) 2008-2011 Atheros Communications Inc.
+ * Licensed under the terms of ISC
+ *
+ */
+static const uint32_t ar9271_init_array[][2] = {
+	{0x0000000c, 0x00000000},
+	{0x00000030, 0x00020045},
+	{0x00000034, 0x00000005},
+	{0x00000040, 0x00000000},
+	{0x00000044, 0x00000008},
+	{0x00000048, 0x00000008},
+	{0x0000004c, 0x00000010},
+	{0x00000050, 0x00000000},
+	{0x00000054, 0x0000001f},
+	{0x00000800, 0x00000000},
+	{0x00000804, 0x00000000},
+	{0x00000808, 0x00000000},
+	{0x0000080c, 0x00000000},
+	{0x00000810, 0x00000000},
+	{0x00000814, 0x00000000},
+	{0x00000818, 0x00000000},
+	{0x0000081c, 0x00000000},
+	{0x00000820, 0x00000000},
+	{0x00000824, 0x00000000},
+	{0x00001040, 0x002ffc0f},
+	{0x00001044, 0x002ffc0f},
+	{0x00001048, 0x002ffc0f},
+	{0x0000104c, 0x002ffc0f},
+	{0x00001050, 0x002ffc0f},
+	{0x00001054, 0x002ffc0f},
+	{0x00001058, 0x002ffc0f},
+	{0x0000105c, 0x002ffc0f},
+	{0x00001060, 0x002ffc0f},
+	{0x00001064, 0x002ffc0f},
+	{0x00001230, 0x00000000},
+	{0x00001270, 0x00000000},
+	{0x00001038, 0x00000000},
+	{0x00001078, 0x00000000},
+	{0x000010b8, 0x00000000},
+	{0x000010f8, 0x00000000},
+	{0x00001138, 0x00000000},
+	{0x00001178, 0x00000000},
+	{0x000011b8, 0x00000000},
+	{0x000011f8, 0x00000000},
+	{0x00001238, 0x00000000},
+	{0x00001278, 0x00000000},
+	{0x000012b8, 0x00000000},
+	{0x000012f8, 0x00000000},
+	{0x00001338, 0x00000000},
+	{0x00001378, 0x00000000},
+	{0x000013b8, 0x00000000},
+	{0x000013f8, 0x00000000},
+	{0x00001438, 0x00000000},
+	{0x00001478, 0x00000000},
+	{0x000014b8, 0x00000000},
+	{0x000014f8, 0x00000000},
+	{0x00001538, 0x00000000},
+	{0x00001578, 0x00000000},
+	{0x000015b8, 0x00000000},
+	{0x000015f8, 0x00000000},
+	{0x00001638, 0x00000000},
+	{0x00001678, 0x00000000},
+	{0x000016b8, 0x00000000},
+	{0x000016f8, 0x00000000},
+	{0x00001738, 0x00000000},
+	{0x00001778, 0x00000000},
+	{0x000017b8, 0x00000000},
+	{0x000017f8, 0x00000000},
+	{0x0000103c, 0x00000000},
+	{0x0000107c, 0x00000000},
+	{0x000010bc, 0x00000000},
+	{0x000010fc, 0x00000000},
+	{0x0000113c, 0x00000000},
+	{0x0000117c, 0x00000000},
+	{0x000011bc, 0x00000000},
+	{0x000011fc, 0x00000000},
+	{0x0000123c, 0x00000000},
+	{0x0000127c, 0x00000000},
+	{0x000012bc, 0x00000000},
+	{0x000012fc, 0x00000000},
+	{0x0000133c, 0x00000000},
+	{0x0000137c, 0x00000000},
+	{0x000013bc, 0x00000000},
+	{0x000013fc, 0x00000000},
+	{0x0000143c, 0x00000000},
+	{0x0000147c, 0x00000000},
+	{0x00004030, 0x00000002},
+	{0x0000403c, 0x00000002},
+	{0x00004024, 0x0000001f},
+	{0x00004060, 0x00000000},
+	{0x00004064, 0x00000000},
+	{0x00008018, 0x00000700},
+	{0x00008020, 0x00000000},
+	{0x00008038, 0x00000000},
+	{0x00008048, 0x00000000},
+	{0x00008054, 0x00000000},
+	{0x00008058, 0x00000000},
+	{0x0000805c, 0x000fc78f},
+	{0x00008060, 0xc7ff000f},
+	{0x00008064, 0x00000000},
+	{0x00008070, 0x00000000},
+	{0x000080b0, 0x00000000},
+	{0x000080b4, 0x00000000},
+	{0x000080b8, 0x00000000},
+	{0x000080bc, 0x00000000},
+	{0x000080c0, 0x2a80001a},
+	{0x000080c4, 0x05dc01e0},
+	{0x000080c8, 0x1f402710},
+	{0x000080cc, 0x01f40000},
+	{0x000080d0, 0x00001e00},
+	{0x000080d4, 0x00000000},
+	{0x000080d8, 0x00400000},
+	{0x000080e0, 0xffffffff},
+	{0x000080e4, 0x0000ffff},
+	{0x000080e8, 0x003f3f3f},
+	{0x000080ec, 0x00000000},
+	{0x000080f0, 0x00000000},
+	{0x000080f4, 0x00000000},
+	{0x000080f8, 0x00000000},
+	{0x000080fc, 0x00020000},
+	{0x00008100, 0x00020000},
+	{0x00008104, 0x00000001},
+	{0x00008108, 0x00000052},
+	{0x0000810c, 0x00000000},
+	{0x00008110, 0x00000168},
+	{0x00008118, 0x000100aa},
+	{0x0000811c, 0x00003210},
+	{0x00008120, 0x08f04810},
+	{0x00008124, 0x00000000},
+	{0x00008128, 0x00000000},
+	{0x0000812c, 0x00000000},
+	{0x00008130, 0x00000000},
+	{0x00008134, 0x00000000},
+	{0x00008138, 0x00000000},
+	{0x0000813c, 0x00000000},
+	{0x00008144, 0xffffffff},
+	{0x00008168, 0x00000000},
+	{0x0000816c, 0x00000000},
+	{0x00008170, 0x32143320},
+	{0x00008174, 0xfaa4fa50},
+	{0x00008178, 0x00000100},
+	{0x0000817c, 0x00000000},
+	{0x000081c0, 0x00000000},
+	{0x000081d0, 0x0000320a},
+	{0x000081ec, 0x00000000},
+	{0x000081f0, 0x00000000},
+	{0x000081f4, 0x00000000},
+	{0x000081f8, 0x00000000},
+	{0x000081fc, 0x00000000},
+	{0x00008200, 0x00000000},
+	{0x00008204, 0x00000000},
+	{0x00008208, 0x00000000},
+	{0x0000820c, 0x00000000},
+	{0x00008210, 0x00000000},
+	{0x00008214, 0x00000000},
+	{0x00008218, 0x00000000},
+	{0x0000821c, 0x00000000},
+	{0x00008220, 0x00000000},
+	{0x00008224, 0x00000000},
+	{0x00008228, 0x00000000},
+	{0x0000822c, 0x00000000},
+	{0x00008230, 0x00000000},
+	{0x00008234, 0x00000000},
+	{0x00008238, 0x00000000},
+	{0x0000823c, 0x00000000},
+	{0x00008240, 0x00100000},
+	{0x00008244, 0x0010f400},
+	{0x00008248, 0x00000100},
+	{0x0000824c, 0x0001e800},
+	{0x00008250, 0x00000000},
+	{0x00008254, 0x00000000},
+	{0x00008258, 0x00000000},
+	{0x0000825c, 0x400000ff},
+	{0x00008260, 0x00080922},
+	{0x00008264, 0x88a00010},
+	{0x00008270, 0x00000000},
+	{0x00008274, 0x40000000},
+	{0x00008278, 0x003e4180},
+	{0x0000827c, 0x00000000},
+	{0x00008284, 0x0000002c},
+	{0x00008288, 0x0000002c},
+	{0x0000828c, 0x00000000},
+	{0x00008294, 0x00000000},
+	{0x00008298, 0x00000000},
+	{0x0000829c, 0x00000000},
+	{0x00008300, 0x00000040},
+	{0x00008314, 0x00000000},
+	{0x00008328, 0x00000000},
+	{0x0000832c, 0x00000001},
+	{0x00008330, 0x00000302},
+	{0x00008334, 0x00000e00},
+	{0x00008338, 0x00ff0000},
+	{0x0000833c, 0x00000000},
+	{0x00008340, 0x00010380},
+	{0x00008344, 0x00481083},  /**< Note: disabled ADHOC_MCAST_KEYID feature */
+	{0x00007010, 0x00000030},
+	{0x00007034, 0x00000002},
+	{0x00007038, 0x000004c2},
+	{0x00007800, 0x00140000},
+	{0x00007804, 0x0e4548d8},
+	{0x00007808, 0x54214514},
+	{0x0000780c, 0x02025820},
+	{0x00007810, 0x71c0d388},
+	{0x00007814, 0x924934a8},
+	{0x0000781c, 0x00000000},
+	{0x00007828, 0x66964300},
+	{0x0000782c, 0x8db6d961},
+	{0x00007830, 0x8db6d96c},
+	{0x00007834, 0x6140008b},
+	{0x0000783c, 0x72ee0a72},
+	{0x00007840, 0xbbfffffc},
+	{0x00007844, 0x000c0db6},
+	{0x00007848, 0x6db6246f},
+	{0x0000784c, 0x6d9b66db},
+	{0x00007850, 0x6d8c6dba},
+	{0x00007854, 0x00040000},
+	{0x00007858, 0xdb003012},
+	{0x0000785c, 0x04924914},
+	{0x00007860, 0x21084210},
+	{0x00007864, 0xf7d7ffde},
+	{0x00007868, 0xc2034080},
+	{0x00007870, 0x10142c00},
+	{0x00009808, 0x00000000},
+	{0x0000980c, 0xafe68e30},
+	{0x00009810, 0xfd14e000},
+	{0x00009814, 0x9c0a9f6b},
+	{0x0000981c, 0x00000000},
+	{0x0000982c, 0x0000a000},
+	{0x00009830, 0x00000000},
+	{0x0000983c, 0x00200400},
+	{0x0000984c, 0x0040233c},
+	{0x00009854, 0x00000044},
+	{0x00009900, 0x00000000},
+	{0x00009904, 0x00000000},
+	{0x00009908, 0x00000000},
+	{0x0000990c, 0x00000000},
+	{0x0000991c, 0x10000fff},
+	{0x00009920, 0x04900000},
+	{0x00009928, 0x00000001},
+	{0x0000992c, 0x00000004},
+	{0x00009934, 0x1e1f2022},
+	{0x00009938, 0x0a0b0c0d},
+	{0x0000993c, 0x00000000},
+	{0x00009940, 0x14750604},
+	{0x00009948, 0x9280c00a},
+	{0x0000994c, 0x00020028},
+	{0x00009954, 0x5f3ca3de},
+	{0x00009958, 0x0108ecff},
+	{0x00009968, 0x000003ce},
+	{0x00009970, 0x192bb514},
+	{0x00009974, 0x00000000},
+	{0x00009978, 0x00000001},
+	{0x0000997c, 0x00000000},
+	{0x00009980, 0x00000000},
+	{0x00009984, 0x00000000},
+	{0x00009988, 0x00000000},
+	{0x0000998c, 0x00000000},
+	{0x00009990, 0x00000000},
+	{0x00009994, 0x00000000},
+	{0x00009998, 0x00000000},
+	{0x0000999c, 0x00000000},
+	{0x000099a0, 0x00000000},
+	{0x000099a4, 0x00000001},
+	{0x000099a8, 0x201fff00},
+	{0x000099ac, 0x2def0400},
+	{0x000099b0, 0x03051000},
+	{0x000099b4, 0x00000820},
+	{0x000099dc, 0x00000000},
+	{0x000099e0, 0x00000000},
+	{0x000099e4, 0xaaaaaaaa},
+	{0x000099e8, 0x3c466478},
+	{0x000099ec, 0x0cc80caa},
+	{0x000099f0, 0x00000000},
+	{0x0000a208, 0x803e68c8},
+	{0x0000a210, 0x4080a333},
+	{0x0000a214, 0x00206c10},
+	{0x0000a218, 0x009c4060},
+	{0x0000a220, 0x01834061},
+	{0x0000a224, 0x00000400},
+	{0x0000a228, 0x000003b5},
+	{0x0000a22c, 0x00000000},
+	{0x0000a234, 0x20202020},
+	{0x0000a238, 0x20202020},
+	{0x0000a244, 0x00000000},
+	{0x0000a248, 0xfffffffc},
+	{0x0000a24c, 0x00000000},
+	{0x0000a254, 0x00000000},
+	{0x0000a258, 0x0ccb5380},
+	{0x0000a25c, 0x15151501},
+	{0x0000a260, 0xdfa90f01},
+	{0x0000a268, 0x00000000},
+	{0x0000a26c, 0x0ebae9e6},
+	{0x0000a388, 0x0c000000},
+	{0x0000a38c, 0x20202020},
+	{0x0000a390, 0x20202020},
+	{0x0000a39c, 0x00000001},
+	{0x0000a3a0, 0x00000000},
+	{0x0000a3a4, 0x00000000},
+	{0x0000a3a8, 0x00000000},
+	{0x0000a3ac, 0x00000000},
+	{0x0000a3b0, 0x00000000},
+	{0x0000a3b4, 0x00000000},
+	{0x0000a3b8, 0x00000000},
+	{0x0000a3bc, 0x00000000},
+	{0x0000a3c0, 0x00000000},
+	{0x0000a3c4, 0x00000000},
+	{0x0000a3cc, 0x20202020},
+	{0x0000a3d0, 0x20202020},
+	{0x0000a3d4, 0x20202020},
+	{0x0000a3e4, 0x00000000},
+	{0x0000a3e8, 0x18c43433},
+	{0x0000a3ec, 0x00f70081},
+	{0x0000a3f0, 0x01036a2f},
+	{0x0000a3f4, 0x00000000},
+	{0x0000d270, 0x0d820820},
+	{0x0000d35c, 0x07ffffef},
+	{0x0000d360, 0x0fffffe7},
+	{0x0000d364, 0x17ffffe5},
+	{0x0000d368, 0x1fffffe4},
+	{0x0000d36c, 0x37ffffe3},
+	{0x0000d370, 0x3fffffe3},
+	{0x0000d374, 0x57ffffe3},
+	{0x0000d378, 0x5fffffe2},
+	{0x0000d37c, 0x7fffffe2},
+	{0x0000d380, 0x7f3c7bba},
+	{0x0000d384, 0xf3307ff0}
+};
+
+#endif
Index: uspace/drv/nic/ar9271/ar9271.ma
===================================================================
--- uspace/drv/nic/ar9271/ar9271.ma	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/nic/ar9271/ar9271.ma	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,1 @@
+90 usb&vendor=0x0cf3&product=0x9271
Index: uspace/drv/nic/ar9271/ath.h
===================================================================
--- uspace/drv/nic/ar9271/ath.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/nic/ar9271/ath.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file ath.h
+ *
+ * Atheros generic wifi device functions definitions.
+ *
+ */
+
+#ifndef ATHEROS_ATH_H
+#define ATHEROS_ATH_H
+
+struct ath;
+
+/** Atheros wifi operations. */
+typedef struct {
+	int (*send_ctrl_message)(struct ath *, void *, size_t);
+	int (*read_ctrl_message)(struct ath *, void *, size_t, size_t *);
+	int (*send_data_message)(struct ath *, void *, size_t);
+	int (*read_data_message)(struct ath *, void *, size_t, size_t *);
+} ath_ops_t;
+
+/** Atheros wifi device structure */
+typedef struct ath {
+	/** Maximum length of data response message. */
+	size_t data_response_length;
+	
+	/** Maximum length of control response message. */
+	size_t ctrl_response_length;
+	
+	/** Implementation specific data. */
+	void *specific_data;
+	
+	/** Generic Atheros wifi operations. */
+	const ath_ops_t *ops;
+} ath_t;
+
+#endif  /* ATHEROS_ATH_H */
Index: uspace/drv/nic/ar9271/ath_usb.c
===================================================================
--- uspace/drv/nic/ar9271/ath_usb.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/nic/ar9271/ath_usb.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file ath_usb.c
+ *
+ * Implementation of Atheros USB wifi device functions.
+ *
+ */
+
+#include <usb/dev/pipes.h>
+#include <usb/debug.h>
+#include <malloc.h>
+#include "ath_usb.h"
+
+static int ath_usb_send_ctrl_message(ath_t *, void *, size_t);
+static int ath_usb_read_ctrl_message(ath_t *, void *, size_t, size_t *);
+static int ath_usb_send_data_message(ath_t *, void *, size_t);
+static int ath_usb_read_data_message(ath_t *, void *, size_t, size_t *);
+
+static ath_ops_t ath_usb_ops = {
+	.send_ctrl_message = ath_usb_send_ctrl_message,
+	.read_ctrl_message = ath_usb_read_ctrl_message,
+	.send_data_message = ath_usb_send_data_message,
+	.read_data_message = ath_usb_read_data_message
+};
+
+/** Initialize Atheros WiFi USB device.
+ *
+ * @param ath Generic Atheros WiFi device structure.
+ * @param usb_device  Connected USB device.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int ath_usb_init(ath_t *ath, usb_device_t *usb_device)
+{
+	ath_usb_t *ath_usb = malloc(sizeof(ath_usb_t));
+	if (!ath_usb) {
+		usb_log_error("Failed to allocate memory for ath usb device "
+		    "structure.\n");
+		return ENOMEM;
+	}
+	
+	ath_usb->usb_device = usb_device;
+	
+	/* TODO: Assign by iterating over pipes. */
+	ath_usb->output_data_pipe_number = 0;
+	ath_usb->input_data_pipe_number = 1;
+	ath_usb->input_ctrl_pipe_number = 2;
+	ath_usb->output_ctrl_pipe_number = 3;
+	
+	ath->ctrl_response_length = 64;
+	ath->data_response_length = 512;
+	
+	ath->specific_data = ath_usb;
+	ath->ops = &ath_usb_ops;
+	
+	return EOK;
+}
+
+/** Send control message.
+ *
+ * @param ath         Generic Atheros WiFi device structure.
+ * @param buffer      Buffer with data to send.
+ * @param buffer_size Buffer size.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+static int ath_usb_send_ctrl_message(ath_t *ath, void *buffer,
+    size_t buffer_size)
+{
+	ath_usb_t *ath_usb = (ath_usb_t *) ath->specific_data;
+	usb_pipe_t *pipe =
+	    &ath_usb->usb_device->pipes[ath_usb->output_ctrl_pipe_number].pipe;
+	
+	return usb_pipe_write(pipe, buffer, buffer_size);
+}
+
+/** Read control message.
+ *
+ * @param ath              Generic Atheros WiFi device structure.
+ * @param buffer           Buffer with data to send.
+ * @param buffer_size      Buffer size.
+ * @param transferred_size Real size of read data.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+static int ath_usb_read_ctrl_message(ath_t *ath, void *buffer,
+    size_t buffer_size, size_t *transferred_size)
+{
+	ath_usb_t *ath_usb = (ath_usb_t *) ath->specific_data;
+	usb_pipe_t *pipe =
+	    &ath_usb->usb_device->pipes[ath_usb->input_ctrl_pipe_number].pipe;
+	
+	return usb_pipe_read(pipe, buffer, buffer_size, transferred_size);
+}
+
+/** Send data message.
+ *
+ * @param ath         Generic Atheros WiFi device structure. 
+ * @param buffer      Buffer with data to send.
+ * @param buffer_size Buffer size.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+static int ath_usb_send_data_message(ath_t *ath, void *buffer,
+    size_t buffer_size)
+{
+	size_t complete_buffer_size = buffer_size +
+	    sizeof(ath_usb_data_header_t);
+	void *complete_buffer = malloc(complete_buffer_size);
+	memcpy(complete_buffer + sizeof(ath_usb_data_header_t),
+	    buffer, buffer_size);
+	
+	ath_usb_data_header_t *data_header =
+	    (ath_usb_data_header_t *) complete_buffer;
+	data_header->length = host2uint16_t_le(buffer_size);
+	data_header->tag = host2uint16_t_le(TX_TAG);
+	
+	ath_usb_t *ath_usb = (ath_usb_t *) ath->specific_data;
+	usb_pipe_t *pipe =
+	    &ath_usb->usb_device->pipes[ath_usb->output_data_pipe_number].pipe;
+	
+	int ret_val = usb_pipe_write(pipe, complete_buffer,
+	    complete_buffer_size);
+	
+	free(complete_buffer);
+	
+	return ret_val;
+}
+
+/** Read data message.
+ *
+ * @param ath              Generic Atheros WiFi device structure.
+ * @param buffer           Buffer with data to send.
+ * @param buffer_size      Buffer size.
+ * @param transferred_size Real size of read data.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+static int ath_usb_read_data_message(ath_t *ath, void *buffer,
+    size_t buffer_size, size_t *transferred_size)
+{
+	ath_usb_t *ath_usb = (ath_usb_t *) ath->specific_data;
+	usb_pipe_t *pipe =
+	    &ath_usb->usb_device->pipes[ath_usb->input_data_pipe_number].pipe;
+	
+	return usb_pipe_read(pipe, buffer, buffer_size,
+	    transferred_size);
+}
Index: uspace/drv/nic/ar9271/ath_usb.h
===================================================================
--- uspace/drv/nic/ar9271/ath_usb.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/nic/ar9271/ath_usb.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file ath_usb.h
+ *
+ * Definitions of Atheros USB wifi device functions.
+ *
+ */
+
+#ifndef ATHEROS_ATH_USB_H
+#define ATHEROS_ATH_USB_H
+
+#include <usb/dev/driver.h>
+#include "ath.h"
+
+#define RX_TAG  0x4e00
+#define TX_TAG  0x697e
+
+/** Atheros USB wifi device structure */
+typedef struct {
+	/** USB pipes indexes */
+	int input_ctrl_pipe_number;
+	int output_ctrl_pipe_number;
+	int input_data_pipe_number;
+	int output_data_pipe_number;
+	
+	/** Pointer to connected USB device. */
+	usb_device_t *usb_device;
+} ath_usb_t;
+
+typedef struct {
+	uint16_t length;  /**< Little Endian value! */
+	uint16_t tag;     /**< Little Endian value! */
+} ath_usb_data_header_t;
+
+extern int ath_usb_init(ath_t *, usb_device_t *);
+
+#endif  /* ATHEROS_ATH_USB_H */
Index: uspace/drv/nic/ar9271/htc.c
===================================================================
--- uspace/drv/nic/ar9271/htc.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/nic/ar9271/htc.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,551 @@
+/*
+ * Copyright (c) 2014 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file htc.c
+ *
+ * Implementation of Atheros HTC communication.
+ *
+ */
+
+#include <usb/debug.h>
+#include <byteorder.h>
+#include <errno.h>
+#include "wmi.h"
+#include "htc.h"
+#include "nic/nic.h"
+#include "ar9271.h"
+
+/** HTC download pipes mapping.
+ *
+ * @param service_id Identification of WMI service.
+ *
+ * @return Number of pipe used for this service.
+ *
+ */
+static inline uint8_t wmi_service_to_download_pipe(wmi_services_t service_id)
+{
+	return (service_id == WMI_CONTROL_SERVICE) ? 3 : 2;
+}
+
+/** HTC upload pipes mapping.
+ *
+ * @param service_id Identification of WMI service.
+ *
+ * @return Number of pipe used for this service.
+ *
+ */
+static inline uint8_t wmi_service_to_upload_pipe(wmi_services_t service_id)
+{
+	return (service_id == WMI_CONTROL_SERVICE) ? 4 : 1;
+}
+
+int htc_init_new_vif(htc_device_t *htc_device)
+{
+	htc_vif_msg_t vif_msg;
+	htc_sta_msg_t sta_msg;
+	
+	memset(&vif_msg, 0, sizeof(htc_vif_msg_t));
+	memset(&sta_msg, 0, sizeof(htc_sta_msg_t));
+	
+	nic_address_t addr;
+	nic_t *nic =
+	    nic_get_from_ddf_dev(ieee80211_get_ddf_dev(htc_device->ieee80211_dev));
+	nic_query_address(nic, &addr);
+	
+	memcpy(&vif_msg.addr, &addr.address, ETH_ADDR);
+	memcpy(&sta_msg.addr, &addr.address, ETH_ADDR);
+	
+	ieee80211_operating_mode_t op_mode =
+	    ieee80211_query_current_op_mode(htc_device->ieee80211_dev);
+	
+	switch (op_mode) {
+	case IEEE80211_OPMODE_ADHOC:
+		vif_msg.op_mode = HTC_OPMODE_ADHOC;
+		break;
+	case IEEE80211_OPMODE_AP:
+		vif_msg.op_mode = HTC_OPMODE_AP;
+		break;
+	case IEEE80211_OPMODE_MESH:
+		vif_msg.op_mode = HTC_OPMODE_MESH;
+		break;
+	case IEEE80211_OPMODE_STATION:
+		vif_msg.op_mode = HTC_OPMODE_STATION;
+		break;
+	}
+	
+	vif_msg.index = 0;
+	vif_msg.rts_thres = host2uint16_t_be(HTC_RTS_THRESHOLD);
+	
+	wmi_send_command(htc_device, WMI_VAP_CREATE, (uint8_t *) &vif_msg,
+	    sizeof(vif_msg), NULL);
+	
+	sta_msg.is_vif_sta = 1;
+	sta_msg.max_ampdu = host2uint16_t_be(0xFFFF);
+	sta_msg.sta_index = 0;
+	sta_msg.vif_index = 0;
+	
+	wmi_send_command(htc_device, WMI_NODE_CREATE, (uint8_t *) &sta_msg,
+	    sizeof(sta_msg), NULL);
+	
+	/* Write first 4 bytes of MAC address. */
+	uint32_t id0;
+	memcpy(&id0, &addr.address, 4);
+	id0 = host2uint32_t_le(id0);
+	wmi_reg_write(htc_device, AR9271_STATION_ID0, id0);
+	
+	/* Write last 2 bytes of MAC address (and preserve existing data). */
+	uint32_t id1;
+	wmi_reg_read(htc_device, AR9271_STATION_ID1, &id1);
+	
+	uint16_t id1_addr;
+	memcpy(&id1_addr, &addr.address[4], 2);
+	id1 = (id1 & ~AR9271_STATION_ID1_MASK) | host2uint16_t_le(id1_addr);
+	wmi_reg_write(htc_device, AR9271_STATION_ID1, id1);
+	
+	return EOK;
+}
+
+static void htc_config_frame_header(htc_frame_header_t *header,
+    size_t buffer_size, uint8_t endpoint_id)
+{
+	memset(header, 0, sizeof(htc_frame_header_t));
+	
+	header->endpoint_id = endpoint_id;
+	header->payload_length =
+	    host2uint16_t_be(buffer_size - sizeof(htc_frame_header_t));
+}
+
+/** Send control HTC message to USB device.
+ *
+ * @param htc_device  HTC device structure.
+ * @param buffer      Buffer with data to be sent to USB device
+ *                    (without HTC frame header).
+ * @param buffer_size Size of buffer (including HTC frame header).
+ * @param endpoint_id Destination endpoint.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int htc_send_control_message(htc_device_t *htc_device, void *buffer,
+    size_t buffer_size, uint8_t endpoint_id)
+{
+	htc_config_frame_header((htc_frame_header_t *) buffer, buffer_size,
+	    endpoint_id);
+	
+	ath_t *ath_device = htc_device->ath_device;
+	
+	return ath_device->ops->send_ctrl_message(ath_device, buffer,
+	    buffer_size);
+}
+
+/** Send data HTC message to USB device.
+ *
+ * @param htc_device  HTC device structure.
+ * @param buffer      Buffer with data to be sent to USB device
+ *                    (without HTC frame header).
+ * @param buffer_size Size of buffer (including HTC frame header).
+ * @param endpoint_id Destination endpoint.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int htc_send_data_message(htc_device_t *htc_device, void *buffer,
+    size_t buffer_size, uint8_t endpoint_id)
+{
+	htc_config_frame_header((htc_frame_header_t *) buffer, buffer_size,
+	    endpoint_id);
+	
+	ath_t *ath_device = htc_device->ath_device;
+	
+	return ath_device->ops->send_data_message(ath_device, buffer,
+	    buffer_size);
+}
+
+/** Read HTC data message from USB device.
+ *
+ * @param htc_device       HTC device structure.
+ * @param buffer           Buffer where data from USB device
+ *                         will be stored.
+ * @param buffer_size      Size of buffer.
+ * @param transferred_size Real size of read data.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int htc_read_data_message(htc_device_t *htc_device, void *buffer,
+    size_t buffer_size, size_t *transferred_size)
+{
+	ath_t *ath_device = htc_device->ath_device;
+	
+	return ath_device->ops->read_data_message(ath_device, buffer,
+	    buffer_size, transferred_size);
+}
+
+/** Read HTC control message from USB device.
+ *
+ * @param htc_device       HTC device structure.
+ * @param buffer           Buffer where data from USB device
+ *                         will be stored.
+ * @param buffer_size      Size of buffer.
+ * @param transferred_size Real size of read data.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int htc_read_control_message(htc_device_t *htc_device, void *buffer,
+    size_t buffer_size, size_t *transferred_size)
+{
+	ath_t *ath_device = htc_device->ath_device;
+	
+	return ath_device->ops->read_ctrl_message(ath_device, buffer,
+	    buffer_size, transferred_size);
+}
+
+/** Initialize HTC service.
+ *
+ * @param htc_device           HTC device structure.
+ * @param service_id           Identification of WMI service.
+ * @param response_endpoint_no HTC endpoint to be used for
+ *                             this service.
+ *
+ * @return EOK if succeed, EINVAL when failed to connect service,
+ *         negative error code otherwise.
+ *
+ */
+static int htc_connect_service(htc_device_t *htc_device,
+    wmi_services_t service_id, int *response_endpoint_no)
+{
+	size_t buffer_size = sizeof(htc_frame_header_t) +
+	    sizeof(htc_service_msg_t);
+	void *buffer = malloc(buffer_size);
+	memset(buffer, 0, buffer_size);
+	
+	/* Fill service message structure. */
+	htc_service_msg_t *service_message = (htc_service_msg_t *)
+	    ((void *) buffer + sizeof(htc_frame_header_t));
+	service_message->service_id =
+	    host2uint16_t_be(service_id);
+	service_message->message_id =
+	    host2uint16_t_be(HTC_MESSAGE_CONNECT_SERVICE);
+	service_message->download_pipe_id =
+	    wmi_service_to_download_pipe(service_id);
+	service_message->upload_pipe_id =
+	    wmi_service_to_upload_pipe(service_id);
+	service_message->connection_flags = 0;
+	
+	/* Send HTC message. */
+	int rc = htc_send_control_message(htc_device, buffer, buffer_size,
+	    htc_device->endpoints.ctrl_endpoint);
+	if (rc != EOK) {
+		free(buffer);
+		usb_log_error("Failed to send HTC message. Error: %d\n", rc);
+		return rc;
+	}
+	
+	free(buffer);
+	
+	buffer_size = htc_device->ath_device->ctrl_response_length;
+	buffer = malloc(buffer_size);
+	
+	/* Read response from device. */
+	rc = htc_read_control_message(htc_device, buffer, buffer_size, NULL);
+	if (rc != EOK) {
+		free(buffer);
+		usb_log_error("Failed to receive HTC service connect response. "
+		    "Error: %d\n", rc);
+		return rc;
+	}
+	
+	htc_service_resp_msg_t *response_message = (htc_service_resp_msg_t *)
+	    ((void *) buffer + sizeof(htc_frame_header_t));
+	
+	/*
+	 * If service was successfully connected,
+	 * write down HTC endpoint number that will
+	 * be used for communication.
+	 */
+	if (response_message->status == HTC_SERVICE_SUCCESS) {
+		*response_endpoint_no = response_message->endpoint_id;
+		rc = EOK;
+	} else {
+		usb_log_error("Failed to connect HTC service. "
+		    "Message status: %d\n", response_message->status);
+		rc = EINVAL;
+	}
+	
+	free(buffer);
+	
+	return rc;
+}
+
+/** HTC credits initialization message.
+ *
+ * @param htc_device HTC device structure.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+static int htc_config_credits(htc_device_t *htc_device)
+{
+	size_t buffer_size = sizeof(htc_frame_header_t) +
+	    sizeof(htc_config_msg_t);
+	void *buffer = malloc(buffer_size);
+	htc_config_msg_t *config_message = (htc_config_msg_t *)
+	    ((void *) buffer + sizeof(htc_frame_header_t));
+	
+	config_message->message_id = 
+	    host2uint16_t_be(HTC_MESSAGE_CONFIG);
+	/* Magic number to initialize device. */
+	config_message->credits = 33;
+	config_message->pipe_id = 1;
+	
+	/* Send HTC message. */
+	int rc = htc_send_control_message(htc_device, buffer, buffer_size,
+	    htc_device->endpoints.ctrl_endpoint);
+	if (rc != EOK) {
+		free(buffer);
+		usb_log_error("Failed to send HTC config message. "
+		    "Error: %d\n", rc);
+		return rc;
+	}
+	
+	free(buffer);
+	
+	buffer_size = htc_device->ath_device->ctrl_response_length;
+	buffer = malloc(buffer_size);
+	
+	/* Check response from device. */
+	rc = htc_read_control_message(htc_device, buffer, buffer_size, NULL);
+	if (rc != EOK) {
+		usb_log_error("Failed to receive HTC config response message. "
+		    "Error: %d\n", rc);
+	}
+	
+	free(buffer);
+	
+	return rc;
+}
+
+/** HTC setup complete confirmation message.
+ *
+ * @param htc_device HTC device structure.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+static int htc_complete_setup(htc_device_t *htc_device)
+{
+	size_t buffer_size = sizeof(htc_frame_header_t) +
+	    sizeof(htc_setup_complete_msg_t);
+	void *buffer = malloc(buffer_size);
+	htc_setup_complete_msg_t *complete_message =
+	    (htc_setup_complete_msg_t *)
+	    ((void *) buffer + sizeof(htc_frame_header_t));
+	
+	complete_message->message_id =
+	    host2uint16_t_be(HTC_MESSAGE_SETUP_COMPLETE);
+	
+	/* Send HTC message. */
+	int rc = htc_send_control_message(htc_device, buffer, buffer_size,
+	    htc_device->endpoints.ctrl_endpoint);
+	if (rc != EOK)
+		usb_log_error("Failed to send HTC setup complete message. "
+		    "Error: %d\n", rc);
+	
+	free(buffer);
+	
+	return rc;
+}
+
+/** Try to fetch ready message from device.
+ *
+ * Checks that firmware was successfully loaded on device side.
+ *
+ * @param htc_device HTC device structure.
+ *
+ * @return EOK if succeed, EINVAL if response error,
+ *         negative error code otherwise.
+ *
+ */
+static int htc_check_ready(htc_device_t *htc_device)
+{
+	size_t buffer_size = htc_device->ath_device->ctrl_response_length;
+	void *buffer = malloc(buffer_size);
+	
+	/* Read response from device. */
+	int rc = htc_read_control_message(htc_device, buffer, buffer_size,
+	    NULL);
+	if (rc != EOK) {
+		free(buffer);
+		usb_log_error("Failed to receive HTC check ready message. "
+		    "Error: %d\n", rc);
+		return rc;
+	}
+	
+	uint16_t *message_id = (uint16_t *) ((void *) buffer +
+	    sizeof(htc_frame_header_t));
+	if (uint16_t_be2host(*message_id) == HTC_MESSAGE_READY)
+		rc = EOK;
+	else
+		rc = EINVAL;
+	
+	free(buffer);
+	
+	return rc;
+}
+
+/** Initialize HTC device structure.
+ *
+ * @param ath_device Atheros WiFi device connected
+ *                   with this HTC device.
+ * @param htc_device HTC device structure to be initialized.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int htc_device_init(ath_t *ath_device, ieee80211_dev_t *ieee80211_dev,
+    htc_device_t *htc_device)
+{
+	fibril_mutex_initialize(&htc_device->rx_lock);
+	fibril_mutex_initialize(&htc_device->tx_lock);
+	
+	htc_device->endpoints.ctrl_endpoint = 0;
+	
+	htc_device->ath_device = ath_device;
+	htc_device->ieee80211_dev = ieee80211_dev;
+	
+	return EOK;
+}
+
+/** HTC communication initalization.
+ *
+ * @param htc_device HTC device structure.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int htc_init(htc_device_t *htc_device)
+{
+	/* First check ready message in device. */
+	int rc = htc_check_ready(htc_device);
+	if (rc != EOK) {
+		usb_log_error("Device is not in ready state after loading "
+		    "firmware.\n");
+		return rc;
+	}
+	
+	/*
+	 * HTC services initialization start.
+	 */
+	rc = htc_connect_service(htc_device, WMI_CONTROL_SERVICE,
+	    &htc_device->endpoints.wmi_endpoint);
+	if (rc != EOK) {
+		usb_log_error("Error while initalizing WMI service.\n");
+		return rc;
+	}
+	
+	rc = htc_connect_service(htc_device, WMI_BEACON_SERVICE,
+	    &htc_device->endpoints.beacon_endpoint);
+	if (rc != EOK) {
+		usb_log_error("Error while initalizing beacon service.\n");
+		return rc;
+	}
+	
+	rc = htc_connect_service(htc_device, WMI_CAB_SERVICE,
+	    &htc_device->endpoints.cab_endpoint);
+	if (rc != EOK) {
+		usb_log_error("Error while initalizing CAB service.\n");
+		return rc;
+	}
+	
+	rc = htc_connect_service(htc_device, WMI_UAPSD_SERVICE,
+	    &htc_device->endpoints.uapsd_endpoint);
+	if (rc != EOK) {
+		usb_log_error("Error while initalizing UAPSD service.\n");
+		return rc;
+	}
+	
+	rc = htc_connect_service(htc_device, WMI_MGMT_SERVICE,
+	    &htc_device->endpoints.mgmt_endpoint);
+	if (rc != EOK) {
+		usb_log_error("Error while initalizing MGMT service.\n");
+		return rc;
+	}
+	
+	rc = htc_connect_service(htc_device, WMI_DATA_BE_SERVICE,
+	    &htc_device->endpoints.data_be_endpoint);
+	if (rc != EOK) {
+		usb_log_error("Error while initalizing data best effort "
+		    "service.\n");
+		return rc;
+	}
+	
+	rc = htc_connect_service(htc_device, WMI_DATA_BK_SERVICE,
+	    &htc_device->endpoints.data_bk_endpoint);
+	if (rc != EOK) {
+		usb_log_error("Error while initalizing data background "
+		    "service.\n");
+		return rc;
+	}
+	
+	rc = htc_connect_service(htc_device, WMI_DATA_VIDEO_SERVICE,
+	    &htc_device->endpoints.data_video_endpoint);
+	if (rc != EOK) {
+		usb_log_error("Error while initalizing data video service.\n");
+		return rc;
+	}
+	
+	rc = htc_connect_service(htc_device, WMI_DATA_VOICE_SERVICE,
+	    &htc_device->endpoints.data_voice_endpoint);
+	if (rc != EOK) {
+		usb_log_error("Error while initalizing data voice service.\n");
+		return rc;
+	}
+	
+	/*
+	 * HTC services initialization end.
+	 */
+	
+	/* Credits initialization message. */
+	rc = htc_config_credits(htc_device);
+	if (rc != EOK) {
+		usb_log_error("Failed to send HTC config message.\n");
+		return rc;
+	}
+	
+	/* HTC setup complete confirmation message. */
+	rc = htc_complete_setup(htc_device);
+	if (rc != EOK) {
+		usb_log_error("Failed to send HTC complete setup message.\n");
+		return rc;
+	}
+	
+	usb_log_info("HTC services initialization finished successfully.\n");
+	
+	return EOK;
+}
Index: uspace/drv/nic/ar9271/htc.h
===================================================================
--- uspace/drv/nic/ar9271/htc.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/nic/ar9271/htc.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2014 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file htc.h
+ *
+ * Definitions of the Atheros HTC (Host Target Communication) technology
+ * for communication between host (PC) and target (device firmware).
+ *
+ */
+
+#ifndef ATHEROS_HTC_H
+#define ATHEROS_HTC_H
+
+#include <ieee80211.h>
+#include <usb/dev/driver.h>
+#include <sys/types.h>
+#include <nic.h>
+#include "ath.h"
+
+#define HTC_RTS_THRESHOLD     2304
+#define HTC_RATES_MAX_LENGTH  30
+
+/** HTC message IDs.
+ *
+ */
+typedef enum {
+	HTC_MESSAGE_READY = 1,
+	HTC_MESSAGE_CONNECT_SERVICE,
+	HTC_MESSAGE_CONNECT_SERVICE_RESPONSE,
+	HTC_MESSAGE_SETUP_COMPLETE,
+	HTC_MESSAGE_CONFIG
+} htc_message_id_t;
+
+/** HTC response message status codes.
+ *
+ */
+typedef enum {
+	HTC_SERVICE_SUCCESS = 0,
+	HTC_SERVICE_NOT_FOUND,
+	HTC_SERVICE_FAILED,
+	HTC_SERVICE_NO_RESOURCES,
+	HTC_SERVICE_NO_MORE_EP
+} htc_response_status_code_t;
+
+/** HTC operating mode definition.
+ *
+ */
+typedef enum {
+	HTC_OPMODE_ADHOC = 0,
+	HTC_OPMODE_STATION = 1,
+	HTC_OPMODE_MESH = 2,
+	HTC_OPMODE_AP = 6
+} htc_operating_mode_t;
+
+/** HTC data type indicator.
+ *
+ */
+typedef enum {
+	HTC_DATA_AMPDU = 1,
+	HTC_DATA_NORMAL = 2,
+	HTC_DATA_BEACON = 3,
+	HTC_DATA_MGMT = 4
+} htc_data_type_t;
+
+/** HTC endpoint numbers.
+ *
+ */
+typedef struct {
+	int ctrl_endpoint;
+	int wmi_endpoint;
+	int beacon_endpoint;
+	int cab_endpoint;
+	int uapsd_endpoint;
+	int mgmt_endpoint;
+	int data_be_endpoint;
+	int data_bk_endpoint;
+	int data_video_endpoint;
+	int data_voice_endpoint;
+} htc_pipes_t;
+
+/** HTC device data.
+ *
+ */
+typedef struct {
+	/** WMI message sequence number */
+	uint16_t sequence_number;
+	
+	/** HTC endpoints numbers */
+	htc_pipes_t endpoints;
+	
+	/** Lock for receiver */
+	fibril_mutex_t rx_lock;
+	
+	/** Lock for transmitter */
+	fibril_mutex_t tx_lock;
+	
+	/** Pointer to related IEEE 802.11 device */
+	ieee80211_dev_t *ieee80211_dev;
+	
+	/** Pointer to Atheros WiFi device structure */
+	ath_t *ath_device;
+} htc_device_t;
+
+/** HTC frame header structure.
+ *
+ */
+typedef struct {
+	uint8_t endpoint_id;
+	uint8_t flags;
+	uint16_t payload_length;   /**< Big Endian value! */
+	uint8_t control_bytes[4];
+} __attribute__((packed)) htc_frame_header_t;
+
+/** HTC management TX frame header structure.
+ *
+ */
+typedef struct {
+	uint8_t node_idx;
+	uint8_t vif_idx;
+	uint8_t tidno;
+	uint8_t flags;
+	uint8_t key_type;
+	uint8_t keyix;
+	uint8_t cookie;
+	uint8_t pad;
+} __attribute__((packed)) htc_tx_management_header_t;
+
+/** HTC data TX frame header structure.
+ *
+ */
+typedef struct {
+	uint8_t data_type;
+	uint8_t node_idx;
+	uint8_t vif_idx;
+	uint8_t tidno;
+	uint32_t flags;    /**< Big Endian value! */
+	uint8_t key_type;
+	uint8_t keyix;
+	uint8_t cookie;
+	uint8_t pad;
+} __attribute__((packed)) htc_tx_data_header_t;
+
+/** HTC ready message structure.
+ *
+ */
+typedef struct {
+	uint16_t message_id;   /**< Big Endian value! */
+	uint16_t credits;      /**< Big Endian value! */
+	uint16_t credit_size;  /**< Big Endian value! */
+	
+	uint8_t max_endpoints;
+	uint8_t pad;
+} __attribute__((packed)) htc_ready_msg_t;
+
+/** HTC service message structure.
+ *
+ */
+typedef struct {
+	uint16_t message_id;        /**< Big Endian value! */
+	uint16_t service_id;        /**< Big Endian value! */
+	uint16_t connection_flags;  /**< Big Endian value! */
+	
+	uint8_t download_pipe_id;
+	uint8_t upload_pipe_id;
+	uint8_t service_meta_length;
+	uint8_t pad;
+} __attribute__((packed)) htc_service_msg_t;
+
+/** HTC service response message structure.
+ *
+ */
+typedef struct {
+	uint16_t message_id;          /**< Big Endian value! */
+	uint16_t service_id;          /**< Big Endian value! */
+	uint8_t status;
+	uint8_t endpoint_id;
+	uint16_t max_message_length;  /**< Big Endian value! */
+	uint8_t service_meta_length;
+	uint8_t pad;
+} __attribute__((packed)) htc_service_resp_msg_t;
+
+/** HTC credits config message structure.
+ *
+ */
+typedef struct {
+	uint16_t message_id;  /**< Big Endian value! */
+	uint8_t pipe_id;
+	uint8_t credits;
+} __attribute__((packed)) htc_config_msg_t;
+
+/** HTC new virtual interface message.
+ *
+ */
+typedef struct {
+	uint8_t index;
+	uint8_t op_mode;
+	uint8_t addr[ETH_ADDR];
+	uint8_t ath_cap;
+	uint16_t rts_thres;      /**< Big Endian value! */
+	uint8_t pad;
+} __attribute__((packed)) htc_vif_msg_t;
+
+/** HTC new station message.
+ *
+ */
+typedef struct {
+	uint8_t addr[ETH_ADDR];
+	uint8_t bssid[ETH_ADDR];
+	uint8_t sta_index;
+	uint8_t vif_index;
+	uint8_t is_vif_sta;
+	
+	uint16_t flags;      /**< Big Endian value! */
+	uint16_t ht_cap;     /**< Big Endian value! */
+	uint16_t max_ampdu;  /**< Big Endian value! */
+	
+	uint8_t pad;
+} __attribute__((packed)) htc_sta_msg_t;
+
+/** HTC message to inform target about available capabilities.
+ *
+ */
+typedef struct {
+	uint32_t ampdu_limit;     /**< Big Endian value! */
+	uint8_t ampdu_subframes;
+	uint8_t enable_coex;
+	uint8_t tx_chainmask;
+	uint8_t pad;
+} __attribute__((packed)) htc_cap_msg_t;
+
+typedef struct {
+	uint8_t sta_index;
+	uint8_t is_new;
+	uint32_t cap_flags;  /**< Big Endian value! */
+	uint8_t legacy_rates_count;
+	uint8_t legacy_rates[HTC_RATES_MAX_LENGTH];
+	uint16_t pad;
+} htc_rate_msg_t;
+
+/** HTC RX status structure used in incoming HTC data messages.
+ *
+ */
+typedef struct {
+	uint64_t timestamp;    /**< Big Endian value! */
+	uint16_t data_length;  /**< Big Endian value! */
+	uint8_t status;
+	uint8_t phy_err;
+	int8_t rssi;
+	int8_t rssi_ctl[3];
+	int8_t rssi_ext[3];
+	uint8_t keyix;
+	uint8_t rate;
+	uint8_t antenna;
+	uint8_t more;
+	uint8_t is_aggr;
+	uint8_t more_aggr;
+	uint8_t num_delims;
+	uint8_t flags;
+	uint8_t dummy;
+	uint32_t evm0;         /**< Big Endian value! */
+	uint32_t evm1;         /**< Big Endian value! */
+	uint32_t evm2;         /**< Big Endian value! */
+} htc_rx_status_t;
+
+/** HTC setup complete message structure
+ *
+ */
+typedef struct {
+	uint16_t message_id;  /**< Big Endian value! */
+} __attribute__((packed)) htc_setup_complete_msg_t;
+
+extern int htc_device_init(ath_t *, ieee80211_dev_t *, htc_device_t *);
+extern int htc_init(htc_device_t *);
+extern int htc_init_new_vif(htc_device_t *);
+extern int htc_read_control_message(htc_device_t *, void *, size_t, size_t *);
+extern int htc_read_data_message(htc_device_t *, void *, size_t, size_t *);
+extern int htc_send_control_message(htc_device_t *, void *, size_t, uint8_t);
+extern int htc_send_data_message(htc_device_t *, void *, size_t, uint8_t);
+
+#endif  /* ATHEROS_HTC_H */
Index: uspace/drv/nic/ar9271/hw.c
===================================================================
--- uspace/drv/nic/ar9271/hw.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/nic/ar9271/hw.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,656 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file hw.c
+ *
+ * AR9271 hardware related functions implementation.
+ *
+ */
+
+#include <macros.h>
+#include <usb/debug.h>
+#include <unistd.h>
+#include <nic.h>
+#include <ieee80211.h>
+#include "hw.h"
+#include "wmi.h"
+
+/** Try to wait for register value repeatedly until timeout is reached.
+ *
+ * @param ar9271 Device structure.
+ * @param offset Registry offset (address) to be read.
+ * @param mask   Mask to apply on read result.
+ * @param value  Required value we are waiting for.
+ *
+ * @return EOK if succeed, ETIMEOUT on timeout,
+ *         negative error code otherwise.
+ *
+ */
+static int hw_read_wait(ar9271_t *ar9271, uint32_t offset, uint32_t mask,
+    uint32_t value)
+{
+	for (size_t i = 0; i < HW_WAIT_LOOPS; i++) {
+		udelay(HW_WAIT_TIME_US);
+		
+		uint32_t result;
+		wmi_reg_read(ar9271->htc_device, offset, &result);
+		if ((result & mask) == value)
+			return EOK;
+	}
+	
+	return ETIMEOUT;
+}
+
+static int hw_reset_power_on(ar9271_t *ar9271)
+{
+	wmi_reg_t buffer[] = {
+		{
+			.offset = AR9271_RTC_FORCE_WAKE,
+			.value = AR9271_RTC_FORCE_WAKE_ENABLE |
+			    AR9271_RTC_FORCE_WAKE_ON_INT
+		},
+		{
+			.offset = AR9271_RC,
+			.value = AR9271_RC_AHB
+		},
+		{
+			.offset = AR9271_RTC_RESET,
+			.value = 0
+		}
+	};
+	
+	wmi_reg_buffer_write(ar9271->htc_device, buffer,
+	    sizeof(buffer) / sizeof(wmi_reg_t));
+	
+	udelay(2);
+	
+	wmi_reg_write(ar9271->htc_device, AR9271_RC, 0);
+	wmi_reg_write(ar9271->htc_device, AR9271_RTC_RESET, 1);
+	
+	int rc = hw_read_wait(ar9271,
+	    AR9271_RTC_STATUS,
+	    AR9271_RTC_STATUS_MASK,
+	    AR9271_RTC_STATUS_ON);
+	if (rc != EOK) {
+		usb_log_error("Failed to wait for RTC wake up register.\n");
+		return rc;
+	}
+	
+	return EOK;
+}
+
+static int hw_set_reset(ar9271_t *ar9271, bool cold)
+{
+	uint32_t reset_value = AR9271_RTC_RC_MAC_WARM;
+	
+	if (cold)
+		reset_value |= AR9271_RTC_RC_MAC_COLD;
+	
+	wmi_reg_t buffer[] = {
+		{
+			.offset = AR9271_RTC_FORCE_WAKE,
+			.value = AR9271_RTC_FORCE_WAKE_ENABLE |
+			    AR9271_RTC_FORCE_WAKE_ON_INT
+		},
+		{
+			.offset = AR9271_RC,
+			.value = AR9271_RC_AHB
+		},
+		{
+			.offset = AR9271_RTC_RC,
+			.value = reset_value
+		}
+	};
+	
+	wmi_reg_buffer_write(ar9271->htc_device, buffer,
+	    sizeof(buffer) / sizeof(wmi_reg_t));
+	
+	udelay(100);
+	
+	wmi_reg_write(ar9271->htc_device, AR9271_RTC_RC, 0);
+	
+	int rc = hw_read_wait(ar9271, AR9271_RTC_RC, AR9271_RTC_RC_MASK, 0);
+	if (rc != EOK) {
+		usb_log_error("Failed to wait for RTC RC register.\n");
+		return rc;
+	}
+	
+	wmi_reg_write(ar9271->htc_device, AR9271_RC, 0);
+	wmi_reg_clear_bit(ar9271->htc_device, AR9271_STATION_ID1,
+	    AR9271_STATION_ID1_POWER_SAVING);
+	
+	return EOK;
+}
+
+static int hw_addr_init(ar9271_t *ar9271)
+{
+	uint32_t value;
+	nic_address_t ar9271_address;
+	
+	for (unsigned int i = 0; i < 3; i++) {
+		wmi_reg_read(ar9271->htc_device,
+		    AR9271_EEPROM_MAC_ADDR_START + i * 4, &value);
+		
+		uint16_t two_bytes = uint16_t_be2host(value);
+		ar9271_address.address[2*i] = two_bytes >> 8;
+		ar9271_address.address[2*i+1] = two_bytes & 0xff;
+	}
+	
+	nic_t *nic = nic_get_from_ddf_dev(ar9271->ddf_dev);
+	
+	int rc = nic_report_address(nic, &ar9271_address);
+	if (rc != EOK) {
+		usb_log_error("Failed to report NIC HW address.\n");
+		return rc;
+	}
+	
+	return EOK;
+}
+
+static int hw_gpio_set_output(ar9271_t *ar9271, uint32_t gpio, uint32_t type)
+{
+	uint32_t address;
+	
+	if (gpio > 11)
+		address = AR9271_GPIO_OUT_MUX3;
+	else if (gpio > 5)
+		address = AR9271_GPIO_OUT_MUX2;
+	else
+		address = AR9271_GPIO_OUT_MUX1;
+	
+	uint32_t gpio_shift = (gpio % 6) * 5;
+	
+	uint32_t temp;
+	wmi_reg_read(ar9271->htc_device, address, &temp);
+	
+	temp = ((temp & 0x1f0) << 1) | (temp & ~0x1f0);
+	temp &= ~(0x1f << gpio_shift);
+	temp |= (type << gpio_shift);
+	
+	wmi_reg_write(ar9271->htc_device, address, temp);
+	
+	gpio_shift = 2 * gpio;
+	
+	wmi_reg_set_clear_bit(ar9271->htc_device, AR9271_GPIO_OE_OUT,
+	    AR9271_GPIO_OE_OUT_ALWAYS << gpio_shift,
+	    AR9271_GPIO_OE_OUT_ALWAYS << gpio_shift);
+	
+	return EOK;
+}
+
+static int hw_gpio_set_value(ar9271_t *ar9271, uint32_t gpio, uint32_t value)
+{
+	wmi_reg_set_clear_bit(ar9271->htc_device, AR9271_GPIO_IN_OUT,
+	    (~value & 1) << gpio, 1 << gpio);
+	return EOK;
+}
+
+/**Hardware init procedure of AR9271 device.
+ *
+ * @param ar9271 Device structure.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+static int hw_init_proc(ar9271_t *ar9271)
+{
+	int rc = hw_reset_power_on(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to HW reset power on.\n");
+		return rc;
+	}
+	
+	rc = hw_set_reset(ar9271, false);
+	if (rc != EOK) {
+		usb_log_error("Failed to HW warm reset.\n");
+		return rc;
+	}
+	
+	rc = hw_addr_init(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to init HW addr.\n");
+		return rc;
+	}
+	
+	return EOK;
+}
+
+static int hw_init_led(ar9271_t *ar9271)
+{
+	int rc = hw_gpio_set_output(ar9271, AR9271_LED_PIN,
+	    AR9271_GPIO_OUT_MUX_AS_OUT);
+	if (rc != EOK) {
+		usb_log_error("Failed to set led GPIO to output.\n");
+		return rc;
+	}
+	
+	rc = hw_gpio_set_value(ar9271, AR9271_LED_PIN, 0);
+	if (rc != EOK) {
+		usb_log_error("Failed to init bring up GPIO led.\n");
+		return rc;
+	}
+	
+	return EOK;
+}
+
+static int hw_activate_phy(ar9271_t *ar9271)
+{
+	wmi_reg_write(ar9271->htc_device, AR9271_PHY_ACTIVE, 1);
+	udelay(1000);
+	
+	return EOK;
+}
+
+static int hw_set_operating_mode(ar9271_t *ar9271,
+    ieee80211_operating_mode_t op_mode)
+{
+	uint32_t set_bit = 0x10000000;
+	
+	switch(op_mode) {
+	case IEEE80211_OPMODE_ADHOC:
+		set_bit |= AR9271_OPMODE_ADHOC_MASK;
+		wmi_reg_set_bit(ar9271->htc_device, AR9271_CONFIG,
+		    AR9271_CONFIG_ADHOC);
+		break;
+	case IEEE80211_OPMODE_MESH:
+	case IEEE80211_OPMODE_AP:
+		set_bit |= AR9271_OPMODE_STATION_AP_MASK;
+	case IEEE80211_OPMODE_STATION:
+		wmi_reg_clear_bit(ar9271->htc_device, AR9271_CONFIG,
+		    AR9271_CONFIG_ADHOC);
+	}
+	
+	wmi_reg_set_clear_bit(ar9271->htc_device, AR9271_STATION_ID1,
+	    set_bit, AR9271_OPMODE_STATION_AP_MASK | AR9271_OPMODE_ADHOC_MASK);
+	
+	ieee80211_report_current_op_mode(ar9271->ieee80211_dev, op_mode);
+	
+	return EOK;
+}
+
+static int hw_reset_operating_mode(ar9271_t *ar9271)
+{
+	int rc = hw_set_operating_mode(ar9271, IEEE80211_OPMODE_STATION);
+	if (rc != EOK) {
+		usb_log_error("Failed to set opmode to station.\n");
+		return rc;
+	}
+	
+	return EOK;
+}
+
+static int hw_noise_floor_calibration(ar9271_t *ar9271)
+{
+	uint32_t value;
+	wmi_reg_read(ar9271->htc_device, AR9271_PHY_CAL, &value);
+	
+	value &= 0xfffffe00;
+	value |= (((uint32_t) AR9271_CALIB_NOMINAL_VALUE_2GHZ << 1) & 0x1ff);
+	
+	wmi_reg_write(ar9271->htc_device, AR9271_PHY_CAL, value);
+	
+	wmi_reg_clear_bit(ar9271->htc_device, AR9271_AGC_CONTROL,
+	    AR9271_AGC_CONTROL_NF_CALIB_EN);
+	
+	wmi_reg_clear_bit(ar9271->htc_device, AR9271_AGC_CONTROL,
+	    AR9271_AGC_CONTROL_NF_NOT_UPDATE);
+	
+	wmi_reg_set_bit(ar9271->htc_device, AR9271_AGC_CONTROL,
+	    AR9271_AGC_CONTROL_NF_CALIB);
+	
+	int rc = hw_read_wait(ar9271, AR9271_AGC_CONTROL,
+	    AR9271_AGC_CONTROL_NF_CALIB, 0);
+	if (rc != EOK) {
+		usb_log_error("Failed to wait for NF calibration.\n");
+		return rc;
+	}
+	
+	wmi_reg_set_bit(ar9271->htc_device, AR9271_AGC_CONTROL,
+	    AR9271_AGC_CONTROL_NF_CALIB_EN);
+	
+	wmi_reg_clear_bit(ar9271->htc_device, AR9271_AGC_CONTROL,
+	    AR9271_AGC_CONTROL_NF_NOT_UPDATE);
+	
+	wmi_reg_set_bit(ar9271->htc_device, AR9271_AGC_CONTROL,
+	    AR9271_AGC_CONTROL_NF_CALIB);
+	
+	return EOK;
+}
+
+static int hw_set_freq(ar9271_t *ar9271, uint16_t freq)
+{
+	/* Not supported channel frequency. */
+	if ((freq < IEEE80211_FIRST_FREQ) || (freq > IEEE80211_MAX_FREQ))
+		return EINVAL;
+	
+	/* Not supported channel frequency. */
+	if ((freq - IEEE80211_FIRST_FREQ) % IEEE80211_CHANNEL_GAP != 0)
+		return EINVAL;
+	
+	uint32_t tx_control;
+	wmi_reg_read(ar9271->htc_device, AR9271_PHY_CCK_TX_CTRL, &tx_control);
+	wmi_reg_write(ar9271->htc_device, AR9271_PHY_CCK_TX_CTRL,
+	    tx_control & ~AR9271_PHY_CCK_TX_CTRL_JAPAN);
+	
+	/* Some magic here. */
+	uint32_t synth_ctl;
+	wmi_reg_read(ar9271->htc_device, AR9271_PHY_SYNTH_CONTROL, &synth_ctl);
+	synth_ctl &= 0xc0000000;
+	uint32_t channel_select = (freq * 0x10000) / 15;
+	synth_ctl = synth_ctl | (1 << 29) | (1 << 28) | channel_select;
+	
+	wmi_reg_write(ar9271->htc_device, AR9271_PHY_SYNTH_CONTROL, synth_ctl);
+	
+	ieee80211_report_current_freq(ar9271->ieee80211_dev, freq);
+	
+	return EOK;
+}
+
+int hw_freq_switch(ar9271_t *ar9271, uint16_t freq)
+{
+	wmi_reg_write(ar9271->htc_device, AR9271_PHY_RFBUS_KILL, 0x1);
+	
+	int rc = hw_read_wait(ar9271, AR9271_PHY_RFBUS_GRANT, 0x1, 0x1);
+	if (rc != EOK) {
+		usb_log_error("Failed to kill RF bus.\n");
+		return rc;
+	}
+	
+	rc = hw_set_freq(ar9271, freq);
+	if (rc != EOK) {
+		usb_log_error("Failed to HW set frequency.\n");
+		return rc;
+	}
+	
+	rc = hw_activate_phy(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to activate physical layer.\n");
+		return rc;
+	}
+	
+	udelay(1000);
+	wmi_reg_write(ar9271->htc_device, AR9271_PHY_RFBUS_KILL, 0x0);
+	
+	rc = hw_noise_floor_calibration(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to do NF calibration.\n");
+		return rc;
+	}
+	
+	return EOK;
+}
+
+int hw_set_rx_filter(ar9271_t *ar9271, bool assoc)
+{
+	uint32_t additional_bits = 0;
+	
+	if (assoc)
+		additional_bits |= AR9271_RX_FILTER_MYBEACON;
+	else
+		additional_bits |= AR9271_RX_FILTER_BEACON;
+	
+	uint32_t filter_bits = AR9271_RX_FILTER_UNI |
+	    AR9271_RX_FILTER_MULTI | AR9271_RX_FILTER_BROAD |
+	    additional_bits;
+	
+	wmi_reg_write(ar9271->htc_device, AR9271_RX_FILTER, filter_bits);
+	
+	return EOK;
+}
+
+int hw_set_bssid(ar9271_t *ar9271)
+{
+	ieee80211_dev_t *ieee80211_dev = ar9271->ieee80211_dev;
+	
+	nic_address_t bssid;
+	ieee80211_query_bssid(ieee80211_dev, &bssid);
+	
+	uint32_t *first_4bytes = (uint32_t *) &bssid.address;
+	uint16_t *last_2bytes = (uint16_t *) &bssid.address[4];
+	
+	wmi_reg_write(ar9271->htc_device, AR9271_BSSID0,
+	    uint32_t_le2host(*first_4bytes));
+	
+	wmi_reg_write(ar9271->htc_device, AR9271_BSSID1,
+	    uint16_t_le2host(*last_2bytes) |
+	    ((ieee80211_get_aid(ieee80211_dev) & 0x3fff) << 16));
+	
+	return EOK;
+}
+
+int hw_rx_init(ar9271_t *ar9271)
+{
+	wmi_reg_write(ar9271->htc_device, AR9271_COMMAND,
+	    AR9271_COMMAND_RX_ENABLE);
+	
+	int rc = hw_set_rx_filter(ar9271, false);
+	if (rc != EOK) {
+		usb_log_error("Failed to set RX filtering.\n");
+		return rc;
+	}
+	
+	wmi_reg_write(ar9271->htc_device, AR9271_MULTICAST_FILTER1, ~0);
+	wmi_reg_write(ar9271->htc_device, AR9271_MULTICAST_FILTER2, ~0);
+	
+	/* Disable RX blocking. */
+	wmi_reg_clear_bit(ar9271->htc_device, AR9271_DIAG, (0x20 | 0x02000000));
+	
+	return EOK;
+}
+
+static int hw_init_pll(ar9271_t *ar9271)
+{
+	/* Some magic here (set for 2GHz channels). But VERY important :-) */
+	uint32_t pll = (0x5 << 10) | 0x2c;
+	
+	wmi_reg_write(ar9271->htc_device, AR9271_RTC_PLL_CONTROL, pll);
+	
+	wmi_reg_write(ar9271->htc_device, AR9271_RTC_SLEEP_CLOCK,
+	    AR9271_RTC_SLEEP_CLOCK_FORCE_DERIVED);
+	wmi_reg_set_bit(ar9271->htc_device, AR9271_RTC_FORCE_WAKE,
+	    AR9271_RTC_FORCE_WAKE_ENABLE);
+	
+	return EOK;
+}
+
+static void hw_set_init_values(ar9271_t *ar9271)
+{
+	uint32_t reg_offset;
+	uint32_t reg_value;
+	
+	size_t size = ARRAY_SIZE(ar9271_2g_mode_array);
+	
+	for (size_t i = 0; i < size; i++) {
+		reg_offset = ar9271_2g_mode_array[i][0];
+		reg_value = ar9271_2g_mode_array[i][1];
+		wmi_reg_write(ar9271->htc_device, reg_offset, reg_value);
+	}
+	
+	size = ARRAY_SIZE(ar9271_2g_tx_array);
+	
+	for (size_t i = 0; i < size; i++) {
+		reg_offset = ar9271_2g_tx_array[i][0];
+		reg_value = ar9271_2g_tx_array[i][1];
+		wmi_reg_write(ar9271->htc_device, reg_offset, reg_value);
+	}
+	
+	size = ARRAY_SIZE(ar9271_init_array);
+	
+	for (size_t i = 0; i < size; i++) {
+		reg_offset = ar9271_init_array[i][0];
+		reg_value = ar9271_init_array[i][1];
+		wmi_reg_write(ar9271->htc_device, reg_offset, reg_value);
+	}
+}
+
+static int hw_calibration(ar9271_t *ar9271)
+{
+	wmi_reg_set_bit(ar9271->htc_device, AR9271_CARRIER_LEAK_CONTROL,
+	    AR9271_CARRIER_LEAK_CALIB);
+	wmi_reg_clear_bit(ar9271->htc_device, AR9271_ADC_CONTROL,
+	    AR9271_ADC_CONTROL_OFF_PWDADC);
+	wmi_reg_set_bit(ar9271->htc_device, AR9271_AGC_CONTROL,
+	    AR9271_AGC_CONTROL_TX_CALIB);
+	wmi_reg_set_bit(ar9271->htc_device, AR9271_PHY_TPCRG1,
+	    AR9271_PHY_TPCRG1_PD_CALIB);
+	wmi_reg_set_bit(ar9271->htc_device, AR9271_AGC_CONTROL,
+	    AR9271_AGC_CONTROL_CALIB);
+	
+	int rc = hw_read_wait(ar9271, AR9271_AGC_CONTROL,
+	    AR9271_AGC_CONTROL_CALIB, 0);
+	if (rc != EOK) {
+		usb_log_error("Failed to wait on calibrate completion.\n");
+		return rc;
+	}
+	
+	wmi_reg_set_bit(ar9271->htc_device, AR9271_ADC_CONTROL,
+	    AR9271_ADC_CONTROL_OFF_PWDADC);
+	wmi_reg_clear_bit(ar9271->htc_device, AR9271_CARRIER_LEAK_CONTROL,
+	    AR9271_CARRIER_LEAK_CALIB);
+	wmi_reg_clear_bit(ar9271->htc_device, AR9271_AGC_CONTROL,
+	    AR9271_AGC_CONTROL_TX_CALIB);
+	
+	return EOK;
+}
+
+int hw_reset(ar9271_t *ar9271)
+{
+	/* Set physical layer as deactivated. */
+	wmi_reg_write(ar9271->htc_device, AR9271_PHY_ACTIVE, 0);
+	
+	if(ar9271->starting_up) {
+		wmi_reg_write(ar9271->htc_device,
+		    AR9271_RESET_POWER_DOWN_CONTROL,
+		    AR9271_RADIO_RF_RESET);
+		
+		udelay(50);
+	}
+	
+	/* Cold reset when RX is enabled. */
+	uint32_t config_reg;
+	wmi_reg_read(ar9271->htc_device, AR9271_COMMAND, &config_reg);
+	if (config_reg & AR9271_COMMAND_RX_ENABLE)
+		hw_set_reset(ar9271, true);
+	
+	int rc = hw_init_pll(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to init PLL.\n");
+		return rc;
+	}
+	
+	udelay(500);
+	
+	wmi_reg_write(ar9271->htc_device, AR9271_CLOCK_CONTROL,
+	    AR9271_MAX_CPU_CLOCK);
+	
+	udelay(100);
+	
+	if (ar9271->starting_up) {
+		wmi_reg_write(ar9271->htc_device,
+		    AR9271_RESET_POWER_DOWN_CONTROL,
+		    AR9271_GATE_MAC_CONTROL);
+		
+		udelay(50);
+	}
+	
+	hw_set_init_values(ar9271);
+	
+	/* Set physical layer mode. */
+	wmi_reg_write(ar9271->htc_device, AR9271_PHY_MODE,
+	    AR9271_PHY_MODE_DYNAMIC);
+	
+	/* Reset device operating mode. */
+	rc = hw_reset_operating_mode(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to reset operating mode.\n");
+		return rc;
+	}
+	
+	/* Set initial channel frequency. */
+	rc = hw_set_freq(ar9271, IEEE80211_FIRST_FREQ);
+	if (rc != EOK) {
+		usb_log_error("Failed to set channel.\n");
+		return rc;
+	}
+	
+	/* Initialize transmission queues. */
+	for (unsigned int i = 0; i < AR9271_QUEUES_COUNT; i++) {
+		wmi_reg_write(ar9271->htc_device,
+		    AR9271_QUEUE_BASE_MASK + (i << 2), 1 << i);
+	}
+	
+	/* Activate physical layer. */
+	rc = hw_activate_phy(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to activate physical layer.\n");
+		return rc;
+	}
+	
+	/* Calibration. */
+	rc = hw_calibration(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to calibrate device.\n");
+		return rc;
+	}
+	
+	rc = hw_noise_floor_calibration(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to calibrate noise floor.\n");
+		return rc;
+	}
+	
+	/* Byteswap TX and RX data buffer words. */
+	wmi_reg_write(ar9271->htc_device, AR9271_CONFIG, 0xA);
+	
+	return EOK;
+}
+
+/** Initialize hardware of AR9271 device.
+ *
+ * @param ar9271 Device structure.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ */
+int hw_init(ar9271_t *ar9271)
+{
+	int rc = hw_init_proc(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to HW reset device.\n");
+		return rc;
+	}
+	
+	rc = hw_init_led(ar9271);
+	if (rc != EOK) {
+		usb_log_error("Failed to HW init led.\n");
+		return rc;
+	}
+	
+	usb_log_info("HW initialization finished successfully.\n");
+	
+	return EOK;
+}
Index: uspace/drv/nic/ar9271/hw.h
===================================================================
--- uspace/drv/nic/ar9271/hw.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/nic/ar9271/hw.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file hw.h
+ *
+ * Definitions of AR9271 hardware related functions.
+ *
+ */
+
+#ifndef ATHEROS_HW_H
+#define ATHEROS_HW_H
+
+#include "ar9271.h"
+
+#define HW_WAIT_LOOPS    1000
+#define HW_WAIT_TIME_US  10
+
+extern int hw_init(ar9271_t *);
+extern int hw_freq_switch(ar9271_t *, uint16_t);
+extern int hw_rx_init(ar9271_t *);
+extern int hw_reset(ar9271_t *);
+extern int hw_set_bssid(ar9271_t *);
+extern int hw_set_rx_filter(ar9271_t *, bool);
+
+#endif  /* ATHEROS_HW_H */
Index: uspace/drv/nic/ar9271/wmi.c
===================================================================
--- uspace/drv/nic/ar9271/wmi.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/nic/ar9271/wmi.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2014 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file wmi.c
+ *
+ * Implementation of Atheros WMI communication.
+ *
+ */
+
+#include <usb/debug.h>
+#include <malloc.h>
+#include <mem.h>
+#include <byteorder.h>
+#include "wmi.h"
+
+/** WMI registry read.
+ *
+ * @param htc_device HTC device structure.
+ * @param reg_offset Registry offset (address) to be read.
+ * @param res        Stored result.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int wmi_reg_read(htc_device_t *htc_device, uint32_t reg_offset, uint32_t *res)
+{
+	uint32_t cmd_value = host2uint32_t_be(reg_offset);
+	
+	void *resp_buffer =
+	    malloc(htc_device->ath_device->ctrl_response_length);
+	
+	int rc = wmi_send_command(htc_device, WMI_REG_READ,
+	    (uint8_t *) &cmd_value, sizeof(cmd_value), resp_buffer);
+	
+	if (rc != EOK) {
+		usb_log_error("Failed to read registry value.\n");
+		return rc;
+	}
+	
+	uint32_t *resp_value = (uint32_t *) ((void *) resp_buffer +
+	    sizeof(htc_frame_header_t) + sizeof(wmi_command_header_t));
+	
+	*res = uint32_t_be2host(*resp_value);
+	
+	return rc;
+}
+
+/** WMI registry write.
+ *
+ * @param htc_device HTC device structure.
+ * @param reg_offset Registry offset (address) to be written.
+ * @param val        Value to be written
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int wmi_reg_write(htc_device_t *htc_device, uint32_t reg_offset, uint32_t val)
+{
+	uint32_t cmd_buffer[] = {
+		host2uint32_t_be(reg_offset),
+		host2uint32_t_be(val)
+	};
+	
+	void *resp_buffer =
+	    malloc(htc_device->ath_device->ctrl_response_length);
+	
+	int rc = wmi_send_command(htc_device, WMI_REG_WRITE,
+	    (uint8_t *) &cmd_buffer, sizeof(cmd_buffer), resp_buffer);
+	
+	free(resp_buffer);
+	
+	if (rc != EOK) {
+		usb_log_error("Failed to write registry value.\n");
+		return rc;
+	}
+	
+	return rc;
+}
+
+/** WMI registry set or clear specified bits.
+ *
+ * @param htc_device HTC device structure.
+ * @param reg_offset Registry offset (address) to be written.
+ * @param set_bit    Bit to be set.
+ * @param clear_bit  Bit to be cleared.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int wmi_reg_set_clear_bit(htc_device_t *htc_device, uint32_t reg_offset,
+    uint32_t set_bit, uint32_t clear_bit)
+{
+	uint32_t value;
+	
+	int rc = wmi_reg_read(htc_device, reg_offset, &value);
+	if (rc != EOK) {
+		usb_log_error("Failed to read registry value in RMW "
+		    "function.\n");
+		return rc;
+	}
+	
+	value &= ~clear_bit;
+	value |= set_bit;
+	
+	rc = wmi_reg_write(htc_device, reg_offset, value);
+	if (rc != EOK) {
+		usb_log_error("Failed to write registry value in RMW "
+		    "function.\n");
+		return rc;
+	}
+	
+	return rc;
+}
+
+/** WMI registry set specified bit.
+ *
+ * @param htc_device HTC device structure.
+ * @param reg_offset Registry offset (address) to be written.
+ * @param set_bit    Bit to be set.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int wmi_reg_set_bit(htc_device_t *htc_device, uint32_t reg_offset,
+    uint32_t set_bit)
+{
+	return wmi_reg_set_clear_bit(htc_device, reg_offset, set_bit, 0);
+}
+
+/** WMI registry clear specified bit.
+ *
+ * @param htc_device HTC device structure.
+ * @param reg_offset Registry offset (address) to be written.
+ * @param clear_bit  Bit to be cleared.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int wmi_reg_clear_bit(htc_device_t *htc_device, uint32_t reg_offset,
+    uint32_t clear_bit)
+{
+	return wmi_reg_set_clear_bit(htc_device, reg_offset, 0, clear_bit);
+}
+
+/** WMI multi registry write.
+ *
+ * @param htc_device HTC device structure.
+ * @param reg_buffer Array of registry values to be written.
+ * @param elements   Number of elements in array.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int wmi_reg_buffer_write(htc_device_t *htc_device, wmi_reg_t *reg_buffer,
+    size_t elements)
+{
+	size_t buffer_size = sizeof(wmi_reg_t) * elements;
+	void *buffer = malloc(buffer_size);
+	void *resp_buffer =
+	    malloc(htc_device->ath_device->ctrl_response_length);
+	
+	/* Convert values to correct endianness. */
+	for (size_t i = 0; i < elements; i++) {
+		wmi_reg_t *buffer_element = &reg_buffer[i];
+		wmi_reg_t *buffer_it = (wmi_reg_t *)
+		    ((void *) buffer + i * sizeof(wmi_reg_t));
+		buffer_it->offset =
+		    host2uint32_t_be(buffer_element->offset);
+		buffer_it->value =
+		    host2uint32_t_be(buffer_element->value);
+	}
+	
+	int rc = wmi_send_command(htc_device, WMI_REG_WRITE,
+	    (uint8_t *) buffer, buffer_size, resp_buffer);
+	
+	free(buffer);
+	free(resp_buffer);
+	
+	if (rc != EOK) {
+		usb_log_error("Failed to write multi registry value.\n");
+		return rc;
+	}
+	
+	return rc;
+}
+
+/** Send WMI message to HTC device.
+ *
+ * @param htc_device      HTC device structure.
+ * @param command_id      Command identification.
+ * @param command_buffer  Buffer with command data.
+ * @param command_length  Length of command data.
+ * @param response_buffer Buffer with response data.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int wmi_send_command(htc_device_t *htc_device, wmi_command_t command_id,
+    uint8_t *command_buffer, uint32_t command_length, void *response_buffer)
+{
+	size_t header_size = sizeof(wmi_command_header_t) +
+	    sizeof(htc_frame_header_t);
+	size_t buffer_size = header_size + command_length;
+	void *buffer = malloc(buffer_size);
+	
+	if (command_buffer != NULL)
+		memcpy(buffer+header_size, command_buffer, command_length);
+	
+	/* Set up WMI header */
+	wmi_command_header_t *wmi_header = (wmi_command_header_t *)
+	    ((void *) buffer + sizeof(htc_frame_header_t));
+	wmi_header->command_id = host2uint16_t_be(command_id);
+	wmi_header->sequence_number =
+	    host2uint16_t_be(++htc_device->sequence_number);
+	
+	/* Send message. */
+	int rc = htc_send_control_message(htc_device, buffer, buffer_size,
+	    htc_device->endpoints.wmi_endpoint);
+	if (rc != EOK) {
+		free(buffer);
+		usb_log_error("Failed to send WMI message. Error: %d\n", rc);
+		return rc;
+	}
+	
+	free(buffer);
+	
+	bool clean_resp_buffer = false;
+	size_t response_buffer_size =
+	    htc_device->ath_device->ctrl_response_length;
+	if (response_buffer == NULL) {
+		response_buffer = malloc(response_buffer_size);
+		clean_resp_buffer = true;
+	}
+	
+	/* Read response. */
+	/* TODO: Ignoring WMI management RX messages ~ TX statuses etc. */
+	uint16_t cmd_id;
+	do {
+		rc = htc_read_control_message(htc_device, response_buffer,
+		    response_buffer_size, NULL);
+		if (rc != EOK) {
+			free(buffer);
+			usb_log_error("Failed to receive WMI message response. "
+			    "Error: %d\n", rc);
+			return rc;
+		}
+		
+		if (response_buffer_size < sizeof(htc_frame_header_t) +
+		    sizeof(wmi_command_header_t)) {
+			free(buffer);
+			usb_log_error("Corrupted response received.\n");
+			return EINVAL;
+		}
+		
+		wmi_command_header_t *wmi_hdr = (wmi_command_header_t *)
+		    ((void *) response_buffer + sizeof(htc_frame_header_t));
+		cmd_id = uint16_t_be2host(wmi_hdr->command_id);
+	} while(cmd_id & WMI_MGMT_CMD_MASK);
+	
+	if (clean_resp_buffer)
+		free(response_buffer);
+	
+	return rc;
+}
Index: uspace/drv/nic/ar9271/wmi.h
===================================================================
--- uspace/drv/nic/ar9271/wmi.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/nic/ar9271/wmi.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2014 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file wmi.h
+ *
+ * Definitions of the Atheros WMI protocol specified in the 
+ * Wireless Module Interface (WMI).
+ *
+ */
+
+#ifndef ATHEROS_WMI_H
+#define ATHEROS_WMI_H
+
+#include "htc.h"
+
+/* Macros for creating service identificators. */
+#define WMI_SERVICE_GROUP  1
+
+#define CREATE_SERVICE_ID(group, i) \
+	(unsigned int) (((unsigned int) group << 8) | (unsigned int) (i))
+
+#define WMI_MGMT_CMD_MASK  0x1000
+
+/** WMI header structure.
+ *
+ */
+typedef struct {
+	uint16_t command_id;       /**< Big Endian value! */
+	uint16_t sequence_number;  /**< Big Endian value! */
+} __attribute__((packed)) wmi_command_header_t;
+
+/** WMI services IDs
+ *
+ */
+typedef enum {
+	WMI_CONTROL_SERVICE = CREATE_SERVICE_ID(WMI_SERVICE_GROUP, 0),
+	WMI_BEACON_SERVICE = CREATE_SERVICE_ID(WMI_SERVICE_GROUP, 1),
+	WMI_CAB_SERVICE = CREATE_SERVICE_ID(WMI_SERVICE_GROUP, 2),
+	WMI_UAPSD_SERVICE = CREATE_SERVICE_ID(WMI_SERVICE_GROUP, 3),
+	WMI_MGMT_SERVICE = CREATE_SERVICE_ID(WMI_SERVICE_GROUP, 4),
+	WMI_DATA_VOICE_SERVICE = CREATE_SERVICE_ID(WMI_SERVICE_GROUP, 5),
+	WMI_DATA_VIDEO_SERVICE = CREATE_SERVICE_ID(WMI_SERVICE_GROUP, 6),
+	WMI_DATA_BE_SERVICE = CREATE_SERVICE_ID(WMI_SERVICE_GROUP, 7),
+	WMI_DATA_BK_SERVICE = CREATE_SERVICE_ID(WMI_SERVICE_GROUP, 8)
+} wmi_services_t;
+
+/** List of WMI commands
+ *
+ */
+typedef enum {
+	WMI_ECHO = 0x0001,
+	WMI_ACCESS_MEMORY,
+	
+	/* Commands used for HOST -> DEVICE communication */
+	WMI_GET_FW_VERSION,
+	WMI_DISABLE_INTR,
+	WMI_ENABLE_INTR,
+	WMI_ATH_INIT,
+	WMI_ABORT_TXQ,
+	WMI_STOP_TX_DMA,
+	WMI_ABORT_TX_DMA,
+	WMI_DRAIN_TXQ,
+	WMI_DRAIN_TXQ_ALL,
+	WMI_START_RECV,
+	WMI_STOP_RECV,
+	WMI_FLUSH_RECV,
+	WMI_SET_MODE,
+	WMI_NODE_CREATE,
+	WMI_NODE_REMOVE,
+	WMI_VAP_REMOVE,
+	WMI_VAP_CREATE,
+	WMI_REG_READ,
+	WMI_REG_WRITE,
+	WMI_RC_STATE_CHANGE,
+	WMI_RC_RATE_UPDATE,
+	WMI_TARGET_IC_UPDATE,
+	WMI_TX_AGGR_ENABLE,
+	WMI_TGT_DETACH,
+	WMI_NODE_UPDATE,
+	WMI_INT_STATS,
+	WMI_TX_STATS,
+	WMI_RX_STATS,
+	WMI_BITRATE_MASK
+} wmi_command_t;
+
+/**Structure used when sending registry buffer
+ *
+ */
+typedef struct {
+	uint32_t offset;  /**< Big Endian value! */
+	uint32_t value;   /**< Big Endian value! */
+} wmi_reg_t;
+
+extern int wmi_reg_read(htc_device_t *, uint32_t, uint32_t *);
+extern int wmi_reg_write(htc_device_t *, uint32_t, uint32_t);
+extern int wmi_reg_set_clear_bit(htc_device_t *, uint32_t, uint32_t, uint32_t);
+extern int wmi_reg_set_bit(htc_device_t *, uint32_t, uint32_t);
+extern int wmi_reg_clear_bit(htc_device_t *, uint32_t, uint32_t);
+extern int wmi_reg_buffer_write(htc_device_t *, wmi_reg_t *, size_t);
+extern int wmi_send_command(htc_device_t *, wmi_command_t, uint8_t *, uint32_t,
+    void *);
+
+#endif  /* ATHEROS_WMI_H */
Index: uspace/drv/platform/msim/Makefile
===================================================================
--- uspace/drv/platform/msim/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/platform/msim/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2010 Lenka Trochtova
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+LIBS = $(LIBDRV_PREFIX)/libdrv.a
+EXTRA_CFLAGS += -I$(LIBDRV_PREFIX)/include
+BINARY = msim
+
+SOURCES = \
+	msim.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/platform/msim/msim.c
===================================================================
--- uspace/drv/platform/msim/msim.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/platform/msim/msim.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2015 Jakub Jermar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @defgroup msim MSIM platform driver.
+ * @brief HelenOS MSIM platform driver.
+ * @{
+ */
+
+/** @file
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <macros.h>
+
+#include <ddi.h>
+#include <ddf/driver.h>
+#include <ddf/log.h>
+#include <ipc/dev_iface.h>
+#include <ops/hw_res.h>
+#include <ops/pio_window.h>
+
+#define NAME "msim"
+
+#define MSIM_DISK_BASE	UINT32_C(0x10000200)
+#define MSIM_DISK_SIZE	UINT32_C(0x00000010)
+
+typedef struct msim_fun {
+	hw_resource_list_t hw_resources;
+	pio_window_t pio_window;
+} msim_fun_t;
+
+static int msim_dev_add(ddf_dev_t *dev);
+static void msim_init(void);
+
+/** The root device driver's standard operations. */
+static driver_ops_t msim_ops = {
+	.dev_add = &msim_dev_add
+};
+
+/** The root device driver structure. */
+static driver_t msim_driver = {
+	.name = NAME,
+	.driver_ops = &msim_ops
+};
+
+static hw_resource_t disk_regs[] = {
+	{
+		.type = MEM_RANGE,
+		.res.mem_range = {
+			.address = 0,
+			.size = 16,
+			.relative = true,
+			.endianness = LITTLE_ENDIAN
+		}
+	},
+	{
+		.type = INTERRUPT,
+		.res.interrupt = {
+			.irq = 6
+		}
+	}
+};
+
+static msim_fun_t disk_data = {
+	.hw_resources = {
+		sizeof(disk_regs) / sizeof(disk_regs[0]),
+		disk_regs
+	},
+	.pio_window = {
+		.mem = {
+			.base = MSIM_DISK_BASE,
+			.size = MSIM_DISK_SIZE
+		}
+	}
+};
+
+/** Obtain function soft-state from DDF function node */
+static msim_fun_t *msim_fun(ddf_fun_t *fnode)
+{
+	return ddf_fun_data_get(fnode);
+}
+
+static hw_resource_list_t *msim_get_resources(ddf_fun_t *fnode)
+{
+	msim_fun_t *fun = msim_fun(fnode);
+	
+	assert(fun != NULL);
+	return &fun->hw_resources;
+}
+
+static bool msim_enable_interrupt(ddf_fun_t *fun)
+{
+	/* Nothing to do. */
+
+	return true;
+}
+
+static pio_window_t *msim_get_pio_window(ddf_fun_t *fnode)
+{
+	msim_fun_t *fun = msim_fun(fnode);
+
+	assert(fun != NULL);
+	return &fun->pio_window;
+}
+
+static hw_res_ops_t fun_hw_res_ops = {
+	.get_resource_list = &msim_get_resources,
+	.enable_interrupt = &msim_enable_interrupt,
+};
+
+static pio_window_ops_t fun_pio_window_ops = {
+	.get_pio_window = &msim_get_pio_window
+};
+
+/* Initialized in msim_init() function. */
+static ddf_dev_ops_t msim_fun_ops;
+
+static bool
+msim_add_fun(ddf_dev_t *dev, const char *name, const char *str_match_id,
+    msim_fun_t *fun_proto)
+{
+	ddf_msg(LVL_DEBUG, "Adding new function '%s'.", name);
+	
+	ddf_fun_t *fnode = NULL;
+	int rc;
+	
+	/* Create new device. */
+	fnode = ddf_fun_create(dev, fun_inner, name);
+	if (fnode == NULL)
+		goto failure;
+	
+	msim_fun_t *fun = ddf_fun_data_alloc(fnode, sizeof(msim_fun_t));
+	*fun = *fun_proto;
+	
+	/* Add match ID */
+	rc = ddf_fun_add_match_id(fnode, str_match_id, 100);
+	if (rc != EOK)
+		goto failure;
+	
+	/* Set provided operations to the device. */
+	ddf_fun_set_ops(fnode, &msim_fun_ops);
+	
+	/* Register function. */
+	if (ddf_fun_bind(fnode) != EOK) {
+		ddf_msg(LVL_ERROR, "Failed binding function %s.", name);
+		goto failure;
+	}
+	
+	return true;
+	
+failure:
+	if (fnode != NULL)
+		ddf_fun_destroy(fnode);
+	
+	ddf_msg(LVL_ERROR, "Failed adding function '%s'.", name);
+	
+	return false;
+}
+
+static bool msim_add_functions(ddf_dev_t *dev)
+{
+	return msim_add_fun(dev, "disk0", "msim/ddisk", &disk_data);
+}
+
+/** Get the root device.
+ *
+ * @param dev		The device which is root of the whole device tree (both
+ *			of HW and pseudo devices).
+ * @return		Zero on success, negative error number otherwise.
+ */
+static int msim_dev_add(ddf_dev_t *dev)
+{
+	ddf_msg(LVL_DEBUG, "msim_dev_add, device handle = %d",
+	    (int) ddf_dev_get_handle(dev));
+
+	/* Register functions. */
+	if (!msim_add_functions(dev))
+		ddf_msg(LVL_ERROR, "Failed to add functions for the MSIM platform.");
+	
+	return EOK;
+}
+
+static void msim_init(void)
+{
+	ddf_log_init(NAME);
+	msim_fun_ops.interfaces[HW_RES_DEV_IFACE] = &fun_hw_res_ops;
+	msim_fun_ops.interfaces[PIO_WINDOW_DEV_IFACE] = &fun_pio_window_ops;
+}
+
+int main(int argc, char *argv[])
+{
+	printf(NAME ": HelenOS MSIM platform driver\n");
+	msim_init();
+	return ddf_driver_main(&msim_driver);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/platform/msim/msim.ma
===================================================================
--- uspace/drv/platform/msim/msim.ma	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/drv/platform/msim/msim.ma	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,1 @@
+10 platform/msim
Index: uspace/lib/c/generic/dhcp.c
===================================================================
--- uspace/lib/c/generic/dhcp.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/c/generic/dhcp.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -84,4 +84,14 @@
 }
 
+int dhcp_discover(sysarg_t link_id)
+{
+	async_exch_t *exch = async_exchange_begin(dhcp_sess);
+
+	int rc = async_req_1_0(exch, DHCP_DISCOVER, link_id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/fibril.c
===================================================================
--- uspace/lib/c/generic/fibril.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/c/generic/fibril.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -57,5 +57,5 @@
 /**
  * This futex serializes access to ready_list,
- * serialized_list and manager_list.
+ * serialized_list, manager_list and fibril_list.
  */
 static futex_t fibril_futex = FUTEX_INITIALIZER;
@@ -126,12 +126,19 @@
 	
 	fibril->waits_for = NULL;
+
+	futex_lock(&fibril_futex);
 	list_append(&fibril->all_link, &fibril_list);
+	futex_unlock(&fibril_futex);
 	
 	return fibril;
 }
 
-void fibril_teardown(fibril_t *fibril)
-{
+void fibril_teardown(fibril_t *fibril, bool locked)
+{	
+	if (!locked)
+		futex_lock(&fibril_futex);
 	list_remove(&fibril->all_link);
+	if (!locked)
+		futex_unlock(&fibril_futex);
 	tls_free(fibril->tcb);
 	free(fibril);
@@ -208,5 +215,5 @@
 					as_area_destroy(stack);
 				}
-				fibril_teardown(srcf->clean_after_me);
+				fibril_teardown(srcf->clean_after_me, true);
 				srcf->clean_after_me = NULL;
 			}
@@ -294,5 +301,5 @@
 	    AS_AREA_LATE_RESERVE);
 	if (fibril->stack == (void *) -1) {
-		fibril_teardown(fibril);
+		fibril_teardown(fibril, false);
 		return 0;
 	}
@@ -321,5 +328,5 @@
 	
 	as_area_destroy(fibril->stack);
-	fibril_teardown(fibril);
+	fibril_teardown(fibril, false);
 }
 
Index: uspace/lib/c/generic/getopt.c
===================================================================
--- uspace/lib/c/generic/getopt.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/c/generic/getopt.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -95,11 +95,8 @@
  * Compute the greatest common divisor of a and b.
  */
-static int
-gcd(a, b)
-	int a;
-	int b;
+static int gcd(int a, int b)
 {
 	int c;
-
+	
 	c = a % b;
 	while (c != 0) {
@@ -108,5 +105,5 @@
 		c = a % b;
 	}
-	   
+	
 	return b;
 }
@@ -117,10 +114,6 @@
  * in each block).
  */
-static void
-permute_args(panonopt_start, panonopt_end, opt_end, nargv)
-	int panonopt_start;
-	int panonopt_end;
-	int opt_end;
-	char **nargv;
+static void permute_args(int panonopt_start, int panonopt_end, int opt_end,
+    char **nargv)
 {
 	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
@@ -157,9 +150,5 @@
  *  Returns -2 if -- is found (can be long option or end of options marker).
  */
-static int
-getopt_internal(nargc, nargv, options)
-	int nargc;
-	char **nargv;
-	const char *options;
+static int getopt_internal(int nargc, char **nargv, const char *options)
 {
 	const char *oli;				/* option letter list index */
@@ -299,9 +288,5 @@
  *	Parse argc/argv argument vector.
  */
-int
-getopt(nargc, nargv, options)
-	int nargc;
-	char * const *nargv;
-	const char *options;
+int getopt(int nargc, char * const *nargv, const char *options)
 {
 	int retval;
@@ -332,11 +317,6 @@
  *	Parse argc/argv argument vector.
  */
-int
-getopt_long(nargc, nargv, options, long_options, idx)
-	int nargc;
-	char * const *nargv;
-	const char *options;
-	const struct option *long_options;
-	int *idx;
+int getopt_long(int nargc, char * const *nargv, const char *options,
+    const struct option *long_options, int *idx)
 {
 	int retval;
Index: uspace/lib/c/generic/irq.c
===================================================================
--- uspace/lib/c/generic/irq.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/c/generic/irq.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -35,4 +35,19 @@
 #include <ipc/irq.h>
 #include <libc.h>
+#include <stdlib.h>
+#include <macros.h>
+
+static irq_cmd_t default_cmds[] = {
+	{
+		.cmd = CMD_ACCEPT
+	}
+};
+
+static const irq_code_t default_ucode = {
+	0,
+	NULL,
+	ARRAY_SIZE(default_cmds),
+	default_cmds
+};
 
 /** Subscribe to IRQ notification.
@@ -49,4 +64,7 @@
     const irq_code_t *ucode)
 {
+	if (ucode == NULL)
+		ucode = &default_ucode;
+	
 	return __SYSCALL4(SYS_IPC_IRQ_SUBSCRIBE, inr, devno, method,
 	    (sysarg_t) ucode);
Index: uspace/lib/c/generic/libc.c
===================================================================
--- uspace/lib/c/generic/libc.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/c/generic/libc.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -122,5 +122,5 @@
 		__stdio_done();
 		task_retval(status);
-		fibril_teardown(__tcb_get()->fibril_data);
+		fibril_teardown(__tcb_get()->fibril_data, false);
 	}
 	
Index: uspace/lib/c/generic/thread.c
===================================================================
--- uspace/lib/c/generic/thread.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/c/generic/thread.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -88,5 +88,5 @@
 #endif
 	
-	fibril_teardown(fibril);
+	fibril_teardown(fibril, false);
 	
 	thread_exit(0);
Index: uspace/lib/c/include/fibril.h
===================================================================
--- uspace/lib/c/include/fibril.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/c/include/fibril.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -94,5 +94,5 @@
 extern void fibril_destroy(fid_t fid);
 extern fibril_t *fibril_setup(void);
-extern void fibril_teardown(fibril_t *f);
+extern void fibril_teardown(fibril_t *f, bool locked);
 extern int fibril_switch(fibril_switch_type_t stype);
 extern void fibril_add_ready(fid_t fid);
Index: uspace/lib/c/include/ieee80211/ieee80211.h
===================================================================
--- uspace/lib/c/include/ieee80211/ieee80211.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/c/include/ieee80211/ieee80211.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ *  @{
+ */
+
+/** @file ieee80211.h
+ *  IEEE 802.11 common definitions.
+ */
+
+#ifndef LIBC_IEEE80211_H_
+#define LIBC_IEEE80211_H_
+
+#include <malloc.h>
+#include <adt/list.h>
+#include <nic/nic.h>
+#include <time.h>
+
+/** Max length of scan results array. */
+#define IEEE80211_MAX_RESULTS_LENGTH  32
+
+/** Max SSID length including null character. */
+#define IEEE80211_MAX_SSID_LENGTH  35
+
+/** WiFi security authentication method indicator. */
+typedef enum {
+	IEEE80211_SECURITY_OPEN,
+	IEEE80211_SECURITY_WEP,
+	IEEE80211_SECURITY_WPA,
+	IEEE80211_SECURITY_WPA2
+} ieee80211_security_type_t;
+
+/** WiFi security suite indicator. */
+typedef enum {
+	IEEE80211_SECURITY_SUITE_WEP40,
+	IEEE80211_SECURITY_SUITE_WEP104,
+	IEEE80211_SECURITY_SUITE_CCMP,
+	IEEE80211_SECURITY_SUITE_TKIP
+} ieee80211_security_suite_t;
+
+/** WiFi security authentication method indicator. */
+typedef enum {
+	IEEE80211_SECURITY_AUTH_PSK,
+	IEEE80211_SECURITY_AUTH_8021X
+} ieee80211_security_auth_t;
+
+/** Structure for indicating security network security settings. */
+typedef struct {
+	int type;
+	int group_alg;
+	int pair_alg;
+	int auth;
+} ieee80211_security_t;
+
+/** Structure with scan result info. */
+typedef struct {
+	nic_address_t bssid;
+	char ssid[IEEE80211_MAX_SSID_LENGTH];
+	uint8_t channel;
+	ieee80211_security_t security;
+} ieee80211_scan_result_t;
+
+/** Array of scan results info. */
+typedef struct {
+	uint8_t length;
+	ieee80211_scan_result_t results[IEEE80211_MAX_RESULTS_LENGTH];
+} ieee80211_scan_results_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/inet/dhcp.h
===================================================================
--- uspace/lib/c/include/inet/dhcp.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/c/include/inet/dhcp.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -41,4 +41,5 @@
 extern int dhcp_link_add(sysarg_t);
 extern int dhcp_link_remove(sysarg_t);
+extern int dhcp_discover(sysarg_t);
 
 #endif
Index: uspace/lib/c/include/ipc/dev_iface.h
===================================================================
--- uspace/lib/c/include/ipc/dev_iface.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/c/include/ipc/dev_iface.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -48,4 +48,7 @@
 	/** Network interface controller interface */
 	NIC_DEV_IFACE,
+		
+	/** IEEE 802.11 interface controller interface */
+	IEEE80211_DEV_IFACE,
 	
 	/** Interface provided by any PCI device. */
Index: uspace/lib/c/include/ipc/dhcp.h
===================================================================
--- uspace/lib/c/include/ipc/dhcp.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/c/include/ipc/dhcp.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -41,5 +41,6 @@
 typedef enum {
 	DHCP_LINK_ADD = IPC_FIRST_USER_METHOD,
-	DHCP_LINK_REMOVE
+	DHCP_LINK_REMOVE,
+	DHCP_DISCOVER
 } dhcp_request_t;
 
Index: uspace/lib/c/include/malloc.h
===================================================================
--- uspace/lib/c/include/malloc.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/c/include/malloc.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -44,5 +44,6 @@
 extern void *memalign(const size_t align, const size_t size)
     __attribute__((malloc));
-extern void *realloc(const void *addr, const size_t size);
+extern void *realloc(const void *addr, const size_t size)
+    __attribute__((warn_unused_result));
 extern void free(const void *addr);
 extern void *heap_check(void);
Index: uspace/lib/c/include/mem.h
===================================================================
--- uspace/lib/c/include/mem.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/c/include/mem.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -40,9 +40,13 @@
 
 extern void *memset(void *, int, size_t)
+    __attribute__((nonnull(1)))
     ATTRIBUTE_OPTIMIZE("-fno-tree-loop-distribute-patterns");
 extern void *memcpy(void *, const void *, size_t)
+    __attribute__((nonnull(1, 2)))
     ATTRIBUTE_OPTIMIZE("-fno-tree-loop-distribute-patterns");
-extern void *memmove(void *, const void *, size_t);
-extern int memcmp(const void *, const void *, size_t);
+extern void *memmove(void *, const void *, size_t)
+    __attribute__((nonnull(1, 2)));
+extern int memcmp(const void *, const void *, size_t)
+    __attribute__((nonnull(1, 2)));
 
 #endif
Index: uspace/lib/c/include/task.h
===================================================================
--- uspace/lib/c/include/task.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/c/include/task.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -60,5 +60,6 @@
 extern int task_spawn(task_id_t *, task_wait_t *, const char *path, int,
     va_list ap);
-extern int task_spawnl(task_id_t *, task_wait_t *, const char *path, ...);
+extern int task_spawnl(task_id_t *, task_wait_t *, const char *path, ...)
+    __attribute__((sentinel));
 
 extern int task_setup_wait(task_id_t, task_wait_t *);
Index: uspace/lib/crypto/Makefile
===================================================================
--- uspace/lib/crypto/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/crypto/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2015 Jan Kolarik
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+LIBRARY = libcrypto
+
+SOURCES = \
+	crypto.c \
+	aes.c \
+	rc4.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/lib/crypto/aes.c
===================================================================
--- uspace/lib/crypto/aes.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/crypto/aes.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,551 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file aes.c
+ *
+ * Implementation of AES-128 symmetric cipher cryptographic algorithm.
+ *
+ * Based on FIPS 197.
+ */
+
+#include <stdbool.h>
+#include <errno.h>
+#include <mem.h>
+#include "crypto.h"
+
+/* Number of elements in rows/columns in AES arrays. */
+#define ELEMS  4
+
+/* Number of elements (words) in cipher. */
+#define CIPHER_ELEMS  4
+
+/* Length of AES block. */
+#define BLOCK_LEN  16
+
+/* Number of iterations in AES algorithm. */
+#define ROUNDS  10
+
+/** Irreducible polynomial used in AES algorithm.
+ *
+ * NOTE: x^8 + x^4 + x^3 + x + 1.
+ *
+ */
+#define AES_IP  0x1b
+
+/** Precomputed values for AES sub_byte transformation. */
+static const uint8_t sbox[BLOCK_LEN][BLOCK_LEN] = {
+	{
+		0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
+		0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76
+	},
+	{
+		0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+		0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0
+	},
+	{
+		0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+		0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15
+	},
+	{
+		0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
+		0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75
+	},
+	{
+		0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+		0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84
+	},
+	{
+		0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
+		0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf
+	},
+	{
+		0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+		0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8
+	},
+	{
+		0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+		0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2
+	},
+	{
+		0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+		0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73
+	},
+	{
+		0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
+		0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb
+	},
+	{
+		0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+		0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79
+	},
+	{
+		0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
+		0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08
+	},
+	{
+		0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
+		0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a
+	},
+	{
+		0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+		0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e
+	},
+	{
+		0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
+		0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf
+	},
+	{
+		0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
+		0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+	}
+};
+
+/** Precomputed values for AES inv_sub_byte transformation. */
+static uint8_t inv_sbox[BLOCK_LEN][BLOCK_LEN] = {
+	{
+		0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
+		0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
+	},
+	{
+		0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
+		0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
+	},
+	{
+		0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
+		0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
+	},
+	{
+		0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
+		0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
+	},
+	{
+		0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
+		0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
+	},
+	{
+		0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
+		0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
+	},
+	{
+		0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
+		0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
+	},
+	{
+		0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
+		0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
+	},
+	{
+		0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
+		0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
+	},
+	{
+		0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
+		0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
+	},
+	{
+		0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
+		0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
+	},
+	{
+		0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
+		0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
+	},
+	{
+		0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
+		0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
+	},
+	{
+		0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
+		0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
+	},
+	{
+		0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
+		0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
+	},
+	{
+		0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
+		0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+	}
+};
+
+/** Precomputed values of powers of 2 in GF(2^8) left shifted by 24b. */
+static const uint32_t r_con_array[] = {
+	0x01000000, 0x02000000, 0x04000000, 0x08000000,
+	0x10000000, 0x20000000, 0x40000000, 0x80000000,
+	0x1b000000, 0x36000000
+};
+
+/** Perform substitution transformation on given byte.
+ *
+ * @param byte Input byte.
+ * @param inv  Flag indicating whether to use inverse table.
+ *
+ * @return Substituted value.
+ *
+ */
+static uint8_t sub_byte(uint8_t byte, bool inv)
+{
+	uint8_t i = byte >> 4;
+	uint8_t j = byte & 0xF;
+	
+	if (!inv)
+		return sbox[i][j];
+	
+	return inv_sbox[i][j];
+}
+
+/** Perform substitution transformation on state table.
+ *
+ * @param state State table to be modified.
+ * @param inv   Flag indicating whether to use inverse table.
+ *
+ */
+static void sub_bytes(uint8_t state[ELEMS][ELEMS], bool inv)
+{
+	uint8_t val;
+	
+	for (size_t i = 0; i < ELEMS; i++) {
+		for (size_t j = 0; j < ELEMS; j++) {
+			val = state[i][j];
+			state[i][j] = sub_byte(val, inv);
+		}
+	}
+}
+
+/** Perform shift rows transformation on state table.
+ *
+ * @param state State table to be modified.
+ *
+ */
+static void shift_rows(uint8_t state[ELEMS][ELEMS])
+{
+	uint8_t temp[ELEMS];
+	
+	for (size_t i = 1; i < ELEMS; i++) {
+		memcpy(temp, state[i], i);
+		memcpy(state[i], state[i] + i, ELEMS - i);
+		memcpy(state[i] + ELEMS - i, temp, i);
+	}
+}
+
+/** Perform inverted shift rows transformation on state table.
+ *
+ * @param state State table to be modified.
+ *
+ */
+static void inv_shift_rows(uint8_t state[ELEMS][ELEMS])
+{
+	uint8_t temp[ELEMS];
+	
+	for (size_t i = 1; i < ELEMS; i++) {
+		memcpy(temp, state[i], ELEMS - i);
+		memcpy(state[i], state[i] + ELEMS - i, i);
+		memcpy(state[i] + i, temp, ELEMS - i);
+	}
+}
+
+/** Multiplication in GF(2^8).
+ *
+ * @param x First factor.
+ * @param y Second factor.
+ *
+ * @return Multiplication of given factors in GF(2^8).
+ *
+ */
+static uint8_t galois_mult(uint8_t x, uint8_t y)
+{
+	uint8_t result = 0;
+	uint8_t f_bith;
+	
+	for (size_t i = 0; i < 8; i++) {
+		if (y & 1)
+			result ^= x;
+		
+		f_bith = (x & 0x80);
+		x <<= 1;
+		
+		if (f_bith)
+			x ^= AES_IP;
+		
+		y >>= 1;
+	}
+	
+	return result;
+}
+
+/** Perform mix columns transformation on state table.
+ *
+ * @param state State table to be modified.
+ *
+ */
+static void mix_columns(uint8_t state[ELEMS][ELEMS])
+{
+	uint8_t orig_state[ELEMS][ELEMS];
+	memcpy(orig_state, state, BLOCK_LEN);
+	
+	for (size_t j = 0; j < ELEMS; j++) {
+		state[0][j] =
+		    galois_mult(0x2, orig_state[0][j]) ^
+		    galois_mult(0x3, orig_state[1][j]) ^
+		    orig_state[2][j] ^
+		    orig_state[3][j];
+		state[1][j] =
+		    orig_state[0][j] ^
+		    galois_mult(0x2, orig_state[1][j]) ^
+		    galois_mult(0x3, orig_state[2][j]) ^
+		    orig_state[3][j];
+		state[2][j] =
+		    orig_state[0][j] ^
+		    orig_state[1][j] ^
+		    galois_mult(0x2, orig_state[2][j]) ^
+		    galois_mult(0x3, orig_state[3][j]);
+		state[3][j] =
+		    galois_mult(0x3, orig_state[0][j]) ^
+		    orig_state[1][j] ^
+		    orig_state[2][j] ^
+		    galois_mult(0x2, orig_state[3][j]);
+	}
+}
+
+/** Perform inverted mix columns transformation on state table.
+ *
+ * @param state State table to be modified.
+ *
+ */
+static void inv_mix_columns(uint8_t state[ELEMS][ELEMS])
+{
+	uint8_t orig_state[ELEMS][ELEMS];
+	memcpy(orig_state, state, BLOCK_LEN);
+	
+	for (size_t j = 0; j < ELEMS; j++) {
+		state[0][j] =
+		    galois_mult(0x0e, orig_state[0][j]) ^
+		    galois_mult(0x0b, orig_state[1][j]) ^
+		    galois_mult(0x0d, orig_state[2][j]) ^
+		    galois_mult(0x09, orig_state[3][j]);
+		state[1][j] =
+		    galois_mult(0x09, orig_state[0][j]) ^
+		    galois_mult(0x0e, orig_state[1][j]) ^
+		    galois_mult(0x0b, orig_state[2][j]) ^
+		    galois_mult(0x0d, orig_state[3][j]);
+		state[2][j] =
+		    galois_mult(0x0d, orig_state[0][j]) ^
+		    galois_mult(0x09, orig_state[1][j]) ^
+		    galois_mult(0x0e, orig_state[2][j]) ^
+		    galois_mult(0x0b, orig_state[3][j]);
+		state[3][j] =
+		    galois_mult(0x0b, orig_state[0][j]) ^
+		    galois_mult(0x0d, orig_state[1][j]) ^
+		    galois_mult(0x09, orig_state[2][j]) ^
+		    galois_mult(0x0e, orig_state[3][j]);
+	}
+}
+
+/** Perform round key transformation on state table.
+ *
+ * @param state     State table to be modified.
+ * @param round_key Round key to be applied on state table.
+ *
+ */
+static void add_round_key(uint8_t state[ELEMS][ELEMS], uint32_t *round_key)
+{
+	uint8_t byte_round;
+	uint8_t shift;
+	uint32_t mask = 0xff;
+	
+	for (size_t j = 0; j < ELEMS; j++) {
+		for (size_t i = 0; i < ELEMS; i++) {
+			shift = 24 - 8 * i;
+			byte_round = (round_key[j] & (mask << shift)) >> shift;
+			state[i][j] = state[i][j] ^ byte_round;
+		}
+	}
+}
+
+/** Perform substitution transformation on given word.
+ *
+ * @param byte Input word.
+ *
+ * @return Substituted word.
+ *
+ */
+static uint32_t sub_word(uint32_t word)
+{
+	uint32_t temp = word;
+	uint8_t *start = (uint8_t *) &temp;
+	
+	for (size_t i = 0; i < 4; i++)
+		*(start + i) = sub_byte(*(start + i), false);
+	
+	return temp;
+}
+
+/** Perform left rotation by one byte on given word.
+ *
+ * @param byte Input word.
+ *
+ * @return Rotated word.
+ *
+ */
+static uint32_t rot_word(uint32_t word)
+{
+	return (word << 8 | word >> 24);
+}
+
+/** Key expansion procedure for AES algorithm.
+ *
+ * @param key     Input key.
+ * @param key_exp Result key expansion.
+ *
+ */
+static void key_expansion(uint8_t *key, uint32_t *key_exp)
+{
+	uint32_t temp;
+	
+	for (size_t i = 0; i < CIPHER_ELEMS; i++) {
+		key_exp[i] =
+		    (key[4 * i] << 24) +
+		    (key[4 * i + 1] << 16) +
+		    (key[4 * i + 2] << 8) +
+		    (key[4 * i + 3]);
+	}
+	
+	for (size_t i = CIPHER_ELEMS; i < ELEMS * (ROUNDS + 1); i++) {
+		temp = key_exp[i - 1];
+		
+		if ((i % CIPHER_ELEMS) == 0) {
+			temp = sub_word(rot_word(temp)) ^
+			    r_con_array[i / CIPHER_ELEMS - 1];
+		}
+		
+		key_exp[i] = key_exp[i - CIPHER_ELEMS] ^ temp;
+	}
+}
+
+/** AES-128 encryption algorithm.
+ *
+ * @param key    Input key.
+ * @param input  Input data sequence to be encrypted.
+ * @param output Encrypted data sequence.
+ *
+ * @return EINVAL when input or key not specified,
+ *         ENOMEM when pointer for output is not allocated,
+ *         otherwise EOK.
+ *
+ */
+int aes_encrypt(uint8_t *key, uint8_t *input, uint8_t *output)
+{
+	if ((!key) || (!input))
+		return EINVAL;
+	
+	if (!output)
+		return ENOMEM;
+	
+	/* Create key expansion. */
+	uint32_t key_exp[ELEMS * (ROUNDS + 1)];
+	key_expansion(key, key_exp);
+	
+	/* Copy input into state array. */
+	uint8_t state[ELEMS][ELEMS];
+	for (size_t i = 0; i < ELEMS; i++) {
+		for (size_t j = 0; j < ELEMS; j++)
+			state[i][j] = input[i + ELEMS * j];
+	}
+	
+	/* Processing loop. */
+	add_round_key(state, key_exp);
+	
+	for (size_t k = 1; k <= ROUNDS; k++) {
+		sub_bytes(state, false);
+		shift_rows(state);
+		
+		if (k < ROUNDS)
+			mix_columns(state);
+		
+		add_round_key(state, key_exp + k * ELEMS);
+	}
+	
+	/* Copy state array into output. */
+	for (size_t i = 0; i < ELEMS; i++) {
+		for (size_t j = 0; j < ELEMS; j++)
+			output[i + j * ELEMS] = state[i][j];
+	}
+	
+	return EOK;
+}
+
+/** AES-128 decryption algorithm.
+ *
+ * @param key    Input key.
+ * @param input  Input data sequence to be decrypted.
+ * @param output Decrypted data sequence.
+ *
+ * @return EINVAL when input or key not specified,
+ *         ENOMEM when pointer for output is not allocated,
+ *         otherwise EOK.
+ *
+ */
+int aes_decrypt(uint8_t *key, uint8_t *input, uint8_t *output)
+{
+	if ((!key) || (!input))
+		return EINVAL;
+	
+	if (!output)
+		return ENOMEM;
+	
+	/* Create key expansion. */
+	uint32_t key_exp[ELEMS * (ROUNDS + 1)];
+	key_expansion(key, key_exp);
+	
+	/* Copy input into state array. */
+	uint8_t state[ELEMS][ELEMS];
+	for (size_t i = 0; i < ELEMS; i++) {
+		for (size_t j = 0; j < ELEMS; j++)
+			state[i][j] = input[i + ELEMS * j];
+	}
+	
+	/* Processing loop. */
+	add_round_key(state, key_exp + ROUNDS * ELEMS);
+	
+	for (int k = ROUNDS - 1; k >= 0; k--) {
+		inv_shift_rows(state);
+		sub_bytes(state, true);
+		add_round_key(state, key_exp + k * ELEMS);
+		
+		if (k > 0)
+			inv_mix_columns(state);
+	}
+	
+	/* Copy state array into output. */
+	for (size_t i = 0; i < ELEMS; i++) {
+		for (size_t j = 0; j < ELEMS; j++)
+			output[i + j * ELEMS] = state[i][j];
+	}
+	
+	return EOK;
+}
Index: uspace/lib/crypto/crypto.c
===================================================================
--- uspace/lib/crypto/crypto.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/crypto/crypto.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file crypto.c
+ *
+ * Cryptographic functions library.
+ */
+
+#include <unistd.h>
+#include <str.h>
+#include <macros.h>
+#include <errno.h>
+#include <byteorder.h>
+#include "crypto.h"
+
+/** Hash function procedure definition. */
+typedef void (*hash_fnc_t)(uint32_t *, uint32_t *);
+
+/** Length of HMAC block. */
+#define HMAC_BLOCK_LENGTH  64
+
+/** Ceiling for uint32_t. */
+#define ceil_uint32(val) \
+	(((val) - (uint32_t) (val)) > 0 ? \
+	(uint32_t) ((val) + 1) : (uint32_t) (val))
+
+/** Floor for uint32_t. */
+#define floor_uint32(val) \
+	(((val) - (uint32_t) (val)) < 0 ? \
+	(uint32_t) ((val) - 1) : (uint32_t) (val))
+
+/** Pick value at specified index from array or zero if out of bounds. */
+#define get_at(input, size, i) \
+	((i) < (size) ? (input[i]) : 0)
+
+/** Init values used in SHA1 and MD5 functions. */
+static const uint32_t hash_init[] = {
+	0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0
+};
+
+/** Shift amount array for MD5 algorithm. */
+static const uint32_t md5_shift[] = {
+	7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,
+	5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,
+	4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,
+	6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21
+};
+
+/** Substitution box for MD5 algorithm. */
+static const uint32_t md5_sbox[] = {
+	0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+	0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+	0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+	0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+	0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+	0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+	0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+	0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+	0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+	0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+	0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
+	0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+	0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+	0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+	0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+	0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+};
+
+/** Working procedure of MD5 cryptographic hash function.
+ *
+ * @param h         Working array with interim hash parts values.
+ * @param sched_arr Input array with scheduled values from input string.
+ *
+ */
+static void md5_proc(uint32_t *h, uint32_t *sched_arr)
+{
+	uint32_t f, g, temp;
+	uint32_t w[HASH_MD5 / 4];
+	
+	memcpy(w, h, (HASH_MD5 / 4) * sizeof(uint32_t));
+	
+	for (size_t k = 0; k < 64; k++) {
+		if (k < 16) {
+			f = (w[1] & w[2]) | (~w[1] & w[3]);
+			g = k;
+		} else if ((k >= 16) && (k < 32)) {
+			f = (w[1] & w[3]) | (w[2] & ~w[3]);
+			g = (5 * k + 1) % 16;
+		} else if ((k >= 32) && (k < 48)) {
+			f = w[1] ^ w[2] ^ w[3];
+			g = (3 * k + 5) % 16;
+		} else {
+			f = w[2] ^ (w[1] | ~w[3]);
+			g = 7 * k % 16;
+		}
+		
+		temp = w[3];
+		w[3] = w[2];
+		w[2] = w[1];
+		w[1] += rotl_uint32(w[0] + f + md5_sbox[k] +
+		    uint32_t_byteorder_swap(sched_arr[g]),
+		    md5_shift[k]);
+		w[0] = temp;
+	}
+	
+	for (uint8_t k = 0; k < HASH_MD5 / 4; k++)
+		h[k] += w[k];
+}
+
+/** Working procedure of SHA-1 cryptographic hash function.
+ *
+ * @param h         Working array with interim hash parts values.
+ * @param sched_arr Input array with scheduled values from input string.
+ *
+ */
+static void sha1_proc(uint32_t *h, uint32_t *sched_arr)
+{
+	uint32_t f, cf, temp;
+	uint32_t w[HASH_SHA1 / 4];
+	
+	for (size_t k = 16; k < 80; k++) {
+		sched_arr[k] = rotl_uint32(
+		    sched_arr[k-3] ^
+		    sched_arr[k-8] ^
+		    sched_arr[k-14] ^
+		    sched_arr[k-16],
+		    1);
+	}
+	
+	memcpy(w, h, (HASH_SHA1 / 4) * sizeof(uint32_t));
+	
+	for (size_t k = 0; k < 80; k++) {
+		if (k < 20) {
+			f = (w[1] & w[2]) | (~w[1] & w[3]);
+			cf = 0x5A827999;
+		} else if ((k >= 20) && (k < 40)) {
+			f = w[1] ^ w[2] ^ w[3];
+			cf = 0x6ed9eba1;
+		} else if ((k >= 40) && (k < 60)) {
+			f = (w[1] & w[2]) | (w[1] & w[3]) | (w[2] & w[3]);
+			cf = 0x8f1bbcdc;
+		} else {
+			f = w[1] ^ w[2] ^ w[3];
+			cf = 0xca62c1d6;
+		}
+		
+		temp = rotl_uint32(w[0], 5) + f + w[4] + cf + sched_arr[k];
+		
+		w[4] = w[3];
+		w[3] = w[2];
+		w[2] = rotl_uint32(w[1], 30);
+		w[1] = w[0];
+		w[0] = temp;
+	}
+	
+	for (uint8_t k = 0; k < HASH_SHA1 / 4; k++)
+		h[k] += w[k];
+}
+
+/** Create hash based on selected algorithm.
+ *
+ * @param input      Input message byte sequence.
+ * @param input_size Size of message sequence.
+ * @param output     Result hash byte sequence.
+ * @param hash_sel   Hash function selector.
+ *
+ * @return EINVAL when input not specified,
+ *         ENOMEM when pointer for output hash result
+ *         is not allocated, otherwise EOK.
+ *
+ */
+int create_hash(uint8_t *input, size_t input_size, uint8_t *output,
+    hash_func_t hash_sel)
+{
+	if (!input)
+		return EINVAL;
+	
+	if (!output)
+		return ENOMEM;
+	
+	hash_fnc_t hash_func = (hash_sel == HASH_MD5) ? md5_proc : sha1_proc;
+	
+	/* Prepare scheduled input. */
+	uint8_t work_input[input_size + 1];
+	memcpy(work_input, input, input_size);
+	work_input[input_size] = 0x80;
+	
+	// FIXME: double?
+	size_t blocks = ceil_uint32((((double) input_size + 1) / 4 + 2) / 16);
+	uint32_t work_arr[blocks * 16];
+	for (size_t i = 0; i < blocks; i++) {
+		for (size_t j = 0; j < 16; j++) {
+			work_arr[i*16 + j] =
+			    (get_at(work_input, input_size + 1, i * 64 + j * 4) << 24) |
+			    (get_at(work_input, input_size + 1, i * 64 + j * 4 + 1) << 16) |
+			    (get_at(work_input, input_size + 1, i * 64 + j * 4 + 2) << 8) |
+			    get_at(work_input, input_size + 1, i * 64 + j * 4 + 3);
+		}
+	}
+	
+	uint64_t bits_size = (uint64_t) (input_size * 8);
+	if (hash_sel == HASH_MD5)
+		bits_size = uint64_t_byteorder_swap(bits_size);
+	
+	work_arr[(blocks - 1) * 16 + 14] = bits_size >> 32;
+	work_arr[(blocks - 1) * 16 + 15] = bits_size & 0xffffffff;
+	
+	/* Hash computation. */
+	uint32_t h[hash_sel / 4];
+	memcpy(h, hash_init, (hash_sel / 4) * sizeof(uint32_t));
+	uint32_t sched_arr[80];
+	for (size_t i = 0; i < blocks; i++) {
+		for (size_t k = 0; k < 16; k++)
+			sched_arr[k] = work_arr[i * 16 + k];
+		
+		hash_func(h, sched_arr);
+	}
+	
+	/* Copy hash parts into final result. */
+	for (size_t i = 0; i < hash_sel / 4; i++) {
+		if (hash_sel == HASH_SHA1)
+			h[i] = uint32_t_byteorder_swap(h[i]);
+		
+		memcpy(output + i * sizeof(uint32_t), &h[i], sizeof(uint32_t));
+	}
+	
+	return EOK;
+}
+
+/** Hash-based message authentication code.
+ *
+ * @param key      Cryptographic key sequence.
+ * @param key_size Size of key sequence.
+ * @param msg      Message sequence.
+ * @param msg_size Size of message sequence.
+ * @param hash     Output parameter for result hash.
+ * @param hash_sel Hash function selector.
+ *
+ * @return EINVAL when key or message not specified,
+ *         ENOMEM when pointer for output hash result
+ *         is not allocated, otherwise EOK.
+ *
+ */
+int hmac(uint8_t *key, size_t key_size, uint8_t *msg, size_t msg_size, 
+    uint8_t *hash, hash_func_t hash_sel)
+{
+	if ((!key) || (!msg))
+		return EINVAL;
+	
+	if (!hash)
+		return ENOMEM;
+	
+	uint8_t work_key[HMAC_BLOCK_LENGTH];
+	uint8_t o_key_pad[HMAC_BLOCK_LENGTH];
+	uint8_t i_key_pad[HMAC_BLOCK_LENGTH];
+	uint8_t temp_hash[hash_sel];
+	memset(work_key, 0, HMAC_BLOCK_LENGTH);
+	
+	if(key_size > HMAC_BLOCK_LENGTH)
+		create_hash(key, key_size, work_key, hash_sel);
+	else
+		memcpy(work_key, key, key_size);
+	
+	for (size_t i = 0; i < HMAC_BLOCK_LENGTH; i++) {
+		o_key_pad[i] = work_key[i] ^ 0x5c;
+		i_key_pad[i] = work_key[i] ^ 0x36;
+	}
+	
+	uint8_t temp_work[HMAC_BLOCK_LENGTH + max(msg_size, hash_sel)];
+	memcpy(temp_work, i_key_pad, HMAC_BLOCK_LENGTH);
+	memcpy(temp_work + HMAC_BLOCK_LENGTH, msg, msg_size);
+	
+	create_hash(temp_work, HMAC_BLOCK_LENGTH + msg_size, temp_hash,
+	    hash_sel);
+	
+	memcpy(temp_work, o_key_pad, HMAC_BLOCK_LENGTH);
+	memcpy(temp_work + HMAC_BLOCK_LENGTH, temp_hash, hash_sel);
+	
+	create_hash(temp_work, HMAC_BLOCK_LENGTH + hash_sel, hash, hash_sel);
+	
+	return EOK;
+}
+
+/** Password-Based Key Derivation Function 2.
+ *
+ * As defined in RFC 2898, using HMAC-SHA1 with 4096 iterations
+ * and 32 bytes key result used for WPA/WPA2.
+ *
+ * @param pass      Password sequence.
+ * @param pass_size Password sequence length.
+ * @param salt      Salt sequence to be used with password.
+ * @param salt_size Salt sequence length.
+ * @param hash      Output parameter for result hash (32 byte value).
+ *
+ * @return EINVAL when pass or salt not specified,
+ *         ENOMEM when pointer for output hash result
+ *         is not allocated, otherwise EOK.
+ *
+ */
+int pbkdf2(uint8_t *pass, size_t pass_size, uint8_t *salt, size_t salt_size,
+    uint8_t *hash)
+{
+	if ((!pass) || (!salt))
+		return EINVAL;
+	
+	if (!hash)
+		return ENOMEM;
+	
+	uint8_t work_salt[salt_size + 4];
+	memcpy(work_salt, salt, salt_size);
+	uint8_t work_hmac[HASH_SHA1];
+	uint8_t temp_hmac[HASH_SHA1];
+	uint8_t xor_hmac[HASH_SHA1];
+	uint8_t temp_hash[HASH_SHA1 * 2];
+	
+	for (size_t i = 0; i < 2; i++) {
+		uint32_t be_i = host2uint32_t_be(i + 1);
+		
+		memcpy(work_salt + salt_size, &be_i, 4);
+		hmac(pass, pass_size, work_salt, salt_size + 4,
+		    work_hmac, HASH_SHA1);
+		memcpy(xor_hmac, work_hmac, HASH_SHA1);
+		
+		for (size_t k = 1; k < 4096; k++) {
+			memcpy(temp_hmac, work_hmac, HASH_SHA1);
+			hmac(pass, pass_size, temp_hmac, HASH_SHA1,
+			    work_hmac, HASH_SHA1);
+			
+			for (size_t t = 0; t < HASH_SHA1; t++)
+				xor_hmac[t] ^= work_hmac[t];
+		}
+		
+		memcpy(temp_hash + i * HASH_SHA1, xor_hmac, HASH_SHA1);
+	}
+	
+	memcpy(hash, temp_hash, PBKDF2_KEY_LENGTH);
+	
+	return EOK;
+}
Index: uspace/lib/crypto/crypto.h
===================================================================
--- uspace/lib/crypto/crypto.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/crypto/crypto.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LIBCRYPTO_H
+#define LIBCRYPTO_H
+
+#include <sys/types.h>
+
+#define AES_CIPHER_LENGTH  16
+#define PBKDF2_KEY_LENGTH  32
+
+/* Left rotation for uint32_t. */
+#define rotl_uint32(val, shift) \
+	(((val) << shift) | ((val) >> (32 - shift)))
+
+/* Right rotation for uint32_t. */
+#define rotr_uint32(val, shift) \
+	(((val) >> shift) | ((val) << (32 - shift)))
+
+/** Hash function selector and also result hash length indicator. */
+typedef enum {
+	HASH_MD5 =  16,
+	HASH_SHA1 = 20
+} hash_func_t;
+
+extern int rc4(uint8_t *, size_t, uint8_t *, size_t, size_t, uint8_t *);
+extern int aes_encrypt(uint8_t *, uint8_t *, uint8_t *);
+extern int aes_decrypt(uint8_t *, uint8_t *, uint8_t *);
+extern int create_hash(uint8_t *, size_t, uint8_t *, hash_func_t);
+extern int hmac(uint8_t *, size_t, uint8_t *, size_t, uint8_t *, hash_func_t);
+extern int pbkdf2(uint8_t *, size_t, uint8_t *, size_t, uint8_t *);
+
+#endif
Index: uspace/lib/crypto/rc4.c
===================================================================
--- uspace/lib/crypto/rc4.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/crypto/rc4.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file rc4.c
+ *
+ * Implementation of ARC4 symmetric cipher cryptographic algorithm.
+ *
+ */
+
+#include <errno.h>
+#include <mem.h>
+#include "crypto.h"
+
+/* Sbox table size. */
+#define SBOX_SIZE  256
+
+/** Swap two values in sbox.
+ *
+ * @param i    First index of value in sbox to be swapped.
+ * @param j    Second index of value in sbox to be swapped.
+ * @param sbox Sbox to be modified.
+ *
+ */
+static void swap(size_t i, size_t j, uint8_t *sbox)
+{
+	uint8_t temp = sbox[i];
+	sbox[i] = sbox[j];
+	sbox[j] = temp;
+}
+
+/** Sbox initialization procedure.
+ *
+ * @param key      Input key.
+ * @param key_size Size of key sequence.
+ * @param sbox     Place for result sbox.
+ *
+ */
+static void create_sbox(uint8_t *key, size_t key_size, uint8_t *sbox)
+{
+	for (size_t i = 0; i < SBOX_SIZE; i++)
+		sbox[i] = i;
+	
+	uint8_t j = 0;
+	for (size_t i = 0; i < SBOX_SIZE; i++) {
+		j = j + sbox[i] + key[i % key_size];
+		swap(i, j, sbox);
+	}
+}
+
+/** ARC4 encryption/decryption algorithm.
+ *
+ * @param key        Input key.
+ * @param key_size   Size of key sequence.
+ * @param input      Input data sequence to be processed.
+ * @param input_size Size of input data sequence.
+ * @param skip       Number of bytes to be skipped from
+ *                   the beginning of key stream.
+ * @param output     Result data sequence.
+ *
+ * @return EINVAL when input or key not specified,
+ *         ENOMEM when pointer for output is not allocated,
+ *         otherwise EOK.
+ *
+ */
+int rc4(uint8_t *key, size_t key_size, uint8_t *input, size_t input_size,
+    size_t skip, uint8_t *output)
+{
+	if ((!key) || (!input))
+		return EINVAL;
+	
+	if (!output)
+		return ENOMEM;
+	
+	/* Initialize sbox. */
+	uint8_t sbox[SBOX_SIZE];
+	create_sbox(key, key_size, sbox);
+	
+	/* Skip first x bytes. */
+	uint8_t i = 0;
+	uint8_t j = 0;
+	for (size_t k = 0; k < skip; k++) {
+		i = i + 1;
+		j = j + sbox[i];
+		swap(i, j, sbox);
+	}
+	
+	/* Processing loop. */
+	uint8_t val;
+	for (size_t k = 0; k < input_size; k++) {
+		i = i + 1;
+		j = j + sbox[i];
+		swap(i, j, sbox);
+		val = sbox[sbox[i] + sbox[j]];
+		output[k] = val ^ input[k];
+	}
+	
+	return EOK;
+}
Index: uspace/lib/drv/Makefile
===================================================================
--- uspace/lib/drv/Makefile	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/drv/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -48,4 +48,5 @@
 	generic/remote_char_dev.c \
 	generic/remote_nic.c \
+	generic/remote_ieee80211.c \
 	generic/remote_usb.c \
 	generic/remote_pci.c \
Index: uspace/lib/drv/generic/dev_iface.c
===================================================================
--- uspace/lib/drv/generic/dev_iface.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/drv/generic/dev_iface.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -46,4 +46,5 @@
 #include "remote_battery_dev.h"
 #include "remote_nic.h"
+#include "remote_ieee80211.h"
 #include "remote_usb.h"
 #include "remote_usbhc.h"
@@ -62,4 +63,5 @@
 		[CHAR_DEV_IFACE] = &remote_char_dev_iface,
 		[NIC_DEV_IFACE] = &remote_nic_iface,
+		[IEEE80211_DEV_IFACE] = &remote_ieee80211_iface,
 		[PCI_DEV_IFACE] = &remote_pci_iface,
 		[USB_DEV_IFACE] = &remote_usb_iface,
Index: uspace/lib/drv/generic/interrupt.c
===================================================================
--- uspace/lib/drv/generic/interrupt.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/drv/generic/interrupt.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -44,17 +44,4 @@
 #include "private/driver.h"
 
-static irq_cmd_t default_cmds[] = {
-	{
-		.cmd = CMD_ACCEPT
-	}
-};
-
-static const irq_code_t default_pseudocode = {
-	0,
-	NULL,
-	ARRAY_SIZE(default_cmds),
-	default_cmds
-};
-
 int register_interrupt_handler(ddf_dev_t *dev, int irq,
     interrupt_handler_t *handler, const irq_code_t *pseudocode)
Index: uspace/lib/drv/generic/private/remote_ieee80211.h
===================================================================
--- uspace/lib/drv/generic/private/remote_ieee80211.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/drv/generic/private/remote_ieee80211.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libdrv
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDRV_REMOTE_IEEE80211_H_
+#define LIBDRV_REMOTE_IEEE80211_H_
+
+extern remote_iface_t remote_ieee80211_iface;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/drv/generic/remote_ieee80211.c
===================================================================
--- uspace/lib/drv/generic/remote_ieee80211.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/drv/generic/remote_ieee80211.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libdrv
+ * @{
+ */
+/**
+ * @file
+ * @brief Driver-side RPC skeletons for IEEE 802.11 interface
+ */
+
+#include <errno.h>
+#include <macros.h>
+#include <str.h>
+#include <inet/dhcp.h>
+#include <inet/inetcfg.h>
+#include "ops/ieee80211.h"
+#include "ieee80211_iface.h"
+#include "nic_iface.h"
+
+#define MAX_STRING_SIZE  32
+
+/** IEEE 802.11 RPC functions IDs. */
+typedef enum {
+	IEEE80211_GET_SCAN_RESULTS,
+	IEEE80211_CONNECT,
+	IEEE80211_DISCONNECT
+} ieee80211_funcs_t;
+
+/** Get scan results from IEEE 802.11 device
+ *
+ * @param[in]  dev_sess Device session.
+ * @param[out] results  Structure where to put scan results.
+ *
+ * @return EOK If the operation was successfully completed,
+ *         negative error code otherwise.
+ *
+ */
+int ieee80211_get_scan_results(async_sess_t *dev_sess,
+    ieee80211_scan_results_t *results, bool now)
+{
+	assert(results);
+	
+	async_exch_t *exch = async_exchange_begin(dev_sess);
+	
+	aid_t aid = async_send_2(exch, DEV_IFACE_ID(IEEE80211_DEV_IFACE),
+	    IEEE80211_GET_SCAN_RESULTS, now, NULL);
+	int rc = async_data_read_start(exch, results,
+	    sizeof(ieee80211_scan_results_t));
+	async_exchange_end(exch);
+	
+	sysarg_t res;
+	async_wait_for(aid, &res);
+	
+	if(res != EOK)
+		return (int) res;
+	
+	return rc;
+}
+
+static bool mac_matches(uint8_t *mac1, uint8_t *mac2)
+{
+	for (size_t i = 0; i < ETH_ADDR; i++) {
+		if (mac1[i] != mac2[i])
+			return false;
+	}
+	
+	return true;
+}
+
+static sysarg_t get_link_id(uint8_t *mac)
+{
+	sysarg_t *link_list;
+	inet_link_info_t link_info;
+	size_t count;
+	
+	int rc = inetcfg_get_link_list(&link_list, &count);
+	if (rc != EOK)
+		return -1;
+	
+	for (size_t i = 0; i < count; i++) {
+		rc = inetcfg_link_get(link_list[i], &link_info);
+		if (rc != EOK)
+			return -1;
+		
+		if (mac_matches(mac, link_info.mac_addr))
+			return link_list[i];
+	}
+	
+	return -1;
+}
+
+/** Connect to specified network.
+ *
+ * @param[in] dev_sess   Device session.
+ * @param[in] ssid_start Network SSID prefix.
+ * @param[in] password   Network password (pass empty string if not needed).
+ *
+ * @return EOK If the operation was successfully completed,
+ *         negative error code otherwise.
+ *
+ */
+int ieee80211_connect(async_sess_t *dev_sess, char *ssid_start, char *password)
+{
+	assert(ssid_start);
+	
+	sysarg_t rc_orig;
+	
+	async_exch_t *exch = async_exchange_begin(dev_sess);
+	
+	aid_t aid = async_send_1(exch, DEV_IFACE_ID(IEEE80211_DEV_IFACE),
+	    IEEE80211_CONNECT, NULL);
+	
+	sysarg_t rc = async_data_write_start(exch, ssid_start,
+	    str_size(ssid_start) + 1);
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_wait_for(aid, &rc_orig);
+		
+		if (rc_orig == EOK)
+			return (int) rc;
+		
+		return (int) rc_orig;
+	}
+	
+	// FIXME: Typecasting string literal
+	if (password == NULL)
+		password = (char *) "";
+	
+	rc = async_data_write_start(exch, password, str_size(password) + 1);
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_wait_for(aid, &rc_orig);
+		
+		if (rc_orig == EOK)
+			return (int) rc;
+		
+		return (int) rc_orig;
+	}
+	
+	async_exchange_end(exch);
+	
+	async_wait_for(aid, &rc);
+	if (rc != EOK)
+		return rc;
+	
+	/* Send DHCP discover. */
+	nic_address_t wifi_mac;
+	rc = nic_get_address(dev_sess, &wifi_mac);
+	if (rc != EOK)
+		return rc;
+	
+	sysarg_t link_id = get_link_id(wifi_mac.address);
+	if (link_id == ((sysarg_t) -1))
+		return EINVAL;
+	
+	rc = dhcp_discover(link_id);
+	
+	return (int) rc;
+}
+
+/** Disconnect device from network.
+ *
+ * @param[in] dev_sess Device session.
+ *
+ * @return EOK If the operation was successfully completed,
+ *         negative error code otherwise.
+ *
+ */
+int ieee80211_disconnect(async_sess_t *dev_sess)
+{
+	async_exch_t *exch = async_exchange_begin(dev_sess);
+	int rc = async_req_1_0(exch, DEV_IFACE_ID(IEEE80211_DEV_IFACE),
+	    IEEE80211_DISCONNECT);
+	async_exchange_end(exch);
+	
+	if (rc != EOK)
+		return rc;
+	
+	nic_address_t wifi_mac;
+	rc = nic_get_address(dev_sess, &wifi_mac);
+	if (rc != EOK)
+		return rc;
+	
+	inet_link_info_t link_info;
+	inet_addr_info_t addr_info;
+	inet_sroute_info_t route_info;
+	sysarg_t *addr_list;
+	sysarg_t *route_list;
+	size_t count;
+	
+	/* Remove previous DHCP address. */
+	rc = inetcfg_get_addr_list(&addr_list, &count);
+	if (rc != EOK)
+		return rc;
+	
+	for (size_t i = 0; i < count; i++) {
+		rc = inetcfg_addr_get(addr_list[i], &addr_info);
+		if (rc != EOK)
+			return rc;
+		
+		rc = inetcfg_link_get(addr_info.ilink, &link_info);
+		if (rc != EOK)
+			return rc;
+		
+		if (mac_matches(wifi_mac.address, link_info.mac_addr)) {
+			if (str_test_prefix(addr_info.name, "dhcp")) {
+				rc = inetcfg_addr_delete(addr_list[i]);
+				if (rc != EOK)
+					return rc;
+				
+				break;
+			}
+		}
+	}
+	
+	/*
+	 * TODO: At this moment there can be only one DHCP route,
+	 * so it must be reimplemented after this limitation will be
+	 * dropped.
+	 */
+	/* Remove previous DHCP static route. */
+	rc = inetcfg_get_sroute_list(&route_list, &count);
+	if (rc != EOK)
+		return rc;
+	
+	for (size_t i = 0; i < count; i++) {
+		rc = inetcfg_sroute_get(route_list[i], &route_info);
+		if (rc != EOK)
+			return rc;
+		
+		if (str_test_prefix(route_info.name, "dhcp")) {
+			rc = inetcfg_sroute_delete(route_list[i]);
+			if (rc != EOK)
+				return rc;
+			
+			break;
+		}
+	}
+	
+	return rc;
+}
+
+static void remote_ieee80211_get_scan_results(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	ieee80211_iface_t *ieee80211_iface = (ieee80211_iface_t *) iface;
+	assert(ieee80211_iface->get_scan_results);
+	
+	ieee80211_scan_results_t scan_results;
+	memset(&scan_results, 0, sizeof(ieee80211_scan_results_t));
+	
+	bool now = IPC_GET_ARG2(*call);
+	
+	int rc = ieee80211_iface->get_scan_results(fun, &scan_results, now);
+	if (rc == EOK) {
+		ipc_callid_t data_callid;
+		size_t max_len;
+		if (!async_data_read_receive(&data_callid, &max_len)) {
+			async_answer_0(data_callid, EINVAL);
+			async_answer_0(callid, EINVAL);
+			return;
+		}
+		
+		if (max_len < sizeof(ieee80211_scan_results_t)) {
+			async_answer_0(data_callid, ELIMIT);
+			async_answer_0(callid, ELIMIT);
+			return;
+		}
+		
+		async_data_read_finalize(data_callid, &scan_results,
+		    sizeof(ieee80211_scan_results_t));
+	}
+	
+	async_answer_0(callid, rc);
+}
+
+static void remote_ieee80211_connect(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	ieee80211_iface_t *ieee80211_iface = (ieee80211_iface_t *) iface;
+	assert(ieee80211_iface->connect);
+	
+	char ssid_start[MAX_STRING_SIZE];
+	char password[MAX_STRING_SIZE];
+	
+	ipc_callid_t data_callid;
+	size_t len;
+	if (!async_data_write_receive(&data_callid, &len)) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	if (len > MAX_STRING_SIZE) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	int rc = async_data_write_finalize(data_callid, ssid_start, len);
+	if (rc != EOK) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	if (!async_data_write_receive(&data_callid, &len)) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	if (len > MAX_STRING_SIZE) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	rc = async_data_write_finalize(data_callid, password, len);
+	if (rc != EOK) {
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	rc = ieee80211_iface->connect(fun, ssid_start, password);
+	
+	async_answer_0(callid, rc);
+}
+
+static void remote_ieee80211_disconnect(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	ieee80211_iface_t *ieee80211_iface = (ieee80211_iface_t *) iface;
+	assert(ieee80211_iface->disconnect);
+	int rc = ieee80211_iface->disconnect(fun);
+	async_answer_0(callid, rc);
+}
+
+/** Remote IEEE 802.11 interface operations.
+ *
+ */
+static const remote_iface_func_ptr_t remote_ieee80211_iface_ops[] = {
+	[IEEE80211_GET_SCAN_RESULTS] = remote_ieee80211_get_scan_results,
+	[IEEE80211_CONNECT] = remote_ieee80211_connect,
+	[IEEE80211_DISCONNECT] = remote_ieee80211_disconnect
+};
+
+/** Remote IEEE 802.11 interface structure.
+ *
+ * Interface for processing request from remote
+ * clients addressed to the IEEE 802.11 interface.
+ *
+ */
+const remote_iface_t remote_ieee80211_iface = {
+	.method_count = ARRAY_SIZE(remote_ieee80211_iface_ops),
+	.methods = remote_ieee80211_iface_ops
+};
+
+/**
+ * @}
+ */
Index: uspace/lib/drv/include/ieee80211_iface.h
===================================================================
--- uspace/lib/drv/include/ieee80211_iface.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/drv/include/ieee80211_iface.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file ieee80211_iface.h
+ */
+
+#ifndef LIBDRV_IEEE80211_IFACE_H_
+#define LIBDRV_IEEE80211_IFACE_H_
+
+#include <ieee80211/ieee80211.h>
+#include <async.h>
+
+extern int ieee80211_get_scan_results(async_sess_t *,
+    ieee80211_scan_results_t *, bool);
+extern int ieee80211_connect(async_sess_t *, char *, char *);
+extern int ieee80211_disconnect(async_sess_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/drv/include/ops/ieee80211.h
===================================================================
--- uspace/lib/drv/include/ops/ieee80211.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/drv/include/ops/ieee80211.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libdrv
+ * @{
+ */
+/** @file ieee80211.h
+ * @brief IEEE 802.11 WiFi interface definition
+ */
+
+#ifndef LIBDRV_OPS_IEEE80211_H_
+#define LIBDRV_OPS_IEEE80211_H_
+
+#include <ddf/driver.h>
+#include <ieee80211/ieee80211.h>
+
+/** IEEE 802.11 interface functions definition. */
+typedef struct ieee80211_iface {
+	/** Fetch scan results from IEEE 802.11 device.
+	 *
+	 * @param fun     IEEE 802.11 function.
+	 * @param results Structure where to put scan results.
+	 * @param now     Whether to initiate scan immediately.
+	 *
+	 * @return EOK if succeed, negative error code otherwise.
+	 *
+	 */
+	int (*get_scan_results)(ddf_fun_t *, ieee80211_scan_results_t *, bool);
+	
+	/** Connect IEEE 802.11 device to specified network.
+	 *
+	 * @param fun      IEEE 802.11 function.
+	 * @param ssid     Network SSID.
+	 * @param password Network password (empty string if not needed).
+	 *
+	 * @return EOK if succeed, negative error code otherwise.
+	 *
+	 */
+	int (*connect)(ddf_fun_t *, char *, char *);
+	
+	/** Disconnect IEEE 802.11 device from network.
+	 *
+	 * @param fun IEEE 802.11 function.
+	 *
+	 * @return EOK if succeed, negative error code otherwise.
+	 *
+	 */
+	int (*disconnect)(ddf_fun_t *);
+} ieee80211_iface_t;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/ext4/libext4_directory.c
===================================================================
--- uspace/lib/ext4/libext4_directory.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/ext4/libext4_directory.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -390,13 +390,9 @@
 	    (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) {
 		int rc = ext4_directory_dx_add_entry(parent, child, name);
-		
+
 		/* Check if index is not corrupted */
-		if (rc != EXT4_ERR_BAD_DX_DIR) {
-			if (rc != EOK)
-				return rc;
-			
-			return EOK;
-		}
-		
+		if (rc != EXT4_ERR_BAD_DX_DIR)
+			return rc;
+
 		/* Needed to clear dir index flag if corrupted */
 		ext4_inode_clear_flag(parent->inode, EXT4_INODE_FLAG_INDEX);
Index: uspace/lib/ext4/libext4_extent.c
===================================================================
--- uspace/lib/ext4/libext4_extent.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/ext4/libext4_extent.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -797,4 +797,5 @@
 			if (rc != EOK) {
 				ext4_balloc_free_block(inode_ref, fblock);
+				block_put(block);
 				return rc;
 			}
Index: uspace/lib/ext4/libext4_filesystem.c
===================================================================
--- uspace/lib/ext4/libext4_filesystem.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/ext4/libext4_filesystem.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -53,33 +53,30 @@
     enum cache_mode cmode)
 {
+	ext4_superblock_t *temp_superblock = NULL;
+
 	fs->device = service_id;
-	
+
 	/* Initialize block library (4096 is size of communication channel) */
 	int rc = block_init(EXCHANGE_SERIALIZE, fs->device, 4096);
 	if (rc != EOK)
-		return rc;
-	
+		goto err;
+
 	/* Read superblock from device to memory */
-	ext4_superblock_t *temp_superblock;
 	rc = ext4_superblock_read_direct(fs->device, &temp_superblock);
-	if (rc != EOK) {
-		block_fini(fs->device);
-		return rc;
-	}
-	
+	if (rc != EOK)
+		goto err_1;
+
 	/* Read block size from superblock and check */
 	uint32_t block_size = ext4_superblock_get_block_size(temp_superblock);
 	if (block_size > EXT4_MAX_BLOCK_SIZE) {
-		block_fini(fs->device);
-		return ENOTSUP;
-	}
-	
+		rc = ENOTSUP;
+		goto err_1;
+	}
+
 	/* Initialize block caching by libblock */
 	rc = block_cache_init(service_id, block_size, 0, cmode);
-	if (rc != EOK) {
-		block_fini(fs->device);
-		return rc;
-	}
-	
+	if (rc != EOK)
+		goto err_1;
+
 	/* Compute limits for indirect block levels */
 	uint32_t block_ids_per_block = block_size / sizeof(uint32_t);
@@ -92,32 +89,37 @@
 		    fs->inode_blocks_per_level[i];
 	}
-	
+
 	/* Return loaded superblock */
 	fs->superblock = temp_superblock;
-	
+
 	uint16_t state = ext4_superblock_get_state(fs->superblock);
-	
+
 	if (((state & EXT4_SUPERBLOCK_STATE_VALID_FS) !=
 	    EXT4_SUPERBLOCK_STATE_VALID_FS) ||
 	    ((state & EXT4_SUPERBLOCK_STATE_ERROR_FS) ==
 	    EXT4_SUPERBLOCK_STATE_ERROR_FS)) {
-		block_cache_fini(fs->device);
-		block_fini(fs->device);
-		return ENOTSUP;
-	}
-	
+		rc = ENOTSUP;
+		goto err_2;
+	}
+
 	/* Mark system as mounted */
 	ext4_superblock_set_state(fs->superblock, EXT4_SUPERBLOCK_STATE_ERROR_FS);
 	rc = ext4_superblock_write_direct(fs->device, fs->superblock);
-	if (rc != EOK) {
-		block_cache_fini(fs->device);
-		block_fini(fs->device);
-		return rc;
-	}
-	
+	if (rc != EOK)
+		goto err_2;
+
 	uint16_t mnt_count = ext4_superblock_get_mount_count(fs->superblock);
 	ext4_superblock_set_mount_count(fs->superblock, mnt_count + 1);
-	
+
 	return EOK;
+
+err_2:
+	block_cache_fini(fs->device);
+err_1:
+	block_fini(fs->device);
+err:
+	if (temp_superblock)
+		ext4_superblock_release(temp_superblock);
+	return rc;
 }
 
@@ -845,6 +847,8 @@
 				
 				rc = block_put(subblock);
-				if (rc != EOK)
+				if (rc != EOK) {
+					block_put(block);
 					return rc;
+				}
 			}
 			
Index: uspace/lib/ext4/libext4_superblock.c
===================================================================
--- uspace/lib/ext4/libext4_superblock.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/ext4/libext4_superblock.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -1178,4 +1178,14 @@
 }
 
+/** Release the memory allocated for the superblock structure
+ *
+ * @param sb         Superblock to be freed
+ *
+ */
+void ext4_superblock_release(ext4_superblock_t *sb)
+{
+	free(sb);
+}
+
 /** Check sanity of the superblock.
  *
Index: uspace/lib/ext4/libext4_superblock.h
===================================================================
--- uspace/lib/ext4/libext4_superblock.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/ext4/libext4_superblock.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -145,4 +145,5 @@
 extern int ext4_superblock_read_direct(service_id_t, ext4_superblock_t **);
 extern int ext4_superblock_write_direct(service_id_t, ext4_superblock_t *);
+extern void ext4_superblock_release(ext4_superblock_t *);
 extern int ext4_superblock_check_sanity(ext4_superblock_t *);
 
Index: uspace/lib/ieee80211/Makefile
===================================================================
--- uspace/lib/ieee80211/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/ieee80211/Makefile	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,48 @@
+#
+# Copyright (c) 2015 Jan Kolarik
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+LIBRARY = libieee80211
+
+LIBS = \
+	$(LIBDRV_PREFIX)/libdrv.a \
+	$(LIBNIC_PREFIX)/libnic.a \
+	$(LIBCRYPTO_PREFIX)/libcrypto.a
+
+EXTRA_CFLAGS += \
+	-Iinclude \
+	-I$(LIBDRV_PREFIX)/include \
+	-I$(LIBNIC_PREFIX)/include \
+	-I$(LIBCRYPTO_PREFIX)
+
+SOURCES = \
+	src/ieee80211.c \
+	src/ieee80211_impl.c \
+	src/ieee80211_iface_impl.c \
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/lib/ieee80211/include/ieee80211.h
===================================================================
--- uspace/lib/ieee80211/include/ieee80211.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/ieee80211/include/ieee80211.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @addtogroup libieee80211
+ * @{
+ */
+/**
+ * @file ieee80211.h
+ * @brief Public header exposing IEEE 802.11 to drivers.
+ */
+
+#ifndef LIB_IEEE80211_H
+#define LIB_IEEE80211_H
+
+#include <ddf/driver.h>
+#include <nic.h>
+#include <ops/ieee80211.h>
+
+#define DEVICE_CATEGORY_IEEE80211  "ieee80211"
+
+struct ieee80211_dev;
+typedef struct ieee80211_dev ieee80211_dev_t;
+
+/** Initial channel frequency. */
+#define IEEE80211_FIRST_FREQ  2412
+
+/** Max supported channel frequency. */
+#define IEEE80211_MAX_FREQ  2472
+
+/** Gap between IEEE80211 channels in MHz. */
+#define IEEE80211_CHANNEL_GAP  5
+
+/** Max AMPDU factor. */
+#define IEEE80211_MAX_AMPDU_FACTOR  13
+
+/** Max authentication password length. */
+#define IEEE80211_MAX_PASSW_LEN  64
+
+/** IEEE 802.11 b/g supported data rates in units of 500 kb/s. */
+static const uint8_t ieee80211bg_data_rates[] = {
+	2, 4, 11, 12, 18, 22, 24, 36, 48, 72, 96, 108
+};
+
+/** Device operating modes. */
+typedef enum {
+	IEEE80211_OPMODE_ADHOC,
+	IEEE80211_OPMODE_MESH,
+	IEEE80211_OPMODE_AP,
+	IEEE80211_OPMODE_STATION
+} ieee80211_operating_mode_t;
+
+/** Key flags. */
+typedef enum {
+	IEEE80211_KEY_FLAG_TYPE_PAIRWISE = 0x01,
+	IEEE80211_KEY_FLAG_TYPE_GROUP = 0x02
+} ieee80211_key_flags_t;
+
+typedef enum {
+	IEEE80211_TKIP_TX_MIC_OFFSET = 16,
+	IEEE80211_TKIP_RX_MIC_OFFSET = 24
+} ieee80211_tkip_mic_offset_t;
+
+/** Key config structure. */
+typedef struct {
+	uint8_t id;
+	uint8_t flags;
+	ieee80211_security_suite_t suite;
+	uint8_t data[32];
+} ieee80211_key_config_t;
+
+/** IEEE 802.11 callback functions. */
+typedef struct {
+	/** unction that is called at device initalization.
+	 *
+	 * This should get device into running state.
+	 *
+	 * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+	 *
+	 * @return EOK if succeed, negative error code otherwise.
+	 *
+	 */
+	int (*start)(struct ieee80211_dev *);
+	
+	/** Scan neighborhood for networks.
+	 *
+	 * There should be implemented scanning of whole bandwidth.
+	 * Incoming results are processed by IEEE 802.11 framework itself.
+	 *
+	 * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+	 *
+	 * @return EOK if succeed, negative error code otherwise.
+	 *
+	 */
+	int (*scan)(struct ieee80211_dev *);
+	
+	/** Handler for TX frames to be send from device.
+	 *
+	 * This should be called for every frame that has to be send
+	 * from IEEE 802.11 device.
+	 *
+	 * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+	 * @param buffer        Buffer with data to be send.
+	 * @param buffer_size   Size of buffer.
+	 *
+	 * @return EOK if succeed, negative error code otherwise.
+	 *
+	 */
+	int (*tx_handler)(struct ieee80211_dev *, void *, size_t);
+	
+	/** Set device operating frequency to given value.
+	 *
+	 * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+	 * @param freq          New device operating frequency.
+	 *
+	 * @return EOK if succeed, negative error code otherwise.
+	 *
+	 */
+	int (*set_freq)(struct ieee80211_dev *, uint16_t);
+	
+	/** Callback to inform device about BSSID change.
+	 *
+	 * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+	 * @param connected     True if connected to new BSSID, otherwise false.
+	 *
+	 * @return EOK if succeed, negative error code otherwise.
+	 *
+	 */
+	int (*bssid_change)(struct ieee80211_dev *, bool);
+	
+	/** Callback to setup encryption key in IEEE 802.11 device.
+	 *
+	 * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+	 * @param key_conf      Key config structure.
+	 * @param insert        True to insert this key to device,
+	 *                      false to remove it.
+	 *
+	 * @return EOK if succeed, negative error code otherwise.
+	 *
+	 */
+	int (*key_config)(struct ieee80211_dev *,
+	    ieee80211_key_config_t *key_conf, bool);
+} ieee80211_ops_t;
+
+/* Initialization functions. */
+extern ieee80211_dev_t *ieee80211_device_create(void);
+extern int ieee80211_device_init(ieee80211_dev_t *, ddf_dev_t *);
+extern int ieee80211_init(ieee80211_dev_t *, ieee80211_ops_t *,
+    ieee80211_iface_t *, nic_iface_t *, ddf_dev_ops_t *);
+
+/* Getters & setters, queries & reports. */
+extern void *ieee80211_get_specific(ieee80211_dev_t *);
+extern void ieee80211_set_specific(ieee80211_dev_t *, void *);
+extern ddf_dev_t *ieee80211_get_ddf_dev(ieee80211_dev_t *);
+extern ieee80211_operating_mode_t
+    ieee80211_query_current_op_mode(ieee80211_dev_t *);
+extern uint16_t ieee80211_query_current_freq(ieee80211_dev_t *);
+extern void ieee80211_query_bssid(ieee80211_dev_t *, nic_address_t *);
+extern bool ieee80211_is_connected(ieee80211_dev_t *);
+extern void ieee80211_report_current_op_mode(ieee80211_dev_t *,
+    ieee80211_operating_mode_t);
+extern void ieee80211_report_current_freq(ieee80211_dev_t *, uint16_t);
+extern uint16_t ieee80211_get_aid(ieee80211_dev_t *);
+extern int ieee80211_get_pairwise_security(ieee80211_dev_t *);
+extern bool ieee80211_is_ready(ieee80211_dev_t *);
+extern void ieee80211_set_ready(ieee80211_dev_t *, bool);
+extern bool ieee80211_query_using_key(ieee80211_dev_t *);
+extern void ieee80211_setup_key_confirm(ieee80211_dev_t *, bool);
+
+extern bool ieee80211_is_data_frame(uint16_t);
+extern bool ieee80211_is_mgmt_frame(uint16_t);
+extern bool ieee80211_is_beacon_frame(uint16_t);
+extern bool ieee80211_is_probe_response_frame(uint16_t);
+extern bool ieee80211_is_auth_frame(uint16_t);
+extern bool ieee80211_is_assoc_response_frame(uint16_t);
+
+/* Worker functions. */
+extern int ieee80211_rx_handler(ieee80211_dev_t *, void *, size_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ieee80211/include/ieee80211_iface_impl.h
===================================================================
--- uspace/lib/ieee80211/include/ieee80211_iface_impl.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/ieee80211/include/ieee80211_iface_impl.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @addtogroup libieee80211
+ * @{
+ */
+
+/** @file ieee80211_iface_impl.h
+ *
+ * IEEE 802.11 default interface functions definition.
+ */
+
+#ifndef LIB_IEEE80211_IFACE_IMPL_H
+#define LIB_IEEE80211_IFACE_IMPL_H
+
+#include <ddf/driver.h>
+#include "ieee80211.h"
+
+extern int ieee80211_get_scan_results_impl(ddf_fun_t *,
+    ieee80211_scan_results_t *, bool);
+extern int ieee80211_connect_impl(ddf_fun_t *, char *, char *);
+extern int ieee80211_disconnect_impl(ddf_fun_t *);
+
+#endif  /* LIB_IEEE80211_IFACE_IMPL_H */
+
+/** @}
+ */
Index: uspace/lib/ieee80211/include/ieee80211_impl.h
===================================================================
--- uspace/lib/ieee80211/include/ieee80211_impl.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/ieee80211/include/ieee80211_impl.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @addtogroup libieee80211
+ * @{
+ */
+
+/** @file ieee80211_impl.h
+ *
+ * IEEE 802.11 default device functions definition.
+ */
+
+#ifndef LIB_IEEE80211_IMPL_H
+#define LIB_IEEE80211_IMPL_H
+
+#include "ieee80211_private.h"
+
+extern int ieee80211_start_impl(ieee80211_dev_t *);
+extern int ieee80211_tx_handler_impl(ieee80211_dev_t *, void *, size_t);
+extern int ieee80211_set_freq_impl(ieee80211_dev_t *, uint16_t);
+extern int ieee80211_bssid_change_impl(ieee80211_dev_t *, bool);
+extern int ieee80211_key_config_impl(ieee80211_dev_t *,
+    ieee80211_key_config_t *, bool);
+extern int ieee80211_scan_impl(ieee80211_dev_t *);
+extern int ieee80211_prf(uint8_t *, uint8_t *, uint8_t *, size_t);
+extern int ieee80211_rc4_key_unwrap(uint8_t *, uint8_t *, size_t, uint8_t *);
+extern int ieee80211_aes_key_unwrap(uint8_t *, uint8_t *, size_t, uint8_t *);
+extern int ieee80211_michael_mic(uint8_t *, uint8_t *, size_t, uint8_t *);
+extern uint16_t uint16le_from_seq(void *);
+extern uint32_t uint32le_from_seq(void *);
+extern uint16_t uint16be_from_seq(void *);
+extern uint32_t uint32be_from_seq(void *);
+extern int rnd_sequence(uint8_t *, size_t);
+extern uint8_t *min_sequence(uint8_t *, uint8_t *, size_t);
+extern uint8_t *max_sequence(uint8_t *, uint8_t *, size_t);
+
+#endif  /* LIB_IEEE80211_IMPL_H */
+
+/** @}
+ */
Index: uspace/lib/ieee80211/include/ieee80211_private.h
===================================================================
--- uspace/lib/ieee80211/include/ieee80211_private.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/ieee80211/include/ieee80211_private.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @addtogroup libieee80211
+ * @{
+ */
+
+/** @file ieee80211.h
+ *
+ * Internal IEEE 802.11 header that should not be included.
+ */
+
+#ifndef LIB_IEEE80211_PRIVATE_H
+#define LIB_IEEE80211_PRIVATE_H
+
+#include <fibril_synch.h>
+#include <byteorder.h>
+#include <ddf/driver.h>
+#include <sys/types.h>
+#include <ieee80211/ieee80211.h>
+#include "ieee80211.h"
+
+/** Timeout in us for waiting to authentication/association response. */
+#define AUTH_TIMEOUT  200000
+
+/** Timeout in us for waiting to finish 4-way handshake process. */
+#define HANDSHAKE_TIMEOUT  5000000
+
+/** Scanning period. */
+#define SCAN_PERIOD_USEC  35000000
+
+/** Time to wait for beacons on channel. */
+#define SCAN_CHANNEL_WAIT_USEC  200000
+
+/** Max time to keep scan result. */
+#define MAX_KEEP_SCAN_SPAN_SEC  120
+
+/** Security bit in capability info field. */
+#define CAP_SECURITY  0x10
+
+/** Protocol type used in EAPOL frames. */
+#define ETH_TYPE_PAE  0x888e
+
+/** WPA OUI used in vendor specific IE. */
+#define WPA_OUI  0x0050f201
+
+/** GTK OUI used in vendor specific IE. */
+#define GTK_OUI  0x000fac01
+
+/** Max PTK key length. */
+#define MAX_PTK_LENGTH  64
+
+/** Max GTK key length. */
+#define MAX_GTK_LENGTH  64
+
+/** KEK offset inside PTK. */
+#define KEK_OFFSET  16
+
+/** TK offset inside PTK. */
+#define TK_OFFSET  32
+
+/** Length of Michael MIC code used in TKIP security suite. */
+#define MIC_LENGTH  8
+
+/** Length of data to be encrypted by PRF function.
+ *
+ * NONCE + SNONCE (2 * 32) + DEST_MAC + SOURCE_MAC (2 * ETH_ADDR)
+ *
+ */
+#define PRF_CRYPT_DATA_LENGTH  (2 * 32 + 2 * ETH_ADDR)
+
+/** Special room in header reserved for encryption. */
+typedef enum {
+	IEEE80211_TKIP_HEADER_LENGTH = 8,
+	IEEE80211_CCMP_HEADER_LENGTH = 8
+} ieee80211_encrypt_header_reserve_length_t;
+
+/** IEEE 802.11 PTK key length. */
+typedef enum {
+	IEEE80211_PTK_CCMP_LENGTH = 48,
+	IEEE80211_PTK_TKIP_LENGTH = 64
+} ieee80211_ptk_length_t;
+
+/** IEEE 802.11 GTK key length. */
+typedef enum {
+	IEEE80211_GTK_CCMP_LENGTH = 16,
+	IEEE80211_GTK_TKIP_LENGTH = 32
+} ieee80211_gtk_length_t;
+
+/** IEEE 802.11 frame types. */
+typedef enum {
+	IEEE80211_MGMT_FRAME = 0x0,
+	IEEE80211_CTRL_FRAME = 0x4,
+	IEEE80211_DATA_FRAME = 0x8,
+	IEEE80211_EXT_FRAME = 0xC
+} ieee80211_frame_type_t;
+
+/** IEEE 802.11 management frame subtypes. */
+typedef enum {
+	IEEE80211_MGMT_ASSOC_REQ_FRAME = 0x00,
+	IEEE80211_MGMT_ASSOC_RESP_FRAME = 0x10,
+	IEEE80211_MGMT_REASSOC_REQ_FRAME = 0x20,
+	IEEE80211_MGMT_REASSOC_RESP_FRAME = 0x30,
+	IEEE80211_MGMT_PROBE_REQ_FRAME = 0x40,
+	IEEE80211_MGMT_PROBE_RESP_FRAME = 0x50,
+	IEEE80211_MGMT_BEACON_FRAME = 0x80,
+	IEEE80211_MGMT_DISASSOC_FRAME = 0xA0,
+	IEEE80211_MGMT_AUTH_FRAME = 0xB0,
+	IEEE80211_MGMT_DEAUTH_FRAME = 0xC0,
+} ieee80211_frame_mgmt_subtype_t;
+
+/** IEEE 802.11 data frame subtypes. */
+typedef enum {
+	IEEE80211_DATA_DATA_FRAME = 0x0000,
+	IEEE80211_DATA_QOS_FRAME = 0x0080
+} ieee80211_frame_data_subtype_t;
+
+/** IEEE 802.11 frame control value masks. */
+typedef enum {
+	IEEE80211_FRAME_CTRL_FRAME_TYPE = 0x000C,
+	IEEE80211_FRAME_CTRL_FRAME_SUBTYPE = 0x00F0,
+	IEEE80211_FRAME_CTRL_PROTECTED = 0x4000
+} ieee80211_frame_ctrl_mask_t;
+
+/** IEEE 802.11 frame control DS field values. */
+typedef enum {
+	IEEE80211_FRAME_CTRL_TODS = 0x0100,
+	IEEE80211_FRAME_CTRL_FROMDS = 0x0200
+} ieee80211_frame_ctrl_ds_t;
+
+/** IEEE 802.11 authentication cipher suites values. */
+typedef enum {
+	IEEE80211_AUTH_CIPHER_TKIP = 0x02,
+	IEEE80211_AUTH_CIPHER_CCMP = 0x04
+} ieee80211_auth_cipher_type_t;
+
+/** IEEE 802.11 AKM suites values. */
+typedef enum {
+	IEEE80211_AUTH_AKM_8021X = 0x01,
+	IEEE80211_AUTH_AKM_PSK = 0x02
+} ieee80211_auth_akm_type_t;
+
+typedef enum {
+	IEEE80211_EAPOL_START = 0x1,
+	IEEE80211_EAPOL_KEY = 0x3
+} ieee80211_eapol_frame_type_t;
+
+typedef enum {
+	IEEE80211_EAPOL_KEY_KEYINFO_KEYTYPE = 0x0008,
+	IEEE80211_EAPOL_KEY_KEYINFO_KEYID = 0x0010,
+	IEEE80211_EAPOL_KEY_KEYINFO_INSTALL = 0x0040,
+	IEEE80211_EAPOL_KEY_KEYINFO_ACK = 0x0080,
+	IEEE80211_EAPOL_KEY_KEYINFO_MIC = 0x0100,
+	IEEE80211_EAPOL_KEY_KEYINFO_SECURE = 0x0200,
+	IEEE80211_EAPOL_KEY_KEYINFO_ENCDATA = 0x1000
+} ieee80211_eapol_key_keyinfo_t;
+
+/** IEEE 802.11 information element types. */
+typedef enum {
+	IEEE80211_SSID_IE = 0,        /**< Target SSID. */
+	IEEE80211_RATES_IE = 1,       /**< Supported data rates. */
+	IEEE80211_CHANNEL_IE = 3,     /**< Current channel number. */
+	IEEE80211_CHALLENGE_IE = 16,  /**< Challenge text. */
+	IEEE80211_RSN_IE = 48,        /**< RSN. */
+	IEEE80211_EXT_RATES_IE = 50,  /**< Extended data rates. */
+	IEEE80211_VENDOR_IE = 221     /**< Vendor specific IE. */
+} ieee80211_ie_type_t;
+
+/** IEEE 802.11 authentication phases. */
+typedef enum {
+	IEEE80211_AUTH_DISCONNECTED,
+	IEEE80211_AUTH_AUTHENTICATED,
+	IEEE80211_AUTH_ASSOCIATED,
+	IEEE80211_AUTH_CONNECTED
+} ieee80211_auth_phase_t;
+
+/** Link with scan result info. */
+typedef struct {
+	link_t link;
+	time_t last_beacon;
+	ieee80211_scan_result_t scan_result;
+	uint8_t auth_ie[256];
+	size_t auth_ie_len;
+} ieee80211_scan_result_link_t;
+
+/** List of scan results info. */
+typedef struct {
+	list_t list;
+	fibril_mutex_t results_mutex;
+	size_t size;
+} ieee80211_scan_result_list_t;
+
+/** BSSID info. */
+typedef struct {
+	uint16_t aid;
+	char password[IEEE80211_MAX_PASSW_LEN];
+	uint8_t ptk[MAX_PTK_LENGTH];
+	uint8_t gtk[MAX_GTK_LENGTH];
+	ieee80211_scan_result_link_t *res_link;
+} ieee80211_bssid_info_t;
+
+/** IEEE 802.11 WiFi device structure. */
+struct ieee80211_dev {
+	/** Backing DDF device. */
+	ddf_dev_t *ddf_dev;
+	
+	/** Pointer to implemented IEEE 802.11 device operations. */
+	ieee80211_ops_t *ops;
+	
+	/** Pointer to implemented IEEE 802.11 interface operations. */
+	ieee80211_iface_t *iface;
+	
+	/** Pointer to driver specific data. */
+	void *specific;
+	
+	/** Current operating frequency. */
+	uint16_t current_freq;
+	
+	/** Current operating mode. */
+	ieee80211_operating_mode_t current_op_mode;
+	
+	/** Info about BSSID we are connected to. */
+	ieee80211_bssid_info_t bssid_info;
+	
+	/**
+	 * Flag indicating that data traffic is encrypted by HW key
+	 * that is set up in device.
+	 */
+	bool using_hw_key;
+	
+	/** BSSIDs we listen to. */
+	nic_address_t bssid_mask;
+	
+	/** List of APs in neighborhood. */
+	ieee80211_scan_result_list_t ap_list;
+	
+	/** Current sequence number used in data frames. */
+	uint16_t sequence_number;
+	
+	/** Current authentication phase. */
+	ieee80211_auth_phase_t current_auth_phase;
+	
+	/** Flag indicating whether client wants connect to network. */
+	bool pending_conn_req;
+	
+	/** Scanning guard. */
+	fibril_mutex_t scan_mutex;
+	
+	/** General purpose guard. */
+	fibril_mutex_t gen_mutex;
+	
+	/** General purpose condition variable. */
+	fibril_condvar_t gen_cond;
+	
+	/** Indicates whether device is fully initialized. */
+	bool ready;
+	
+	/** Indicates whether driver has already started. */
+	bool started;
+};
+
+/** IEEE 802.3 (ethernet) header. */
+typedef struct {
+	uint8_t dest_addr[ETH_ADDR];
+	uint8_t src_addr[ETH_ADDR];
+	uint16_t proto;  /**< Big Endian value! */
+} __attribute__((packed)) __attribute__((aligned(2)))
+    eth_header_t;
+
+/** IEEE 802.11 management header structure. */
+typedef struct {
+	uint16_t frame_ctrl;          /**< Little Endian value! */
+	uint16_t duration_id;         /**< Little Endian value! */
+	uint8_t dest_addr[ETH_ADDR];
+	uint8_t src_addr[ETH_ADDR];
+	uint8_t bssid[ETH_ADDR];
+	uint16_t seq_ctrl;            /**< Little Endian value! */
+} __attribute__((packed)) __attribute__((aligned(2)))
+    ieee80211_mgmt_header_t;
+
+/** IEEE 802.11 data header structure. */
+typedef struct {
+	uint16_t frame_ctrl;         /**< Little Endian value! */
+	uint16_t duration_id;        /**< Little Endian value! */
+	uint8_t address1[ETH_ADDR];
+	uint8_t address2[ETH_ADDR];
+	uint8_t address3[ETH_ADDR];
+	uint16_t seq_ctrl;           /**< Little Endian value! */
+} __attribute__((packed)) __attribute__((aligned(2)))
+    ieee80211_data_header_t;
+
+/** IEEE 802.11 information element header. */
+typedef struct {
+	uint8_t element_id;
+	uint8_t length;
+} __attribute__((packed)) __attribute__((aligned(2)))
+    ieee80211_ie_header_t;
+
+/** IEEE 802.11 authentication frame body. */
+typedef struct {
+	uint16_t auth_alg;       /**< Little Endian value! */
+	uint16_t auth_trans_no;  /**< Little Endian value! */
+	uint16_t status;         /**< Little Endian value! */
+} __attribute__((packed)) __attribute__((aligned(2)))
+    ieee80211_auth_body_t;
+
+/** IEEE 802.11 deauthentication frame body. */
+typedef struct {
+	uint16_t reason;    /**< Little Endian value! */
+} __attribute__((packed)) __attribute__((aligned(2)))
+    ieee80211_deauth_body_t;
+
+/** IEEE 802.11 association request frame body. */
+typedef struct {
+	uint16_t capability;       /**< Little Endian value! */
+	uint16_t listen_interval;  /**< Little Endian value! */
+} __attribute__((packed)) __attribute__((aligned(2)))
+    ieee80211_assoc_req_body_t;
+
+/** IEEE 802.11 association response frame body. */
+typedef struct {
+	uint16_t capability;  /**< Little Endian value! */
+	uint16_t status;      /**< Little Endian value! */
+	uint16_t aid;         /**< Little Endian value! */
+} __attribute__((packed)) __attribute__((aligned(2)))
+    ieee80211_assoc_resp_body_t;
+
+/** IEEE 802.11 beacon frame body start. */
+typedef struct {
+	uint8_t timestamp[8];
+	uint16_t beacon_interval;  /**< Little Endian value! */
+	uint16_t capability;       /**< Little Endian value! */
+} __attribute__((packed)) __attribute__((aligned(2)))
+    ieee80211_beacon_start_t;
+
+/** IEEE 802.11i EAPOL-Key frame format. */
+typedef struct {
+	uint8_t proto_version;
+	uint8_t packet_type;
+	uint16_t body_length;      /**< Big Endian value! */
+	uint8_t descriptor_type;
+	uint16_t key_info;         /**< Big Endian value! */
+	uint16_t key_length;       /**< Big Endian value! */
+	uint8_t key_replay_counter[8];
+	uint8_t key_nonce[32];
+	uint8_t eapol_key_iv[16];
+	uint8_t key_rsc[8];
+	uint8_t reserved[8];
+	uint8_t key_mic[16];
+	uint16_t key_data_length;  /**< Big Endian value! */
+} __attribute__((packed)) ieee80211_eapol_key_frame_t;
+
+#define ieee80211_scan_result_list_foreach(results, iter) \
+	list_foreach((results).list, link, ieee80211_scan_result_link_t, (iter))
+
+static inline void
+    ieee80211_scan_result_list_init(ieee80211_scan_result_list_t *results)
+{
+	list_initialize(&results->list);
+	fibril_mutex_initialize(&results->results_mutex);
+}
+
+static inline void
+    ieee80211_scan_result_list_remove(ieee80211_scan_result_list_t *results,
+    ieee80211_scan_result_link_t *result)
+{
+	list_remove(&result->link);
+	results->size--;
+}
+
+static inline void
+    ieee80211_scan_result_list_append(ieee80211_scan_result_list_t *results,
+    ieee80211_scan_result_link_t *result)
+{
+	list_append(&result->link, &results->list);
+	results->size++;
+}
+
+extern bool ieee80211_is_fromds_frame(uint16_t);
+extern bool ieee80211_is_tods_frame(uint16_t);
+extern void ieee80211_set_connect_request(ieee80211_dev_t *);
+extern bool ieee80211_pending_connect_request(ieee80211_dev_t *);
+extern ieee80211_auth_phase_t ieee80211_get_auth_phase(ieee80211_dev_t *);
+extern void ieee80211_set_auth_phase(ieee80211_dev_t *, ieee80211_auth_phase_t);
+extern int ieee80211_probe_request(ieee80211_dev_t *, char *);
+extern int ieee80211_authenticate(ieee80211_dev_t *);
+extern int ieee80211_associate(ieee80211_dev_t *, char *);
+extern int ieee80211_deauthenticate(ieee80211_dev_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ieee80211/src/ieee80211.c
===================================================================
--- uspace/lib/ieee80211/src/ieee80211.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/ieee80211/src/ieee80211.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,1727 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libieee80211
+ * @{
+ */
+
+/** @file ieee80211.c
+ *
+ * IEEE 802.11 interface implementation.
+ */
+
+#include <stdio.h>
+#include <crypto.h>
+#include <str.h>
+#include <macros.h>
+#include <errno.h>
+#include <ieee80211.h>
+#include <ieee80211_impl.h>
+#include <ieee80211_iface_impl.h>
+#include <ieee80211_private.h>
+#include <ops/ieee80211.h>
+
+#define IEEE80211_DATA_RATES_SIZE      8
+#define IEEE80211_EXT_DATA_RATES_SIZE  4
+
+#define ATOMIC_GET(state)
+
+/** Frame encapsulation used in IEEE 802.11. */
+static const uint8_t rfc1042_header[] = {
+	0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 
+};
+
+/** Broadcast MAC address. */
+static const uint8_t ieee80211_broadcast_mac_addr[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+/** Check data frame.
+ *
+ * @param frame_ctrl Frame control field in little endian (!).
+ *
+ * @return True if it is data frame, otherwise false.
+ *
+ */
+inline bool ieee80211_is_data_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_FRAME_TYPE) ==
+	    IEEE80211_DATA_FRAME;
+}
+
+/** Check management frame.
+ *
+ * @param frame_ctrl Frame control field in little endian (!).
+ *
+ * @return True if it is management frame, otherwise false.
+ *
+ */
+inline bool ieee80211_is_mgmt_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_FRAME_TYPE) ==
+	    IEEE80211_MGMT_FRAME;
+}
+
+/** Check management beacon frame.
+ *
+ * @param frame_ctrl Frame control field in little endian (!).
+ *
+ * @return True if it is beacon frame, otherwise false.
+ *
+ */
+inline bool ieee80211_is_beacon_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_FRAME_SUBTYPE) ==
+	    IEEE80211_MGMT_BEACON_FRAME;
+}
+
+/** Check management probe response frame.
+ *
+ * @param frame_ctrl Frame control field in little endian (!).
+ *
+ * @return True if it is probe resp frame, otherwise false.
+ *
+ */
+inline bool ieee80211_is_probe_response_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_FRAME_SUBTYPE) ==
+	    IEEE80211_MGMT_PROBE_RESP_FRAME;
+}
+
+/** Check management authentication frame.
+ *
+ * @param frame_ctrl Frame control field in little endian (!).
+ *
+ * @return True if it is auth frame, otherwise false.
+ *
+ */
+inline bool ieee80211_is_auth_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_FRAME_SUBTYPE) ==
+	    IEEE80211_MGMT_AUTH_FRAME;
+}
+
+/** Check management association response frame.
+ *
+ * @param frame_ctrl Frame control field in little endian (!).
+ *
+ * @return True if it is assoc resp frame, otherwise false.
+ *
+ */
+inline bool ieee80211_is_assoc_response_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_FRAME_SUBTYPE) ==
+	    IEEE80211_MGMT_ASSOC_RESP_FRAME;
+}
+
+/** Check data frame "to distribution system" direction.
+ *
+ * @param frame_ctrl Frame control field in little endian (!).
+ *
+ * @return True if it is TODS frame, otherwise false.
+ *
+ */
+inline bool ieee80211_is_tods_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_TODS);
+}
+
+/** Check data frame "from distribution system" direction.
+ *
+ * @param frame_ctrl Frame control field in little endian (!).
+ *
+ * @return True if it is FROMDS frame, otherwise false.
+ *
+ */
+inline bool ieee80211_is_fromds_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_FROMDS);
+}
+
+/** Check if it is data frame containing payload data.
+ *
+ * @param frame_ctrl Frame control field in little endian (!).
+ *
+ * @return True if it has payload data, otherwise false.
+ *
+ */
+static inline bool ieee80211_has_data_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & (IEEE80211_FRAME_CTRL_FRAME_TYPE | 0x40)) ==
+	    IEEE80211_DATA_FRAME;
+}
+
+/** Check if it is encrypted frame.
+ *
+ * @param frame_ctrl Frame control field in little endian (!).
+ *
+ * @return True if the frame is encrypted, otherwise false.
+ *
+ */
+static inline bool ieee80211_is_encrypted_frame(uint16_t frame_ctrl)
+{
+	frame_ctrl = uint16_t_le2host(frame_ctrl);
+	
+	return (frame_ctrl & IEEE80211_FRAME_CTRL_PROTECTED);
+}
+
+/** Check if PAE packet is EAPOL-Key frame.
+ *
+ * @param key_frame Pointer to start of EAPOL frame.
+ *
+ * @return True if it is EAPOL-Key frame, otherwise false.
+ *
+ */
+static inline bool
+    ieee80211_is_eapol_key_frame(ieee80211_eapol_key_frame_t *key_frame)
+{
+	return (key_frame->packet_type == IEEE80211_EAPOL_KEY);
+}
+
+/** Generate packet sequence number.
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ *
+ * @return True if it has payload data, otherwise false.
+ *
+ */
+static uint16_t ieee80211_get_sequence_number(ieee80211_dev_t *ieee80211_dev)
+{
+	uint16_t ret_val = ieee80211_dev->sequence_number;
+	ieee80211_dev->sequence_number += (1 << 4);
+	
+	return ret_val;
+}
+
+/** Get driver-specific structure for IEEE 802.11 device.
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ *
+ * @return Driver-specific structure.
+ *
+ */
+void *ieee80211_get_specific(ieee80211_dev_t *ieee80211_dev)
+{
+	return ieee80211_dev->specific;
+}
+
+/** Set driver-specific structure for IEEE 802.11 device.
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ * @param specific      Driver-specific structure.
+ *
+ */
+void ieee80211_set_specific(ieee80211_dev_t *ieee80211_dev,
+    void *specific)
+{
+	ieee80211_dev->specific = specific;
+}
+
+/** Get related DDF device.
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ *
+ * @return DDF device.
+ *
+ */
+ddf_dev_t *ieee80211_get_ddf_dev(ieee80211_dev_t *ieee80211_dev)
+{
+	return ieee80211_dev->ddf_dev;
+}
+
+/** Query current operating mode of IEEE 802.11 device.
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ *
+ * @return Current IEEE 802.11 operating mode.
+ *
+ */
+ieee80211_operating_mode_t
+    ieee80211_query_current_op_mode(ieee80211_dev_t *ieee80211_dev)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	ieee80211_operating_mode_t op_mode = ieee80211_dev->current_op_mode;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return op_mode;
+}
+
+/** Query current frequency of IEEE 802.11 device.
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ *
+ * @return Current device operating frequency.
+ *
+ */
+uint16_t ieee80211_query_current_freq(ieee80211_dev_t *ieee80211_dev)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	uint16_t current_freq = ieee80211_dev->current_freq;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return current_freq;
+}
+
+/** Query BSSID the device is connected to.
+ *
+ * Note: Expecting locked results_mutex.
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ * @param bssid         Pointer to structure where should be stored BSSID.
+ *
+ */
+void ieee80211_query_bssid(ieee80211_dev_t *ieee80211_dev,
+    nic_address_t *bssid)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	
+	if (bssid) {
+		ieee80211_scan_result_link_t *res_link =
+		    ieee80211_dev->bssid_info.res_link;
+		
+		if (res_link) {
+			memcpy(bssid, &res_link->scan_result.bssid,
+			    sizeof(nic_address_t));
+		} else {
+			nic_address_t broadcast_addr;
+			memcpy(broadcast_addr.address,
+			    ieee80211_broadcast_mac_addr, ETH_ADDR);
+			memcpy(bssid, &broadcast_addr, sizeof(nic_address_t));
+		}
+	}
+	
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+}
+
+/** Get AID of network we are connected to.
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ *
+ * @return AID.
+ *
+ */
+uint16_t ieee80211_get_aid(ieee80211_dev_t *ieee80211_dev)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	uint16_t aid = ieee80211_dev->bssid_info.aid;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return aid;
+}
+
+/** Get pairwise security suite used for HW encryption. 
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ *
+ * @return Security suite indicator.
+ *
+ */
+int ieee80211_get_pairwise_security(ieee80211_dev_t *ieee80211_dev)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	ieee80211_scan_result_link_t *auth_link =
+	    ieee80211_dev->bssid_info.res_link;
+	int suite = auth_link->scan_result.security.pair_alg;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return suite;
+}
+
+/** Check if IEEE 802.11 device is connected to network.
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ *
+ * @return True if device is connected to network, otherwise false.
+ *
+ */
+bool ieee80211_is_connected(ieee80211_dev_t *ieee80211_dev)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	bool conn_state =
+	    ieee80211_dev->current_auth_phase == IEEE80211_AUTH_CONNECTED;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return conn_state;
+}
+
+void ieee80211_set_auth_phase(ieee80211_dev_t *ieee80211_dev,
+    ieee80211_auth_phase_t auth_phase)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	ieee80211_dev->current_auth_phase = auth_phase;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+}
+
+ieee80211_auth_phase_t ieee80211_get_auth_phase(ieee80211_dev_t *ieee80211_dev)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	ieee80211_auth_phase_t conn_state = ieee80211_dev->current_auth_phase;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return conn_state;
+}
+
+void ieee80211_set_connect_request(ieee80211_dev_t *ieee80211_dev)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	ieee80211_dev->pending_conn_req = true;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+}
+
+bool ieee80211_pending_connect_request(ieee80211_dev_t *ieee80211_dev)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	bool conn_request = ieee80211_dev->pending_conn_req;
+	ieee80211_dev->pending_conn_req = false;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return conn_request;
+}
+
+/** Report current operating mode for IEEE 802.11 device.
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ * @param op_mode       Current IEEE 802.11 operating mode.
+ *
+ */
+void ieee80211_report_current_op_mode(ieee80211_dev_t *ieee80211_dev,
+    ieee80211_operating_mode_t op_mode)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	ieee80211_dev->current_op_mode = op_mode;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+}
+
+/** Report current frequency for IEEE 802.11 device.
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ * @param freq          Current device operating frequency.
+ *
+ */
+void ieee80211_report_current_freq(ieee80211_dev_t *ieee80211_dev,
+    uint16_t freq)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	ieee80211_dev->current_freq = freq;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+}
+
+/** Check if IEEE 802.11 device is ready (fully initialized).
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ *
+ * @return True if device is ready to work, otherwise false.
+ *
+ */
+bool ieee80211_is_ready(ieee80211_dev_t *ieee80211_dev)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	bool ready_state = ieee80211_dev->ready;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return ready_state;
+}
+
+/** Set IEEE 802.11 device to ready state.
+ *
+ * @param ieee80211_dev IEEE 802.11 device.
+ * @param ready         Ready state to be set.
+ *
+ */
+void ieee80211_set_ready(ieee80211_dev_t *ieee80211_dev, bool ready)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	ieee80211_dev->ready = ready;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+}
+
+extern bool ieee80211_query_using_key(ieee80211_dev_t *ieee80211_dev)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	bool using_key = ieee80211_dev->using_hw_key;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return using_key;
+}
+
+void ieee80211_setup_key_confirm(ieee80211_dev_t *ieee80211_dev,
+    bool using_key)
+{
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	ieee80211_dev->using_hw_key = using_key;
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+}
+
+static int ieee80211_scan(void *arg)
+{
+	assert(arg);
+	
+	ieee80211_dev_t *ieee80211_dev = (ieee80211_dev_t *) arg;
+	
+	while (true) {
+		ieee80211_dev->ops->scan(ieee80211_dev);
+		async_usleep(SCAN_PERIOD_USEC);
+	}
+	
+	return EOK;
+}
+
+/** Implementation of NIC open callback for IEEE 802.11 devices.
+ *
+ * @param fun NIC function.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+static int ieee80211_open(ddf_fun_t *fun)
+{
+	nic_t *nic_data = nic_get_from_ddf_fun(fun);
+	ieee80211_dev_t *ieee80211_dev = nic_get_specific(nic_data);
+	
+	if (ieee80211_dev->started)
+		return EOK;
+	
+	ieee80211_dev->started = true;
+	
+	int rc = ieee80211_dev->ops->start(ieee80211_dev);
+	if (rc != EOK)
+		return rc;
+	
+	/* Add scanning fibril. */
+	fid_t fibril = fibril_create(ieee80211_scan, ieee80211_dev);
+	if (fibril == 0)
+		return ENOMEM;
+	
+	fibril_add_ready(fibril);
+	
+	return EOK;
+}
+
+/** Send frame handler.
+ *
+ * @param nic  Pointer to NIC device.
+ * @param data Data buffer.
+ * @param size Data buffer size.
+ *
+ */
+static void ieee80211_send_frame(nic_t *nic, void *data, size_t size)
+{
+	ieee80211_dev_t *ieee80211_dev = (ieee80211_dev_t *)
+	    nic_get_specific(nic);
+	
+	ieee80211_auth_phase_t auth_phase =
+	    ieee80211_get_auth_phase(ieee80211_dev);
+	if ((auth_phase != IEEE80211_AUTH_ASSOCIATED) &&
+	    (auth_phase != IEEE80211_AUTH_CONNECTED))
+		return;
+	
+	ieee80211_scan_result_t *auth_data =
+	    &ieee80211_dev->bssid_info.res_link->scan_result;
+	
+	/* We drop part of IEEE 802.3 ethernet header. */
+	size_t drop_bytes = sizeof(eth_header_t) - 2;
+	
+	size_t complete_size = (size - drop_bytes) +
+	    sizeof(ieee80211_data_header_t) +
+	    ARRAY_SIZE(rfc1042_header);
+	
+	/* Init crypto data. */
+	bool add_mic = false;
+	size_t head_space = 0, mic_space = 0;
+	uint16_t crypto = 0;
+	uint8_t head_data[head_space];
+	memset(head_data, 0, head_space);
+	
+	// TODO: Distinguish used key (pair/group) by dest address ?
+	if (ieee80211_query_using_key(ieee80211_dev)) {
+		int sec_suite = auth_data->security.pair_alg;
+		switch (sec_suite) {
+		case IEEE80211_SECURITY_SUITE_TKIP:
+			head_space = IEEE80211_TKIP_HEADER_LENGTH;
+			mic_space = MIC_LENGTH;
+			add_mic = true;
+			break;
+		case IEEE80211_SECURITY_SUITE_CCMP:
+			head_space = IEEE80211_CCMP_HEADER_LENGTH;
+			head_data[3] = 0x20;
+			break;
+		default:
+			break;
+		}
+		
+		crypto = uint16_t_le2host(IEEE80211_FRAME_CTRL_PROTECTED);
+	}
+	
+	complete_size += head_space + mic_space;
+	
+	void *complete_buffer = malloc(complete_size);
+	if (!complete_buffer)
+		return;
+	
+	memset(complete_buffer, 0, complete_size);
+	
+	if (head_space)
+		memcpy(complete_buffer + sizeof(ieee80211_data_header_t),
+		    head_data, head_space);
+	
+	memcpy(complete_buffer + sizeof(ieee80211_data_header_t) + head_space,
+	    rfc1042_header, ARRAY_SIZE(rfc1042_header));
+	
+	memcpy(complete_buffer + sizeof(ieee80211_data_header_t) +
+	    ARRAY_SIZE(rfc1042_header) + head_space,
+	    data + drop_bytes, size - drop_bytes);
+	
+	ieee80211_data_header_t *data_header =
+	    (ieee80211_data_header_t *) complete_buffer;
+	data_header->frame_ctrl =
+	    uint16_t_le2host(IEEE80211_DATA_FRAME) |
+	    uint16_t_le2host(IEEE80211_DATA_DATA_FRAME) |
+	    uint16_t_le2host(IEEE80211_FRAME_CTRL_TODS) |
+	    crypto;
+	data_header->seq_ctrl = ieee80211_get_sequence_number(ieee80211_dev);
+	
+	/* BSSID, SA, DA. */
+	memcpy(data_header->address1, auth_data->bssid.address, ETH_ADDR);
+	memcpy(data_header->address2, data + ETH_ADDR, ETH_ADDR);
+	memcpy(data_header->address3, data, ETH_ADDR);
+	
+	if (add_mic) {
+		size_t size_wo_mic = complete_size - MIC_LENGTH;
+		uint8_t *tx_mic = ieee80211_dev->bssid_info.ptk +
+		    TK_OFFSET + IEEE80211_TKIP_TX_MIC_OFFSET;
+		ieee80211_michael_mic(tx_mic, complete_buffer, size_wo_mic,
+		    complete_buffer + size_wo_mic);
+	}
+	
+	ieee80211_dev->ops->tx_handler(ieee80211_dev,
+	    complete_buffer, complete_size);
+	
+	free(complete_buffer);
+}
+
+/** Fill out IEEE 802.11 device functions implementations.
+ *
+ * @param ieee80211_dev   IEEE 802.11 device.
+ * @param ieee80211_ops   Callbacks implementation.
+ * @param ieee80211_iface Interface functions implementation.
+ * @param nic_iface       NIC interface functions implementation.
+ * @param nic_dev_ops     NIC device functions implementation.
+ *
+ * @return EINVAL when missing pointer to ieee80211_ops
+ *         or ieee80211_iface, otherwise EOK.
+ *
+ */
+static int ieee80211_implement(ieee80211_dev_t *ieee80211_dev,
+    ieee80211_ops_t *ieee80211_ops, ieee80211_iface_t *ieee80211_iface,
+    nic_iface_t *nic_iface, ddf_dev_ops_t *nic_dev_ops)
+{
+	if (ieee80211_ops) {
+		if (!ieee80211_ops->start)
+			ieee80211_ops->start = ieee80211_start_impl;
+		
+		if (!ieee80211_ops->tx_handler)
+			ieee80211_ops->tx_handler = ieee80211_tx_handler_impl;
+		
+		if (!ieee80211_ops->set_freq)
+			ieee80211_ops->set_freq = ieee80211_set_freq_impl;
+		
+		if (!ieee80211_ops->bssid_change)
+			ieee80211_ops->bssid_change = ieee80211_bssid_change_impl;
+		
+		if (!ieee80211_ops->key_config)
+			ieee80211_ops->key_config = ieee80211_key_config_impl;
+		
+		if (!ieee80211_ops->scan)
+			ieee80211_ops->scan = ieee80211_scan_impl;
+	} else
+		return EINVAL;
+	
+	ieee80211_dev->ops = ieee80211_ops;
+	
+	if (ieee80211_iface) {
+		if (nic_dev_ops)
+			if (!nic_dev_ops->interfaces[IEEE80211_DEV_IFACE])
+				nic_dev_ops->interfaces[IEEE80211_DEV_IFACE] =
+				    ieee80211_iface;
+		
+		if (!ieee80211_iface->get_scan_results)
+			ieee80211_iface->get_scan_results =
+			    ieee80211_get_scan_results_impl;
+		
+		if (!ieee80211_iface->connect)
+			ieee80211_iface->connect = ieee80211_connect_impl;
+		
+		if (!ieee80211_iface->disconnect)
+			ieee80211_iface->disconnect = ieee80211_disconnect_impl;
+	} else
+		return EINVAL;
+	
+	if (nic_dev_ops) {
+		if (!nic_dev_ops->open)
+			nic_dev_ops->open = ieee80211_open;
+	} else
+		return EINVAL;
+	
+	ieee80211_dev->iface = ieee80211_iface;
+	
+	nic_driver_implement(NULL, nic_dev_ops, nic_iface);
+	
+	return EOK;
+}
+
+/** Allocate IEEE802.11 device structure.
+ *
+ * @return Pointer to allocated IEEE802.11 device structure.
+ *
+ */
+ieee80211_dev_t *ieee80211_device_create(void)
+{
+	return calloc(1, sizeof(ieee80211_dev_t));
+}
+
+/** Initialize an IEEE802.11 framework structure.
+ *
+ * @param ieee80211_dev Device structure to initialize.
+ * @param ddf_dev       Pointer to backing DDF device structure.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int ieee80211_device_init(ieee80211_dev_t *ieee80211_dev, ddf_dev_t *ddf_dev)
+{
+	ieee80211_dev->ddf_dev = ddf_dev;
+	ieee80211_dev->started = false;
+	ieee80211_dev->ready = false;
+	ieee80211_dev->using_hw_key = false;
+	ieee80211_dev->pending_conn_req = false;
+	ieee80211_dev->current_op_mode = IEEE80211_OPMODE_STATION;
+	ieee80211_dev->current_auth_phase = IEEE80211_AUTH_DISCONNECTED;
+	
+	memcpy(ieee80211_dev->bssid_mask.address, ieee80211_broadcast_mac_addr,
+	    ETH_ADDR);
+	
+	ieee80211_scan_result_list_init(&ieee80211_dev->ap_list);
+	
+	fibril_mutex_initialize(&ieee80211_dev->scan_mutex);
+	fibril_mutex_initialize(&ieee80211_dev->gen_mutex);
+	fibril_condvar_initialize(&ieee80211_dev->gen_cond);
+	
+	/* Bind NIC to device */
+	nic_t *nic = nic_create_and_bind(ddf_dev);
+	if (!nic)
+		return ENOMEM;
+	
+	nic_set_specific(nic, ieee80211_dev);
+	
+	return EOK;
+}
+
+/** IEEE802.11 WiFi framework initialization.
+ *
+ * @param ieee80211_dev   Device structure to initialize.
+ * @param ieee80211_ops   Structure with implemented IEEE802.11
+ *                        device operations.
+ * @param ieee80211_iface Structure with implemented IEEE802.11
+ *                        interface operations.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int ieee80211_init(ieee80211_dev_t *ieee80211_dev,
+    ieee80211_ops_t *ieee80211_ops, ieee80211_iface_t *ieee80211_iface,
+    nic_iface_t *ieee80211_nic_iface, ddf_dev_ops_t *ieee80211_nic_dev_ops)
+{
+	int rc = ieee80211_implement(ieee80211_dev,
+	    ieee80211_ops, ieee80211_iface,
+	    ieee80211_nic_iface, ieee80211_nic_dev_ops);
+	if (rc != EOK)
+		return rc;
+	
+	nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
+	
+	/* TODO: Set NIC handlers here. */
+	nic_set_send_frame_handler(nic, ieee80211_send_frame);
+	
+	ddf_fun_t *fun = ddf_fun_create(ieee80211_dev->ddf_dev, fun_exposed,
+	    "port0");
+	if (fun == NULL)
+		return EINVAL;
+	
+	nic_set_ddf_fun(nic, fun);
+	ddf_fun_set_ops(fun, ieee80211_nic_dev_ops);
+	
+	rc = ddf_fun_bind(fun);
+	if (rc != EOK) {
+		ddf_fun_destroy(fun);
+		return rc;
+	}
+	
+	rc = ddf_fun_add_to_category(fun, DEVICE_CATEGORY_NIC);
+	if (rc != EOK) {
+		ddf_fun_unbind(fun);
+		return rc;
+	}
+	
+	rc = ddf_fun_add_to_category(fun, DEVICE_CATEGORY_IEEE80211);
+	if (rc != EOK) {
+		ddf_fun_unbind(fun);
+		return rc;
+	}
+	
+	return EOK;
+}
+
+/** Convert frequency value to channel number.
+ *
+ * @param freq IEEE 802.11 operating frequency.
+ *
+ * @return Operating channel number.
+ *
+ */
+static uint8_t ieee80211_freq_to_channel(uint16_t freq)
+{
+	return (freq - IEEE80211_FIRST_FREQ) / IEEE80211_CHANNEL_GAP + 1;
+}
+
+static void ieee80211_prepare_ie_header(void **ie_header,
+    uint8_t id, uint8_t length, void *data)
+{
+	ieee80211_ie_header_t *header =
+	    (ieee80211_ie_header_t *) *ie_header;
+	
+	header->element_id = id;
+	header->length = length;
+	
+	memcpy(*ie_header + sizeof(ieee80211_ie_header_t), data, length);
+	
+	*ie_header = (void *) ((void *) header +
+	    sizeof(ieee80211_ie_header_t) + length);
+}
+
+/** Probe request implementation.
+ *
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * @param ssid          Probing SSID or NULL if broadcast.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int ieee80211_probe_request(ieee80211_dev_t *ieee80211_dev, char *ssid)
+{
+	nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
+	nic_address_t nic_address;
+	nic_query_address(nic, &nic_address);
+	
+	size_t ssid_data_size = (ssid != NULL) ? str_size(ssid) : 0;
+	size_t channel_data_size = 1;
+	
+	uint8_t channel =
+	    ieee80211_freq_to_channel(ieee80211_dev->current_freq);
+	
+	/*
+	 * 4 headers - (ssid, rates, ext rates, current channel)
+	 * and their data lengths.
+	 */
+	size_t payload_size =
+	    sizeof(ieee80211_ie_header_t) * 4 +
+	    ssid_data_size +
+	    IEEE80211_DATA_RATES_SIZE + IEEE80211_EXT_DATA_RATES_SIZE +
+	    channel_data_size;
+	
+	size_t buffer_size = sizeof(ieee80211_mgmt_header_t) + payload_size;
+	void *buffer = malloc(buffer_size);
+	if (!buffer)
+		return ENOMEM;
+	
+	memset(buffer, 0, buffer_size);
+	
+	ieee80211_mgmt_header_t *mgmt_header =
+	    (ieee80211_mgmt_header_t *) buffer;
+	
+	mgmt_header->frame_ctrl =
+	    host2uint16_t_le(IEEE80211_MGMT_FRAME |
+	    IEEE80211_MGMT_PROBE_REQ_FRAME);
+	memcpy(mgmt_header->dest_addr, ieee80211_broadcast_mac_addr, ETH_ADDR);
+	memcpy(mgmt_header->src_addr, nic_address.address, ETH_ADDR);
+	memcpy(mgmt_header->bssid, ieee80211_broadcast_mac_addr, ETH_ADDR);
+	mgmt_header->seq_ctrl =
+	    host2uint16_t_le(ieee80211_get_sequence_number(ieee80211_dev));
+	
+	/* Jump to payload. */
+	void *it = (void *) buffer + sizeof(ieee80211_mgmt_header_t);
+	ieee80211_prepare_ie_header(&it, IEEE80211_SSID_IE, ssid_data_size,
+	    (void *) ssid);
+	ieee80211_prepare_ie_header(&it, IEEE80211_RATES_IE,
+	    IEEE80211_DATA_RATES_SIZE, (void *) &ieee80211bg_data_rates);
+	ieee80211_prepare_ie_header(&it, IEEE80211_EXT_RATES_IE,
+	    IEEE80211_EXT_DATA_RATES_SIZE,
+	    (void *) &ieee80211bg_data_rates[IEEE80211_DATA_RATES_SIZE]);
+	ieee80211_prepare_ie_header(&it, IEEE80211_CHANNEL_IE,
+	    channel_data_size, (void *) &channel);
+	
+	ieee80211_dev->ops->tx_handler(ieee80211_dev, buffer, buffer_size);
+	
+	free(buffer);
+	
+	return EOK;
+}
+
+/** IEEE 802.11 authentication implementation.
+ *
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int ieee80211_authenticate(ieee80211_dev_t *ieee80211_dev)
+{
+	nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
+	nic_address_t nic_address;
+	nic_query_address(nic, &nic_address);
+	
+	ieee80211_scan_result_t *auth_data =
+	    &ieee80211_dev->bssid_info.res_link->scan_result;
+	
+	size_t buffer_size = sizeof(ieee80211_mgmt_header_t) +
+	    sizeof(ieee80211_auth_body_t);
+	
+	void *buffer = malloc(buffer_size);
+	if (!buffer)
+		return ENOMEM;
+	
+	memset(buffer, 0, buffer_size);
+	
+	ieee80211_mgmt_header_t *mgmt_header =
+	    (ieee80211_mgmt_header_t *) buffer;
+	
+	mgmt_header->frame_ctrl =
+	    host2uint16_t_le(IEEE80211_MGMT_FRAME |
+	    IEEE80211_MGMT_AUTH_FRAME);
+	memcpy(mgmt_header->dest_addr, auth_data->bssid.address, ETH_ADDR);
+	memcpy(mgmt_header->src_addr, nic_address.address, ETH_ADDR);
+	memcpy(mgmt_header->bssid, auth_data->bssid.address, ETH_ADDR);
+	
+	ieee80211_auth_body_t *auth_body =
+	    (ieee80211_auth_body_t *)
+	    (buffer + sizeof(ieee80211_mgmt_header_t));
+	auth_body->auth_alg = host2uint16_t_le(0);
+	auth_body->auth_trans_no = host2uint16_t_le(1);
+	
+	ieee80211_dev->ops->tx_handler(ieee80211_dev, buffer, buffer_size);
+	
+	free(buffer);
+	
+	return EOK;
+}
+
+/** IEEE 802.11 association implementation.
+ *
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * @param password      Passphrase to be used in encrypted communication
+ *                      or NULL for open networks.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int ieee80211_associate(ieee80211_dev_t *ieee80211_dev, char *password)
+{
+	nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
+	nic_address_t nic_address;
+	nic_query_address(nic, &nic_address);
+	
+	ieee80211_scan_result_link_t *auth_link =
+	    ieee80211_dev->bssid_info.res_link;
+	
+	ieee80211_scan_result_t *auth_data = &auth_link->scan_result;
+	
+	size_t ssid_data_size = str_size(auth_data->ssid);
+	
+	size_t payload_size =
+	    sizeof(ieee80211_ie_header_t) * 3 +
+	    ssid_data_size +
+	    IEEE80211_DATA_RATES_SIZE +
+	    IEEE80211_EXT_DATA_RATES_SIZE;
+	
+	size_t buffer_size =
+	    sizeof(ieee80211_mgmt_header_t) +
+	    sizeof(ieee80211_assoc_req_body_t) +
+	    payload_size;
+	
+	if ((auth_data->security.type == IEEE80211_SECURITY_WPA) ||
+	    (auth_data->security.type == IEEE80211_SECURITY_WPA2))
+		buffer_size += auth_link->auth_ie_len;
+	
+	void *buffer = malloc(buffer_size);
+	if (!buffer)
+		return ENOMEM;
+	
+	memset(buffer, 0, buffer_size);
+	
+	ieee80211_mgmt_header_t *mgmt_header =
+	    (ieee80211_mgmt_header_t *) buffer;
+	
+	mgmt_header->frame_ctrl =
+	    host2uint16_t_le(IEEE80211_MGMT_FRAME |
+	    IEEE80211_MGMT_ASSOC_REQ_FRAME);
+	memcpy(mgmt_header->dest_addr, auth_data->bssid.address, ETH_ADDR);
+	memcpy(mgmt_header->src_addr, nic_address.address, ETH_ADDR);
+	memcpy(mgmt_header->bssid, auth_data->bssid.address, ETH_ADDR);
+	
+	ieee80211_assoc_req_body_t *assoc_body =
+	    (ieee80211_assoc_req_body_t *)
+	    (buffer + sizeof(ieee80211_mgmt_header_t));
+	assoc_body->listen_interval = host2uint16_t_le(1);
+	
+	/* Jump to payload. */
+	void *it = buffer + sizeof(ieee80211_mgmt_header_t) +
+	    sizeof(ieee80211_assoc_req_body_t);
+	ieee80211_prepare_ie_header(&it, IEEE80211_SSID_IE,
+	    ssid_data_size, (void *) auth_data->ssid);
+	ieee80211_prepare_ie_header(&it, IEEE80211_RATES_IE,
+	    IEEE80211_DATA_RATES_SIZE, (void *) &ieee80211bg_data_rates);
+	ieee80211_prepare_ie_header(&it, IEEE80211_EXT_RATES_IE,
+	    IEEE80211_EXT_DATA_RATES_SIZE,
+	    (void *) &ieee80211bg_data_rates[IEEE80211_DATA_RATES_SIZE]);
+	
+	if (auth_data->security.type != IEEE80211_SECURITY_OPEN)
+		assoc_body->capability |= host2uint16_t_le(CAP_SECURITY);
+	
+	if ((auth_data->security.type == IEEE80211_SECURITY_WPA) ||
+	    (auth_data->security.type == IEEE80211_SECURITY_WPA2))
+		memcpy(it, auth_link->auth_ie, auth_link->auth_ie_len);
+	
+	ieee80211_dev->ops->tx_handler(ieee80211_dev, buffer, buffer_size);
+	
+	/*
+	 * Save password to be used in eventual authentication handshake.
+	 */
+	memset(ieee80211_dev->bssid_info.password, 0, IEEE80211_MAX_PASSW_LEN);
+	memcpy(ieee80211_dev->bssid_info.password, password,
+	    str_size(password));
+	
+	free(buffer);
+	
+	return EOK;
+}
+
+/** IEEE 802.11 deauthentication implementation.
+ *
+ * Note: Expecting locked results_mutex or scan_mutex.
+ *
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int ieee80211_deauthenticate(ieee80211_dev_t *ieee80211_dev)
+{
+	ieee80211_scan_result_t *auth_data =
+	    &ieee80211_dev->bssid_info.res_link->scan_result;
+	
+	nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
+	nic_address_t nic_address;
+	nic_query_address(nic, &nic_address);
+	
+	size_t buffer_size = sizeof(ieee80211_mgmt_header_t) +
+	    sizeof(ieee80211_deauth_body_t);
+	
+	void *buffer = malloc(buffer_size);
+	if (!buffer)
+		return ENOMEM;
+	
+	memset(buffer, 0, buffer_size);
+	
+	ieee80211_mgmt_header_t *mgmt_header =
+	    (ieee80211_mgmt_header_t *) buffer;
+	
+	mgmt_header->frame_ctrl =
+	    host2uint16_t_le(IEEE80211_MGMT_FRAME |
+	    IEEE80211_MGMT_DEAUTH_FRAME);
+	memcpy(mgmt_header->dest_addr, auth_data->bssid.address, ETH_ADDR);
+	memcpy(mgmt_header->src_addr, nic_address.address, ETH_ADDR);
+	memcpy(mgmt_header->bssid, auth_data->bssid.address, ETH_ADDR);
+	
+	ieee80211_dev->ops->tx_handler(ieee80211_dev, buffer, buffer_size);
+	
+	free(buffer);
+	
+	ieee80211_dev->bssid_info.res_link = NULL;
+	ieee80211_dev->ops->bssid_change(ieee80211_dev, false);
+	
+	if (ieee80211_query_using_key(ieee80211_dev))
+		ieee80211_dev->ops->key_config(ieee80211_dev, NULL, false);
+	
+	ieee80211_set_auth_phase(ieee80211_dev, IEEE80211_AUTH_DISCONNECTED);
+	
+	return EOK;
+}
+
+static void ieee80211_process_auth_info(ieee80211_scan_result_link_t *ap_data,
+    void *buffer)
+{
+	uint8_t *it = (uint8_t *) buffer;
+	
+	uint16_t *version = (uint16_t *) it;
+	if (uint16_t_le2host(*version) != 0x1) {
+		ap_data->scan_result.security.type = -1;
+		return;
+	}
+	
+	it += sizeof(uint16_t);
+	
+	uint32_t group_cipher = *(it + 3);
+	switch (group_cipher) {
+	case IEEE80211_AUTH_CIPHER_TKIP:
+		ap_data->scan_result.security.group_alg =
+		    IEEE80211_SECURITY_SUITE_TKIP;
+		break;
+	case IEEE80211_AUTH_CIPHER_CCMP:
+		ap_data->scan_result.security.group_alg =
+		    IEEE80211_SECURITY_SUITE_CCMP;
+		break;
+	default:
+		ap_data->scan_result.security.group_alg = -1;
+	}
+	
+	it += 4 * sizeof(uint8_t);
+	
+	uint16_t *pairwise_count = (uint16_t *) it;
+	uint32_t pairwise_cipher = *(it + sizeof(uint16_t) + 3);
+	switch (pairwise_cipher) {
+	case IEEE80211_AUTH_CIPHER_TKIP:
+		ap_data->scan_result.security.pair_alg =
+		    IEEE80211_SECURITY_SUITE_TKIP;
+		break;
+	case IEEE80211_AUTH_CIPHER_CCMP:
+		ap_data->scan_result.security.pair_alg =
+		    IEEE80211_SECURITY_SUITE_CCMP;
+		break;
+	default:
+		ap_data->scan_result.security.pair_alg = -1;
+	}
+	
+	it += 2 * sizeof(uint16_t) +
+	    uint16_t_le2host(*pairwise_count) * sizeof(uint32_t);
+	
+	uint32_t auth_suite = *(it + 3);
+	switch (auth_suite) {
+	case IEEE80211_AUTH_AKM_PSK:
+		ap_data->scan_result.security.auth =
+		    IEEE80211_SECURITY_AUTH_PSK;
+		break;
+	case IEEE80211_AUTH_AKM_8021X:
+		ap_data->scan_result.security.auth =
+		    IEEE80211_SECURITY_AUTH_8021X;
+		break;
+	default:
+		ap_data->scan_result.security.auth = -1;
+	}
+}
+
+static void copy_auth_ie(ieee80211_ie_header_t *ie_header,
+    ieee80211_scan_result_link_t *ap_data, void *it)
+{
+	ap_data->auth_ie_len = ie_header->length +
+	    sizeof(ieee80211_ie_header_t);
+	
+	memcpy(ap_data->auth_ie, it, ap_data->auth_ie_len);
+}
+
+static uint8_t *ieee80211_process_ies(ieee80211_dev_t *ieee80211_dev,
+    ieee80211_scan_result_link_t *ap_data, void *buffer, size_t buffer_size)
+{
+	void *it = buffer;
+	while ((it + sizeof(ieee80211_ie_header_t)) < buffer + buffer_size) {
+		ieee80211_ie_header_t *ie_header =
+		    (ieee80211_ie_header_t *) it;
+		uint8_t *channel;
+		uint32_t oui;
+		
+		switch (ie_header->element_id) {
+		case IEEE80211_CHANNEL_IE:
+			if (!ap_data)
+				break;
+			
+			channel = (uint8_t *)
+			    (it + sizeof(ieee80211_ie_header_t));
+			ap_data->scan_result.channel = *channel;
+			break;
+		case IEEE80211_RSN_IE:
+			if (!ap_data)
+				break;
+			
+			ap_data->scan_result.security.type =
+			    IEEE80211_SECURITY_WPA2;
+			ieee80211_process_auth_info(ap_data,
+			    it + sizeof(ieee80211_ie_header_t));
+			copy_auth_ie(ie_header, ap_data, it);
+			break;
+		case IEEE80211_VENDOR_IE:
+			oui = uint32be_from_seq(it +
+			    sizeof(ieee80211_ie_header_t));
+			
+			if (oui == WPA_OUI) {
+				if (!ap_data)
+					break;
+				
+				/* Prefering WPA2. */
+				if (ap_data->scan_result.security.type ==
+				    IEEE80211_SECURITY_WPA2)
+					break;
+				
+				ap_data->scan_result.security.type =
+				    IEEE80211_SECURITY_WPA;
+				
+				ieee80211_process_auth_info(ap_data,
+				    it + sizeof(ieee80211_ie_header_t) +
+				    sizeof(uint32_t));
+				copy_auth_ie(ie_header, ap_data, it);
+			} else if (oui == GTK_OUI) {
+				return it +
+				    sizeof(ieee80211_ie_header_t) +
+				    sizeof(uint32_t);
+			}
+		}
+		
+		it += sizeof(ieee80211_ie_header_t) + ie_header->length;
+	}
+	
+	return NULL;
+}
+
+/** Process probe response and store results.
+ *
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * @param mgmt_header   Pointer to start of management frame header.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+static int ieee80211_process_probe_response(ieee80211_dev_t *ieee80211_dev,
+    ieee80211_mgmt_header_t *mgmt_header, size_t buffer_size)
+{
+	ieee80211_beacon_start_t *beacon_body = (ieee80211_beacon_start_t *)
+	    ((void *) mgmt_header + sizeof(ieee80211_mgmt_header_t));
+	
+	ieee80211_ie_header_t *ssid_ie_header = (ieee80211_ie_header_t *)
+	    ((void *) beacon_body + sizeof(ieee80211_beacon_start_t));
+	
+	/* Not empty SSID. */
+	if (ssid_ie_header->length > 0) {
+		ieee80211_scan_result_list_t *result_list =
+		    &ieee80211_dev->ap_list;
+		
+		uint8_t *ssid_start = (uint8_t *) ((void *) ssid_ie_header +
+		    sizeof(ieee80211_ie_header_t));
+		char ssid[IEEE80211_MAX_SSID_LENGTH];
+		
+		memcpy(ssid, ssid_start, ssid_ie_header->length);
+		ssid[ssid_ie_header->length] = '\0';
+		
+		/* Check whether SSID is already in results. */
+		ieee80211_scan_result_list_foreach(*result_list, result) {
+			if (!str_cmp(ssid, result->scan_result.ssid)) {
+				result->last_beacon = time(NULL);
+				return EOK;
+			}
+		}
+		
+		/* Results are full. */
+		if (result_list->size == IEEE80211_MAX_RESULTS_LENGTH - 1)
+			return EOK;
+		
+		ieee80211_scan_result_link_t *ap_data =
+		    malloc(sizeof(ieee80211_scan_result_link_t));
+		if (!ap_data)
+			return ENOMEM;
+		
+		memset(ap_data, 0, sizeof(ieee80211_scan_result_link_t));
+		link_initialize(&ap_data->link);
+		
+		memcpy(ap_data->scan_result.bssid.address,
+		    mgmt_header->bssid, ETH_ADDR);
+		memcpy(ap_data->scan_result.ssid, ssid,
+		    ssid_ie_header->length + 1);
+		
+		if (uint16_t_le2host(beacon_body->capability) & CAP_SECURITY) {
+			ap_data->scan_result.security.type =
+			    IEEE80211_SECURITY_WEP;
+		} else {
+			ap_data->scan_result.security.type =
+			    IEEE80211_SECURITY_OPEN;
+			ap_data->scan_result.security.auth = -1;
+			ap_data->scan_result.security.pair_alg = -1;
+			ap_data->scan_result.security.group_alg = -1;
+		}
+		
+		void *rest_ies_start = ssid_start + ssid_ie_header->length;
+		size_t rest_buffer_size =
+		    buffer_size -
+		    sizeof(ieee80211_mgmt_header_t) -
+		    sizeof(ieee80211_beacon_start_t) -
+		    sizeof(ieee80211_ie_header_t) -
+		    ssid_ie_header->length;
+		
+		ieee80211_process_ies(ieee80211_dev, ap_data, rest_ies_start,
+		    rest_buffer_size);
+		
+		ap_data->last_beacon = time(NULL);
+		
+		fibril_mutex_lock(&ieee80211_dev->ap_list.results_mutex);
+		ieee80211_scan_result_list_append(result_list, ap_data);
+		fibril_mutex_unlock(&ieee80211_dev->ap_list.results_mutex);
+	}
+	
+	return EOK;
+}
+
+/** Process authentication response.
+ *
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * @param mgmt_header   Pointer to start of management frame header.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+static int ieee80211_process_auth_response(ieee80211_dev_t *ieee80211_dev,
+    ieee80211_mgmt_header_t *mgmt_header)
+{
+	ieee80211_auth_body_t *auth_body =
+	    (ieee80211_auth_body_t *)
+	    ((void *) mgmt_header + sizeof(ieee80211_mgmt_header_t));
+	
+	if (auth_body->status != 0)
+		ieee80211_set_auth_phase(ieee80211_dev,
+		    IEEE80211_AUTH_DISCONNECTED);
+	else
+		ieee80211_set_auth_phase(ieee80211_dev,
+		    IEEE80211_AUTH_AUTHENTICATED);
+	
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	fibril_condvar_signal(&ieee80211_dev->gen_cond);
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return EOK;
+}
+
+/** Process association response.
+ *
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * @param mgmt_header   Pointer to start of management frame header.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+static int ieee80211_process_assoc_response(ieee80211_dev_t *ieee80211_dev,
+    ieee80211_mgmt_header_t *mgmt_header)
+{
+	ieee80211_assoc_resp_body_t *assoc_resp =
+	    (ieee80211_assoc_resp_body_t *) ((void *) mgmt_header +
+	    sizeof(ieee80211_mgmt_header_t));
+	
+	if (assoc_resp->status != 0)
+		ieee80211_set_auth_phase(ieee80211_dev,
+		    IEEE80211_AUTH_DISCONNECTED);
+	else {
+		ieee80211_dev->bssid_info.aid =
+		    uint16_t_le2host(assoc_resp->aid);
+		ieee80211_set_auth_phase(ieee80211_dev,
+		    IEEE80211_AUTH_ASSOCIATED);
+		ieee80211_dev->ops->bssid_change(ieee80211_dev, true);
+	}
+	
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	fibril_condvar_signal(&ieee80211_dev->gen_cond);
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	return EOK;
+}
+
+static int ieee80211_process_4way_handshake(ieee80211_dev_t *ieee80211_dev,
+    void *buffer, size_t buffer_size)
+{
+	ieee80211_eapol_key_frame_t *key_frame =
+	    (ieee80211_eapol_key_frame_t *) buffer;
+	
+	ieee80211_scan_result_link_t *auth_link =
+	    ieee80211_dev->bssid_info.res_link;
+	
+	ieee80211_scan_result_t *auth_data = &auth_link->scan_result;
+	
+	/* We don't support 802.1X authentication yet. */
+	if (auth_data->security.auth == IEEE80211_AUTH_AKM_8021X)
+		return ENOTSUP;
+	
+	uint8_t *ptk = ieee80211_dev->bssid_info.ptk;
+	uint8_t *gtk = ieee80211_dev->bssid_info.gtk;
+	uint8_t gtk_id = 1;
+	
+	bool handshake_done = false;
+	
+	bool old_wpa =
+	    auth_data->security.type == IEEE80211_SECURITY_WPA;
+	
+	bool key_phase =
+	    uint16_t_be2host(key_frame->key_info) &
+	    IEEE80211_EAPOL_KEY_KEYINFO_MIC;
+	
+	bool final_phase =
+	    uint16_t_be2host(key_frame->key_info) &
+	    IEEE80211_EAPOL_KEY_KEYINFO_SECURE;
+	
+	bool ccmp_used =
+	    (auth_data->security.pair_alg == IEEE80211_SECURITY_SUITE_CCMP) ||
+	    (auth_data->security.group_alg == IEEE80211_SECURITY_SUITE_CCMP);
+	
+	size_t ptk_key_length, gtk_key_length;
+	hash_func_t mic_hash;
+	if (ccmp_used)
+		mic_hash = HASH_SHA1;
+	else
+		mic_hash = HASH_MD5;
+	
+	if (auth_data->security.pair_alg == IEEE80211_SECURITY_SUITE_CCMP)
+		ptk_key_length = IEEE80211_PTK_CCMP_LENGTH;
+	else
+		ptk_key_length = IEEE80211_PTK_TKIP_LENGTH;
+	
+	if (auth_data->security.group_alg == IEEE80211_SECURITY_SUITE_CCMP)
+		gtk_key_length = IEEE80211_GTK_CCMP_LENGTH;
+	else
+		gtk_key_length = IEEE80211_GTK_TKIP_LENGTH;
+	
+	size_t output_size =
+	    sizeof(eth_header_t) +
+	    sizeof(ieee80211_eapol_key_frame_t);
+	
+	if (!(uint16_t_be2host(key_frame->key_info) &
+	    IEEE80211_EAPOL_KEY_KEYINFO_MIC))
+		output_size += auth_link->auth_ie_len;
+	
+	nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
+	nic_address_t nic_address;
+	nic_query_address(nic, &nic_address);
+	
+	void *output_buffer = malloc(output_size);
+	if (!output_buffer)
+		return ENOMEM;
+	
+	memset(output_buffer, 0, output_size);
+	
+	/* Setup ethernet header. */
+	eth_header_t *eth_header = (eth_header_t *) output_buffer;
+	memcpy(eth_header->dest_addr, auth_data->bssid.address, ETH_ADDR);
+	memcpy(eth_header->src_addr, nic_address.address, ETH_ADDR);
+	eth_header->proto = host2uint16_t_be(ETH_TYPE_PAE);
+	
+	ieee80211_eapol_key_frame_t *output_key_frame =
+	    (ieee80211_eapol_key_frame_t *)
+	    (output_buffer + sizeof(eth_header_t));
+	
+	/* Copy content of incoming EAPOL-Key frame. */
+	memcpy((void *) output_key_frame, buffer,
+	    sizeof(ieee80211_eapol_key_frame_t));
+	
+	output_key_frame->proto_version = 0x1;
+	output_key_frame->body_length =
+	    host2uint16_t_be(output_size - sizeof(eth_header_t) - 4);
+	output_key_frame->key_info &=
+	    ~host2uint16_t_be(IEEE80211_EAPOL_KEY_KEYINFO_ACK);
+	
+	if (key_phase) {
+		output_key_frame->key_info &=
+		    ~host2uint16_t_be(IEEE80211_EAPOL_KEY_KEYINFO_ENCDATA);
+		output_key_frame->key_info &=
+		    ~host2uint16_t_be(IEEE80211_EAPOL_KEY_KEYINFO_INSTALL);
+		output_key_frame->key_data_length = 0;
+		memset(output_key_frame->key_nonce, 0, 32);
+		memset(output_key_frame->key_mic, 0, 16);
+		memset(output_key_frame->key_rsc, 0, 8);
+		memset(output_key_frame->eapol_key_iv, 0, 16);
+		
+		/* Derive GTK and save it. */
+		if (final_phase) {
+			uint16_t key_data_length =
+			    uint16_t_be2host(key_frame->key_data_length);
+			uint8_t key_data[key_data_length];
+			uint8_t *data_ptr = (uint8_t *)
+			    (buffer + sizeof(ieee80211_eapol_key_frame_t));
+			
+			int rc;
+			uint8_t work_key[32];
+			
+			if (ccmp_used) {
+				rc = ieee80211_aes_key_unwrap(ptk + KEK_OFFSET,
+				    data_ptr, key_data_length, key_data);
+			} else {
+				memcpy(work_key, key_frame->eapol_key_iv, 16);
+				memcpy(work_key + 16, ptk + KEK_OFFSET, 16);
+				rc = ieee80211_rc4_key_unwrap(work_key,
+				    data_ptr, key_data_length, key_data);
+			}
+			
+			if (rc == EOK) {
+				uint8_t *key_data_ptr = old_wpa ? key_data :
+				    ieee80211_process_ies(ieee80211_dev,
+				    NULL, key_data, key_data_length);
+				
+				if (key_data_ptr) {
+					uint8_t *key_ptr;
+					
+					if (old_wpa)
+						key_ptr = key_data_ptr;
+					else {
+						gtk_id = *key_data_ptr & 0x3;
+						key_ptr = key_data_ptr + 2;
+					}
+					
+					memcpy(gtk, key_ptr, gtk_key_length);
+					handshake_done = true;
+				}
+			}
+		}
+	} else {
+		output_key_frame->key_info |=
+		    host2uint16_t_be(IEEE80211_EAPOL_KEY_KEYINFO_MIC);
+		output_key_frame->key_data_length =
+		    host2uint16_t_be(auth_link->auth_ie_len);
+		memcpy((void *) output_key_frame +
+		    sizeof(ieee80211_eapol_key_frame_t),
+		    auth_link->auth_ie, auth_link->auth_ie_len);
+		
+		/* Compute PMK. */
+		uint8_t pmk[PBKDF2_KEY_LENGTH];
+		pbkdf2((uint8_t *) ieee80211_dev->bssid_info.password,
+		    str_size(ieee80211_dev->bssid_info.password),
+		    (uint8_t *) auth_data->ssid,
+		    str_size(auth_data->ssid), pmk);
+		
+		uint8_t *anonce = key_frame->key_nonce;
+		
+		/* Generate SNONCE. */
+		uint8_t snonce[32];
+		rnd_sequence(snonce, 32);
+		
+		memcpy(output_key_frame->key_nonce, snonce, 32);
+		
+		uint8_t *dest_addr = eth_header->dest_addr;
+		uint8_t *src_addr = eth_header->src_addr;
+		
+		/* Derive PTK and save it. */
+		uint8_t crypt_data[PRF_CRYPT_DATA_LENGTH];
+		memcpy(crypt_data,
+		    min_sequence(dest_addr, src_addr, ETH_ADDR), ETH_ADDR);
+		memcpy(crypt_data + ETH_ADDR,
+		    max_sequence(dest_addr, src_addr, ETH_ADDR), ETH_ADDR);
+		memcpy(crypt_data + 2*ETH_ADDR,
+		    min_sequence(anonce, snonce, 32), 32);
+		memcpy(crypt_data + 2*ETH_ADDR + 32,
+		    max_sequence(anonce, snonce, 32), 32);
+		ieee80211_prf(pmk, crypt_data, ptk, ptk_key_length);
+	}
+	
+	/* Compute MIC of key frame data from KCK part of PTK. */
+	uint8_t mic[mic_hash];
+	hmac(ptk, 16, (uint8_t *) output_key_frame,
+	    output_size - sizeof(eth_header_t), mic, mic_hash);
+	
+	memcpy(output_key_frame->key_mic, mic, 16);
+	
+	ieee80211_send_frame(nic, output_buffer, output_size);
+	
+	free(output_buffer);
+	
+	ieee80211_key_config_t key_config;
+	
+	/* Insert Pairwise key. */
+	if ((key_phase && old_wpa) || (final_phase && !old_wpa)) {
+		key_config.suite = auth_data->security.pair_alg;
+		key_config.flags =
+		    IEEE80211_KEY_FLAG_TYPE_PAIRWISE;
+		memcpy(key_config.data,
+		    ptk + TK_OFFSET, ptk_key_length - TK_OFFSET);
+		
+		ieee80211_dev->ops->key_config(ieee80211_dev,
+		    &key_config, true);
+	}
+	
+	/* Insert Group key. */
+	if (final_phase) {
+		key_config.id = gtk_id;
+		key_config.suite = auth_data->security.group_alg;
+		key_config.flags = IEEE80211_KEY_FLAG_TYPE_GROUP;
+		memcpy(key_config.data, gtk, gtk_key_length);
+		
+		ieee80211_dev->ops->key_config(ieee80211_dev,
+		    &key_config, true);
+	}
+	
+	/* Signal successful handshake completion. */
+	if (handshake_done) {
+		fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+		fibril_condvar_signal(&ieee80211_dev->gen_cond);
+		fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	}
+	
+	return EOK;
+}
+
+static int ieee80211_process_eapol_frame(ieee80211_dev_t *ieee80211_dev,
+    void *buffer, size_t buffer_size)
+{
+	ieee80211_eapol_key_frame_t *key_frame =
+	    (ieee80211_eapol_key_frame_t *) buffer;
+	
+	if (ieee80211_is_eapol_key_frame(key_frame))
+		return ieee80211_process_4way_handshake(ieee80211_dev, buffer,
+		    buffer_size);
+	
+	return EOK;
+}
+
+/** Process data frame.
+ *
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * @param buffer        Data buffer starting with IEEE 802.11 data header.
+ * @param buffer_size   Size of buffer.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+static int ieee80211_process_data(ieee80211_dev_t *ieee80211_dev,
+    void *buffer, size_t buffer_size)
+{
+	ieee80211_data_header_t *data_header =
+	    (ieee80211_data_header_t *) buffer;
+	
+	if (ieee80211_has_data_frame(data_header->frame_ctrl)) {
+		nic_t *nic = nic_get_from_ddf_dev(ieee80211_dev->ddf_dev);
+		size_t strip_length = sizeof(ieee80211_data_header_t) +
+		    ARRAY_SIZE(rfc1042_header);
+		
+		/* TODO: Different by used security alg. */
+		/* TODO: Trim frame by used security alg. */
+		// TODO: Distinguish used key (pair/group) by dest address ?
+		if (ieee80211_is_encrypted_frame(data_header->frame_ctrl))
+			strip_length += 8;
+		
+		/* Process 4-way authentication handshake. */
+		uint16_t *proto = (uint16_t *) (buffer + strip_length);
+		if (uint16_t_be2host(*proto) == ETH_TYPE_PAE)
+			return ieee80211_process_eapol_frame(ieee80211_dev,
+			    buffer + strip_length + sizeof(uint16_t),
+			    buffer_size - strip_length - sizeof(uint16_t));
+		
+		/*
+		 * Note: ETH protocol ID is already there, so we don't create
+		 * whole ETH header.
+		 */
+		size_t frame_size =
+		    buffer_size - strip_length + sizeof(eth_header_t) - 2;
+		nic_frame_t *frame = nic_alloc_frame(nic, frame_size);
+		
+		if(frame == NULL)
+			return ENOMEM;
+		
+		uint8_t *src_addr =
+		    ieee80211_is_fromds_frame(data_header->frame_ctrl) ?
+		    data_header->address3 : data_header->address2;
+		uint8_t *dest_addr =
+		    ieee80211_is_tods_frame(data_header->frame_ctrl) ?
+		    data_header->address3 : data_header->address1;
+		
+		eth_header_t *eth_header = (eth_header_t *) frame->data;
+		memcpy(eth_header->src_addr, src_addr, ETH_ADDR);
+		memcpy(eth_header->dest_addr, dest_addr, ETH_ADDR);
+		
+		memcpy(frame->data + sizeof(eth_header_t) - 2,
+		    buffer + strip_length, buffer_size - strip_length);
+		
+		nic_received_frame(nic, frame);
+	}
+	
+	return EOK;
+}
+
+/** IEEE 802.11 RX frames handler.
+ *
+ * @param ieee80211_dev Pointer to IEEE 802.11 device structure.
+ * @param buffer        Buffer with data.
+ * @param buffer_size   Size of buffer.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int ieee80211_rx_handler(ieee80211_dev_t *ieee80211_dev, void *buffer,
+    size_t buffer_size)
+{
+	uint16_t frame_ctrl = *((uint16_t *) buffer);
+	
+	if (ieee80211_is_mgmt_frame(frame_ctrl)) {
+		ieee80211_mgmt_header_t *mgmt_header =
+		    (ieee80211_mgmt_header_t *) buffer;
+		
+		if ((ieee80211_is_probe_response_frame(mgmt_header->frame_ctrl)) ||
+		    (ieee80211_is_beacon_frame(mgmt_header->frame_ctrl)))
+			return ieee80211_process_probe_response(ieee80211_dev,
+			    mgmt_header, buffer_size);
+		
+		if (ieee80211_is_auth_frame(mgmt_header->frame_ctrl))
+			return ieee80211_process_auth_response(ieee80211_dev,
+			    mgmt_header);
+		
+		if (ieee80211_is_assoc_response_frame(mgmt_header->frame_ctrl))
+			return ieee80211_process_assoc_response(ieee80211_dev,
+			    mgmt_header);
+	} else if (ieee80211_is_data_frame(frame_ctrl))
+		return ieee80211_process_data(ieee80211_dev, buffer,
+		    buffer_size);
+	
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/ieee80211/src/ieee80211_iface_impl.c
===================================================================
--- uspace/lib/ieee80211/src/ieee80211_iface_impl.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/ieee80211/src/ieee80211_iface_impl.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libieee80211
+ * @{
+ */
+
+/** @file ieee80211_iface_impl.c
+ *
+ * IEEE 802.11 default interface functions implementation.
+ */
+
+#include <str.h>
+#include <errno.h>
+#include <ieee80211_private.h>
+#include <ieee80211_iface_impl.h>
+
+/** Implementation of fetching scan results.
+ *
+ * @param fun     Device function.
+ * @param results Structure where should be stored scan results.
+ *
+ * @return EOK if everything went OK,
+ *         EREFUSED when device is not ready yet.
+ *
+ */
+int ieee80211_get_scan_results_impl(ddf_fun_t *fun,
+    ieee80211_scan_results_t *results, bool now)
+{
+	nic_t *nic_data = nic_get_from_ddf_fun(fun);
+	ieee80211_dev_t *ieee80211_dev = nic_get_specific(nic_data);
+	
+	if (!ieee80211_is_ready(ieee80211_dev))
+		return EREFUSED;
+	
+	if (now)
+		ieee80211_dev->ops->scan(ieee80211_dev);
+	
+	fibril_mutex_lock(&ieee80211_dev->ap_list.results_mutex);
+	
+	if (results) {
+		ieee80211_scan_result_list_t *result_list =
+		    &ieee80211_dev->ap_list;
+		
+		unsigned int i = 0;
+		ieee80211_scan_result_list_foreach(*result_list, result) {
+			memcpy(&results->results[i],
+			    &result->scan_result,
+			    sizeof(ieee80211_scan_result_t));
+			i++;
+		}
+		
+		results->length = i;
+	}
+	
+	fibril_mutex_unlock(&ieee80211_dev->ap_list.results_mutex);
+	
+	return EOK;
+}
+
+static uint16_t ieee80211_channel_to_freq(uint8_t channel)
+{
+	return IEEE80211_CHANNEL_GAP * (channel - 1) + IEEE80211_FIRST_FREQ;
+}
+
+/** Working procedure of connect function.
+ *
+ * @param ieee80211_dev Pointer to IEEE 802.11 device.
+ * @param auth_data     Selected AP data we want to connect to.
+ *
+ * @return EOK if everything OK,
+ *         ETIMEOUT when timeout during authenticating.
+ *
+ */
+static int ieee80211_connect_proc(ieee80211_dev_t *ieee80211_dev,
+    ieee80211_scan_result_link_t *auth_data, char *password)
+{
+	ieee80211_dev->bssid_info.res_link = auth_data;
+	
+	/* Set channel. */
+	int rc = ieee80211_dev->ops->set_freq(ieee80211_dev,
+	    ieee80211_channel_to_freq(auth_data->scan_result.channel));
+	if (rc != EOK)
+		return rc;
+	
+	/* Try to authenticate. */
+	ieee80211_authenticate(ieee80211_dev);
+	
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	rc = fibril_condvar_wait_timeout(&ieee80211_dev->gen_cond,
+	    &ieee80211_dev->gen_mutex, AUTH_TIMEOUT);
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	if (rc != EOK)
+		return rc;
+	
+	if (ieee80211_get_auth_phase(ieee80211_dev) !=
+	    IEEE80211_AUTH_AUTHENTICATED) {
+		ieee80211_set_auth_phase(ieee80211_dev,
+		    IEEE80211_AUTH_DISCONNECTED);
+		return EINVAL;
+	}
+	
+	/* Try to associate. */
+	ieee80211_associate(ieee80211_dev, password);
+	
+	fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+	rc = fibril_condvar_wait_timeout(&ieee80211_dev->gen_cond,
+	    &ieee80211_dev->gen_mutex, AUTH_TIMEOUT);
+	fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+	
+	if (rc != EOK)
+		return rc;
+	
+	if (ieee80211_get_auth_phase(ieee80211_dev) !=
+	    IEEE80211_AUTH_ASSOCIATED) {
+		ieee80211_set_auth_phase(ieee80211_dev,
+		    IEEE80211_AUTH_DISCONNECTED);
+		return EINVAL;
+	}
+	
+	/* On open network, we are finished. */
+	if (auth_data->scan_result.security.type !=
+	    IEEE80211_SECURITY_OPEN) {
+		/* Otherwise wait for 4-way handshake to complete. */
+		
+		fibril_mutex_lock(&ieee80211_dev->gen_mutex);
+		rc = fibril_condvar_wait_timeout(&ieee80211_dev->gen_cond,
+		    &ieee80211_dev->gen_mutex, HANDSHAKE_TIMEOUT);
+		fibril_mutex_unlock(&ieee80211_dev->gen_mutex);
+		
+		if (rc != EOK) {
+			ieee80211_deauthenticate(ieee80211_dev);
+			return rc;
+		}
+	}
+	
+	ieee80211_set_auth_phase(ieee80211_dev, IEEE80211_AUTH_CONNECTED);
+	
+	return EOK;
+}
+
+/** Implementation of connecting to specified SSID.
+ *
+ * @param fun        Device function.
+ * @param ssid_start SSID prefix of access point we want to connect to.
+ *
+ * @return EOK if everything OK,
+ *         ETIMEOUT when timeout during authenticating,
+ *         EINVAL when SSID not in scan results list,
+ *         EPERM when incorrect password passed,
+ *         EREFUSED when device is not ready yet.
+ *
+ */
+int ieee80211_connect_impl(ddf_fun_t *fun, char *ssid_start, char *password)
+{
+	assert(ssid_start);
+	assert(password);
+	
+	nic_t *nic_data = nic_get_from_ddf_fun(fun);
+	ieee80211_dev_t *ieee80211_dev = nic_get_specific(nic_data);
+	
+	if (!ieee80211_is_ready(ieee80211_dev))
+		return EREFUSED;
+	
+	if (ieee80211_is_connected(ieee80211_dev)) {
+		int rc = ieee80211_dev->iface->disconnect(fun);
+		if (rc != EOK)
+			return rc;
+	}
+	
+	ieee80211_set_connect_request(ieee80211_dev);
+	
+	int rc = ENOENT;
+	fibril_mutex_lock(&ieee80211_dev->scan_mutex);
+	
+	ieee80211_dev->pending_conn_req = false;
+	
+	ieee80211_scan_result_list_foreach(ieee80211_dev->ap_list, result) {
+		if (!str_lcmp(ssid_start, result->scan_result.ssid,
+		    str_size(ssid_start))) {
+			rc = ieee80211_connect_proc(ieee80211_dev, result,
+			    password);
+			break;
+		}
+	}
+	
+	fibril_mutex_unlock(&ieee80211_dev->scan_mutex);
+	
+	return rc;
+}
+
+/** Implementation of disconnecting device from network.
+ *
+ * @param fun Device function.
+ *
+ * @return EOK if everything OK,
+ *         EREFUSED if device is not ready yet.
+ *
+ */
+int ieee80211_disconnect_impl(ddf_fun_t *fun)
+{
+	nic_t *nic_data = nic_get_from_ddf_fun(fun);
+	ieee80211_dev_t *ieee80211_dev = nic_get_specific(nic_data);
+	
+	if (!ieee80211_is_ready(ieee80211_dev))
+		return EREFUSED;
+	
+	if (!ieee80211_is_connected(ieee80211_dev))
+		return EOK;
+	
+	fibril_mutex_lock(&ieee80211_dev->ap_list.results_mutex);
+	int rc = ieee80211_deauthenticate(ieee80211_dev);
+	fibril_mutex_unlock(&ieee80211_dev->ap_list.results_mutex);
+	
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/ieee80211/src/ieee80211_impl.c
===================================================================
--- uspace/lib/ieee80211/src/ieee80211_impl.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
+++ uspace/lib/ieee80211/src/ieee80211_impl.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libieee80211
+ * @{
+ */
+
+/** @file ieee80211_impl.c
+ *
+ * IEEE 802.11 default device functions implementation.
+ */
+
+#include <stdio.h>
+#include <crypto.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ieee80211_impl.h>
+
+/** Default implementation of IEEE802.11 start function.
+ *
+ * @param ieee80211_dev Structure of IEEE802.11 device.
+ *
+ * @return EOK.
+ *
+ */
+int ieee80211_start_impl(ieee80211_dev_t *ieee80211_dev)
+{
+	return EOK;
+}
+
+/** Default implementation of IEEE802.11 TX handler function.
+ *
+ * @param ieee80211_dev Structure of IEEE802.11 device.
+ * @param buffer        Buffer with data to send.
+ * @param buffer_size   Size of buffer.
+ *
+ * @return EOK.
+ *
+ */
+int ieee80211_tx_handler_impl(ieee80211_dev_t *ieee80211_dev, void *buffer,
+    size_t buffer_size)
+{
+	return EOK;
+}
+
+/** Default implementation of IEEE802.11 set frequency function.
+ *
+ * @param ieee80211_dev Structure of IEEE802.11 device.
+ * @param freq          Value of frequency to be switched on.
+ *
+ * @return EOK.
+ *
+ */
+int ieee80211_set_freq_impl(ieee80211_dev_t *ieee80211_dev, uint16_t freq)
+{
+	return EOK;
+}
+
+/** Default implementation of IEEE802.11 BSSID change function.
+ *
+ * @param ieee80211_dev Structure of IEEE802.11 device.
+ *
+ * @return EOK.
+ *
+ */
+int ieee80211_bssid_change_impl(ieee80211_dev_t *ieee80211_dev,
+    bool connected)
+{
+	return EOK;
+}
+
+/** Default implementation of IEEE802.11 key config function.
+ *
+ * @param ieee80211_dev Structure of IEEE802.11 device.
+ *
+ * @return EOK.
+ *
+ */
+int ieee80211_key_config_impl(ieee80211_dev_t *ieee80211_dev,
+    ieee80211_key_config_t *key_conf, bool insert)
+{
+	return EOK;
+}
+
+/** Default implementation of IEEE802.11 scan function.
+ *
+ * @param ieee80211_dev Structure of IEEE802.11 device.
+ * @param clear         Whether to clear current scan results.
+ *
+ * @return EOK if succeed, negative error code otherwise.
+ *
+ */
+int ieee80211_scan_impl(ieee80211_dev_t *ieee80211_dev)
+{
+	fibril_mutex_lock(&ieee80211_dev->scan_mutex);
+	
+	if (ieee80211_get_auth_phase(ieee80211_dev) ==
+	    IEEE80211_AUTH_DISCONNECTED) {
+		fibril_mutex_lock(&ieee80211_dev->ap_list.results_mutex);
+		
+		/* Remove old entries we don't receive beacons from. */
+		ieee80211_scan_result_list_t *result_list =
+		    &ieee80211_dev->ap_list;
+		
+		list_foreach_safe(result_list->list, cur_link, next_link) {
+			ieee80211_scan_result_link_t *cur_result =
+			    list_get_instance(cur_link,
+			    ieee80211_scan_result_link_t, link);
+			
+			if ((time(NULL) - cur_result->last_beacon) >
+			    MAX_KEEP_SCAN_SPAN_SEC)
+				ieee80211_scan_result_list_remove(result_list,
+				    cur_result);
+		}
+		
+		fibril_mutex_unlock(&ieee80211_dev->ap_list.results_mutex);
+		
+		uint16_t orig_freq = ieee80211_dev->current_freq;
+		
+		for (uint16_t freq = IEEE80211_FIRST_FREQ;
+		    freq <= IEEE80211_MAX_FREQ; freq += IEEE80211_CHANNEL_GAP) {
+			if (ieee80211_pending_connect_request(ieee80211_dev))
+				break;
+			
+			ieee80211_dev->ops->set_freq(ieee80211_dev, freq);
+			ieee80211_probe_request(ieee80211_dev, NULL);
+			
+			/* Wait for probe responses. */
+			async_usleep(SCAN_CHANNEL_WAIT_USEC);
+		}
+		
+		ieee80211_dev->ops->set_freq(ieee80211_dev, orig_freq);
+	}
+	
+	fibril_mutex_unlock(&ieee80211_dev->scan_mutex);
+	
+	return EOK;
+}
+
+/** Pseudorandom function used for IEEE 802.11 pairwise key computation.
+ *
+ * Using SHA1 hash algorithm.
+ *
+ * @param key         Key with PBKDF2 encrypted passphrase.
+ * @param data        Concatenated sequence of both MAC
+ *                    addresses and nonces.
+ * @param hash        Output parameter for result hash.
+ * @param output_size Length of output sequence to be generated.
+ *
+ * @return EINVAL when key or data not specified,
+ *         ENOMEM when pointer for output hash result
+ *         is not allocated, otherwise EOK.
+ *
+ */
+int ieee80211_prf(uint8_t *key, uint8_t *data, uint8_t *hash,
+    size_t output_size)
+{
+	if ((!key) || (!data))
+		return EINVAL;
+	
+	if (!hash)
+		return ENOMEM;
+	
+	size_t iters = ((output_size * 8) + 159) / 160;
+	
+	const char *a = "Pairwise key expansion";
+	uint8_t result[HASH_SHA1 * iters];
+	uint8_t temp[HASH_SHA1];
+	
+	size_t data_size = PRF_CRYPT_DATA_LENGTH + str_size(a) + 2;
+	uint8_t work_arr[data_size];
+	memset(work_arr, 0, data_size);
+	
+	memcpy(work_arr, a, str_size(a));
+	memcpy(work_arr + str_size(a) + 1, data, PRF_CRYPT_DATA_LENGTH);
+	
+	for (uint8_t i = 0; i < iters; i++) {
+		memcpy(work_arr + data_size - 1, &i, 1);
+		hmac(key, PBKDF2_KEY_LENGTH, work_arr, data_size, temp,
+		    HASH_SHA1);
+		memcpy(result + i*HASH_SHA1, temp, HASH_SHA1);
+	}
+	
+	memcpy(hash, result, output_size);
+	
+	return EOK;
+}
+
+int ieee80211_rc4_key_unwrap(uint8_t *key, uint8_t *data, size_t data_size,
+    uint8_t *output)
+{
+	return rc4(key, 32, data, data_size, 256, output);
+}
+
+int ieee80211_aes_key_unwrap(uint8_t *kek, uint8_t *data, size_t data_size,
+    uint8_t *output)
+{
+	if ((!kek) || (!data))
+		return EINVAL;
+	
+	if (!output)
+		return ENOMEM;
+	
+	uint32_t n = data_size / 8 - 1;
+	uint8_t work_data[n * 8];
+	uint8_t work_input[AES_CIPHER_LENGTH];
+	uint8_t work_output[AES_CIPHER_LENGTH];
+	uint8_t *work_block;
+	uint8_t a[8];
+	
+	memcpy(a, data, 8);
+	
+	uint64_t mask = 0xff;
+	uint8_t shift, shb;
+	
+	memcpy(work_data, data + 8, n * 8);
+	for (int j = 5; j >= 0; j--) {
+		for (int i = n; i > 0; i--) {
+			for (size_t k = 0; k < 8; k++) {
+				shift = 56 - 8 * k;
+				shb = ((n * j + i) & (mask << shift)) >> shift;
+				a[k] ^= shb;
+			}
+			
+			work_block = work_data + (i - 1) * 8;
+			memcpy(work_input, a, 8);
+			memcpy(work_input + 8, work_block, 8);
+			aes_decrypt(kek, work_input, work_output);
+			memcpy(a, work_output, 8);
+			memcpy(work_data + (i - 1) * 8, work_output + 8, 8);
+		}
+	}
+	
+	size_t it;
+	for (it = 0; it < 8; it++) {
+		if (a[it] != 0xa6)
+			break;
+	}
+	
+	if (it == 8) {
+		memcpy(output, work_data, n * 8);
+		return EOK;
+	}
+	
+	return EINVAL;
+}
+
+static void ieee80211_michael_mic_block(uint32_t *l, uint32_t *r,
+    uint32_t value)
+{
+	*l ^= value;
+	*r ^= rotl_uint32(*l, 17);
+	*l += *r;
+	*r ^= ((*l & 0x00ff00ff) << 8) | ((*l & 0xff00ff00) >> 8);
+	*l += *r;
+	*r ^= rotl_uint32(*l, 3);
+	*l += *r;
+	*r ^= rotr_uint32(*l, 2);
+	*l += *r;
+}
+
+int ieee80211_michael_mic(uint8_t *key, uint8_t *buffer, size_t size,
+    uint8_t *mic)
+{
+	if ((!key) || (!buffer))
+		return EINVAL;
+	
+	if (!mic)
+		return ENOMEM;
+	
+	uint32_t l = uint32le_from_seq(key);
+	uint32_t r = uint32le_from_seq(key + 4);
+	
+	ieee80211_data_header_t *data_header =
+	    (ieee80211_data_header_t *) buffer;
+	
+	uint8_t *data = buffer + sizeof(ieee80211_data_header_t) +
+	    IEEE80211_TKIP_HEADER_LENGTH;
+	size_t data_size = size - sizeof(ieee80211_data_header_t) -
+	    IEEE80211_TKIP_HEADER_LENGTH;
+	
+	/* Process header. */
+	uint8_t *src_addr =
+	    ieee80211_is_fromds_frame(data_header->frame_ctrl) ?
+	    data_header->address3 : data_header->address2;
+	uint8_t *dest_addr =
+	    ieee80211_is_tods_frame(data_header->frame_ctrl) ?
+	    data_header->address3 : data_header->address1;
+	
+	ieee80211_michael_mic_block(&l, &r, uint32le_from_seq(dest_addr));
+	ieee80211_michael_mic_block(&l, &r,
+	    uint16le_from_seq(dest_addr + 4) |
+	    (uint16le_from_seq(src_addr) << 16));
+	ieee80211_michael_mic_block(&l, &r, uint32le_from_seq(src_addr + 2));
+	ieee80211_michael_mic_block(&l, &r, 0);
+	
+	/* Process data. */
+	size_t blocks = data_size / 4;
+	size_t pad = data_size % 4;
+	
+	for (size_t k = 0; k < blocks; k++) {
+		ieee80211_michael_mic_block(&l, &r,
+		    uint32le_from_seq(&data[k * 4]));
+	}
+	
+	/* Add padding. */
+	uint32_t value = 0x5a;
+	for (size_t i = pad; i > 0; i--) {
+		value <<= 8;
+		value |= data[blocks * 4 + (i - 1)];
+	}
+	
+	ieee80211_michael_mic_block(&l, &r, value);
+	ieee80211_michael_mic_block(&l, &r, 0);
+	
+	l = host2uint32_t_le(l);
+	r = host2uint32_t_le(r);
+	
+	memcpy(mic, &l, 4);
+	memcpy(mic + 4, &r, 4);
+	
+	return EOK;
+}
+
+uint16_t uint16le_from_seq(void *seq)
+{
+	uint16_t *u16 = (uint16_t *) seq;
+	return uint16_t_le2host(*u16);
+}
+
+uint32_t uint32le_from_seq(void *seq)
+{
+	uint32_t *u32 = (uint32_t *) seq;
+	return uint32_t_le2host(*u32);
+}
+
+uint16_t uint16be_from_seq(void *seq)
+{
+	uint16_t *u16 = (uint16_t *) seq;
+	return uint16_t_be2host(*u16);
+}
+
+uint32_t uint32be_from_seq(void *seq)
+{
+	uint32_t *u32 = (uint32_t *) seq;
+	return uint32_t_be2host(*u32);
+}
+
+int rnd_sequence(uint8_t *sequence, size_t length)
+{
+	if (!sequence)
+		return ENOMEM;
+	
+	for (size_t i = 0; i < length; i++)
+		sequence[i] = (uint8_t) rand();
+	
+	return EOK;
+}
+
+uint8_t *min_sequence(uint8_t *seq1, uint8_t *seq2, size_t size)
+{
+	if ((!seq1) || (!seq2))
+		return NULL;
+	
+	for (size_t i = 0; i < size; i++) {
+		if (seq1[i] < seq2[i])
+			return seq1;
+		else if (seq1[i] > seq2[i])
+			return seq2;
+	}
+	
+	return seq1;
+}
+
+uint8_t *max_sequence(uint8_t *seq1, uint8_t *seq2, size_t size)
+{
+	uint8_t *min = min_sequence(seq1, seq2, size);
+	if (min == seq1)
+		return seq2;
+	
+	return seq1;
+}
+
+/** @}
+ */
Index: uspace/lib/pcut/src/run.c
===================================================================
--- uspace/lib/pcut/src/run.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/pcut/src/run.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -73,8 +73,9 @@
 static int default_suite_initialized = 0;
 
-static void init_default_suite_when_needed() {
-	if (default_suite_initialized) {
+static void init_default_suite_when_needed(void)
+{
+	if (default_suite_initialized)
 		return;
-	}
+	
 	default_suite.id = -1;
 	default_suite.kind = PCUT_KIND_TESTSUITE;
@@ -91,11 +92,13 @@
  * @return Always a valid test suite item.
  */
-static pcut_item_t *pcut_find_parent_suite(pcut_item_t *it) {
+static pcut_item_t *pcut_find_parent_suite(pcut_item_t *it)
+{
 	while (it != NULL) {
-		if (it->kind == PCUT_KIND_TESTSUITE) {
+		if (it->kind == PCUT_KIND_TESTSUITE)
 			return it;
-		}
+		
 		it = it->previous;
 	}
+	
 	init_default_suite_when_needed();
 	return &default_suite;
@@ -106,8 +109,8 @@
  * @param func Function to run (can be NULL).
  */
-static void run_setup_teardown(pcut_setup_func_t func) {
-	if (func != NULL) {
+static void run_setup_teardown(pcut_setup_func_t func)
+{
+	if (func != NULL)
 		func();
-	}
 }
 
@@ -119,11 +122,11 @@
  * @param outcome Outcome of the current test.
  */
-static void leave_test(int outcome) {
+static void leave_test(int outcome)
+{
 	PCUT_DEBUG("leave_test(outcome=%d), will_exit=%s", outcome,
-		leave_means_exit ? "yes" : "no");
-	if (leave_means_exit) {
+	    leave_means_exit ? "yes" : "no");
+	if (leave_means_exit)
 		exit(outcome);
-	}
-
+	
 #ifndef PCUT_NO_LONG_JUMP
 	longjmp(start_test_jump, 1);
@@ -138,6 +141,8 @@
  * @param message Message describing the failure.
  */
-void pcut_failed_assertion(const char *message) {
+void pcut_failed_assertion(const char *message)
+{
 	static const char *prev_message = NULL;
+	
 	/*
 	 * The assertion failed. We need to abort the current test,
@@ -145,13 +150,12 @@
 	 * include running the tear-down routine.
 	 */
-	if (print_test_error) {
+	if (print_test_error)
 		pcut_print_fail_message(message);
-	}
-
+	
 	if (execute_teardown_on_failure) {
 		execute_teardown_on_failure = 0;
 		prev_message = message;
 		run_setup_teardown(current_suite->teardown_func);
-
+		
 		/* Tear-down was okay. */
 		if (report_test_result) {
@@ -165,7 +169,7 @@
 		}
 	}
-
+	
 	prev_message = NULL;
-
+	
 	leave_test(TEST_OUTCOME_FAIL); /* No return. */
 }
@@ -176,5 +180,6 @@
  * @return Error status (zero means success).
  */
-static int run_test(pcut_item_t *test) {
+static int run_test(pcut_item_t *test)
+{
 	/*
 	 * Set here as the returning point in case of test failure.
@@ -182,20 +187,19 @@
 	 * test execution.
 	 */
+	
 #ifndef PCUT_NO_LONG_JUMP
 	int test_finished = setjmp(start_test_jump);
-	if (test_finished) {
+	if (test_finished)
 		return 1;
-	}
 #endif
-
-	if (report_test_result) {
+	
+	if (report_test_result)
 		pcut_report_test_start(test);
-	}
-
+	
 	current_suite = pcut_find_parent_suite(test);
 	current_test = test;
-
+	
 	pcut_hook_before_test(test);
-
+	
 	/*
 	 * If anything goes wrong, execute the tear-down function
@@ -203,10 +207,10 @@
 	 */
 	execute_teardown_on_failure = 1;
-
+	
 	/*
 	 * Run the set-up function.
 	 */
 	run_setup_teardown(current_suite->setup_func);
-
+	
 	/*
 	 * The setup function was performed, it is time to run
@@ -214,5 +218,5 @@
 	 */
 	test->test_func();
-
+	
 	/*
 	 * Finally, run the tear-down function. We need to clear
@@ -221,14 +225,13 @@
 	execute_teardown_on_failure = 0;
 	run_setup_teardown(current_suite->teardown_func);
-
+	
 	/*
 	 * If we got here, it means everything went well with
 	 * this test.
 	 */
-	if (report_test_result) {
+	if (report_test_result)
 		pcut_report_test_done(current_test, TEST_OUTCOME_PASS,
-			NULL, NULL, NULL);
-	}
-
+		    NULL, NULL, NULL);
+	
 	return 0;
 }
@@ -242,16 +245,15 @@
  * @return Error status (zero means success).
  */
-int pcut_run_test_forked(pcut_item_t *test) {
-	int rc;
-
+int pcut_run_test_forked(pcut_item_t *test)
+{
 	report_test_result = 0;
 	print_test_error = 1;
 	leave_means_exit = 1;
-
-	rc = run_test(test);
-
+	
+	int rc = run_test(test);
+	
 	current_test = NULL;
 	current_suite = NULL;
-
+	
 	return rc;
 }
@@ -265,16 +267,15 @@
  * @return Error status (zero means success).
  */
-int pcut_run_test_single(pcut_item_t *test) {
-	int rc;
-
+int pcut_run_test_single(pcut_item_t *test)
+{
 	report_test_result = 1;
 	print_test_error = 0;
 	leave_means_exit = 0;
-
-	rc = run_test(test);
-
+	
+	int rc = run_test(test);
+	
 	current_test = NULL;
 	current_suite = NULL;
-
+	
 	return rc;
 }
@@ -285,16 +286,16 @@
  * @return Timeout in seconds.
  */
-int pcut_get_test_timeout(pcut_item_t *test) {
+int pcut_get_test_timeout(pcut_item_t *test)
+{
 	int timeout = PCUT_DEFAULT_TEST_TIMEOUT;
 	pcut_extra_t *extras = test->extras;
-
-
+	
 	while (extras->type != PCUT_EXTRA_LAST) {
-		if (extras->type == PCUT_EXTRA_TIMEOUT) {
+		if (extras->type == PCUT_EXTRA_TIMEOUT)
 			timeout = extras->timeout;
-		}
+		
 		extras++;
 	}
-
+	
 	return timeout;
 }
Index: uspace/lib/posix/include/posix/stdlib.h
===================================================================
--- uspace/lib/posix/include/posix/stdlib.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/posix/include/posix/stdlib.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -116,7 +116,10 @@
 
 /* Memory Allocation */
-extern void *__POSIX_DEF__(malloc)(size_t size);
-extern void *__POSIX_DEF__(calloc)(size_t nelem, size_t elsize);
-extern void *__POSIX_DEF__(realloc)(void *ptr, size_t size);
+extern void *__POSIX_DEF__(malloc)(size_t size)
+    __attribute__((malloc));
+extern void *__POSIX_DEF__(calloc)(size_t nelem, size_t elsize)
+    __attribute__((malloc));
+extern void *__POSIX_DEF__(realloc)(void *ptr, size_t size)
+    __attribute__((warn_unused_result));
 extern void __POSIX_DEF__(free)(void *ptr);
 
Index: uspace/lib/posix/include/posix/string.h
===================================================================
--- uspace/lib/posix/include/posix/string.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/posix/include/posix/string.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -67,7 +67,10 @@
 /* From mem.h */
 // #define bzero(ptr, len)  memset((ptr), 0, (len))
-extern void *memset(void *, int, size_t);
-extern void *memcpy(void *, const void *, size_t);
-extern void *memmove(void *, const void *, size_t);
+extern void *memset(void *, int, size_t)
+    __attribute__((nonnull(1)));
+extern void *memcpy(void *, const void *, size_t)
+    __attribute__((nonnull(1, 2)));
+extern void *memmove(void *, const void *, size_t)
+    __attribute__((nonnull(1, 2)));
 
 
Index: uspace/lib/posix/source/signal.c
===================================================================
--- uspace/lib/posix/source/signal.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/lib/posix/source/signal.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -382,5 +382,5 @@
  * Raise all unblocked previously queued signals.
  */
-static void _dequeue_unblocked_signals()
+static void _dequeue_unblocked_signals(void)
 {
 	link_t *iterator = _signal_queue.head.next;
Index: uspace/srv/bd/sata_bd/sata_bd.c
===================================================================
--- uspace/srv/bd/sata_bd/sata_bd.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/srv/bd/sata_bd/sata_bd.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -147,5 +147,5 @@
  *
  */
-static int get_sata_disks()
+static int get_sata_disks(void)
 {
 	devman_handle_t root_fun;
Index: uspace/srv/klog/klog.c
===================================================================
--- uspace/srv/klog/klog.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/srv/klog/klog.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -51,5 +51,5 @@
 #include <io/logctl.h>
 
-#define NAME       "klog"
+#define NAME  "klog"
 
 typedef size_t __attribute__ ((aligned(1))) unaligned_size_t;
@@ -98,5 +98,5 @@
  *
  */
-static void producer()
+static void producer(void)
 {
 	int read = klog_read(buffer, BUFFER_SIZE);
Index: uspace/srv/locsrv/locsrv.c
===================================================================
--- uspace/srv/locsrv/locsrv.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/srv/locsrv/locsrv.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -1357,4 +1357,7 @@
 	cat = category_new("nic");
 	categ_dir_add_cat(&cdir, cat);
+	
+	cat = category_new("ieee80211");
+	categ_dir_add_cat(&cdir, cat);
 
 	cat = category_new("visualizer");
Index: uspace/srv/net/dhcp/dhcp.c
===================================================================
--- uspace/srv/net/dhcp/dhcp.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/srv/net/dhcp/dhcp.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -419,4 +419,22 @@
 }
 
+static int dhcp_discover_proc(dhcp_link_t *dlink)
+{
+	dlink->state = ds_selecting;
+
+	int rc = dhcp_send_discover(dlink);
+	if (rc != EOK)
+		return EIO;
+
+	dlink->retries_left = dhcp_discover_retries;
+	
+	if ((dlink->timeout->state == fts_not_set) ||
+	    (dlink->timeout->state == fts_fired))
+		fibril_timer_set(dlink->timeout, dhcp_discover_timeout_val,
+		    dhcpsrv_discover_timeout, dlink);
+	
+	return rc;
+}
+
 int dhcpsrv_link_add(service_id_t link_id)
 {
@@ -460,8 +478,6 @@
 	}
 
-	dlink->state = ds_selecting;
-
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPDISCOVER");
-	rc = dhcp_send_discover(dlink);
+	rc = dhcp_discover_proc(dlink);
 	if (rc != EOK) {
 		log_msg(LOG_DEFAULT, LVL_ERROR, "Error sending DHCPDISCOVER.");
@@ -470,8 +486,4 @@
 		goto error;
 	}
-
-	dlink->retries_left = dhcp_discover_retries;
-	fibril_timer_set(dlink->timeout, dhcp_discover_timeout_val,
-	    dhcpsrv_discover_timeout, dlink);
 
 	list_append(&dlink->links, &dhcp_links);
@@ -490,4 +502,19 @@
 }
 
+int dhcpsrv_discover(service_id_t link_id)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcpsrv_link_add(%zu)", link_id);
+	
+	dhcp_link_t *dlink = dhcpsrv_link_find(link_id);
+	
+	if (dlink == NULL) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Link %zu doesn't exist",
+		    link_id);
+		return EINVAL;
+	}
+	
+	return dhcp_discover_proc(dlink);
+}
+
 static void dhcpsrv_recv_offer(dhcp_link_t *dlink, dhcp_offer_t *offer)
 {
@@ -535,4 +562,7 @@
 		return;
 	}
+
+	/* XXX Work around multiple simultaneous sessions issue */
+	dhcp_transport_fini(&dlink->dt);
 
 	log_msg(LOG_DEFAULT, LVL_NOTE, "%s: Successfully configured.",
Index: uspace/srv/net/dhcp/dhcp.h
===================================================================
--- uspace/srv/net/dhcp/dhcp.h	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/srv/net/dhcp/dhcp.h	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -43,4 +43,5 @@
 extern int dhcpsrv_link_add(service_id_t);
 extern int dhcpsrv_link_remove(service_id_t);
+extern int dhcpsrv_discover(service_id_t);
 
 #endif
Index: uspace/srv/net/dhcp/main.c
===================================================================
--- uspace/srv/net/dhcp/main.c	(revision c3f7d37086023df6580ccd4691db2d699c80ccf6)
+++ uspace/srv/net/dhcp/main.c	(revision 048cd69e69ce8a5ca8bbf5d8a5b5451745dca93a)
@@ -109,4 +109,17 @@
 }
 
+static void dhcp_discover_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	sysarg_t link_id;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcp_discover_srv()");
+
+	link_id = IPC_GET_ARG1(*call);
+
+	rc = dhcpsrv_discover(link_id);
+	async_answer_0(callid, rc);
+}
+
 static void dhcp_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
@@ -133,4 +146,7 @@
 		case DHCP_LINK_REMOVE:
 			dhcp_link_remove_srv(callid, &call);
+			break;
+		case DHCP_DISCOVER:
+			dhcp_discover_srv(callid, &call);
 			break;
 		default:
