#include #include #include #include #include /*************************************************************************************************/ MODULE_AUTHOR("h4ndh"); //hessamoddin hediyehloo MODULE_AUTHOR("alihatamitajik"); MODULE_LICENSE("Dual BSD/GPL"); /*************************************************************************************************/ #define VENDOR_ID 0x10EE #define DEVICE_ID 0x7024 struct pci_device_id pcie_ids[] = { {PCI_DEVICE(VENDOR_ID, DEVICE_ID)}, {0, } }; MODULE_DEVICE_TABLE(pci, pcie_ids); /*************************************************************************************************/ static struct pci_dev *pcidev = NULL; /*************************************************************************************************/ static int pcie_init(void); static void pcie_exit(void); static int pcie_probe (struct pci_dev *dev, const struct pci_device_id *id); static void pcie_remove(struct pci_dev *dev); static int proc_init(void); static int sono_bar_open(struct inode *inode, struct file *flip); static int sono_bar_close(struct inode *inode, struct file *filp); static int sono_bar_mmap(struct file *filp, struct vm_area_struct *vma); static int sono_buffer_open(struct inode *inode, struct file *flip); static int sono_buffer_close(struct inode *inode, struct file *filp); static int sono_buffer_mmap(struct file *filp, struct vm_area_struct *vma); static int create_dma_buffer(void); static void release_dma_buffer(int num); static void unmap_bar_buffer(int num); static void pass_dma_address_to_hw(void); /*************************************************************************************************/ #define DEV_NAME "sonoDevice" #define CLASS_NAME "sonoClass" /*************************************************************************************************/ #define TOTAL_BAR_NUM 3 struct bar_t { resource_size_t start_addr; resource_size_t end_addr; resource_size_t length; unsigned long flags; void* bar_ptr; }; struct bar_t bars[TOTAL_BAR_NUM]; /*************************************************************************************************/ #define TOTAL_BUFFER_NUM 16 struct buffer_t { void * ptr; dma_addr_t address; }; struct buffer_t buffers[TOTAL_BUFFER_NUM]; /*************************************************************************************************/ #define DMA_SIZE 4*1024*1024 /*************************************************************************************************/ static struct pci_driver pci_driver = { .name = CLASS_NAME, .id_table = pcie_ids, .probe = pcie_probe, .remove = __exit_p(pcie_remove), }; /*************************************************************************************************/ static struct proc_ops bar_ops = { .proc_open = sono_bar_open, .proc_release = sono_bar_close, .proc_mmap = sono_bar_mmap }; static struct proc_ops buffer_ops = { .proc_open = sono_buffer_open, .proc_release = sono_buffer_close, .proc_mmap = sono_buffer_mmap }; /*************************************************************************************************/ #define PRINT_PREFIX "Sono pcie driver" #define PRINT_ALERT(STR) printk(KERN_ALERT "%s %s\n", PRINT_PREFIX, STR) #define PRINT_WARN(STR) printk(KERN_WARN "%s %s\n", PRINT_PREFIX, STR) /*************************************************************************************************/ static int __init pcie_init(void) { int res = 0; PRINT_ALERT("driver initializing"); res = pci_register_driver(&pci_driver); if ( res ) { PRINT_ALERT("device not found"); return res; } PRINT_ALERT("register driver success" ); res = proc_init(); if ( res ) { PRINT_ALERT("proc file creation failed"); return res; } PRINT_ALERT("driver loaded"); return res; } /*************************************************************************************************/ static int proc_init(void) { PRINT_ALERT("proc_init"); proc_create("sono_bars", 0, NULL, &bar_ops); proc_create("sono_buffers", 0, NULL, &buffer_ops); return 0; } /*************************************************************************************************/ static int sono_bar_open(struct inode *inode, struct file *filp) { return 0; } /*************************************************************************************************/ static int sono_buffer_open(struct inode *inode, struct file *filp) { return 0; } /*************************************************************************************************/ static int sono_bar_close(struct inode *inode, struct file *filp) { return 0; } /*************************************************************************************************/ static int sono_buffer_close(struct inode *inode, struct file *filp) { return 0; } /*************************************************************************************************/ static int sono_bar_mmap(struct file *filp, struct vm_area_struct *vma) { PRINT_ALERT("BAR MMAP"); unsigned long offset = vma->vm_pgoff; int bar_num = offset; unsigned long physical = bars[bar_num].start_addr; unsigned long bar_size = bars[bar_num].length; unsigned long vsize = vma->vm_end - vma->vm_start; if(vsize > bar_size) return -EINVAL; #if LINUX_VERSION_CODE <= KERNEL_VERSION(5,4,0) vma->vm_flags |= VM_IO;// ubuntu 14 #elif LINUX_VERSION_CODE <= KERNEL_VERSION(6,8,0) vm_flags_set(vma, VM_IO); #endif vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); int res = remap_pfn_range(vma, vma->vm_start, physical >> PAGE_SHIFT, vsize, vma->vm_page_prot); if(res) { printk(KERN_ERR "Failed to map bar @ %d\n", bar_num); } return res; } /*************************************************************************************************/ static int sono_buffer_mmap(struct file *filp, struct vm_area_struct *vma) { PRINT_ALERT("BUFFER MMAP"); unsigned long offset = vma->vm_pgoff; int buffer_num = offset; unsigned long physical = virt_to_phys(buffers[buffer_num].ptr); unsigned long vsize = vma->vm_end - vma->vm_start; if(vsize > DMA_SIZE) return -EINVAL; int res = remap_pfn_range(vma, vma->vm_start, physical >> PAGE_SHIFT, vsize, vma->vm_page_prot); if(res) { printk(KERN_ERR "failed to map mapped DMA buffer at phys: %p\n", buffers[buffer_num].address); } return res; } /*************************************************************************************************/ static int create_dma_buffer() { int i = 0; for(; i < TOTAL_BUFFER_NUM; i++) { buffers[i].ptr = dma_alloc_coherent(&pcidev->dev, DMA_SIZE, &buffers[i].address, GFP_USER); if(buffers[i].ptr == NULL) { PRINT_ALERT("I can't allocate the DMA buffer"); while(--i >= 0) { release_dma_buffer(i); } return 1; } } return 0; } /*************************************************************************************************/ static int __init pcie_probe (struct pci_dev *dev, const struct pci_device_id *id) { int rc = 0; if(pcidev) { PRINT_ALERT("Cant handle more than one device at a time"); return -EINVAL; } pcidev = dev; rc = pci_enable_device(dev); if(rc) { PRINT_ALERT("driver pci_enable_device() failed"); goto probe_fail_enable; } if (!dma_set_mask(&pcidev->dev, DMA_BIT_MASK(64))) { if (dma_set_coherent_mask(&pcidev->dev, DMA_BIT_MASK(64))) { printk(KERN_ERR "Unable to obtain 64bit DMA for consistent allocations\n"); rc = 1; goto probe_fail; } } create_dma_buffer(); rc = pci_request_regions(pcidev, DEV_NAME); if(rc) { PRINT_ALERT("Failed to request regoins"); goto probe_fail; } for(int i = 0; i < TOTAL_BAR_NUM; i++) { bars[i].flags = pci_resource_flags(pcidev, i); if (!(bars[i].flags & IORESOURCE_MEM)) { printk(KERN_ERR "Sono pcie driver incorrect BAR configuration\n" ); rc = 1; goto probe_fail_release_region; } bars[i].start_addr = pci_resource_start(pcidev, i); bars[i].end_addr = pci_resource_end(pcidev, i); bars[i].length = pci_resource_len(pcidev, i); bars[i].bar_ptr = pci_iomap(pcidev, i, bars[i].length); u32* temp = (u32*)bars[i].bar_ptr; if(temp == 0) { printk(KERN_ERR "driver failed to map BAR %d\n", i); rc = 1; while(--i >= 0) { unmap_bar_buffer(i); } for(; i < TOTAL_BUFFER_NUM; i++) { release_dma_buffer(i); } goto probe_fail_release_region; } } pci_set_master(dev); pass_dma_address_to_hw(); return 0; probe_fail_release_region: pci_release_regions(dev); probe_fail: pci_disable_device(dev); probe_fail_enable: return rc; } /*************************************************************************************************/ static void pcie_remove(struct pci_dev *dev) { PRINT_ALERT("remove driver"); pci_clear_master(dev); PRINT_ALERT("Cleared PCIe master"); int i = 0; for(; i < TOTAL_BAR_NUM ; i++) { unmap_bar_buffer(i); } for(; i < TOTAL_BUFFER_NUM ; i++) { release_dma_buffer(i); } PRINT_ALERT("IOunmap"); pci_release_regions(dev); PRINT_ALERT("pci_release_regions"); pci_disable_device(dev); PRINT_ALERT("pci_disable_device"); } /*************************************************************************************************/ static void __exit pcie_exit(void) { PRINT_ALERT("driver unloading"); PRINT_ALERT("driver unregistering"); pci_unregister_driver(&pci_driver); remove_proc_entry("sono_bars", NULL); remove_proc_entry("sono_buffers", NULL); PRINT_ALERT("driver unloaded"); } module_init(pcie_init); module_exit(pcie_exit); /*************************************************************************************************/ void release_dma_buffer(int num) { dma_free_coherent(&pcidev->dev, DMA_SIZE, buffers[num].ptr, buffers[num].address); } /*************************************************************************************************/ void unmap_bar_buffer(int num) { pci_iounmap(pcidev, bars[num].bar_ptr); } /*************************************************************************************************/ //This function is tightly coupled with hw def void pass_dma_address_to_hw(void) { int index = 0x00002060; u32* temp = (u32*)bars[2].bar_ptr; int i; for(i = 0; i < TOTAL_BUFFER_NUM; i++) { dma_addr_t address = buffers[i].address; temp[index / 4] = address >> 32; index += 4; temp[index / 4] = (address & 0xFFFFFFFF); index += 4; } } /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/