Results 1 to 1 of 1
Hi, i am developing a driver for a custom PCI board. I am following
the example on Essential Linux Device Drivers, but when i am calling
the pci_alloc_consitent i get ...
Enjoy an ad free experience by logging in. Not a member yet? Register.
- 03-29-2011 #1Just Joined!
- Join Date
- May 2010
- Posts
- 11
Help with DMA on PCI Driver Options
Hi, i am developing a driver for a custom PCI board. I am following
the example on Essential Linux Device Drivers, but when i am calling
the pci_alloc_consitent i get the following error:
Code:Mar 29 15:18:42 luis-desktop kernel: [ 4520.075823] BUG: unable to handle kernel NULL pointer dereference at 00000004 Mar 29 15:18:42 luis-desktop kernel: [ 4520.078277] IP: [<c010797f>] dma_generic_alloc_coherent+0xaf/0xc0 Mar 29 15:18:42 luis-desktop kernel: [ 4520.078277] *pde = 0a417067 *pte = 00000000 Mar 29 15:18:42 luis-desktop kernel: [ 4520.078277] Oops: 0002 [#1] SMP
I am running on a x86 (Pentium III) with Ubuntu Kernel 2.6.32-24
I tryed to take a look at ldd3 but no answer so far...
The driver code is:
Code:/* * pci_bridge-driver.c - template Linux driver for the opencores' pci bridge . Works on * kernel 2.6.x * * tested on Xubuntu, kernel 2.6.20-15-generic * * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY * RIGHTS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /* * Build/use notes (you'll probably need to be superuser to run most of * the steps below): * * 1) How to build the driver: * * $ make * * 2) How to install the driver: * * $ insmod pci_bridge-driver.ko * * TODO: change the code and use misc device and udev, so to avoid the stuff below (3-4) !! * 3) If pci_bridge_init_major (below) is 0, then obtain the major number: * * $ cat /proc/devices * * Look for the line that contains the string "pci_bridge". The major * number is to the left. * * 4) Make the pci_bridge device special file. Substitute the major * number obtained above for <major_number>. * * $ mknod /dev/pci_bridge c <major_number> 0 * * 5) How to remove the driver: * * $ rmmod pci_bridge-driver */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/ioctl.h> //#include <asm/byteorder.h> /* PCI is little endian */ #include <asm/uaccess.h> /* copy to/from user */ #include "kint.h" #define PCI_BRIDGE_DEVICE_ID 0x0001 #define PCI_BRIDGE_VENDOR_ID 0x1895 #define PCI_DRIVER_NAME "pci_bridge" /* driver name */ #define BRIDGE_MEM_MAPPED 0 #define BRIDGE_IO_MAPPED 1 /* * PCI device IDs supported by this driver. The PCI_DEVICE macro sets * the vendor and device fields to its input arguments, sets subvendor * and subdevice to PCI_ANY_ID. It does not set the class fields. */ static struct pci_device_id pci_bridge_ids[] = { { PCI_DEVICE(PCI_BRIDGE_VENDOR_ID, PCI_BRIDGE_DEVICE_ID), }, { 0, } }; /* * For completeness, tell the module loading and hotplug systems * what PCI devices this module supports. */ MODULE_DEVICE_TABLE(pci, pci_bridge_ids); /* * pci_register_driver parameter. */ static int pci_bridge_probe(struct pci_dev *, const struct pci_device_id *); static void pci_bridge_remove(struct pci_dev *); static struct pci_driver pci_bridge_driver = { .name = PCI_DRIVER_NAME, .id_table = pci_bridge_ids, .probe = pci_bridge_probe, .remove = pci_bridge_remove, }; /* * File operations i/f. */ int pci_bridge_open(struct inode *, struct file *); int pci_bridge_release(struct inode *, struct file *); ssize_t pci_bridge_read(struct file *, char __user *, size_t, loff_t *); ssize_t pci_bridge_write(struct file *, const char __user *, size_t, loff_t *); int pci_bridge_ioctl(struct inode *pnode, struct file *filp, unsigned int cmd, unsigned long arg); // seek file operation function loff_t bridge_lseek(struct file *filp, loff_t offset, int origin); static void dma_descriptor_release(struct pci_dev *pdev); static void dma_descriptor_setup(struct pci_dev *pdev); static struct file_operations pci_bridge_fops = { read: pci_bridge_read, write: pci_bridge_write, open: pci_bridge_open, release: pci_bridge_release, ioctl: pci_bridge_ioctl, llseek: bridge_lseek }; static int __init pci_bridge_init(void); static void __exit pci_bridge_exit(void); /* * Driver major number. 0 = allocate dynamically. */ static int pci_bridge_init_major = 0; static int pci_bridge_major; /* * Per-device structure. */ static struct pci_bridge_dev { struct cdev cdev; /* Char device structure */ struct pci_dev *pcidev; /* PCI device pointer */ int current_resource; u32 page_addr; u8 num_of_bases; int base_map[6]; u32 bases[6]; u32 base_size[6]; u32 base_page_offset; u32 offset; u8 interrupt_line; } *pci_bridge_devices; static struct device_data { void *dma_buffer_rx; dma_addr_t dma_bus_rx; void *dma_buffer_tx; dma_addr_t dma_bus_tx; }*c_memory_map; /* * pci_bridge_probe - pci_driver probe function. Just enable the PCI device. * Could also check various configuration registers, find a specific PCI * device, request a specific region, etc. */ static int pci_bridge_probe(struct pci_dev *pcidev, const struct pci_device_id *id) { struct pci_bridge_dev *dev; printk("pci_bridge_probe called ...\n"); if(pcidev == NULL) { printk(KERN_NOTICE "pci_bridge_probe: PCI DEV is NULL\n"); return -EINVAL; } dev = pci_bridge_devices; // only one device for now if(dev == NULL) printk("pci_bridge_probe: device structure not allocated\n"); else { pci_enable_device(pcidev); pci_set_master(pcidev); dev->pcidev = pcidev; } return 0; } /* * pci_bridge_remove - pci_driver remove function. Release allocated resources, * etc. */ static void __devexit pci_bridge_remove(struct pci_dev *dev) { printk("pci_bridge_remove called ...\n"); } /* * pci_bridge_init - module init function. By convention, the function is * declared static, even though it is not exported to the rest of the * kernel unless explicitly requested via the EXPORT_SYMBOL macro. The * __init qualifier tells the loader that the function is only used at * module initialization time. */ static int __init pci_bridge_init(void) { struct pci_bridge_dev *dev; dev_t devno; int result; unsigned short num_of_bases; u32 base_address; printk("pci_bridge_init called ...\n"); /* * Allocate the per-device structure(s). */ pci_bridge_devices = kmalloc(sizeof(struct pci_bridge_dev), GFP_KERNEL); if(pci_bridge_devices == NULL) { result = -ENOMEM; goto fail; } /* * Get a range of minor numbers to work with, asking for a dynamic * major unless directed otherwise at load time. */ if(pci_bridge_init_major) { pci_bridge_major = pci_bridge_init_major; devno = MKDEV(pci_bridge_major, 0); result = register_chrdev_region(devno, 1, PCI_DRIVER_NAME); } else { result = alloc_chrdev_region(&devno, 0, 1, PCI_DRIVER_NAME); pci_bridge_major = MAJOR(devno); } if(result < 0) { printk(KERN_NOTICE "pci_bridge: can't get major %d\n", pci_bridge_major); goto fail; } dev = pci_bridge_devices;/* only one device for now */ /* * Initialize and add this device's character device table entry. */ dev->pcidev = NULL; cdev_init(&dev->cdev, &pci_bridge_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &pci_bridge_fops; dev->offset = 0; result = cdev_add(&dev->cdev, devno, 1); if(result) { printk(KERN_NOTICE "Error %d adding %s device", result, PCI_DRIVER_NAME); goto fail; } if((result = pci_register_driver(&pci_bridge_driver)) != 0) { printk(KERN_NOTICE "Error %d registering %s PCI device",result, PCI_DRIVER_NAME); goto fail; } if(dev->pcidev == NULL) { printk(KERN_NOTICE "PCI DEV is NULL, probe failed?\n"); goto fail; } base_address = pci_resource_start(dev->pcidev, 0); printk("<1> First base address register found at %08X \n ", pci_resource_start(dev->pcidev, 0)); num_of_bases = 0; while ((base_address = pci_resource_start(dev->pcidev, num_of_bases))!= 0x00000000 && (num_of_bases < 6)) { unsigned long flags; flags = pci_resource_flags(dev->pcidev, num_of_bases); dev->bases[num_of_bases] = base_address; dev->base_size[num_of_bases] = pci_resource_end(dev->pcidev, num_of_bases) - base_address + 1; // check if resource is IO mapped if (flags & IORESOURCE_IO) dev->base_map[num_of_bases] = BRIDGE_IO_MAPPED; else dev->base_map[num_of_bases] = BRIDGE_MEM_MAPPED; num_of_bases++; } if (num_of_bases < 1) printk("<1>No implemented base address registers found! \n "); dev->current_resource = - 1; // store number of bases in structure dev->num_of_bases = num_of_bases; printk("num_of_bases found %d \n", num_of_bases); // display information about all base addresses found in this procedure for (num_of_bases = 0; num_of_bases < dev->num_of_bases; num_of_bases+ +) { printk("<1>BAR%d range from %08X to %08X \n ", num_of_bases, dev- >bases[num_of_bases], dev->bases[num_of_bases] + dev- >base_size[num_of_bases]); } if(pci_read_config_byte(dev->pcidev,PCI_INTERRUPT_LINE, &dev- >interrupt_line)) { printk("Could not get interrupt line"); } else { printk("Interrupt Line is %d \n", dev->interrupt_line); } dma_descriptor_setup(dev->pcidev); return 0; fail: pci_bridge_exit(); return result; } /* * pci_bridge_exit - module exit function. Release resources allocated * by pci_bridge_init. */ static void __exit pci_bridge_exit(void) { printk("pci_bridge_exit called ...\n"); if(pci_bridge_devices) { struct pci_bridge_dev *dev; dev = &pci_bridge_devices[0]; dma_descriptor_release(dev->pcidev); cdev_del(&dev->cdev); kfree(pci_bridge_devices); pci_bridge_devices = NULL; } unregister_chrdev_region(MKDEV(pci_bridge_major, 0), 1); pci_bridge_major = 0; pci_unregister_driver(&pci_bridge_driver); } /* * pci_bridge_open - open file processing. */ int pci_bridge_open(struct inode *inode, struct file *filep) { struct pci_bridge_dev *dev; dev = container_of(inode->i_cdev, struct pci_bridge_dev, cdev); filep->private_data = dev; // used by read, write, etc dev->current_resource = -1; /* Success */ return 0; } /* * pci_bridge_release - close processing. */ int pci_bridge_release(struct inode *inode, struct file *filep) { /* Success */ return 0; } /* * seek file operation function */ loff_t bridge_lseek(struct file *filp, loff_t offset, int origin) { struct pci_bridge_dev *dev; loff_t requested_offset; int resource_num; dev = filp->private_data; resource_num = dev->current_resource; switch (origin) { case SEEK_CUR:requested_offset = dev->offset + offset; break; case SEEK_END:requested_offset = dev->base_size[resource_num] + offset; break; default:requested_offset = offset; break; } if ((requested_offset < 0) || (requested_offset > dev- >base_size[resource_num])) return -EFAULT; dev->offset = requested_offset; return requested_offset; } /* * pci_bridge_read - read processing. */ ssize_t pci_bridge_read (struct file *filp, char *buf, size_t count, loff_t *offset_out ) { struct pci_bridge_dev *dev; unsigned long current_address; unsigned long actual_count; unsigned long offset; int resource_num; int i; unsigned int value; unsigned int *kern_buf; unsigned int *kern_buf_tmp; unsigned long size; int result; dev = filp->private_data; offset = dev->offset; resource_num = dev->current_resource; size = dev->base_size[resource_num]; current_address = dev->page_addr + dev->base_page_offset + dev- >offset; if (dev->current_resource < 0) return -ENODEV; if (offset == size) return 0; if ( (offset + count) > size ) actual_count = size - offset; else actual_count = count; // verify range if it is OK to copy from if ((result = access_ok(VERIFY_WRITE, buf, actual_count)) ==0) return result; kern_buf = kmalloc(actual_count, GFP_KERNEL | GFP_DMA); kern_buf_tmp = kern_buf; if (kern_buf <= 0) return 0; memcpy_fromio(kern_buf, current_address, actual_count); i = actual_count/4; while(i--) { // value = readl(current_address); value = *(kern_buf); put_user(value, ((unsigned int *)buf)); buf += 4; ++kern_buf; // current_address += 4; } kfree(kern_buf_tmp); dev->offset = dev->offset + actual_count; *(offset_out) = dev->offset; return actual_count; } /* * pci_bridge_write - write processing. */ ssize_t pci_bridge_write (struct file *filp, const char *buf, size_t count, loff_t *offset_out) { struct pci_bridge_dev *dev; unsigned long current_address; unsigned long actual_count; unsigned long offset; int resource_num; int i; int value; unsigned long size; int result; int *kern_buf; int *kern_buf_tmp; dev = filp->private_data; current_address = dev->page_addr + dev->base_page_offset + dev- >offset; resource_num = dev->current_resource; size = dev->base_size[resource_num]; offset = dev->offset; if (dev->current_resource < 0) return -ENODEV; if (offset == size) return 0; if ( (offset + count) > size ) actual_count = size - offset; else actual_count = count; // verify range if it is OK to copy from if ((result = access_ok(VERIFY_READ, buf, actual_count)) == 0) return result; kern_buf = kmalloc(actual_count, GFP_KERNEL | GFP_DMA); kern_buf_tmp = kern_buf; if (kern_buf <= 0) return 0; i = actual_count/4; while(i--) { get_user(value, ((int *)buf)); // writel(value, current_address); *kern_buf = value; buf += 4; //current_address += 4; ++kern_buf; } memcpy_toio(current_address, kern_buf_tmp, actual_count); kfree(kern_buf_tmp); dev->offset = dev->offset + actual_count; *(offset_out) = dev->offset; return actual_count; } /* * helper function for memory remaping */ int open_mem_mapped(struct pci_bridge_dev *dev) { int resource_num = dev->current_resource; unsigned long num_of_pages = 0; unsigned long base = dev->bases[resource_num]; unsigned long size = dev->base_size[resource_num]; //printk("\n current resource=%d , size = %d, base=%08X", dev- >current_resource , size, dev->bases[resource_num]); if (!(num_of_pages = (unsigned long)(size/PAGE_SIZE))); num_of_pages++; dev->base_page_offset = base & ~PAGE_MASK; if ((dev->base_page_offset + size) < (num_of_pages*PAGE_SIZE)) num_of_pages++; // remap memory mapped space dev->page_addr = (unsigned long)ioremap(base & PAGE_MASK, num_of_pages * PAGE_SIZE); if (dev->page_addr == 0x00000000) return -ENOMEM; return 0; } /* * ioctl: see kint.h for the meaning of args */ int pci_bridge_ioctl(struct inode *pnode, struct file *filp, unsigned int cmd, unsigned long arg) { int error = 0; unsigned long base; unsigned long base_size; struct pci_bridge_dev *dev; dev = filp->private_data; if (_IOC_TYPE(cmd) != BRIDGE_IOC_NUM) return -EINVAL; if (_IOC_NR(cmd) > BRIDGE_IOC_MAX_NUM) return -EINVAL; switch (cmd) { case BRIDGE_IOC_CURRESGET: // current resource - they start at 1 return (dev->current_resource + 1); case BRIDGE_IOC_CURRESSET: // check if resource is in a range of implemented resources if (arg < 0 ) return -EINVAL; // unmap previous resource if it was mapped if (dev->current_resource >= 0) { iounmap((void *)dev->page_addr); } if (arg == 0) { // previous resource unmaped - that's all dev->current_resource = -1; return 0; } if (dev->num_of_bases < arg) return -ENODEV; // IO mapped not supported yet if (dev->base_map[arg-1] == BRIDGE_IO_MAPPED) { // set current resource to none, since it was unmapped dev->current_resource = -1; return -ENODEV; } dev->current_resource= (int)(arg-1); // remap new resource if ( (error = open_mem_mapped(dev)) ) { dev->current_resource = -1; return error; } return 0; case BRIDGE_IOC_CURBASE: // check if any resource is currently activated if (dev->current_resource>=0) { base = dev->bases[dev->current_resource]; printk("\n CURR_RES = %d",dev->current_resource ); } else base = 0x00000000; *(unsigned long *)arg = base; return 0; case BRIDGE_IOC_CURBASEMAP: // check if any resource is currently activated if (dev->current_resource>=0) base = dev->page_addr; else base = 0x00000000; *(unsigned long *)arg = base; return 0; case BRIDGE_IOC_CURBASESIZE: // check if any resource is currently activated if (dev->current_resource>=0) base_size = dev->base_size[dev->current_resource]; else base_size = 0x00000000; *(unsigned long *)arg = base_size; return 0; case BRIDGE_IOC_NUMOFRES: return (dev->num_of_bases); default: return -EINVAL; } } #define TX_BUFFER_FLAGS 0x00 #define RX_BUFFER_FLAGS 0x00 #define TX_DATA_LEN 0x04 #define RX_DATA_LEN 0x04 #define TX_BUFFER_OFFSET 0x0c #define RX_BUFFER_OFFSET 0x0c #define TX_BUFFER_SIZE 4096 #define RX_BUFFER_SIZE 4096 #define WB_RX_BUFFER_AM 0xFF000000 #define WB_TX_BUFFER_AM 0xFF000000 static void dma_descriptor_setup(struct pci_dev *pdev) { unsigned long current_address; unsigned int register_value; int error = 0; unsigned int c_tx_data; printk("DMA ALLOC ROUTINE STARTED\n"); c_memory_map->dma_buffer_rx = pci_alloc_consistent(pdev, RX_BUFFER_SIZE+RX_BUFFER_OFFSET, &c_memory_map->dma_bus_rx); printk("RX DMA BUFFER READY\n"); c_memory_map->dma_buffer_tx = pci_alloc_consistent(pdev, TX_BUFFER_SIZE+TX_BUFFER_OFFSET, &c_memory_map->dma_bus_tx); printk("TX DMA BUFFER READY\n"); pci_bridge_devices->current_resource = 0; if ( (error = open_mem_mapped(pci_bridge_devices)) ) { pci_bridge_devices->current_resource = -1; printk("Could not set BAR0 on DMA alloc\n"); return; } printk("PCI ADDRESS FOR TX IS %d AND PCI ADDRESS FOR RX IS %d\n", c_memory_map->dma_bus_rx, c_memory_map->dma_bus_tx); current_address = pci_bridge_devices->page_addr + pci_bridge_devices- >base_page_offset + BRIDGE_W_AM1_ADDR; register_value = WB_RX_BUFFER_AM; memcpy_toio(current_address, ®ister_value, 4); current_address = pci_bridge_devices->page_addr + pci_bridge_devices- >base_page_offset + BRIDGE_W_AM2_ADDR; register_value = WB_TX_BUFFER_AM; memcpy_toio(current_address, ®ister_value, 4); } static void dma_descriptor_release(struct pci_dev *pdev) { pci_free_consistent(pdev, RX_BUFFER_SIZE+RX_BUFFER_OFFSET, c_memory_map->dma_buffer_rx, c_memory_map->dma_bus_rx); pci_free_consistent(pdev, TX_BUFFER_SIZE+TX_BUFFER_OFFSET, c_memory_map->dma_buffer_tx, c_memory_map->dma_bus_tx); } MODULE_LICENSE("GPL"); module_init(pci_bridge_init); module_exit(pci_bridge_exit);
Any help would be apreciated.
Thank you


Reply With Quote
