Linux - KernelThis forum is for all discussion relating to the Linux kernel.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
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);
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.