|
|
@ -0,0 +1,402 @@ |
|
|
|
#include <linux/module.h> |
|
|
|
#include <linux/proc_fs.h> |
|
|
|
#include <linux/cdev.h> |
|
|
|
#include <linux/pci.h> |
|
|
|
|
|
|
|
/*************************************************************************************************/ |
|
|
|
MODULE_AUTHOR("h4ndh"); //hessamoddin hediyehloo
|
|
|
|
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 file_operations bar_fops = |
|
|
|
{ |
|
|
|
.owner = THIS_MODULE, |
|
|
|
.open = sono_bar_open, |
|
|
|
.release = sono_bar_close, |
|
|
|
.mmap = sono_bar_mmap |
|
|
|
}; |
|
|
|
|
|
|
|
static struct file_operations buffer_fops = |
|
|
|
{ |
|
|
|
.owner = THIS_MODULE, |
|
|
|
.open = sono_buffer_open, |
|
|
|
.release = sono_buffer_close, |
|
|
|
.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_fops); |
|
|
|
proc_create("sono_buffers", 0, NULL, &buffer_fops); |
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
vma->vm_flags |= VM_IO; |
|
|
|
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 (!pci_set_dma_mask(pcidev, DMA_BIT_MASK(64))) |
|
|
|
{ |
|
|
|
if (pci_set_consistent_dma_mask(pcidev, 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; |
|
|
|
} |
|
|
|
|
|
|
|
int i = 0; |
|
|
|
for(; 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; |
|
|
|
} |
|
|
|
} |
|
|
|
/*************************************************************************************************/ |
|
|
|
/*************************************************************************************************/ |
|
|
|
/*************************************************************************************************/ |