diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/Makefile b/targets/ARCH/EXMIMO/DRIVER/exmimo3/Makefile
new file mode 100755
index 0000000000000000000000000000000000000000..30dba76a2f31f1a8c76233bd2c6d9b143272c3f6
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/Makefile
@@ -0,0 +1,22 @@
+CCC = gcc
+KERNEL_MAIN_TYPE=$(shell echo `uname -r | cut -d. -f-2  | tr "." "_"`)
+export KERNEL_MAIN_TYPE
+
+SUBVERSION=$(shell echo `grep '^SUBLEVEL =' /usr/src/linux/Makefile | sed -e 's, ,,g' | sed -e 's/SUBLEVEL=//'`)
+IS_KERNEL_SUBVERSION_GREATER_THAN_20=$(shell if [ $(SUBVERSION) -ge 20 ] ; then echo true ; fi)
+
+EXTRA_CFLAGS = -ggdb -D__KERNEL__ -DMODULE -D_LOOSE_KERNEL_NAMES -I/lib/modules/$(shell uname -r)/build/include -I/lib/modules/$(shell uname -r)/build/include/asm/mach-default -include /lib/modules/$(shell uname -r)/build/include/linux/autoconf.h 
+
+EXTRA_CFLAGS += -I$(PWD)/../../DEFS
+
+obj-m += openair_rf.o
+
+openair_rf-objs += module_main.o irq.o fileops.o exmimo_fw.o
+
+all:
+	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+
+clean:
+	rm -rf *.o
+	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
+
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/Module.symvers b/targets/ARCH/EXMIMO/DRIVER/exmimo3/Module.symvers
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/compile.sh b/targets/ARCH/EXMIMO/DRIVER/exmimo3/compile.sh
new file mode 100644
index 0000000000000000000000000000000000000000..dc912a6ebaa0783f1ebb85a0b756307397269e12
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/compile.sh
@@ -0,0 +1,4 @@
+echo "Hint: this may not work if /usr/src/linux is not set to currently booted kernel."
+echo "Try 'make' instead."
+
+make -C /usr/src/linux V=1 M=`pwd`
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/defs.h b/targets/ARCH/EXMIMO/DRIVER/exmimo3/defs.h
new file mode 100755
index 0000000000000000000000000000000000000000..5da6b679430a01afe04713aabe3102a62f38adc7
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/defs.h
@@ -0,0 +1,52 @@
+#ifndef __DEFS_H__
+#define __DEFS_H__
+
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/delay.h>
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+
+#include <linux/slab.h>
+//#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+
+#include <linux/errno.h>
+
+#ifdef KERNEL2_6
+#include <linux/slab.h>
+#endif
+
+#include "openair_device.h"
+
+#include "linux/moduleparam.h"
+
+
+/*------------------------------------------------*/
+/*   Prototypes                                   */
+/*------------------------------------------------*/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+long openair_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); 
+#else
+int openair_device_ioctl(struct inode *inode,struct file *filp, unsigned int cmd, unsigned long arg); 
+#endif
+int openair_device_open    (struct inode *inode,struct file *filp);
+int openair_device_release (struct inode *inode,struct file *filp);
+int openair_device_mmap    (struct file *filp, struct vm_area_struct *vma);
+
+int exmimo_memory_alloc(int card);
+int exmimo_firmware_init(int card);
+int exmimo_firmware_cleanup(int card_id);
+
+int exmimo_send_pccmd(int card_id, unsigned int cmd);
+
+#endif
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/do_reenumerate_expressmimo.sh b/targets/ARCH/EXMIMO/DRIVER/exmimo3/do_reenumerate_expressmimo.sh
new file mode 100755
index 0000000000000000000000000000000000000000..09ee7050fbe99b8779a89ad1c5beade0f702612f
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/do_reenumerate_expressmimo.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# Re-Enumerate FPGA card
+
+# After resetting or powering up the card or after reloading the FPGA bitstream,
+# run this script to re-enumerate the PCIe device in Linux.
+
+# You may need to change the device path. Check lspci output for this.
+
+# You need to run this as root:
+# sudo ./do_reenumerate_expressmimo.sh
+
+# Matthias <ihmig@eurecom.fr>, 2013
+
+rmmod openair_rf
+echo 1 > /sys/bus/pci/devices/0000\:01\:00.0/remove
+echo 1 > /sys/bus/pci/devices/0000\:05\:00.0/remove
+echo 1 > /sys/bus/pci/rescan
+
+insmod openair_rf.ko
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/do_reinsert_driver.sh b/targets/ARCH/EXMIMO/DRIVER/exmimo3/do_reinsert_driver.sh
new file mode 100755
index 0000000000000000000000000000000000000000..75198b35d1164a72545217791b203bfd749c7d2b
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/do_reinsert_driver.sh
@@ -0,0 +1 @@
+rmmod openair_rf && sleep 1 ; insmod openair_rf.ko
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/exmimo_fw.c b/targets/ARCH/EXMIMO/DRIVER/exmimo3/exmimo_fw.c
new file mode 100644
index 0000000000000000000000000000000000000000..ab48233443080147cf253a51a5a574fb4a1757ea
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/exmimo_fw.c
@@ -0,0 +1,337 @@
+/** exmimo_fw.c
+ * 
+ *  Initialization routines for
+ *  Shared Memory management for ExMIMO and PC(kernel) data exchange
+ *  (memory allocation, pointer management)
+ * 
+ *  There is now one pci_alloc_consistent for each RX and TX buffer
+ *  and one for all structures, including pointers, printk, firmware_dump and config
+ * 
+ *  Authors: Matthias Ihmig <matthias.ihmig@mytum.de>, 2012, 2013
+ *           Riadh Ghaddab <riadh.ghaddab@eurecom.fr>
+ *           Raymond Knopp <raymond.knopp@eurecom.fr>
+ * 
+ *  Changelog:
+ *  24.01.2013: added memory management functions for bigshm, lots of cleanups
+ */
+
+#include <linux/delay.h> 
+#include "extern.h"
+#include "defs.h"
+#include "pcie_interface.h"
+
+/*****************************************************
+ *  Private functions within this file
+ */
+
+void mem_SetPageReserved(void *kvirt_addr, unsigned int size_pages)
+{
+    size_t temp_size = size_pages << PAGE_SHIFT;
+    void *adr = kvirt_addr;
+
+    while (temp_size > 0)
+    {
+        SetPageReserved( virt_to_page(adr) );
+        adr += PAGE_SIZE;
+        temp_size -= PAGE_SIZE;
+    } 
+}
+
+void mem_ClearPageReserved(void *kvirt_addr, unsigned int size_pages)
+{
+    size_t temp_size = size_pages << PAGE_SHIFT;
+    void *adr = kvirt_addr;
+ 
+    while (temp_size > 0) {
+        ClearPageReserved( virt_to_page(adr) );
+        adr += PAGE_SIZE;
+        temp_size -= PAGE_SIZE;
+    } 
+}
+
+// allocates big shared memory, to be used for structures, pointers, etc.
+// bigshm allocs a single larger memory block for shared structures and pointers, one per card, except: ADC+DAC buffers!
+// returns -1 on error, 0 on success
+int bigshm_init(int card)
+{
+    printk("[openair][module] calling pci_alloc_consistent for card %d, bigshm (size: %lu*%lu bytes)...\n", card, BIGSHM_SIZE_PAGES, PAGE_SIZE);
+
+    if ( sizeof(dma_addr_t) != 4)
+        printk("!!! WARNING: sizeof (dma_addr_t) = %lu! Only 32bit mode (= 4) (also: no PAE) is supported at this time!\n", sizeof(dma_addr_t));
+    
+    if ( bigshm_head[card] == NULL )
+        bigshm_head[card] = pci_alloc_consistent( pdev[card], BIGSHM_SIZE_PAGES<<PAGE_SHIFT, &bigshm_head_phys[card] );
+
+    if (bigshm_head[card] == NULL) {
+        printk("[openair][MODULE][ERROR] Cannot Allocate Memory (%lu bytes) for shared data (bigshm)\n", BIGSHM_SIZE_PAGES<<PAGE_SHIFT);
+        return -ENOMEM;
+    }
+    else {
+        printk("[openair][MODULE][INFO] Bigshm at %p (phys %x)\n", bigshm_head[card], (unsigned int) bigshm_head_phys[card]);
+
+        bigshm_currentptr[card] = bigshm_head[card];
+
+        mem_SetPageReserved( bigshm_head[card], BIGSHM_SIZE_PAGES);
+        memset(bigshm_head[card], 0, BIGSHM_SIZE_PAGES << PAGE_SHIFT);
+    }
+    return 0;
+}
+
+
+// use this instead of pci_alloc_consistent to assign memory from the bigshm block
+// return kernel virtual pointer and sets physical DMA address in dma_handle
+void *bigshm_assign( int card, size_t size_bytes, dma_addr_t *dma_handle_ptr )
+{
+    void *ret;
+    size_t size = size_bytes;
+
+    //size = (size-1) + 4 - ( (size-1) % 4); // round up to keep addresses aligned to DWs
+    //size = (size-1) + 16 - ( (size-1) % 16); // round up to keep addresses aligned to 4 DWs
+
+    // round up to the next 64 DW (workaround for current bug in DMA on ExMIMO2) 
+    size = (size-1) + 256 - ( (size-1) % 256);
+
+    if ( (bigshm_currentptr[card] - bigshm_head[card]) > (BIGSHM_SIZE_PAGES<<PAGE_SHIFT) -size ) {
+        printk("Not enough memory in bigshm! Make BIGSHM_SIZE_PAGES bigger!\n");
+        return NULL;
+    }
+
+    *dma_handle_ptr = bigshm_head_phys[card] + ( (dma_addr_t)bigshm_currentptr[card] - (dma_addr_t)bigshm_head[card] );
+    ret = bigshm_currentptr[card];
+    bigshm_currentptr[card] = (char *)bigshm_currentptr[card] + size;
+
+    //printk("bigshm_assign: size %d, virt = %p, dma = %x,    bigshm_phys=%x, _current=%p, _head=%p\n",
+    // size, ret, *dma_handle_ptr, bigshm_head_phys[card], bigshm_currentptr[card], bigshm_head[card]);
+
+    return ret;
+}
+
+
+// assign shared memory between card an PC for data exchange: interface structure with pointers,
+// firmware- and printk buffers, configuration structure
+// returns -1 on error, 0 on success
+int exmimo_assign_shm_vars(int card_id)
+{
+    int j;
+    
+    if (p_exmimo_pci_phys[card_id] == NULL)
+    {
+        p_exmimo_pci_phys[card_id] = (exmimo_pci_interface_bot_t *) bigshm_assign( card_id,
+                                      sizeof(exmimo_pci_interface_bot_t),
+                                      &pphys_exmimo_pci_phys[card_id]);
+        printk("Intializing EXMIMO interface support (exmimo_pci_bot at %p, phys %x, size %lu bytes)\n",p_exmimo_pci_phys[card_id],(unsigned int)pphys_exmimo_pci_phys[card_id], sizeof(exmimo_pci_interface_bot_t));
+
+        exmimo_pci_kvirt[card_id].firmware_block_ptr = (char *) bigshm_assign( card_id,
+                                            MAX_FIRMWARE_BLOCK_SIZE_B,
+                                            (dma_addr_t*)&(p_exmimo_pci_phys[card_id]->firmware_block_ptr));
+        printk("firmware_code_block_ptr : %p (phys = %08x)\n", exmimo_pci_kvirt[card_id].firmware_block_ptr, p_exmimo_pci_phys[card_id]->firmware_block_ptr);
+
+
+        exmimo_pci_kvirt[card_id].printk_buffer_ptr = (char *) bigshm_assign( card_id,
+                                           MAX_PRINTK_BUFFER_B,
+                                           (dma_addr_t*)&(p_exmimo_pci_phys[card_id]->printk_buffer_ptr));
+        printk("printk_buffer_ptr : %p (phys = %08x)\n", exmimo_pci_kvirt[card_id].printk_buffer_ptr, p_exmimo_pci_phys[card_id]->printk_buffer_ptr);
+
+
+        exmimo_pci_kvirt[card_id].exmimo_config_ptr = (exmimo_config_t *) bigshm_assign( card_id,
+                                               sizeof(exmimo_config_t),
+                                               (dma_addr_t*)&(p_exmimo_pci_phys[card_id]->exmimo_config_ptr));
+        printk("exmimo_config_ptr : %p (phys = %08x)\n", exmimo_pci_kvirt[card_id].exmimo_config_ptr, p_exmimo_pci_phys[card_id]->exmimo_config_ptr);  
+
+
+        exmimo_pci_kvirt[card_id].exmimo_id_ptr = (exmimo_id_t *) bigshm_assign( card_id,
+                                               sizeof(exmimo_id_t),
+                                               (dma_addr_t*)&(p_exmimo_pci_phys[card_id]->exmimo_id_ptr));
+        printk("exmimo_id_ptr : %p (phys = %08x)\n", exmimo_pci_kvirt[card_id].exmimo_id_ptr, p_exmimo_pci_phys[card_id]->exmimo_id_ptr);
+
+
+        if (    p_exmimo_pci_phys[card_id] == NULL || exmimo_pci_kvirt[card_id].firmware_block_ptr == NULL
+             || exmimo_pci_kvirt[card_id].printk_buffer_ptr == NULL || exmimo_pci_kvirt[card_id].exmimo_config_ptr == NULL 
+             || exmimo_pci_kvirt[card_id].exmimo_id_ptr == NULL )
+                return -1;
+
+        for (j=0; j<MAX_ANTENNAS; j++)
+        {
+            // size 4*1 should be sufficient, but just to make sure we can also use DMA of size 4DW as fallback
+            exmimo_pci_kvirt[card_id].rxcnt_ptr[j] = (uint32_t *) bigshm_assign( card_id, 4*4,
+                                               (dma_addr_t*)&(p_exmimo_pci_phys[card_id]->rxcnt_ptr[j]) );
+            printk("exmimo_pci_kvirt[%d].rxcnt_ptr[%d] = %p (phys = %08x)\n", card_id, j, exmimo_pci_kvirt[card_id].rxcnt_ptr[j], p_exmimo_pci_phys[card_id]->rxcnt_ptr[j]);
+                
+            exmimo_pci_kvirt[card_id].txcnt_ptr[j] = (uint32_t *) bigshm_assign( card_id, 4*4,
+                                               (dma_addr_t*)&(p_exmimo_pci_phys[card_id]->txcnt_ptr[j]) );
+            printk("exmimo_pci_kvirt[%d].txcnt_ptr[%d] = %p (phys = %08x)\n", card_id, j, exmimo_pci_kvirt[card_id].txcnt_ptr[j], p_exmimo_pci_phys[card_id]->txcnt_ptr[j]);
+            
+            if ( exmimo_pci_kvirt[card_id].rxcnt_ptr[j] == NULL || exmimo_pci_kvirt[card_id].txcnt_ptr[j] == NULL)
+                return -1;
+        }
+    }
+    return 0;
+}
+
+// allocate memory for RX and TX chains for all antennas
+// returns -1 on error, 0 on success
+int exmimo_allocate_rx_tx_buffers(int card_id)
+{
+    size_t size;
+    int j,i;
+    dma_addr_t dma_addr_dummy; 
+    // Round up to the next PAGE_SIZE (typ. 4096 bytes)
+    size = (ADAC_BUFFERSZ_PERCHAN_B >> PAGE_SHIFT) + 1;
+    size <<= PAGE_SHIFT;
+    
+    for (j=0; j<MAX_ANTENNAS; j++)
+    {
+
+        exmimo_pci_kvirt[card_id].adc_head[j] = (uint32_t *)pci_alloc_consistent( pdev[card_id], size, &dma_addr_dummy);
+                                                    p_exmimo_pci_phys[card_id]->adc_head[j]=((uint32_t*)&dma_addr_dummy)[0]; 
+        
+        printk("exmimo_pci_kvirt[%d].adc_head[%d] = %p (phys = %08x)\n", card_id, j, exmimo_pci_kvirt[card_id].adc_head[j], p_exmimo_pci_phys[card_id]->adc_head[j]);
+        if ( exmimo_pci_kvirt[card_id].adc_head[j] == NULL)
+            return -1;
+    //  printk("[MODULE MAIN0]Phys address TX[0] : (%8lx)\n",p_exmimo_pci_phys[0]->dac_head[ openair_mmap_getAntTX(2) ]);     
+   
+        mem_SetPageReserved( exmimo_pci_kvirt[card_id].adc_head[j], size >> PAGE_SHIFT );
+        memset( exmimo_pci_kvirt[card_id].adc_head[j], 0x10+j, size);
+        
+    //  printk("[MODULE MAIN1]Phys address TX[0] : (%8lx)\n",p_exmimo_pci_phys[0]->dac_head[ openair_mmap_getAntTX(2) ]);
+
+        exmimo_pci_kvirt[card_id].dac_head[j] = (uint32_t *)pci_alloc_consistent( pdev[card_id], size,&dma_addr_dummy);
+                                                    p_exmimo_pci_phys[card_id]->dac_head[j]=((uint32_t*)&dma_addr_dummy)[0]; 
+
+    //    printk("exmimo_pci_kvirt[%d].dac_head[%d] = %p (phys = %08x)\n", card_id, j, exmimo_pci_kvirt[card_id].dac_head[j], p_exmimo_pci_phys[card_id]->dac_head[j]);
+        if ( exmimo_pci_kvirt[card_id].dac_head[j] == NULL)
+            return -ENOMEM;
+   //   printk("[MODULE MAIN2]Phys address TX[0] : (%8lx)\n",p_exmimo_pci_phys[0]->dac_head[ openair_mmap_getAntTX(2) ]);
+
+        mem_SetPageReserved( exmimo_pci_kvirt[card_id].dac_head[j], size >> PAGE_SHIFT );
+        memset( exmimo_pci_kvirt[card_id].dac_head[j], 0x20+j, size);
+   //   printk("[MODULE MAIN3]Phys address TX[0] : (%8lx)\n",p_exmimo_pci_phys[0]->dac_head[ openair_mmap_getAntTX(2) ]);
+    }
+    return 0;
+}
+
+/*********************************************
+ *  Public functions ExpressMIMO Interface 
+ */
+
+/* Allocates buffer and assigns pointers
+ * 
+ * return 0 on success
+ */
+int exmimo_memory_alloc(int card)
+{
+int i;
+    if ( bigshm_init( card ) ) {
+        printk("exmimo_memory_alloc(): bigshm_init failed for card %d.\n", card);
+        return -ENOMEM;
+    }
+
+    if ( exmimo_assign_shm_vars( card ) ) {
+        printk("exmimo_memory_alloc(): exmimo_assign_shm_vars failed to assign enough shared memory for all variables and structures for card %i!\n", card);
+        return -ENOMEM;
+    }
+    
+    if ( exmimo_allocate_rx_tx_buffers( card ) ) {
+        printk("exmimo_memory_alloc(): exmimo_allocate_rx_tx_buffers() failed to allocate enough memory for RX and TX buffers for card %i!\n", card);
+        return -ENOMEM;
+    }
+
+    return 0;
+}
+
+/*
+ * Copies pointer to Leon
+ */
+int exmimo_firmware_init(int card)
+{
+
+    // put DMA pointer to exmimo_pci_interface_bot into LEON register
+    iowrite32( pphys_exmimo_pci_phys[card], (bar[card]+PCIE_PCIBASEL) );  // lower 32bit of address
+    iowrite32( 0, (bar[card]+PCIE_PCIBASEH) );                            // higher 32bit of address
+
+    iowrite32(pphys_exmimo_pci_phys[card]->    p_exmimo_pci_phys[card_id]->adc_head[j]);
+
+    if (exmimo_pci_kvirt[card].exmimo_id_ptr->board_swrev == BOARD_SWREV_CMDREGISTERS)
+      iowrite32( EXMIMO_CONTROL2_COOKIE, bar[card]+ PCIE_CONTROL2);
+    
+    //printk("exmimo_firmware_init(): initializing Leon (EXMIMO_PCIE_INIT)...\n");
+    exmimo_send_pccmd(card, EXMIMO_PCIE_INIT);
+    
+    return 0;
+}
+
+/*
+ * Free memory on unloading the kernel driver
+ */
+int exmimo_firmware_cleanup(int card)
+{
+    size_t size;
+    int j;
+
+    // free exmimo_allocate_rx_tx_buffers
+
+    size = (ADAC_BUFFERSZ_PERCHAN_B >> PAGE_SHIFT) + 1;
+    size <<= PAGE_SHIFT;
+    for (j=0; j<MAX_ANTENNAS; j++)
+    {
+        if ( exmimo_pci_kvirt[card].adc_head[j] ) {
+            mem_ClearPageReserved( exmimo_pci_kvirt[card].adc_head[j], size >> PAGE_SHIFT );
+            pci_free_consistent( pdev[card], size, exmimo_pci_kvirt[card].adc_head[j], p_exmimo_pci_phys[card]->adc_head[j] );
+        }
+       
+        if ( exmimo_pci_kvirt[card].dac_head[j] ) {
+            mem_ClearPageReserved( exmimo_pci_kvirt[card].dac_head[j], size >> PAGE_SHIFT );
+            pci_free_consistent( pdev[card], size, exmimo_pci_kvirt[card].dac_head[j], p_exmimo_pci_phys[card]->dac_head[j] ); 
+        }
+    }
+
+    if ( bigshm_head[card] ) {
+        printk("free bigshm_head[%d] pdev %p, size %lu, head %p, phys %x\n", card, pdev[card], BIGSHM_SIZE_PAGES<<PAGE_SHIFT, bigshm_head[card], (unsigned int)bigshm_head_phys[card]);
+        mem_ClearPageReserved( bigshm_head[card], BIGSHM_SIZE_PAGES );
+
+        pci_free_consistent( pdev[card], BIGSHM_SIZE_PAGES<<PAGE_SHIFT, bigshm_head[card], bigshm_head_phys[card]);
+    }
+
+    return 0;
+}
+
+
+/*
+ * Send command to Leon and wait until command is completed
+ */
+int exmimo_send_pccmd(int card_id, unsigned int cmd)
+{
+    unsigned int val;
+    unsigned int cnt=0;
+        
+    //printk("Sending command to ExpressMIMO (card %d) : %x\n",card_id, cmd);
+    iowrite32(cmd,(bar[card_id]+PCIE_CONTROL1));
+    // printk("Readback of control1 %x\n",ioread32(bar[0]+PCIE_CONTROL1));
+    
+
+   switch(cmd)
+   {
+     case EXMIMO_CONFIG :
+ 
+     break;
+
+     case EXMIMO_PCIE_INIT :
+
+     break;
+
+
+   } 
+    
+    // workaround for ExMIMO1&2: no command ack -> sleep
+        while (cnt<100 && ( ioread32(bar[card_id]+PCIE_CONTROL1) != EXMIMO_NOP )) {
+            //printk("ctrl0: %08x, ctrl1: %08x, ctrl2: %08x, status: %08x\n", ioread32(bar[card_id]+PCIE_CONTROL0), ioread32(bar[card_id]+PCIE_CONTROL1), ioread32(bar[card_id]+PCIE_CONTROL2), ioread32(bar[card_id]+PCIE_STATUS));
+            msleep(100);
+            cnt++;
+        }
+        if (cnt==100)
+            printk("exmimo_send_pccmd error: Timeout: no EXMIMO_NOP received within 10sec for card%d, pccmd=%x\n", card_id, cmd);
+    
+    //printk("Ctrl0: %08x, ctrl1: %08x, ctrl2: %08x, status: %08x\n", ioread32(bar[card_id]+PCIE_CONTROL0), ioread32(bar[card_id]+PCIE_CONTROL1), ioread32(bar[card_id]+PCIE_CONTROL2), ioread32(bar[card_id]+PCIE_STATUS));
+    return(0);
+}
+
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/extern.h b/targets/ARCH/EXMIMO/DRIVER/exmimo3/extern.h
new file mode 100755
index 0000000000000000000000000000000000000000..751574fe76658439f3355160e653bf3f0c7dce25
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/extern.h
@@ -0,0 +1,30 @@
+#ifndef __EXTERN_H__
+#define __EXTERN_H__
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#ifdef KERNEL2_6
+#include <linux/slab.h>
+#endif
+
+#include "defs.h"
+#include "pcie_interface.h"
+
+extern char number_of_cards;
+
+extern int major;
+
+extern struct pci_dev *pdev[MAX_CARDS];
+extern void __iomem *bar[MAX_CARDS];
+
+extern void *bigshm_head[MAX_CARDS];
+extern void *bigshm_currentptr[MAX_CARDS];
+extern dma_addr_t bigshm_head_phys[MAX_CARDS];
+
+extern dma_addr_t                      pphys_exmimo_pci_phys[MAX_CARDS]; 
+extern exmimo_pci_interface_bot_t         *p_exmimo_pci_phys[MAX_CARDS];
+extern exmimo_pci_interface_bot_virtual_t    exmimo_pci_kvirt[MAX_CARDS];
+
+#endif // __EXTERN_H__
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/fileops.c b/targets/ARCH/EXMIMO/DRIVER/exmimo3/fileops.c
new file mode 100755
index 0000000000000000000000000000000000000000..fc7e4e3d637f1dec50d1e34f1c34df9ff6b4b553
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/fileops.c
@@ -0,0 +1,435 @@
+/** fileops.c
+ * 
+ *  Device IOCTL File Operations on character device /dev/openair0
+ * 
+ *  Authors: Matthias Ihmig <matthias.ihmig@mytum.de>, 2012, 2013
+ *           Riadh Ghaddab <riadh.ghaddab@eurecom.fr>
+ *           Raymond Knopp <raymond.knopp@eurecom.fr>
+ * 
+ *  Changelog:
+ *  14.01.2013: removed remaining of BIGPHYS stuff and replaced with pci_alloc_consistent
+ */
+ 
+#include <linux/delay.h>
+
+#include "openair_device.h"
+#include "defs.h"
+#include "extern.h"
+
+#include "pcie_interface.h"
+
+#define invert4(x)  { \
+    unsigned int ltmp; \
+    ltmp=x; x=((ltmp & 0xff)<<24) | ((ltmp & 0xff00)<<8) | \
+    ((ltmp & 0xff0000)>>8) | ((ltmp & 0xff000000)>>24); \
+}
+
+extern int get_frame_done;
+
+
+int is_card_num_invalid(int card)
+{
+    if (card<0 || card>=number_of_cards)
+    {
+        printk("[openair][IOCTL]: ERROR: received invalid card number (%d)!\n", card);
+        return -1;
+    }
+    else
+        return 0;
+}
+
+//-----------------------------------------------------------------------------
+int openair_device_open (struct inode *inode,struct file *filp)
+{
+    //printk("[openair][MODULE]  openair_open()\n");
+    return 0;
+}
+
+//-----------------------------------------------------------------------------
+int openair_device_release (struct inode *inode,struct file *filp)
+{
+  //  printk("[openair][MODULE]  openair_release(), MODE = %d\n",openair_daq_vars.mode);
+  return 0;
+}
+
+//-----------------------------------------------------------------------------
+int openair_device_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+    unsigned long phys;
+    unsigned long start = (unsigned long)vma->vm_start; 
+    unsigned long size = (unsigned long)(vma->vm_end-vma->vm_start);
+    unsigned long maxsize;
+    unsigned int memblock_ind;
+    unsigned int card;
+
+    memblock_ind = openair_mmap_getMemBlock( vma->vm_pgoff );
+    card         = openair_mmap_getCard( vma->vm_pgoff );
+
+    /*printk("[openair][MMAP] called (start %lx, end %lx, pg_off %lx, size %lx)\n", 
+        vma->vm_start, 
+        vma->vm_end,
+        vma->vm_pgoff,
+        size);
+    */
+    vma->vm_pgoff = 0;
+    
+    // not supported by 64 bit kernels
+    //vma->vm_flags |= VM_RESERVED;
+      vma->vm_flags |= VM_IO;
+        
+    if ( is_card_num_invalid(card) )
+        return -EINVAL;
+
+    if (memblock_ind == openair_mmap_BIGSHM)
+    {
+        // map a buffer from bigshm
+        maxsize = BIGSHM_SIZE_PAGES<<PAGE_SHIFT;
+        if ( size > maxsize) {
+            printk("[openair][MMAP][ERROR] Trying to map more than %d bytes (req size=%d)\n",
+                (unsigned int)(BIGSHM_SIZE_PAGES<<PAGE_SHIFT), (unsigned int)size);
+            return -EINVAL;
+        }
+        phys = bigshm_head_phys[card];
+    }
+    else if ( (memblock_ind & 1) == 1) 
+    {
+        // mmap a RX buffer
+        maxsize = ADAC_BUFFERSZ_PERCHAN_B;
+        if ( size > maxsize) {
+            printk("[openair][MMAP][ERROR] Trying to map more than %d bytes (req size=%d)\n",
+                (unsigned int)(ADAC_BUFFERSZ_PERCHAN_B), (unsigned int)size);
+            return -EINVAL;
+        }
+        phys = p_exmimo_pci_phys[card]->adc_head[ openair_mmap_getAntRX(memblock_ind) ];
+    }
+    else   
+    {
+        // mmap a TX buffer
+        maxsize = ADAC_BUFFERSZ_PERCHAN_B;
+        if ( size > maxsize) {
+            printk("[openair][MMAP][ERROR] Trying to map more than %d bytes (%d)\n",
+                (unsigned int)(ADAC_BUFFERSZ_PERCHAN_B), (unsigned int)size);
+            return -EINVAL;
+        }
+        phys = p_exmimo_pci_phys[card]->dac_head[ openair_mmap_getAntTX(memblock_ind) ];
+    }
+    
+    if (0)
+        printk("[openair][MMAP] card%d: map phys (%08lx) at start %lx, end %lx, pg_off %lx, size %lx\n",
+            card,
+            phys,
+            vma->vm_start, 
+            vma->vm_end,
+            vma->vm_pgoff,
+            size);
+  
+    /* loop through all the physical pages in the buffer */ 
+    /* Remember this won't work for vmalloc()d memory ! */
+    if (remap_pfn_range(vma, 
+                      start, 
+                      phys>>PAGE_SHIFT, 
+                      vma->vm_end-vma->vm_start, 
+                      vma->vm_page_prot))
+    {
+        printk("[openair][MMAP] ERROR EAGAIN\n");
+        return -EAGAIN;
+    }
+
+    return 0; 
+}
+
+//-----------------------------------------------------------------------------
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+long openair_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 
+#else
+int openair_device_ioctl(struct inode *inode,struct file *filp, unsigned int cmd, unsigned long arg) 
+#endif
+{
+    /* arg is not meaningful if no arg is passed in user space */
+    //-----------------------------------------------------------------------------
+
+    int i, c;
+  
+    int tmp;
+    
+    static unsigned int update_firmware_command;
+    static unsigned int update_firmware_address;
+    static unsigned int update_firmware_length;
+    static unsigned int* update_firmware_kbuffer;
+    static unsigned int* __user update_firmware_ubuffer;
+    static unsigned int update_firmware_start_address;
+    static unsigned int update_firmware_stack_pointer;
+    static unsigned int update_firmware_bss_address;
+    static unsigned int update_firmware_bss_size;
+    unsigned int *fw_block;
+    unsigned int sparc_tmp_0;
+    unsigned int sparc_tmp_1;
+    static unsigned int lendian_length;
+
+    unsigned int get_frame_cnt=0;
+    
+    switch(cmd)
+    {
+    case openair_STOP:
+        
+        printk("[openair][IOCTL]     openair_STOP(card%d)\n", (int)arg);
+        if ( is_card_num_invalid((int)arg) )
+            return -EINVAL;
+        
+        exmimo_send_pccmd((int)arg, EXMIMO_STOP);
+
+        break;
+ 
+    case openair_STOP_WITHOUT_RESET:
+
+        printk("[openair][IOCTL]     openair_STOP_WITHOUT_RESET(card%d)\n", (int)arg);
+        if ( is_card_num_invalid((int)arg) )
+            return -EINVAL;
+
+        exmimo_send_pccmd((int)arg, EXMIMO_STOP_WITHOUT_RESET);
+
+        break;
+ 
+    case openair_GET_FRAME:
+
+        get_frame_cnt=0;
+        get_frame_done = 0;
+        printk("[openair][IOCTL] : openair_GET_FRAME: calling exmimo_send_pccmd(%d, EXMIMO_GET_FRAME)\n", (int)arg);
+        if ( is_card_num_invalid((int)arg) )
+            return -EINVAL;
+            
+        exmimo_send_pccmd((int)arg, EXMIMO_GET_FRAME);
+        
+        while (get_frame_cnt<10 && !get_frame_done) {
+            msleep(10);
+            get_frame_cnt++;
+        }
+        if (get_frame_cnt==200)
+            printk("[openair][IOCTL] : Get frame error: no IRQ received within 2000ms.\n");
+        
+        get_frame_done = 0;
+
+        break;
+
+
+    case openair_GET_BIGSHMTOPS_KVIRT:
+    
+        //printk("[openair][IOCTL] : openair_GET_BIGSHMTOPS_KVIRT  (0x%p)[0] = %p[0] (bigshm_head) for 0..3 (sizeof %d) \n", (void *)arg, bigshm_head[0], sizeof(bigshm_head));
+        copy_to_user((void *)arg, bigshm_head, sizeof(bigshm_head));
+
+        break;
+        
+        
+    case openair_GET_PCI_INTERFACE_BOTS_KVIRT:
+        
+        //printk("[openair][IOCTL] : openair_GET_PCI_INTERFACE_BOTS_KVIRT: copying exmimo_pci_kvirt(@%8p) to %lx (sizeof %d)\n", &exmimo_pci_kvirt[0], arg, sizeof(exmimo_pci_kvirt));
+        copy_to_user((void *)arg, exmimo_pci_kvirt, sizeof(exmimo_pci_kvirt));
+   
+        break;
+    
+        
+    case openair_GET_NUM_DETECTED_CARDS:
+
+        //printk("[openair][IOCTL] : openair_GET_NUM_DETECTED_CARDS: *(0x%p) = %d\n", (void *)arg, number_of_cards);
+        copy_to_user((void *)arg, &number_of_cards, sizeof(number_of_cards));
+   
+        break;
+
+
+    case openair_DUMP_CONFIG:
+
+        //printk("[openair][IOCTL]     openair_DUMP_CONFIG(%d)\n", (int)arg);
+        if ( is_card_num_invalid((int)arg) ) {
+            printk("[openair][IOCTL]     openair_DUMP_CONFIG: Invalid card number %d.\n", (int)arg);
+            return -EINVAL;
+        }
+        printk("[openair][IOCTL] : openair_DUMP_CONFIG(%d):  exmimo_pci_kvirt[%d].exmimo_config_ptr = %p (phys %08x)\n",
+            (int)arg, (int)arg, exmimo_pci_kvirt[(int)arg].exmimo_config_ptr, p_exmimo_pci_phys[(int)arg]->exmimo_config_ptr);
+            
+        /*printk("EXMIMO_CONFIG: freq0 %d Hz, freq1 %d Hz, freqtx0 %d Hz, freqtx1 %d Hz, \nRX gain0 %d dB, RX Gain1 %d dB\n",  
+                    exmimo_pci_kvirt[(int)arg].exmimo_config_ptr->rf.rf_freq_rx[0],
+                    exmimo_pci_kvirt[(int)arg].exmimo_config_ptr->rf.rf_freq_rx[1],
+                    exmimo_pci_kvirt[(int)arg].exmimo_config_ptr->rf.rf_freq_tx[0],
+                    exmimo_pci_kvirt[(int)arg].exmimo_config_ptr->rf.rf_freq_tx[1],
+                    exmimo_pci_kvirt[(int)arg].exmimo_config_ptr->rf.rx_gain[0][0],
+                    exmimo_pci_kvirt[(int)arg].exmimo_config_ptr->rf.rx_gain[1][0]);        
+        */
+        
+        exmimo_send_pccmd((int)arg, EXMIMO_CONFIG);
+    
+        break;
+
+
+    case openair_START_RT_ACQUISITION:
+    
+        printk("[openair][IOCTL]     openair_START_TX_SIG(%d): send_pccmd(EXMIMO_START_RT_ACQUISITION).\n", (int) arg);
+        if ( is_card_num_invalid((int)arg) )
+            return -EINVAL;
+
+        exmimo_send_pccmd((int)arg, EXMIMO_START_RT_ACQUISITION);
+
+        break;
+
+
+    case openair_UPDATE_FIRMWARE:
+
+        printk("[openair][IOCTL]     openair_UPDATE_FIRMWARE\n");
+        /***************************************************
+        *   Updating the firmware of Cardbus-MIMO-1 or ExpressMIMO SoC   *
+        ***************************************************/
+        /* 1st argument of this ioctl indicates the action to perform among these:
+           - Transfer a block of data at a specified address (given as the 2nd argument)
+           and for a specified length (given as the 3rd argument, in number of 32-bit words).
+         The USER-SPACE address where to find the block of data is given as the 4th
+         argument.
+             - Ask the Leon processor to clear the .bss section. In this case, the base
+         address of section .bss is given as the 2nd argument, and its size is
+         given as the 3rd one.
+             - Ask the Leon processor to jump at a specified address (given as the 2nd
+         argument, most oftenly expected to be the top address of Ins, Scratch Pad
+         Ram), after having set the stack pointer (given as the 3rd argument).
+         For the openair_UPDATE_FIRMWARE ioctl, we perform a partial infinite loop
+         while acknowledging the PCI irq from Leon software: the max number of loop
+         is yielded by preprocessor constant MAX_IOCTL_ACK_CNT. This avoids handing
+         the kernel with an infinite polling loop. An exception is the case of clearing
+         the bss: it takes time to Leon3 to perform this operation, so we poll te
+         acknowledge with no limit */
+
+#define MAX_IOCTL_ACK_CNT    500
+        update_firmware_command = *((unsigned int*)arg);
+    
+    
+        switch (update_firmware_command)
+        {
+            case UPDATE_FIRMWARE_TRANSFER_BLOCK:
+                update_firmware_address   = ((unsigned int*)arg)[1];
+                update_firmware_length    = ((unsigned int*)arg)[2];
+      
+                update_firmware_ubuffer   = (unsigned int*)((unsigned int*)arg)[3];
+                update_firmware_kbuffer = (unsigned int*)kmalloc(update_firmware_length * 4 /* 4 because kmalloc expects bytes */,
+                                            GFP_KERNEL);
+                if (!update_firmware_kbuffer) {
+                    printk("[openair][IOCTL]  Could NOT allocate %u bytes from kernel memory (kmalloc failed).\n", lendian_length * 4);
+                    return -1; 
+                    break;
+                }
+                
+                // update all cards at the same time
+                for (c=0; c<number_of_cards; c++)
+                {
+                    fw_block = (unsigned int *)exmimo_pci_kvirt[c].firmware_block_ptr;
+                    /* Copy the data block from user space */
+                    fw_block[0] = update_firmware_address;
+                    fw_block[1] = update_firmware_length;
+                    //printk("copy_from_user %p => %p (pci) => fw[0]=fw_addr=%08x (ahb), fw[1]=fw_length=%d DW\n",update_firmware_ubuffer,&fw_block[16],update_firmware_address,update_firmware_length);
+                    tmp = copy_from_user(update_firmware_kbuffer,
+                            update_firmware_ubuffer, /* from */
+                            update_firmware_length * 4       /* in bytes */
+                            );
+                    if (tmp) {
+                        printk("[openair][IOCTL] Could NOT copy all data from user-space to kernel-space (%d bytes remained uncopied).\n", tmp);
+                        return -1;
+                    }
+                    // pci_map_single(pdev[0],(void*)fw_block, update_firmware_length*4,PCI_DMA_BIDIRECTIONAL);
+                    for (i=0;i<update_firmware_length;i++)
+                    {
+                        fw_block[32+i] = ((unsigned int *)update_firmware_kbuffer)[i];
+                        // Endian flipping is done in user-space so undo it
+                        invert4(fw_block[32+i]);
+                    }
+                    
+                    exmimo_send_pccmd(c, EXMIMO_FW_INIT);
+                    printk("[openair][IOCTL] card%d: ok %u DW copied to address 0x%08x  (fw_block_ptr %p)\n",
+                        c, fw_block[1], fw_block[0],fw_block);
+                }
+                
+                kfree(update_firmware_kbuffer);
+      
+
+                
+                /*
+                for (i=0; i<update_firmware_length;i++) {
+                    printk("%08x ", fw_block[32+i]);
+                    if ((i % 8) == 7)
+                        printk("\n");
+                }*/
+                
+            break;
+
+        case UPDATE_FIRMWARE_CLEAR_BSS:
+
+            update_firmware_bss_address   = ((unsigned int*)arg)[1];
+            update_firmware_bss_size      = ((unsigned int*)arg)[2];
+            sparc_tmp_0 = update_firmware_bss_address;
+            sparc_tmp_1 = update_firmware_bss_size;
+
+            printk("[openair][IOCTL] ok asked Leon to clear .bss (addr 0x%08x, size %d bytes)\n", sparc_tmp_0, sparc_tmp_1);
+            
+            // update all cards at the same time
+            for (c=0; c<number_of_cards; c++)
+            {
+                fw_block = (unsigned int *)exmimo_pci_kvirt[c].firmware_block_ptr;
+                fw_block[0] = update_firmware_bss_address;
+                fw_block[1] = update_firmware_bss_size;
+    
+                exmimo_send_pccmd(c, EXMIMO_FW_CLEAR_BSS);
+            }
+
+            break;
+        
+        
+        case UPDATE_FIRMWARE_START_EXECUTION:
+
+            update_firmware_start_address = ((unsigned int*)arg)[1];
+            update_firmware_stack_pointer = ((unsigned int*)arg)[2];
+            sparc_tmp_0 = update_firmware_start_address;
+            sparc_tmp_1 = update_firmware_stack_pointer;
+
+            printk("[openair][IOCTL] ok asked Leon to set stack and start execution (addr 0x%08x, stackptr %08x)\n", sparc_tmp_0, sparc_tmp_1);
+            
+            for (c=0; c<number_of_cards; c++)
+            {
+                fw_block = (unsigned int *)exmimo_pci_kvirt[c].firmware_block_ptr;
+                fw_block[0] = update_firmware_start_address;
+                fw_block[1] = update_firmware_stack_pointer;
+      
+                exmimo_send_pccmd(c, EXMIMO_FW_START_EXEC);
+                msleep(10); 
+                exmimo_firmware_init(c);
+            }
+            break;
+          
+        case UPDATE_FIRMWARE_FORCE_REBOOT:
+
+            printk("[openair][IOCTL] ok asked Leon to reboot.\n");
+            
+            for (c=0; c<number_of_cards; c++)
+            {
+                exmimo_send_pccmd(c, EXMIMO_REBOOT);
+            
+                exmimo_firmware_init(c);
+            }
+            break;
+      
+        case UPDATE_FIRMWARE_TEST_GOK:
+            printk("[openair][IOCTL] TEST_GOK command doesn't work with ExpressMIMO. Ignored.\n");
+            break;
+
+        default:
+            return -1;
+            break;
+        }
+        
+        break;
+ 
+ 
+    default:
+        //----------------------
+        printk("[IOCTL] openair_IOCTL unknown: basecmd = %i  (cmd=%X)\n", _IOC_NR(cmd), cmd );
+        return -EPERM;
+        break;
+    }
+    return 0;
+}
+
+
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/irq.c b/targets/ARCH/EXMIMO/DRIVER/exmimo3/irq.c
new file mode 100644
index 0000000000000000000000000000000000000000..6ab9bbb9610b437169ab42fcdcfe9b4e58052cd1
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/irq.c
@@ -0,0 +1,171 @@
+/** irq.c
+      - IRQ Handler: IRQ from Leon to PCIe/Kernel: exmimo_irq_handler
+      - sends received packets to userspace
+
+      - send command from PC to Leon and trigger IRQ on Leon using CONTROL1 register
+      - commands are defined in $OPENAIR0/express-mimo/software/pcie_interface.h
+
+      - added: pass card_id as parameter to tasklet and irq handler
+
+   Authors: 
+       Raymond Knopp <raymond.knopp@eurecom.fr>
+       Matthias Ihmig <matthias.ihmig@mytum.de>, 2011, 2013
+   */
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/swab.h>
+
+#include "openair_device.h"
+#include "extern.h"
+
+unsigned int openair_bh_cnt;
+
+int get_frame_done = 0;
+
+void pcie_printk(int card_id);
+
+void openair_do_tasklet (unsigned long card_id);
+DECLARE_TASKLET (openair_tasklet, openair_do_tasklet, 0);
+
+irqreturn_t openair_irq_handler(int irq, void *cookie)
+{
+    unsigned int irqval;
+    unsigned int irqcmd = EXMIMO_NOP;
+    unsigned long card_id; // = (unsigned long) cookie;
+    unsigned int pcie_control = PCIE_CONTROL2;
+
+    // check interrupt status register
+    //pci_read_config_word(pdev[0],6 , &irqval);
+    
+    // find card_id. cookie is set by request_irq and static, so we will always find it
+    for (card_id=0; card_id<MAX_CARDS; card_id++)
+        if ( pdev[card_id] == cookie )
+            break;
+
+    if (exmimo_pci_kvirt[card_id].exmimo_id_ptr->board_swrev == BOARD_SWREV_LEGACY)
+      pcie_control = PCIE_CONTROL1;
+    else
+      pcie_control = PCIE_CONTROL2;
+
+    //printk("irq hndl called: card_id=%i, irqval=%i\n", card_id, irqval);
+
+    // get AHBPCIE interrupt line (bit 7) to determine if IRQ was for us from ExMIMO card, or from a different device
+    // reading CONTROL0 will also clear this bit and the LEON-to-PC IRQ line
+    irqval = ioread32(bar[card_id]+PCIE_CONTROL0);
+
+    irqcmd = ioread32(bar[card_id]+pcie_control);
+    //printk("IRQ handler: ctrl0: %08x, ctrl1: %08x, ctrl2: %08x, status: %08x\n", irqval, ioread32(bar[card_id]+PCIE_CONTROL1), ioread32(bar[card_id]+PCIE_CONTROL2), ioread32(bar[card_id]+PCIE_STATUS));
+    
+    if ( (irqval & 0x80) == 0 )  {  // CTRL0.bit7 is no set -> IRQ is not from ExMIMO i.e. not for us 
+        if (exmimo_pci_kvirt[card_id].exmimo_id_ptr->board_swrev == BOARD_SWREV_CMDREGISTERS){
+          if (irqcmd != EXMIMO_NOP && irqcmd != EXMIMO_CONTROL2_COOKIE) {          
+            if (irqcmd == GET_FRAME_DONE)
+            {
+              get_frame_done = 1;
+            }
+            openair_tasklet.data = card_id; 
+            tasklet_schedule(&openair_tasklet);
+            openair_bh_cnt++;
+            return IRQ_HANDLED;
+          }
+          else
+          {
+            return IRQ_NONE;
+          }
+        }
+        else
+          return IRQ_NONE;
+    }
+    else
+    {
+    if (exmimo_pci_kvirt[card_id].exmimo_id_ptr->board_swrev == BOARD_SWREV_LEGACY){
+    // clear PCIE interrupt (bit 7 of register 0x0)
+    iowrite32(irqval&0xffffff7f,bar[card_id]+PCIE_CONTROL0);
+    }
+    if (irqcmd == GET_FRAME_DONE)
+    {
+        get_frame_done = 1;
+    }
+    
+    openair_tasklet.data = card_id;
+    tasklet_schedule(&openair_tasklet);
+    openair_bh_cnt++;
+
+    return IRQ_HANDLED;
+    }
+}
+
+void openair_do_tasklet (unsigned long card_id)
+{
+    int save_irq_cnt = openair_bh_cnt;
+    unsigned int irqcmd;
+    unsigned int pcie_control = PCIE_CONTROL2;
+    openair_bh_cnt = 0;
+    
+    if (exmimo_pci_kvirt[card_id].exmimo_id_ptr->board_swrev == BOARD_SWREV_LEGACY)
+      pcie_control = PCIE_CONTROL1;
+    else
+      pcie_control = PCIE_CONTROL2;
+
+    irqcmd = ioread32(bar[card_id]+pcie_control);
+    
+    if (save_irq_cnt > 1)
+        printk("[openair][IRQ tasklet(%ld)]: Warning: received more than 1 PCIE IRQ (openair_bh_cnt=%i), only the last one is acted upon(irqcmd= %i (0x%X)\n", card_id, openair_bh_cnt, irqcmd, irqcmd);
+    
+    switch( irqcmd )
+    {
+        case PCI_PRINTK:
+            // printk("Got PCIe interrupt for printk ...\n");
+            pcie_printk((int) card_id);
+            break;
+            
+        case GET_FRAME_DONE:
+            printk("[openair][IRQ tasklet] : Got PCIe interrupt for GET_FRAME_DONE ...\n");
+            break;
+            
+        case EXMIMO_NOP:
+            printk("[openair][IRQ tasklet] : Received IRQ, with CONTROL0.bit7(IRQ Leon2PC) set, but irqcmd = EXMIMO_NOP. This should not happen with bitstreams built after June 3rd 2013.\n");
+            break;
+            
+        default:
+            printk("[openair][IRQ tasklet] : Got unknown PCIe cmd: card_id = %li, irqcmd(CONTROL1) = %i (0x%X)\n", card_id, irqcmd, irqcmd);
+    }
+    
+    iowrite32(EXMIMO_NOP, bar[card_id]+pcie_control);
+}   
+
+void pcie_printk(int card_id)
+{
+    char *buffer = exmimo_pci_kvirt[card_id].printk_buffer_ptr;
+    unsigned int len = ((unsigned int *)buffer)[0];
+    unsigned int off=0,i;
+    unsigned char *dword;
+    unsigned char tmp;
+
+    //printk("In pci_fifo_printk : buffer %p, len %d: \n",buffer,len);
+    printk("[LEON card%d]: ", card_id);
+
+    if (len<1024)
+    {
+        if ( (len&3) >0 )
+            off=1;
+    
+        for (i=0; i<(off+(len>>2)); i++)
+        {
+            dword = &((unsigned char *)buffer)[(1+i)<<2];
+            tmp = dword[3];
+            dword[3] = dword[0];
+            dword[0] = tmp;
+            tmp = dword[2];
+            dword[2] = dword[1];
+            dword[1] = tmp;
+        }
+        for (i=0; i<len; i++)
+            printk("%c",((char*)&buffer[4])[i]);
+    }
+}
+
+
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/module_main.c b/targets/ARCH/EXMIMO/DRIVER/exmimo3/module_main.c
new file mode 100755
index 0000000000000000000000000000000000000000..1124ee53baaee19230e24624fed98f7713785a0c
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/module_main.c
@@ -0,0 +1,333 @@
+/** module_main.c (was: device.c)
+ * 
+ *  Main Kernel module functions for load/init and cleanup of the kernel driver
+ * 
+ *  Supports multiple ExpressMIMO cards
+ * 
+ *  Authors: Matthias Ihmig <matthias.ihmig@mytum.de>, 2012, 2013
+ *           Riadh Ghaddab <riadh.ghaddab@eurecom.fr>
+ *           Raymond Knopp <raymond.knopp@eurecom.fr>
+ * 
+ *  Changelog:
+ *  14.01.2013: removed remaining of BIGPHYS stuff and replaced with pci_alloc_consistent
+ *  20.01.2013: added support for multiple cards
+ *  24.01.2013: restructured interfaces & structures of how physical and virtual pointers are handled
+ */
+
+#include "openair_device.h"
+#include "defs.h"
+#include "vars.h"
+
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/aer.h>
+#include <linux/pci_regs.h>
+#include <linux/delay.h>
+
+static void openair_cleanup(void);
+
+extern irqreturn_t openair_irq_handler(int irq, void *cookie);
+
+static int irq = 0;
+module_param(irq,int,S_IRUSR|S_IWUSR);
+
+
+static struct file_operations openair_fops = {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+    unlocked_ioctl:openair_device_ioctl,
+#else
+    ioctl:  openair_device_ioctl,
+#endif
+    open:   openair_device_open,
+    release:openair_device_release,
+    mmap:   openair_device_mmap
+};
+
+static int __init openair_init_module( void )
+{
+    int res = 0;
+    unsigned int readback;
+    unsigned int card, j;
+    unsigned int vid,did;
+    unsigned short vendor, subid;
+    exmimo_id_t exmimo_id_tmp[MAX_CARDS];
+
+    //------------------------------------------------
+    // Find and enable ExpressMIMO PCI cards
+    //------------------------------------------------
+    //
+    card = 0;
+    
+    pdev[card] = pci_get_device(XILINX_VENDOR, XILINX_ID, NULL);
+
+    if( pdev[card] )
+    {
+       // This print does not work for 64 bit kernels
+      //  printk("[openair][INIT_MODULE][INFO]:  openair card (ExpressMIMO) %d found, bus 0x%x, primary 0x%x, secondary 0x%x\n",card,
+      //           pdev[card]->bus->number, pdev[card]->bus->primary,pdev[card]->bus->secondary);
+
+        pci_read_config_word(pdev[card], PCI_SUBSYSTEM_ID, &subid);
+        pci_read_config_word(pdev[card], PCI_SUBSYSTEM_VENDOR_ID, &vendor);
+        exmimo_id_tmp[card].board_vendor = vendor;
+        
+        if ( exmimo_id_tmp[card].board_vendor == XILINX_VENDOR )
+            exmimo_id_tmp[card].board_vendor = EURECOM_VENDOR; // set default to EURECOM
+            
+        exmimo_id_tmp[card].board_exmimoversion = (subid >> 12) & 0x0F;
+        if (exmimo_id_tmp[card].board_exmimoversion == 0) 
+            exmimo_id_tmp[card].board_exmimoversion = 1;   // default (for old bitstreams) is ExpressMIMO-1
+
+        exmimo_id_tmp[card].board_hwrev  = (subid >>  8) & 0x0F;
+        exmimo_id_tmp[card].board_swrev  = (subid      ) & 0xFF;
+
+        printk("[openair][INIT_MODULE][INFO]: card %d: ExpressMIMO-%i (HW Rev %i), Bitstream: %s, SW/Protocol Revision: 0x%02X\n", card,
+                 exmimo_id_tmp[card].board_exmimoversion, exmimo_id_tmp[card].board_hwrev, ( (exmimo_id_tmp[card].board_vendor == EURECOM_VENDOR) ? "Eurecom" : "Telecom Paristech"), exmimo_id_tmp[card].board_swrev );
+        
+        card++;
+        vid = XILINX_VENDOR;
+        did = XILINX_ID;
+    }
+    else {
+        printk("[openair][INIT_MODULE][INFO]:  no card found, stopping.\n");
+        return -ENODEV;
+    }
+
+    // Now look for more cards on the same bus
+    while (card<MAX_CARDS)
+    {
+        pdev[card] = pci_get_device(vid,did, pdev[card-1]);
+        if(pdev[card])
+        {
+          // This print does not work for 64 bit kernels
+          //  printk("[openair][INIT_MODULE][INFO]: openair card %d found, bus 0x%x, primary 0x%x, secondary 0x%x\n",card,
+          //          pdev[card]->bus->number,pdev[card]->bus->primary,pdev[card]->bus->secondary);
+
+            pci_read_config_word(pdev[card], PCI_SUBSYSTEM_ID, &subid);
+            pci_read_config_word(pdev[card], PCI_SUBSYSTEM_VENDOR_ID, &vendor);
+            exmimo_id_tmp[card].board_vendor = vendor;
+            
+            if ( exmimo_id_tmp[card].board_vendor == XILINX_VENDOR )
+                exmimo_id_tmp[card].board_vendor = EURECOM_VENDOR;     // default (for old bitstreams) is EURECOM_VENDOR
+            
+            exmimo_id_tmp[card].board_exmimoversion = (subid >> 12) & 0x0F;
+            if (exmimo_id_tmp[card].board_exmimoversion == 0) 
+                exmimo_id_tmp[card].board_exmimoversion = 1;   // default (for old bitstreams) is ExpressMIMO-1
+            
+            exmimo_id_tmp[card].board_hwrev  = (subid >>  8) & 0x0F;
+            exmimo_id_tmp[card].board_swrev  = (subid      ) & 0xFF;
+            printk("[openair][INIT_MODULE][INFO]: card %d: ExpressMIMO-%i (HW Rev %i), Bitstream: %s, SW/Protocol Revision: 0x%02X\n", card,
+                     exmimo_id_tmp[card].board_exmimoversion, exmimo_id_tmp[card].board_hwrev, ( (exmimo_id_tmp[card].board_vendor == EURECOM_VENDOR) ? "Eurecom" : "Telecom Paristech"), exmimo_id_tmp[card].board_swrev );
+
+            card++;
+        }
+        else
+            break;
+    }
+
+    // at least one device found, enable it
+    number_of_cards = card;
+
+    for (card=0; card<number_of_cards; card++)
+    {
+        if( pci_enable_device(pdev[card]) )
+        {
+            printk("[openair][INIT_MODULE][INFO]: Could not enable PCI card device %d\n",card);
+            openair_cleanup();
+            return -ENODEV;
+        }
+        else {
+            printk("[openair][INIT_MODULE][INFO]: *** CARD DEVICE %d (pdev=%p) ENABLED, irq %d\n",card,pdev[card],irq);
+            printk("[openair][INIT_MODULE][INFO]: *** CARD DEVICE %d (pdev=%p) ENABLED, irq %d\n",card,pdev[card],pdev[card]->irq);
+            openair_pci_device_enabled[card] = 1;
+        }
+        // Make the FPGA to a PCI master
+        pci_set_master(pdev[card]);
+
+        // set DMA mask
+        if (!pci_set_dma_mask(pdev[card], DMA_BIT_MASK(32))) {
+            if (pci_set_consistent_dma_mask(pdev[card], DMA_BIT_MASK(32))) {
+                printk(KERN_INFO "[openair][INIT_MODULE]: Unable to obtain 32bit DMA for consistent allocations\n");
+                openair_cleanup();
+                return -EIO;
+            }
+        }
+
+        //if (pci_enable_pcie_error_reporting(pdev[card]) > 0)
+        //    printk("[openair][INIT_MODULE][INFO]: Enabled PCIe error reporting\n");
+        //else
+        //    printk("[openair][INIT_MODULE][INFO]: Failed to enable PCIe error reporting\n");
+
+        //pci_cleanup_aer_uncorrect_error_status(pdev[card]);
+        
+        mmio_start[card]  = pci_resource_start(pdev[card], 0); // get start of BAR0
+        mmio_length[card] = pci_resource_len  (pdev[card], 0);
+        mmio_flags[card]  = pci_resource_flags(pdev[card], 0);
+
+        if (check_mem_region(mmio_start[card],256) < 0)
+        {
+            printk("[openair][INIT_MODULE][FATAL] : Cannot get memory region 0, aborting\n");
+            mmio_start[card] = 0;
+            openair_cleanup();
+            return -EBUSY;
+        }
+        else 
+            printk("[openair][INIT_MODULE][INFO] : Reserving memory region 0 : mmio_start = 0x%x\n",(unsigned int)mmio_start[card]);
+
+        request_mem_region(mmio_start[card], 256, "openair_rf");
+
+        bar[card] = pci_iomap( pdev[card], 0, mmio_length[card] );   // get virtual kernel address for BAR0
+        
+        printk("[openair][INIT_MODULE][INFO]: BAR0 card %d = 0x%p\n", card, bar[card]);
+
+        printk("[openair][INIT_MODULE][INFO]: Writing 0x%x to BAR0+0x1c (PCIBASEL)\n", 0x12345678);
+
+        iowrite32( 0x12345678, (bar[card]+PCIE_PCIBASEL) );
+        udelay(100);
+        readback = ioread32( bar[card]+PCIE_PCIBASEL );
+        if (readback != 0x12345678)
+        {
+            printk("[openair][INIT_MODULE][INFO]: Readback of FPGA register failed (%x)\n",readback);
+            openair_cleanup();
+            return -EIO;
+        }
+        iowrite32((1<<8), bar[card] +PCIE_CONTROL0 ); // bit8=AHBPCIE_CTL0_SOFTRESET, but what is bit9 and bit10?
+        udelay(1000);
+        readback = ioread32( bar[card] +PCIE_CONTROL0);
+        printk("CONTROL0 readback %x\n",readback);
+    
+        // allocating buffers
+        if ( exmimo_memory_alloc( card ) ) {
+            printk("[openair][MODULE][ERROR] exmimo_memory_alloc() failed!\n");
+            openair_cleanup();
+            return -ENOMEM;
+        }
+       
+        exmimo_pci_kvirt[card].exmimo_id_ptr->board_vendor = exmimo_id_tmp[card].board_vendor;
+        exmimo_pci_kvirt[card].exmimo_id_ptr->board_exmimoversion = exmimo_id_tmp[card].board_exmimoversion;
+        exmimo_pci_kvirt[card].exmimo_id_ptr->board_hwrev = exmimo_id_tmp[card].board_hwrev;
+        exmimo_pci_kvirt[card].exmimo_id_ptr->board_swrev = exmimo_id_tmp[card].board_swrev;
+       
+      if (irq!=0)
+        printk("[OPENAIR][SCHED][INIT] card %d: Trying to get IRQ %d\n", card,irq);
+      else
+        printk("[OPENAIR][SCHED][INIT] card %d: Trying to get IRQ %d\n", card, pdev[card]->irq);
+
+        openair_irq_enabled[card] = 0;
+
+// #ifdef CONFIG_PREEMPT_RT doesn't work -> fix misconfigured header files?
+#if 1
+        if (irq!=0){
+        if ( (res = request_irq(irq, openair_irq_handler,
+                        IRQF_SHARED , "openair_rf", pdev[card] )) == 0)        {
+            openair_irq_enabled[card] = 1;
+        }
+        else {
+            printk("[EXMIMO][SCHED][INIT] Cannot get IRQ %d for HW, error: %d\n", irq, res);
+            openair_cleanup();
+            return -EBUSY;
+        }}
+        else
+        {
+        if ( (res = request_irq(pdev[card]->irq, openair_irq_handler,
+                        IRQF_SHARED , "openair_rf", pdev[card] )) == 0)        {
+            openair_irq_enabled[card] = 1;
+        }
+        else {
+            printk("[EXMIMO][SCHED][INIT] Cannot get IRQ %d for HW, error: %d\n", pdev[card]->irq, res);
+            openair_cleanup();
+            return -EBUSY;
+        }}
+
+
+#else
+        printk("Warning: didn't request IRQ for PREEMPT_REALTIME\n");
+#endif
+
+    } // for (i=0; i<number_of_cards; i++)
+    
+    //------------------------------------------------
+    // Register the device in /dev
+    //------------------------------------------------
+    //
+    major = openair_MAJOR;
+
+    if( (res = register_chrdev(major, "openair", &openair_fops )) < 0)
+    {
+        printk("[openair][INIT_MODULE][ERROR]:  can't register char device driver, major : %d, error: %d\n", major, res);
+        for (j=0; j<=number_of_cards; j++)
+            release_mem_region(mmio_start[j],256);
+        return -EBUSY;
+    } else {
+        printk("[openair][INIT_MODULE][INFO]:  char device driver registered major : %d\n", major);
+        openair_chrdev_registered = 1;
+    }
+
+    printk("[openair][MODULE][INFO] Initializing Leon (EXMIMO_PCIE_INIT) on all cards...\n");
+
+    for (card=0; card<number_of_cards; card++)
+        exmimo_firmware_init( card );
+
+    printk("[openair][MODULE][INFO] Done module init\n");
+
+    return 0;
+}
+
+
+static void __exit openair_cleanup_module(void)
+{
+    //int card;
+    printk("[openair][CLEANUP MODULE]\n");
+
+    // stop any ongoing acquisition
+    //for (card = 0; card < number_of_cards; card++)
+    //    exmimo_send_pccmd(card, EXMIMO_STOP);
+    
+    openair_cleanup();
+}
+
+static void  openair_cleanup(void)
+{
+    int card;
+
+    if ( openair_chrdev_registered )
+        unregister_chrdev(major,"openair");
+    openair_chrdev_registered = 0;
+
+    for (card=0; card<number_of_cards; card++)
+    {
+        // unregister interrupt
+        if ( openair_irq_enabled[card] ) {
+            printk("[openair][CLEANUP] disabling interrupt card %d\n", card);
+           if (irq!=0)
+             free_irq(irq, pdev[card] );
+           else
+             free_irq( pdev[card]->irq, pdev[card] );
+
+            openair_irq_enabled[card] = 0;
+        }
+
+        exmimo_firmware_cleanup( card );
+
+        if ( bar[card] ) {
+            printk("unmap bar[%d] %p\n", card, bar[card]);
+            iounmap((void *)bar[card]);
+        }
+
+        if ( mmio_start[card] ) {
+            printk("release mem[%d] %x\n", card, (unsigned int)mmio_start[card]);
+            release_mem_region(mmio_start[card],256);
+        }
+        
+        if ( openair_pci_device_enabled[card] ) {
+            printk("pci_disable_device %i\n", card);
+            pci_disable_device( pdev[card] ); 
+        }
+    }
+}
+
+
+MODULE_AUTHOR ("Raymond KNOPP <raymond.knopp@eurecom.fr>, Matthias IHMIG <matthias.ihmig@eurecom.fr>, Florian KALTENBERGER <florian.kaltenberger@eurecom.fr>, Riadh GHADDAB <riadh.ghaddab@eurecom.fr>");
+MODULE_DESCRIPTION ("openair ExpressMIMO/ExpressMIMO3 driver");
+MODULE_LICENSE ("GPL");
+module_init (openair_init_module);
+module_exit (openair_cleanup_module);
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/modules.order b/targets/ARCH/EXMIMO/DRIVER/exmimo3/modules.order
new file mode 100644
index 0000000000000000000000000000000000000000..4c907820b7b23e69731312243784c8b4f9f6bb39
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/modules.order
@@ -0,0 +1 @@
+kernel//homes/ghaddab/riadh/openair4G/trunk/targets/ARCH/EXMIMO/DRIVER/eurecom/openair_rf.ko
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/openair_rf.ko b/targets/ARCH/EXMIMO/DRIVER/exmimo3/openair_rf.ko
new file mode 100644
index 0000000000000000000000000000000000000000..2945847c2b6e074ad67dbf0f133abf8b8d8bfb46
Binary files /dev/null and b/targets/ARCH/EXMIMO/DRIVER/exmimo3/openair_rf.ko differ
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/openair_rf.mod.c b/targets/ARCH/EXMIMO/DRIVER/exmimo3/openair_rf.mod.c
new file mode 100644
index 0000000000000000000000000000000000000000..1981fc51b30863b7f3e118a36e9ab3a4133b3a84
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/openair_rf.mod.c
@@ -0,0 +1,68 @@
+#include <linux/module.h>
+#include <linux/vermagic.h>
+#include <linux/compiler.h>
+
+MODULE_INFO(vermagic, VERMAGIC_STRING);
+
+struct module __this_module
+__attribute__((section(".gnu.linkonce.this_module"))) = {
+	.name = KBUILD_MODNAME,
+	.init = init_module,
+#ifdef CONFIG_MODULE_UNLOAD
+	.exit = cleanup_module,
+#endif
+	.arch = MODULE_ARCH_INIT,
+};
+
+static const struct modversion_info ____versions[]
+__used
+__attribute__((section("__versions"))) = {
+	{ 0x4d6582e4, "module_layout" },
+	{ 0x6bc3fbc0, "__unregister_chrdev" },
+	{ 0x1fedf0f4, "__request_region" },
+	{ 0x12da5bb2, "__kmalloc" },
+	{ 0xf9a482f9, "msleep" },
+	{ 0xb8957094, "mem_map" },
+	{ 0x15692c87, "param_ops_int" },
+	{ 0x69a358a6, "iomem_resource" },
+	{ 0x763c5db, "dma_set_mask" },
+	{ 0x184a8a78, "pci_disable_device" },
+	{ 0x38620fb, "__register_chrdev" },
+	{ 0x3573f04f, "x86_dma_fallback_dev" },
+	{ 0xeae3dfd6, "__const_udelay" },
+	{ 0xb74d03f5, "pci_set_master" },
+	{ 0x2bc95bd4, "memset" },
+	{ 0x50eedeb8, "printk" },
+	{ 0xae88b44d, "ipipe_percpu" },
+	{ 0xfaef0ed, "__tasklet_schedule" },
+	{ 0x2f287f0d, "copy_to_user" },
+	{ 0xb4390f9a, "mcount" },
+	{ 0x16305289, "warn_slowpath_null" },
+	{ 0x7e61808f, "dma_release_from_coherent" },
+	{ 0x2072ee9b, "request_threaded_irq" },
+	{ 0x98fb1f75, "dma_alloc_from_coherent" },
+	{ 0xa8a6f639, "__check_region" },
+	{ 0x19c39034, "pci_bus_read_config_word" },
+	{ 0x7c61340c, "__release_region" },
+	{ 0x37a0cba, "kfree" },
+	{ 0xf85828f1, "remap_pfn_range" },
+	{ 0xec591975, "dma_supported" },
+	{ 0xedc03953, "iounmap" },
+	{ 0x7628f3c7, "this_cpu_off" },
+	{ 0x94828082, "pci_get_device" },
+	{ 0x471d2cd9, "pci_iomap" },
+	{ 0x436c2179, "iowrite32" },
+	{ 0xc1a6619, "pci_enable_device" },
+	{ 0x362ef408, "_copy_from_user" },
+	{ 0xf358d5ac, "dma_ops" },
+	{ 0xe484e35f, "ioread32" },
+	{ 0xf20dabd8, "free_irq" },
+};
+
+static const char __module_depends[]
+__used
+__attribute__((section(".modinfo"))) =
+"depends=";
+
+
+MODULE_INFO(srcversion, "BAA63F2436D299D87A90400");
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/test_script.sh b/targets/ARCH/EXMIMO/DRIVER/exmimo3/test_script.sh
new file mode 100755
index 0000000000000000000000000000000000000000..0e2ae5968dc68637c7ddb1371d868406cc64b902
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/test_script.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+PCI=`lspci -m | grep Xilinx`
+if [ -z "$PCI" ]; then
+ echo "No card found. Stopping!"
+ return
+fi
+
+SLOT_NUMBER=`echo $PCI | awk -F\" '{print $1}'`
+
+setpci -s $SLOT_NUMBER 60.b=10
+
diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/vars.h b/targets/ARCH/EXMIMO/DRIVER/exmimo3/vars.h
new file mode 100755
index 0000000000000000000000000000000000000000..5a284fbe9e099b594ae33e18b10c6e59370af235
--- /dev/null
+++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/vars.h
@@ -0,0 +1,41 @@
+#ifndef __VARS_H__
+#define __VARS_H__
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#ifdef KERNEL2_6
+#include <linux/slab.h>
+#endif
+
+#include "defs.h"
+#include "pcie_interface.h"
+
+unsigned int openair_irq_enabled[MAX_CARDS] = INIT_ZEROS;
+unsigned int openair_chrdev_registered = 0;
+unsigned int openair_pci_device_enabled[MAX_CARDS] = INIT_ZEROS;
+
+struct pci_dev *pdev[MAX_CARDS] = INIT_ZEROS;
+void __iomem   *bar[MAX_CARDS]  = INIT_ZEROS;
+
+resource_size_t mmio_start[MAX_CARDS] = INIT_ZEROS;
+resource_size_t mmio_length[MAX_CARDS];
+unsigned int    mmio_flags[MAX_CARDS];
+
+int major;
+
+char number_of_cards;
+
+// bigshm allocs a single larger block, used for shared structures and pointers
+dma_addr_t bigshm_head_phys[MAX_CARDS] = INIT_ZEROS;
+void      *bigshm_head[MAX_CARDS]      = INIT_ZEROS;
+void      *bigshm_currentptr[MAX_CARDS];
+
+dma_addr_t                      pphys_exmimo_pci_phys[MAX_CARDS];  // phys pointer to pci_bot structure in shared mem
+
+exmimo_pci_interface_bot_t         *p_exmimo_pci_phys[MAX_CARDS] = INIT_ZEROS;  // inside struct has physical (DMA) pointers to pci_bot memory blocks
+
+exmimo_pci_interface_bot_virtual_t    exmimo_pci_kvirt[MAX_CARDS]; // has virtual pointers to pci_bot memory blocks
+
+#endif