Fork for kernel 5.18 API change.
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.

403 lines
11 KiB

4 years ago
#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;
}
}
/*************************************************************************************************/
/*************************************************************************************************/
/*************************************************************************************************/