lkml.org 
[lkml]   [2011]   [Mar]   [30]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    Date
    SubjectHelp with dma on PCI driver
    From
    I tryied to post this on using google groups, but as i had some
    filtering problem on other lists, i am sending using the direct
    e-mail. Please answer direct to me as i am not subscribed (i am trying
    to use a newsreader). Sorry if i am missing any information. If so,
    please just tell me what so i can provide it.

    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:
    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 idea is to alloc a buffer so my PCI card can master the bus and
    place the data on that buffer....

    The driver code is:

    //*

    * 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, &register_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, &register_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..


    \
     
     \ /
      Last update: 2011-03-30 12:09    [W:0.084 / U:0.092 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site