forked from Sepanta/pcie-driver
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
477 lines
14 KiB
477 lines
14 KiB
#include <linux/init.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/pci.h>
|
|
#include <linux/pci_ids.h>
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/highmem.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
#include <linux/spinlock_types.h>
|
|
|
|
#include <linux/fs.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/cdev.h>
|
|
|
|
#include <linux/sched.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/poll.h>
|
|
|
|
/*************************************************************************************************/
|
|
#define PRINT(A) printk(KERN_ALERT ">>>>> #A %x\n", A);
|
|
|
|
/*************************************************************************************************/
|
|
MODULE_AUTHOR("h4ndh"); //hediyehloo based on Talaee works
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
|
|
/*************************************************************************************************/
|
|
static struct pci_device_id pcie_ids[] =
|
|
{
|
|
{PCI_DEVICE(0x10ee, 0x7024)},
|
|
{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 chrdev_init(void);
|
|
|
|
static int usd_open_0(struct inode *inode, struct file *flip);
|
|
static int usd_close_0(struct inode *inode, struct file *filp);
|
|
static int usd_mmap_0(struct file *filp, struct vm_area_struct *vma);
|
|
|
|
static int usd_open_1(struct inode *inode, struct file *flip);
|
|
static int usd_close_1(struct inode *inode, struct file *filp);
|
|
static int usd_mmap_1(struct file *filp, struct vm_area_struct *vma);
|
|
|
|
static int usd_open_2(struct inode *inode, struct file *flip);
|
|
static int usd_close_2(struct inode *inode, struct file *filp);
|
|
static int usd_mmap_2(struct file *filp, struct vm_area_struct *vma);
|
|
|
|
static int create_dma_buffer(unsigned int bufcount);
|
|
|
|
/*************************************************************************************************/
|
|
static dev_t chrdev;
|
|
static struct cdev cdev;
|
|
static struct class *class = NULL;
|
|
|
|
#define DEV_NAME "usd_reg"
|
|
#define CLASS_NAME "usdpci"
|
|
|
|
/*************************************************************************************************/
|
|
struct page** dma_pages = NULL;
|
|
|
|
/*************************************************************************************************/
|
|
#define TOTAL_BAR_NUM 3
|
|
|
|
struct bar_t{
|
|
unsigned long bar_addr;
|
|
unsigned long bar_size;
|
|
void* bar_ptr;
|
|
struct file_operations chrdev_fops;
|
|
};
|
|
|
|
struct bar_t bars[TOTAL_BAR_NUM];
|
|
|
|
/*************************************************************************************************/
|
|
static struct pci_driver pci_driver =
|
|
{
|
|
.name = CLASS_NAME,
|
|
.id_table = pcie_ids,
|
|
.probe = pcie_probe,
|
|
.remove = __exit_p(pcie_remove),
|
|
};
|
|
|
|
/*************************************************************************************************/
|
|
#define PRINT_PREFIX "USDriver PCIe"
|
|
#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;
|
|
}
|
|
|
|
//gone have 3 bar for now
|
|
bars[0].chrdev_fops.owner = THIS_MODULE;
|
|
bars[0].chrdev_fops.open = usd_open_0;
|
|
bars[0].chrdev_fops.mmap = usd_mmap_0;
|
|
bars[0].chrdev_fops.release = usd_close_0;
|
|
bars[0].bar_size = 64 * 1024;
|
|
|
|
bars[1].chrdev_fops.owner = THIS_MODULE;
|
|
bars[1].chrdev_fops.open = usd_open_1;
|
|
bars[1].chrdev_fops.mmap = usd_mmap_1;
|
|
bars[1].chrdev_fops.release = usd_close_1;
|
|
bars[1].bar_size = 128 * 1024 * 1024;
|
|
|
|
bars[2].chrdev_fops.owner = THIS_MODULE;
|
|
bars[2].chrdev_fops.open = usd_open_2;
|
|
bars[2].chrdev_fops.mmap = usd_mmap_2;
|
|
bars[2].chrdev_fops.release = usd_close_2;
|
|
bars[2].bar_size = 8 * 1024;
|
|
|
|
PRINT_ALERT("register driver success" );
|
|
|
|
res = chrdev_init();
|
|
if ( res ) {
|
|
PRINT_ALERT("character device file creation failed");
|
|
return res;
|
|
}
|
|
|
|
PRINT_ALERT("driver loaded");
|
|
return res;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
static int chrdev_init(void)
|
|
{
|
|
PRINT_ALERT("chrdev_init");
|
|
|
|
char name[16];
|
|
int i = 0;
|
|
for( ;i < TOTAL_BAR_NUM; i++)
|
|
{
|
|
sprintf(name, "%s_%d", DEV_NAME, i);
|
|
proc_create(name, 0, NULL, &bars[i].chrdev_fops);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
static int usd_open_0(struct inode *inode, struct file *filp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
static int usd_open_1(struct inode *inode, struct file *filp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
static int usd_open_2(struct inode *inode, struct file *filp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
static int usd_close_0(struct inode *inode, struct file *filp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
static int usd_close_1(struct inode *inode, struct file *filp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
static int usd_close_2(struct inode *inode, struct file *filp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
static int usd_mmap_0(struct file *filp, struct vm_area_struct *vma)
|
|
{
|
|
PRINT_ALERT("MMAP");
|
|
|
|
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
|
|
unsigned long vsize = vma->vm_end - vma->vm_start;
|
|
|
|
unsigned long physical = bars[0].bar_addr + off;
|
|
|
|
unsigned int intvsize = bars[0].bar_size - off;
|
|
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
|
vma->vm_flags |= VM_IO;
|
|
|
|
if ( vsize < intvsize )
|
|
intvsize = vsize;
|
|
|
|
printk(KERN_ALERT "-----------------------------------\n");
|
|
printk(KERN_ALERT "BAR0 reg 0 = 0x%x\n", intvsize);
|
|
|
|
remap_pfn_range(vma, vma->vm_start, physical>>PAGE_SHIFT, intvsize, vma->vm_page_prot);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
static int usd_mmap_1(struct file *filp, struct vm_area_struct *vma)
|
|
{
|
|
PRINT_ALERT("MMAP");
|
|
|
|
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
|
|
unsigned long vsize = vma->vm_end - vma->vm_start;
|
|
|
|
unsigned long physical = bars[1].bar_addr + off;
|
|
|
|
unsigned int intvsize = bars[1].bar_size - off;
|
|
// these are required for io maps, but is it okay for the buffer as well?
|
|
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
|
vma->vm_flags |= VM_IO;
|
|
if ( vsize < intvsize )
|
|
intvsize = vsize;
|
|
remap_pfn_range(vma, vma->vm_start, physical>>PAGE_SHIFT, intvsize, vma->vm_page_prot);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
static int usd_mmap_2(struct file *filp, struct vm_area_struct *vma)
|
|
{
|
|
PRINT_ALERT("MMAP");
|
|
|
|
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
|
|
unsigned long vsize = vma->vm_end - vma->vm_start;
|
|
|
|
unsigned long physical = bars[2].bar_addr + off;
|
|
|
|
unsigned int intvsize = bars[2].bar_size - off;
|
|
// these are required for io maps, but is it okay for the buffer as well?
|
|
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
|
vma->vm_flags |= VM_IO;
|
|
if ( vsize < intvsize )
|
|
intvsize = vsize;
|
|
remap_pfn_range(vma, vma->vm_start, physical>>PAGE_SHIFT, intvsize, vma->vm_page_prot);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
static int create_dma_buffer(unsigned int bufcount)
|
|
{
|
|
int i;
|
|
int bufidx = 0;
|
|
unsigned int gfp_mask = GFP_KERNEL | __GFP_DMA;
|
|
dma_addr_t bus_addr;
|
|
u8* bar2_data;
|
|
bar2_data = (u8*)bars[2].bar_ptr;
|
|
|
|
|
|
printk(KERN_ALERT "USDriver PCIe DMA buffer alloc request: %d pages\n", bufcount);
|
|
|
|
if ( dma_pages != NULL )
|
|
{
|
|
PRINT_ALERT("DMA buffer already exist! Strange!");
|
|
}
|
|
|
|
dma_pages = kmalloc(sizeof(struct page*) * bufcount, GFP_KERNEL);
|
|
if ( dma_pages == NULL ) {
|
|
PRINT_ALERT("DMA dma_pages alloc failed!" );
|
|
return 1;
|
|
}
|
|
|
|
for ( bufidx = 0; bufidx < bufcount; bufidx++ ) {
|
|
void __iomem *maddr = NULL;
|
|
struct page *pages = alloc_page(gfp_mask);
|
|
if ( pages == NULL ) {
|
|
PRINT_ALERT("DMA buffer alloc failed!" );
|
|
return 1;
|
|
}
|
|
maddr = page_address(pages);
|
|
dma_pages[bufidx] = pages;
|
|
|
|
bus_addr = pci_map_single(pcidev, maddr, PAGE_SIZE, DMA_BIDIRECTIONAL);
|
|
printk(KERN_ALERT "%d -- [%d] -- >>%lx\n", PAGE_SIZE, bufidx, bus_addr);
|
|
|
|
iowrite32(bus_addr >> 32, &bar2_data[bufidx * 8]);
|
|
wmb();
|
|
|
|
iowrite32(bus_addr, &bar2_data[bufidx * 8 + 4]);
|
|
wmb();
|
|
|
|
if ( pci_dma_mapping_error(pcidev, bus_addr) ) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
PRINT_ALERT("DMA buffer alloc successful");
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
static int __init pcie_probe (struct pci_dev *dev, const struct pci_device_id *id)
|
|
{
|
|
int rc = 0;
|
|
|
|
pcidev = dev;
|
|
|
|
rc = pci_enable_device(dev);
|
|
if(rc)
|
|
{
|
|
PRINT_ALERT("driver pci_enable_device() failed");
|
|
goto probe_fail_enable;
|
|
}
|
|
|
|
/***************BAR 0***********************/
|
|
if(!(pci_resource_flags(dev, 0) & IORESOURCE_MEM))
|
|
{
|
|
printk(KERN_ERR "USDriver PCIe driver incorrect BAR configuration\n" );
|
|
rc = 1;
|
|
goto probe_fail;
|
|
}
|
|
|
|
bars[0].bar_addr = pci_resource_start(dev, 0);
|
|
rc = pci_request_region(dev, 0, "bar0");
|
|
if(rc)
|
|
{
|
|
PRINT_ALERT("driver pci_request_regions bar0 failed");
|
|
goto probe_fail;
|
|
}
|
|
|
|
bars[0].bar_ptr = pci_iomap(dev, 0, bars[0].bar_size);
|
|
u32* bar0_data = (u32*)bars[0].bar_ptr;
|
|
|
|
if(bar0_data == 0)
|
|
{
|
|
PRINT_ALERT("driver failed to map BAR 0");
|
|
rc = 1;
|
|
goto probe_fail_release_region;
|
|
}
|
|
|
|
/***************BAR 1***********************/
|
|
if(!(pci_resource_flags(dev, 1) & IORESOURCE_MEM))
|
|
{
|
|
printk(KERN_ERR "USDriver PCIe driver incorrect BAR configuration\n" );
|
|
rc = 1;
|
|
goto probe_fail;
|
|
}
|
|
|
|
bars[1].bar_addr = pci_resource_start(dev, 1);
|
|
rc = pci_request_region(dev, 1, "bar1");
|
|
if(rc)
|
|
{
|
|
PRINT_ALERT("driver pci_request_regions bar1 failed");
|
|
goto probe_fail;
|
|
}
|
|
|
|
bars[1].bar_ptr = pci_iomap(dev, 1, bars[1].bar_size);
|
|
u32* bar1_data = (u32*)bars[1].bar_ptr;
|
|
|
|
if(bar1_data == 0)
|
|
{
|
|
PRINT_ALERT("driver failed to map BAR 1");
|
|
rc = 1;
|
|
goto probe_fail_release_region;
|
|
}
|
|
|
|
/*************************BAR2***************************************/
|
|
if(!(pci_resource_flags(dev, 2) & IORESOURCE_MEM))
|
|
{
|
|
printk(KERN_ERR "USDriver PCIe driver incorrect BAR configuration\n" );
|
|
rc = 1;
|
|
goto probe_fail;
|
|
}
|
|
|
|
bars[2].bar_addr = pci_resource_start(dev, 2);
|
|
rc = pci_request_region(dev, 2, "bar2");
|
|
if(rc)
|
|
{
|
|
PRINT_ALERT("driver pci_request_regions bar2 failed");
|
|
goto probe_fail;
|
|
}
|
|
|
|
bars[2].bar_ptr = pci_iomap(dev, 2, bars[2].bar_size);
|
|
u32* bar2_data = (u32*)bars[2].bar_ptr;
|
|
|
|
if(bar2_data == 0)
|
|
{
|
|
PRINT_ALERT("driver failed to map BAR 2");
|
|
rc = 1;
|
|
goto probe_fail_release_region;
|
|
}
|
|
|
|
pci_set_master(dev);
|
|
|
|
printk(KERN_ALERT "-----------------------------------\n");
|
|
printk(KERN_ALERT "BAR0 reg 0 = 0x%x\n", ioread32(bar0_data));
|
|
*bar1_data = 0x12345678;
|
|
printk(KERN_ALERT "BAR1 reg 0 = 0x%x\n", ioread32(bar1_data));
|
|
printk(KERN_ALERT "-----------------------------------\n");
|
|
|
|
create_dma_buffer(2);
|
|
|
|
return 0;
|
|
|
|
map_error_handling:
|
|
PRINT_ALERT("DMA ERR");
|
|
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");
|
|
|
|
pci_iounmap(dev, bars[0].bar_ptr);
|
|
pci_iounmap(dev, bars[1].bar_ptr);
|
|
pci_iounmap(dev, bars[2].bar_ptr);
|
|
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);
|
|
|
|
char name[16];
|
|
int i = 0;
|
|
for( ;i < TOTAL_BAR_NUM; i++)
|
|
{
|
|
sprintf(name, "%s_%d", DEV_NAME, i);
|
|
remove_proc_entry(name, NULL);
|
|
}
|
|
|
|
PRINT_ALERT("driver unloaded");
|
|
}
|
|
|
|
module_init(pcie_init);
|
|
module_exit(pcie_exit);
|
|
|
|
/*************************************************************************************************/
|
|
/*************************************************************************************************/
|
|
/*************************************************************************************************/
|
|
/*************************************************************************************************/
|
|
/*************************************************************************************************/
|
|
/*************************************************************************************************/
|
|
/*************************************************************************************************/
|
|
/*************************************************************************************************/
|
|
|