//This is a simple dma driver for a Xilinx AXI DMA engine. It is used to transfer data between memory and a peripheral device. The driver is used in the following way: // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Anatoliy Chigirinskiy"); MODULE_DESCRIPTION("A simple dma driver for a Xilinx AXI DMA engine"); MODULE_VERSION("0.1"); static dma_addr_t dma_handle = 0; static int major_number; static void *dma_buffer = NULL; static size_t dma_size = 0; static resource_size_t dma_start, dma_len, pci_start, pci_len; static struct class *pci_class = NULL; static struct pci_device_data pci_dev_data; struct pci_device_data { struct device *pcidev; struct cdev cdev; }; static struct pci_device_id pci_ids[] = { {PCI_DEVICE(0x10EE, 0x7024)}, {PCI_DEVICE(0x10EE, 0x7022)}, {PCI_DEVICE(0x10EE, 0x7014)}, {PCI_DEVICE(0x10EE, 0x7012)}, {0,} }; MODULE_DEVICE_TABLE(pci, pci_ids); int read_device_config(struct pci_dev *pdev) { u16 vendor_id; u16 device_id; u8 revision; u8 class; u8 subclass; u8 prog_if; u8 header_type; u8 irq_line; u8 irq_pin; u32 bar0; u32 bar1; u32 bar2; pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id); pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id); pci_read_config_byte(pdev, PCI_REVISION_ID, &revision); pci_read_config_byte(pdev, PCI_CLASS_DEVICE, &class); pci_read_config_byte(pdev, PCI_CLASS_PROG, &prog_if); pci_read_config_byte(pdev, PCI_HEADER_TYPE, &header_type); pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq_line); pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &irq_pin); pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &bar0); pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1, &bar1); pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2, &bar2); printk(KERN_INFO "Vendor ID: 0x%04x\n", vendor_id); printk(KERN_INFO "Device ID: 0x%04x\n", device_id); printk(KERN_INFO "Revision: 0x%02x\n", revision); printk(KERN_INFO "Class: 0x%02x\n", class); printk(KERN_INFO "Prog IF: 0x%02x\n", prog_if); printk(KERN_INFO "Header type: 0x%02x\n", header_type); printk(KERN_INFO "IRQ line: 0x%02x\n", irq_line); printk(KERN_INFO "IRQ pin: 0x%02x\n", irq_pin); printk(KERN_INFO "BAR0: 0x%08x\n", bar0); printk(KERN_INFO "BAR1: 0x%08x\n", bar1); printk(KERN_INFO "BAR2: 0x%08x\n", bar2); return 0; } static ssize_t dma_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { return 0; } static ssize_t dma_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { return 0; } static int dma_open(struct inode *inode, struct file *file) { printk(KERN_INFO "dma_open\n"); return 0; } static int dma_release(struct inode *inode, struct file *file) { printk(KERN_INFO "dma_release\n"); return 0; } static int dma_mmap(struct file *file, struct vm_area_struct *vma) { uint64_t off = vma->vm_pgoff = (pci_start) >>PAGE_SHIFT; printk("Offset in mmap 0x%lu\n",off); uint64_t vsize = vma->vm_end - vma->vm_start; printk("Virt size is 0x%lu\n",vsize); uint64_t psize = pci_len - off; printk("Phys size is 0x%lu\n",psize); int rv; if (vsize > psize) return -EINVAL; vma->vm_page_prot = pgprot_noncached(vma-> vm_page_prot); printk("VMA page protection set\n"); rv = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, vsize, vma->vm_page_prot); if (rv) return -EAGAIN; printk("Physical memory mapped to user space\n"); return 0; } static ssize_t dma_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return 0; } static struct file_operations dma_fops = { .owner = THIS_MODULE, .read = dma_read, .write = dma_write, .mmap = dma_mmap, .open = dma_open, .release = dma_release }; int create_char_dev(struct pci_dev *pdev) { int rc; dev_t dev; rc = alloc_chrdev_region(&dev, 0, 1, "MyDmaModule"); if (rc) { printk(KERN_ERR "alloc_chrdev_region failed\n"); return rc; } major_number = MAJOR(dev); printk(KERN_INFO "major_number=%d\n", major_number); pci_class = class_create(THIS_MODULE,"MyDmaModule"); if (IS_ERR(pci_class)) { printk(KERN_ERR "class_create failed\n"); return PTR_ERR(pci_class); } cdev_init(&pci_dev_data.cdev, &dma_fops); pci_dev_data.pcidev = device_create(pci_class, NULL, MKDEV(major_number, 0), NULL, "MyDmaModule"); pci_dev_data.cdev.owner = THIS_MODULE; rc = cdev_add(&pci_dev_data.cdev, MKDEV(major_number, 0), 1); if (rc) { printk(KERN_ERR "cdev_add failed\n"); return rc; } printk(KERN_INFO "Device created\n"); return 0; } int remove_char_dev(struct pci_dev *pdev) { device_destroy(pci_class, MKDEV(major_number, 0)); cdev_del(&pci_dev_data.cdev); class_destroy(pci_class); unregister_chrdev_region(MKDEV(major_number, 0), 1); return 0; } static int dma_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int rc; size_t dma_size; struct device *dev = &pdev->dev; if (read_device_config(pdev)) { dev_err(dev, "read_device_config failed\n"); return -ENODEV; } rc = pci_enable_device(pdev); if (rc) { dev_err(dev, "pci_enable_device failed\n"); return rc; } rc = pci_request_regions(pdev, "dma"); if (rc) { dev_err(dev, "pci_request_regions failed\n"); pci_disable_device(pdev); } pci_start = pci_resource_start(pdev, 0); pci_len = pci_resource_len(pdev, 0); create_char_dev(pdev); dma_buffer = dma_alloc_coherent(dev, PAGE_SIZE, &dma_handle, GFP_KERNEL); if (!dma_buffer) { dev_err(dev, "dma_alloc_coherent failed\n"); rc = -ENOMEM; pci_request_regions(pdev, "dma"); } pci_set_drvdata(pdev, &pci_dev_data); dma_size = PAGE_SIZE; dev_info(dev, "dma_buffer=%p, dma_handle=0x%llx, dma_size=%zu\n", dma_buffer, (unsigned long long) dma_handle, dma_size); // Test DMA transfer memset(dma_buffer, 0x55, dma_size); printk(KERN_INFO "dma_buffer[0]=0x%02x\n", ((unsigned char *) dma_buffer)[0]); return 0; } static void dma_remove(struct pci_dev *pdev) { struct device *dev = &pdev->dev; remove_char_dev(pdev); dma_free_coherent(dev, PAGE_SIZE, dma_buffer, dma_handle); pci_release_regions(pdev); pci_disable_device(pdev); } static struct pci_driver dma_driver = { .name = "MyDMADriver", .id_table = pci_ids, .probe = dma_probe, .remove = dma_remove, }; static int __init dma_init(void) { int rc; rc = pci_register_driver(&dma_driver); if (rc) { printk(KERN_ERR "pci_register_driver failed\n"); return rc; } printk(KERN_INFO "dma_init\n"); return 0; } static void __exit dma_exit(void) { pci_unregister_driver(&dma_driver); printk(KERN_INFO "dma_exit\n"); } //module init and exit module_init(dma_init); module_exit(dma_exit);