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; +}