diff --git a/Driver/pcie40_driver/ecs_emu.c b/Driver/pcie40_driver/ecs_emu.c
new file mode 100755
index 0000000000000000000000000000000000000000..9d5e98fdcca833daf510f958c0b714dce15dbadc
--- /dev/null
+++ b/Driver/pcie40_driver/ecs_emu.c
@@ -0,0 +1,202 @@
+//p40emu``+
+#define P40_FMT "P40ECSemu:%s(): "
+#define PCIE40_ECS_CLASS "lhcb_pcie40_ecs_emu"
+#define PCIE40_EMU
+
+#include "ecs.h"
+#include "pcie40_ioctl.h"
+
+//static void ecs_emu_vma_open(struct vm_area_struct *vma)
+//{
+//  printk(P40_INFO "VIRT=%lx, PHYS=%lx SIZE=%lx\n", P40_PARM, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, vma->vm_end - vma->vm_start);
+//}
+
+//static void ecs_emu_vma_close(struct vm_area_struct *vma)
+//{
+//  printk(P40_INFO "VIRT=%lx, PHYS=%lx SIZE=%lx\n", P40_PARM, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, vma->vm_end - vma->vm_start);
+//}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0))
+static int ecs_emu_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+#else
+static int ecs_emu_vma_fault(struct vm_fault *vmf)
+#endif
+{
+  struct pcie40_ecs_state *state = vma->vm_private_data;
+  unsigned long offset =
+   (unsigned long)(vmf->virtual_address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
+  void *ptr = NULL;
+
+  switch (iminor(vma->vm_file->f_path.dentry->d_inode)) {
+    case BAR0_CDEV_MINOR:
+      ptr = state->common->bar0_regs + offset;
+      break;
+    case BAR2_CDEV_MINOR:
+      ptr = state->common->bar2_regs + offset;
+      break;
+    default:
+      return -EINVAL;
+  }
+
+  vmf->page = vmalloc_to_page(ptr);
+  get_page(vmf->page);
+
+  //printk(P40_DIAG "fault @ 0x%08lX\n", P40_PARM, vmf->pgoff);
+  return 0;
+}
+
+static struct vm_operations_struct ecs_emu_vm_ops = {
+  //.access = generic_access_phys,
+  //.open = ecs_emu_vma_open,
+  //.close = ecs_emu_vma_close,
+  .fault = ecs_emu_vma_fault,
+};
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
+static int ecs_emu_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+#else
+static long ecs_emu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+#endif //+`ecs_emu_ioctl`
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+  struct inode *inode = filp->f_inode;
+#endif
+  struct pcie40_ecs_state *state = filp->private_data;
+
+  int bar;
+  switch (iminor(inode)) {
+    case BAR0_CDEV_MINOR:
+      bar = 0;
+      break;
+    case BAR2_CDEV_MINOR:
+      bar = 2;
+      break;
+    default:
+      return -EINVAL;
+  }
+
+  //ioctl.pcie`P40_ECS_GET_BAR_SIZE`
+  if (cmd != P40_ECS_GET_BAR_SIZE) {
+    printk(P40_DIAG "invalid ioctl command\n", P40_PARM);
+    return -EINVAL;
+  }
+  printk(P40_INFO "ECS BAR size is %lu\n", P40_PARM, state->common->bar_size[bar]);
+
+  return state->common->bar_size[bar];
+}
+
+//+`ecs_emu_mmap`
+static int ecs_emu_mmap(struct file* filp, struct vm_area_struct* vma)//;?>
+{
+  struct pcie40_ecs_state *state = filp->private_data;
+
+  int bar;
+  switch (iminor(filp->f_path.dentry->d_inode)) {
+    case BAR0_CDEV_MINOR:
+      bar = 0;
+      break;
+    case BAR2_CDEV_MINOR:
+      bar = 2;
+      break;
+    default:
+      return -EINVAL;
+  }
+  vma->vm_flags |= VM_SHARED;
+  vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+  printk(P40_INFO "VIRT=%lx PHYS=%lx SIZE=%lu\n", P40_PARM,
+         vma->vm_start, state->common->bar_start[bar], vma->vm_end - vma->vm_start);
+
+  vma->vm_ops = &ecs_emu_vm_ops;
+  vma->vm_private_data = state;
+  return 0;
+}
+
+static struct file_operations ecs_file_ops = {
+  .owner = THIS_MODULE,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
+  .ioctl = ecs_emu_ioctl,
+#else
+  .unlocked_ioctl = ecs_emu_ioctl,
+#endif
+  .mmap    = ecs_emu_mmap,
+  .open    = ecs_open,
+  .release = ecs_release,
+};
+
+int pcie40_ecs_emu_probe(struct pcie40_state *common)
+{
+  int rc = 0;
+  struct pcie40_ecs_state *state = NULL;
+
+  state = kzalloc(sizeof(struct pcie40_ecs_state), GFP_KERNEL);
+  if (IS_ERR(state)) {
+    printk(P40_ERR "kzalloc()\n", P40_PARM);
+    rc = PTR_ERR(state);
+    goto err_kzalloc;
+  }
+  state->common = common;
+  printk(P40_DIAG "state = 0x%p\n", P40_PARM, state);
+
+  rc = alloc_chrdev_region(&(state->dev_num), P40_ECS_CDEV_BASEMINOR, P40_ECS_CDEV_COUNT, P40_DRV_NAME);
+  if (rc < 0) {
+    printk(P40_ERR "alloc_chrdev_region()\n", P40_PARM);
+    goto err_alloc_chrdev_region;
+  }
+
+  // BAR0 endpoint
+  if (state->common->bar_size[0]) {
+    rc = pcie40_setup_cdev(pcie40_ecs_class, &(state->bar0_cdev), state->dev_num, BAR0_CDEV_MINOR, 0, BAR0_CDEV_NAME, state->common->dev_id, &ecs_file_ops);
+    if (rc < 0) {
+      goto err_bar0_dev;
+    }
+  }
+  // BAR2 endpoint
+  if (state->common->bar_size[2]) {
+    rc = pcie40_setup_cdev(pcie40_ecs_class, &(state->bar2_cdev), state->dev_num, BAR2_CDEV_MINOR, 2, BAR2_CDEV_NAME, state->common->dev_id, &ecs_file_ops);
+    if (rc < 0) {
+      goto err_bar2_dev;
+    }
+  }
+
+  common->ecs_state = state;
+
+  return rc;
+
+err_bar2_dev:
+  if (state->common->bar_size[0]) {
+    printk(P40_INFO "remove /dev/pcie40_%d_%s\n", P40_PARM, state->common->dev_id, BAR0_CDEV_NAME);
+    device_destroy(pcie40_ecs_class, MKDEV(MAJOR(state->dev_num), MINOR(state->dev_num)+BAR0_CDEV_MINOR));
+  }
+err_bar0_dev:
+  unregister_chrdev_region(state->dev_num, P40_ECS_CDEV_COUNT);
+err_alloc_chrdev_region:
+  kfree(state);
+  common->ecs_state = NULL;
+err_kzalloc:
+  return rc;
+}
+
+void pcie40_ecs_emu_remove(struct pcie40_state *common)
+{
+  struct pcie40_ecs_state *state = common->ecs_state;
+
+  printk(P40_DIAG "state = 0x%p\n", P40_PARM, state);
+  if (!state) {
+    printk(P40_ERR "no state\n", P40_PARM);
+    return;
+  }
+
+  if (state->common->bar_size[2]) {
+    printk(P40_INFO "remove /dev/pcie40_%d_%s\n", P40_PARM, state->common->dev_id, BAR2_CDEV_NAME);
+    device_destroy(pcie40_ecs_class, MKDEV(MAJOR(state->dev_num), MINOR(state->dev_num)+BAR2_CDEV_MINOR));
+  }
+  if (state->common->bar_size[0]) {
+    printk(P40_INFO "remove /dev/pcie40_%d_%s\n", P40_PARM, state->common->dev_id, BAR0_CDEV_NAME);
+    device_destroy(pcie40_ecs_class, MKDEV(MAJOR(state->dev_num), MINOR(state->dev_num)+BAR0_CDEV_MINOR));
+  }
+
+  unregister_chrdev_region(state->dev_num, P40_ECS_CDEV_COUNT);
+  kfree(state);
+  common->ecs_state = NULL;
+}