LinuxQuestions.org
Help answer threads with 0 replies.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Software > Linux - Kernel
User Name
Password
Linux - Kernel This forum is for all discussion relating to the Linux kernel.

Notices


Reply
  Search this Thread
Old 09-08-2010, 06:44 PM   #1
Sink0
LQ Newbie
 
Registered: Sep 2010
Posts: 6

Rep: Reputation: 0
Questions about PCI driver


Hi i am developing a PCI driver based on an example and LDD book and i have some questions.

Can i register my driver with insmod if my PCI card is not conected?

Looking the example code i got on hands the function pci_resource_start is called inside the init but to that work the probe is suposed to be already called. If do not have my card on the PC will probe be called? If not, what will happen if i call pci_resource_start with a null pointer? Am i doing any mess? I did build my module and tryed register it. it worked and i could see it with lsmod. But when i use rmmod and look with lsmod again he is still there. Whats wrong?


The driver code i am following as example:


/*
* 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
*
* Author: Paolo Prete <p4olo_prete@yahoo.it>
*
* 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/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/cdev.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 struct file_operations pci_bridge_fops =
{
.owner = THIS_MODULE,
.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;
} *pci_bridge_devices;


/*
* 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 __devinit 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);
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;
int flag = 0;
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;
}
else
{
flag = 1;
}
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]);
}

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];
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;

}

}

MODULE_LICENSE("GPL");

module_init(pci_bridge_init);
module_exit(pci_bridge_exit);



Thank you!!

Last edited by Sink0; 09-08-2010 at 06:46 PM.
 
Old 09-08-2010, 10:02 PM   #2
archieval
Member
 
Registered: Apr 2007
Location: Philippines
Distribution: Kubuntu, Ubuntu, CentOS
Posts: 289

Rep: Reputation: 41
Usually this is not compiled as module because it should be called at boot up. If it cannot be removed, then something is using it.

Code:
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/cdev.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 struct file_operations pci_bridge_fops = {
    .owner = THIS_MODULE,
    .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;
} *pci_bridge_devices;

/*
* 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 __devinit 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);
        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;
    int flag = 0;
    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;
    } else {
        flag = 1;
    }
    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]);
    }

    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];
        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;

    }

}

MODULE_LICENSE("GPL");

module_init(pci_bridge_init);
module_exit(pci_bridge_exit);
 
Old 09-09-2010, 05:38 AM   #3
Sink0
LQ Newbie
 
Registered: Sep 2010
Posts: 6

Original Poster
Rep: Reputation: 0
What you mean is not compiled as a module? So what should i do with it? How can i make it be called on boot?

Thank you
 
Old 09-12-2010, 11:26 PM   #4
archieval
Member
 
Registered: Apr 2007
Location: Philippines
Distribution: Kubuntu, Ubuntu, CentOS
Posts: 289

Rep: Reputation: 41
Include it in your kernel source, .config, and Makefile. If it is already there, run make menuconfig and specify Y not M on its menu.
 
Old 09-14-2010, 07:51 AM   #5
Sink0
LQ Newbie
 
Registered: Sep 2010
Posts: 6

Original Poster
Rep: Reputation: 0
Would i have to recompile my kernel? Have you ever developed a PCI driver? Thank you!!
 
Old 09-16-2010, 03:16 AM   #6
archieval
Member
 
Registered: Apr 2007
Location: Philippines
Distribution: Kubuntu, Ubuntu, CentOS
Posts: 289

Rep: Reputation: 41
A full pci driver I haven't, I only edit existing ones for my devices. Yes you have to recompile, make clean first on that folder to ensure the configuration takes effect.

Last edited by archieval; 09-16-2010 at 03:35 AM.
 
Old 09-16-2010, 11:21 PM   #7
Sink0
LQ Newbie
 
Registered: Sep 2010
Posts: 6

Original Poster
Rep: Reputation: 0
Quote:
Originally Posted by archieval View Post
Include it in your kernel source, .config, and Makefile. If it is already there, run make menuconfig and specify Y not M on its menu.
I am noob on linux, can you help me on this taks? Actually i just got my driver to work but i have to register it every time i want to use my pci card. How can i include it on kernel source, .config and Makefile, actually which Makefile? There are so many!!! Thank you
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
fpga driver using PCI linux driver Linux_Kid_ Linux - Embedded & Single-board computer 0 06-09-2009 11:53 PM
Debian netgear wireless pci card questions revenge80200 Linux - Wireless Networking 2 04-01-2007 08:21 PM
Questions on USB, Parrallel Port, and PCI support. Jorophose Linux - Hardware 9 01-09-2007 03:10 PM
linux wireless pci card questions farkus888 Fedora 2 11-01-2006 06:11 PM
sata-1 PCI controller many questions fakie_flip Linux - Hardware 4 08-27-2006 01:55 PM

LinuxQuestions.org > Forums > Linux Forums > Linux - Software > Linux - Kernel

All times are GMT -5. The time now is 03:09 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration