OpenCores
URL https://opencores.org/ocsvn/pcie_ds_dma/pcie_ds_dma/trunk

Subversion Repositories pcie_ds_dma

[/] [pcie_ds_dma/] [trunk/] [soft/] [linux/] [driver/] [pexdrv/] [pexmodule.c] - Rev 15

Go to most recent revision | Compare with Previous | Blame | View Log

 
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/interrupt.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/io.h>
 
#include <asm/uaccess.h>
#include <linux/types.h>
#include <linux/ioport.h>
#include <linux/poll.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
 
#include "pexmodule.h"
#include "hardware.h"
#include "pexioctl.h"
#include "ioctlrw.h"
#include "ambpexregs.h"
#include "pexproc.h"
 
//-----------------------------------------------------------------------------
 
MODULE_AUTHOR("Vladimir Karakozov. karakozov@gmail.com");
MODULE_LICENSE("GPL");
 
//-----------------------------------------------------------------------------
 
static dev_t devno = MKDEV(0, 0);
static struct class *pex_class = NULL;
static LIST_HEAD(device_list);
static int boards_count = 0;
static struct mutex pex_mutex;
int dbg_trace = 1;
int err_trace = 1;
 
//-----------------------------------------------------------------------------
 
static int free_memory(struct pex_device *brd)
{
    struct list_head *pos, *n;
    struct mem_t *m = NULL;
    int unlocked = 0;
 
    spin_lock(&brd->m_MemListLock);
 
    list_for_each_safe(pos, n, &brd->m_MemList) {
 
        m = list_entry(pos, struct mem_t, list);
 
        unlocked = unlock_pages(m->cpu_addr, m->size);
 
        dma_free_coherent(&brd->m_pci->dev, m->size, m->cpu_addr, m->dma_handle);
 
        dbg_msg(dbg_trace, "%s(): %d: PA = 0x%zx, VA = %p, SZ = 0x%zx, PAGES = %d\n",
                __FUNCTION__, atomic_read(&brd->m_MemListCount), (size_t)m->dma_handle, m->cpu_addr, m->size, unlocked );
 
        list_del(pos);
 
        atomic_dec(&brd->m_MemListCount);
 
        kfree(m);
    }
 
    spin_unlock(&brd->m_MemListLock);
 
    return 0;
}
 
//-----------------------------------------------------------------------------
 
static struct pex_device *file_to_device( struct file *file )
{
    return (struct pex_device*)file->private_data;
}
 
//-----------------------------------------------------------------------------
 
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
static struct pex_device *inode_to_device( struct list_head *head, struct inode *inode )
{
    struct list_head *p;
    struct pex_device *entry;
    unsigned int minor = MINOR(inode->i_rdev);
 
    list_for_each(p, head) {
        entry = list_entry(p, struct pex_device, m_list);
        if(entry->m_BoardIndex == minor)
            return entry;
    }
 
    return NULL;
}
#endif
 
//-----------------------------------------------------------------------------
 
static int pex_device_fasync(int fd, struct file *file, int mode)
{
    struct pex_device *pDevice = file->private_data;
    if(!pDevice)
        return -ENODEV;
 
    return 0;
}
 
//-----------------------------------------------------------------------------
 
static unsigned int pex_device_poll(struct file *filp, poll_table *wait)
{
    unsigned int mask = 0;
 
    struct pex_device *pDevice = file_to_device(filp);
    if(!pDevice)
        return -ENODEV;
 
    return mask;
}
 
//-----------------------------------------------------------------------------
 
static int pex_device_open( struct inode *inode, struct file *file )
{
    struct pex_device *pDevice = container_of(inode->i_cdev, struct pex_device, m_cdev);
    if(!pDevice) {
        err_msg(err_trace, "%s(): Open device failed\n", __FUNCTION__);
        return -ENODEV;
    }
 
    file->private_data = (void*)pDevice;
 
    dbg_msg(dbg_trace, "%s(): Open device %s\n", __FUNCTION__, pDevice->m_name);
 
    return 0;
}
 
//-----------------------------------------------------------------------------
 
static int pex_device_close( struct inode *inode, struct file *file )
{
    struct pex_device *pDevice = container_of(inode->i_cdev, struct pex_device, m_cdev);
    if(!pDevice) {
        err_msg(err_trace, "%s(): Close device failed\n", __FUNCTION__);
        return -ENODEV;
    }
 
    file->private_data = NULL;
 
    dbg_msg(dbg_trace, "%s(): Close device %s\n", __FUNCTION__, pDevice->m_name);
 
    return 0;
}
 
//-----------------------------------------------------------------------------
 
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
static long pex_device_ioctl( struct file *file, unsigned int cmd, unsigned long arg )
#else
static int pex_device_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg )
#endif
{
    int error = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
    struct pex_device *pDevice = file_to_device(file);
#else
    struct pex_device *pDevice = inode_to_device(&device_list, inode);
#endif
    if(!pDevice) {
        err_msg(err_trace, "%s(): ioctl device failed\n", __FUNCTION__);
        return -ENODEV;
    }
 
    mutex_lock(&pDevice->m_BoardMutex);
 
    switch(cmd) {
    case IOCTL_PEX_BOARD_INFO:
        error = ioctl_board_info(pDevice, arg);
        break;
    case IOCTL_PEX_MEM_ALLOC:
        error = ioctl_memory_alloc(pDevice, arg);
        break;
    case IOCTL_PEX_MEM_FREE:
        error = ioctl_memory_free(pDevice, arg);
        break;
    case IOCTL_PEX_STUB_ALLOC:
        error = ioctl_stub_alloc(pDevice, arg);
        break;
    case IOCTL_PEX_STUB_FREE:
        error = ioctl_stub_free(pDevice, arg);
        break;
    case IOCTL_AMB_SET_MEMIO:
        error = ioctl_set_mem(pDevice, arg);
        break;
    case IOCTL_AMB_FREE_MEMIO:
        error = ioctl_free_mem(pDevice, arg);
        break;
    case IOCTL_AMB_START_MEMIO:
        error = ioctl_start_mem(pDevice, arg);
        break;
    case IOCTL_AMB_STOP_MEMIO:
        error = ioctl_stop_mem(pDevice, arg);
        break;
    case IOCTL_AMB_STATE_MEMIO:
        error = ioctl_state_mem(pDevice, arg);
        break;
    case IOCTL_AMB_WAIT_DMA_BUFFER:
        error = ioctl_wait_dma_buffer(pDevice, arg);
        break;
    case IOCTL_AMB_WAIT_DMA_BLOCK:
        error = ioctl_wait_dma_block(pDevice, arg);
        break;
    case IOCTL_AMB_SET_SRC_MEM:
        error = ioctl_set_src_mem(pDevice, arg);
        break;
    case IOCTL_AMB_SET_DIR_MEM:
        error = ioctl_set_dir_mem(pDevice, arg);
        break;
    case IOCTL_AMB_SET_DRQ_MEM:
        error = ioctl_set_drq_mem(pDevice, arg);
        break;
    case IOCTL_AMB_RESET_FIFO:
        error = ioctl_reset_fifo(pDevice, arg);
        break;
    case IOCTL_AMB_DONE:
        error = ioctl_done(pDevice, arg);
        break;
    case IOCTL_AMB_ADJUST:
        error = ioctl_adjust(pDevice, arg);
        break;
 
    default:
        dbg_msg(dbg_trace, "%s(): Unknown command\n", __FUNCTION__);
        error = -EINVAL;
        break;
    }
 
    mutex_unlock(&pDevice->m_BoardMutex);
 
    return error;
}
 
//-----------------------------------------------------------------------------
 
static inline int private_mapping_ok(struct vm_area_struct *vma)
{
    return vma->vm_flags & VM_MAYSHARE;
}
 
//-----------------------------------------------------------------------------
 
static int pex_device_mmap(struct file *file, struct vm_area_struct *vma)
{
    size_t size = vma->vm_end - vma->vm_start;
 
    if (!private_mapping_ok(vma))
        return -ENOSYS;
 
    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 
    if (remap_pfn_range(vma,
                        vma->vm_start,
                        vma->vm_pgoff,
                        size,
                        vma->vm_page_prot)) {
        err_msg(err_trace, "%s(): error in remap_page_range.\n", __FUNCTION__ );
        return -EAGAIN;
    }
    return 0;
}
 
//-----------------------------------------------------------------------------
 
static ssize_t pex_device_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long count, loff_t off)
{
    struct pex_device *pDevice = file_to_device(iocb->ki_filp);
    if(!pDevice) {
        err_msg(err_trace, "%s(): ioctl device failed\n", __FUNCTION__);
        return -ENODEV;
    }
    return -ENOSYS;
}
 
//-----------------------------------------------------------------------------
 
static ssize_t pex_device_aio_write(struct kiocb *iocb, const struct iovec *iov, unsigned long count, loff_t off)
{
    struct pex_device *pDevice = file_to_device(iocb->ki_filp);
    if(!pDevice) {
        err_msg(err_trace, "%s(): ioctl device failed\n", __FUNCTION__);
        return -ENODEV;
    }
    return -ENOSYS;
}
 
//-----------------------------------------------------------------------------
 
static irqreturn_t pex_device_isr( int irq, void *pContext )
{
    FIFO_STATUS FifoStatus;  //
 
    struct pex_device* pDevice = (struct pex_device*)pContext;            // our device
 
    if(!pDevice->m_DmaIrqEnbl && !pDevice->m_FlgIrqEnbl)
        return IRQ_NONE;
 
    if(pDevice->m_FlgIrqEnbl)
    {  // прерывание от флагов состояния
        /*
            u32 status = ReadOperationWordReg(pDevice, PEMAINadr_BRD_STATUS);
            err_msg(err_trace, "%s(): BRD_STATUS = 0x%X.\n", __FUNCTION__, status);
            if(status & 0x4000)
            {
                    for(int i = 0; i < NUM_TETR_IRQ; i++)
                            if(pDevice->m_TetrIrq[i] != 0)
                            {
                                    u32 status = ReadAmbMainReg(pDevice, pDevice->m_TetrIrq[i].Address);
                                    KdPrint(("CWambpex::WambpexIsr: TetrIrq = %d, Address = 0x%X, IrqInv = 0x%X, IrqMask = 0x%X, Status = 0x%X.\n",
                                                            i, pDevice->m_TetrIrq[i].Address, pDevice->m_TetrIrq[i].IrqInv, pDevice->m_TetrIrq[i].IrqMask, status));
                                    status ^= pDevice->m_TetrIrq[i].IrqInv;
                                    status &= pDevice->m_TetrIrq[i].IrqMask;
                                    KdPrint(("CWambpex::WambpexIsr: TetrIrq = %d, Address = 0x%X, IrqInv = 0x%X, IrqMask = 0x%X, Status = 0x%X.\n",
                                                            i, pDevice->m_TetrIrq[i].Address, pDevice->m_TetrIrq[i].IrqInv, pDevice->m_TetrIrq[i].IrqMask, status));
                                    if(status)
                                    {
                                            KeInsertQueueDpc(&pDevice->m_TetrIrq[i].Dpc, NULL, NULL);
                                            KdPrint(("CWambpex::WambpexIsr - Tetrad IRQ address = %d\n", pDevice->m_TetrIrq[i].Address));
                                            // сброс статусного бита, вызвавшего прерывание
                                            //pDevice->WriteAmbMainReg(pDevice->m_TetrIrq[i].Address + 0x200);
                                            ULONG CmdAddress = pDevice->m_TetrIrq[i].Address + TRDadr_CMD_ADR * REG_SIZE;
                                            pDevice->WriteAmbMainReg(CmdAddress, 0);
                                            ULONG DataAddress = pDevice->m_TetrIrq[i].Address + TRDadr_CMD_DATA * REG_SIZE;
                                            ULONG Mode0Value = pDevice->ReadAmbMainReg(DataAddress);
                                            Mode0Value &= 0xFFFB;
                                            //pDevice->WriteAmbMainReg(CmdAddress, 0);
                                            pDevice->WriteAmbMainReg(DataAddress, Mode0Value);
                                            break;
                                    }
                            }
                return IRQ_HANDLED;
            }
            else // вообще не наше прерывание !!!
                    return IRQ_NONE;	// we did not interrupt
            */
    }
 
    if(pDevice->m_DmaIrqEnbl)
    {	// прерывание от каналов ПДП
        u32 i=0;
        u32 FifoAddr = 0;
        u32 iChan = pDevice->m_primChan;
        u32 NumberOfChannel = -1;
 
        for(i = 0; i < MAX_NUMBER_OF_DMACHANNELS; i++)
        {
            if(pDevice->m_DmaChanMask & (1 << iChan))
            {
                FifoAddr = pDevice->m_FifoAddr[iChan];
                FifoStatus.AsWhole = ReadOperationWordReg(pDevice, PEFIFOadr_FIFO_STATUS + FifoAddr);
                if(FifoStatus.ByBits.IntRql)
                {
                    //err_msg(err_trace, "%s(): - Channel = %d, Fifo Status = 0x%X\n", __FUNCTION__, iChan, FifoStatus.AsWhole);
                    NumberOfChannel = iChan;
                    pDevice->m_primChan = ((pDevice->m_primChan+1) >= MAX_NUMBER_OF_DMACHANNELS) ? 0 : pDevice->m_primChan+1;
                    break;
                }
            }
            iChan = ((iChan+1) >= MAX_NUMBER_OF_DMACHANNELS) ? 0 : iChan+1;
        }
 
        if(NumberOfChannel != -1)
        {
            u32 flag = 0;
 
            //err_msg(err_trace, "%s(%d)\n", __FUNCTION__, atomic_read(&pDevice->m_TotalIRQ));
 
            flag = NextDmaTransfer(pDevice->m_DmaChannel[NumberOfChannel]);
            if(!flag)
            {
                DMA_CTRL_EXT CtrlExt;
                CtrlExt.AsWhole = 0;
                CtrlExt.ByBits.Pause = 1;
                CtrlExt.ByBits.Start = 1;
                WriteOperationWordReg(pDevice, PEFIFOadr_DMA_CTRL + FifoAddr, CtrlExt.AsWhole);
                //err_msg(err_trace, "%s(): - Pause (%d) - m_CurBlockNum = %d, m_DoneBlock = %d\n", __FUNCTION__, atomic_read(&pDevice->m_TotalIRQ),
                //        pDevice->m_DmaChannel[NumberOfChannel]->m_CurBlockNum,
                //        pDevice->m_DmaChannel[NumberOfChannel]->m_DoneBlock);
            }
 
            //err_msg(err_trace, "%s(): - Flag Clear\n", __FUNCTION__);
            WriteOperationWordReg(pDevice, PEFIFOadr_FLAG_CLR + FifoAddr, 0x10);
            WriteOperationWordReg(pDevice, PEFIFOadr_FLAG_CLR + FifoAddr, 0x00);
            //err_msg(err_trace, "%s(): - Complete\n", __FUNCTION__);
 
            atomic_inc(&pDevice->m_TotalIRQ);
 
            return IRQ_HANDLED;
        }
    }
    return IRQ_NONE;	// we did not interrupt
}
 
//-----------------------------------------------------------------------------
 
struct file_operations pex_fops = {
 
    .owner = THIS_MODULE,
    .read = NULL,
    .write = NULL,
 
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
    .unlocked_ioctl = pex_device_ioctl,
    .compat_ioctl = pex_device_ioctl,
#else
    .ioctl = pex_device_ioctl,
#endif
 
    .mmap = pex_device_mmap,
    .open = pex_device_open,
    .release = pex_device_close,
    .fasync = pex_device_fasync,
    .poll = pex_device_poll,
    .aio_read =  pex_device_aio_read,
    .aio_write = pex_device_aio_write,
};
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
 
static const struct pci_device_id pex_device_id[] = {
{
        .vendor =       INSYS_VENDOR_ID,
        .device =       AMBPEX5_DEVID,
        .subvendor =    PCI_ANY_ID,
        .subdevice =    PCI_ANY_ID,
},
{
        .vendor =       INSYS_VENDOR_ID,
        .device =       AMBPEX8_DEVID,
        .subvendor =    PCI_ANY_ID,
        .subdevice =    PCI_ANY_ID,
},
{
        .vendor =       INSYS_VENDOR_ID,
        .device =       ADP201X1AMB_DEVID,
        .subvendor =    PCI_ANY_ID,
        .subdevice =    PCI_ANY_ID,
},
{
        .vendor =       INSYS_VENDOR_ID,
        .device =       ADP201X1DSP_DEVID,
        .subvendor =    PCI_ANY_ID,
        .subdevice =    PCI_ANY_ID,
},
{
        .vendor =       INSYS_VENDOR_ID,
        .device =       AMBPEXARM_DEVID,
        .subvendor =    PCI_ANY_ID,
        .subdevice =    PCI_ANY_ID,
},
{
        .vendor =       INSYS_VENDOR_ID,
        .device =       AMBFMC106P_DEVID,
        .subvendor =    PCI_ANY_ID,
        .subdevice =    PCI_ANY_ID,
},
{
        .vendor =       INSYS_VENDOR_ID,
        .device =       AMBFMC114V_DEVID,
        .subvendor =    PCI_ANY_ID,
        .subdevice =    PCI_ANY_ID,
},
{
        .vendor =       INSYS_VENDOR_ID,
        .device =       AMBKU_SSCOS_DEVID,
        .subvendor =    PCI_ANY_ID,
        .subdevice =    PCI_ANY_ID,
},
{ },
};
 
MODULE_DEVICE_TABLE(pci, pex_device_id);
 
//-----------------------------------------------------------------------------
 
static int  __devinit pex_device_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
    int error = 0;
    int i = 0;
    struct pex_device *brd = NULL;
 
    mutex_lock(&pex_mutex);
 
    brd = kzalloc(sizeof(struct pex_device), GFP_KERNEL);
    if(!brd) {
        error = -ENOMEM;
        goto do_out;
    }
 
    INIT_LIST_HEAD(&brd->m_list);
    mutex_init(&brd->m_BoardMutex);
    sema_init(&brd->m_BoardSem, 1);
    spin_lock_init(&brd->m_BoardLock);
    atomic_set(&brd->m_TotalIRQ, 0);
    init_waitqueue_head(&brd->m_WaitQueue);
    init_timer(&brd->m_TimeoutTimer);
    spin_lock_init(&brd->m_MemListLock);
    atomic_set(&brd->m_MemListCount, 0);
    INIT_LIST_HEAD(&brd->m_MemList);
    brd->m_pci = dev;
    brd->m_Interrupt = -1;
    brd->m_DmaIrqEnbl = 0;
    brd->m_FlgIrqEnbl = 0;
    brd->m_class = pex_class;
 
    set_device_name(brd, dev->device, boards_count);
 
    dbg_msg(dbg_trace, "%s(): device_id = %x, vendor_id = %x, board name %s\n", __FUNCTION__, dev->device, dev->vendor, brd->m_name);
 
    error = pci_enable_device(dev);
    if(error) {
        err_msg(err_trace, "%s(): error enabling pci device\n", __FUNCTION__);
        goto do_free_memory;
    }
 
    if (pci_set_dma_mask(dev, DMA_BIT_MASK(64)) || pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(64))) {
        printk("%s(): error set pci dma mask\n", __FUNCTION__);
        goto do_disable_device;
    }
 
    pci_set_master(dev);
 
    brd->m_BAR0.physical_address = pci_resource_start(dev, 0);
    brd->m_BAR0.size = pci_resource_len(dev, 0);
    brd->m_BAR0.virtual_address = ioremap_nocache(brd->m_BAR0.physical_address, brd->m_BAR0.size);
    if(!brd->m_BAR0.virtual_address) {
        error = -ENOMEM;
        err_msg(err_trace, "%s(): error map device memory at bar%d\n", __FUNCTION__, 0);
        goto do_disable_device;
    }
 
    dbg_msg(dbg_trace, "%s(): map bar0 %zx -> %p\n", __FUNCTION__, brd->m_BAR0.physical_address, brd->m_BAR0.virtual_address);
 
    brd->m_BAR1.physical_address = pci_resource_start(dev, 1);
    brd->m_BAR1.size = pci_resource_len(dev, 1);
    brd->m_BAR1.virtual_address = ioremap_nocache(brd->m_BAR1.physical_address, brd->m_BAR1.size);
    if(!brd->m_BAR1.virtual_address) {
        error = -ENOMEM;
        err_msg(err_trace, "%s(): error map device memory at bar%d\n", __FUNCTION__, 0);
        goto do_unmap_bar0;
    }
 
    dbg_msg(dbg_trace, "%s(): map bar1 %zx -> %p\n", __FUNCTION__, brd->m_BAR1.physical_address, brd->m_BAR1.virtual_address);
 
    error = request_irq(dev->irq, pex_device_isr, IRQF_SHARED, brd->m_name, brd);
    if( error < 0) {
        error = -EBUSY;
        err_msg( err_trace, "%s(): error in request_irq()\n", __FUNCTION__ );
        goto do_unmap_bar1;
    }
 
    brd->m_Interrupt = dev->irq;
 
    cdev_init(&brd->m_cdev, &pex_fops);
    brd->m_cdev.owner = THIS_MODULE;
    brd->m_cdev.ops = &pex_fops;
    brd->m_devno = MKDEV(MAJOR(devno), boards_count);
 
    error = cdev_add(&brd->m_cdev, brd->m_devno, 1);
    if(error) {
        err_msg(err_trace, "%s(): Error add char device %d\n", __FUNCTION__, boards_count);
        error = -EINVAL;
        goto do_free_irq;
    }
 
    dbg_msg(dbg_trace, "%s(): Add cdev %d\n", __FUNCTION__, boards_count);
 
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
    brd->m_device = device_create(pex_class, NULL, brd->m_devno, "%s%d", "pexdrv", boards_count);
#else
    brd->m_device = device_create(pex_class, NULL, brd->m_devno, NULL, "%s%d", "pexdrv", boards_count);
#endif
    if(!brd->m_device ) {
        err_msg(err_trace, "%s(): Error create device for board: %s\n", __FUNCTION__, brd->m_name);
        error = -EINVAL;
        goto do_delete_cdev;
    }
 
    dbg_msg(dbg_trace, "%s(): Create device file for board: %s\n", __FUNCTION__, brd->m_name);
 
    brd->m_BoardIndex = boards_count;
 
    InitializeBoard(brd);
 
    for(i = 0; i < MAX_NUMBER_OF_DMACHANNELS; i++) {
 
        if(brd->m_DmaChanMask & (1 << i)) {
 
            brd->m_DmaChannel[i] = CDmaChannelCreate( i,  brd,
                                                      &brd->m_pci->dev,
                                                      brd->m_MaxDmaSize[i],
                                                      brd->m_BlockFifoId[i], 1 );
        }
    }
 
    pex_register_proc(brd->m_name, pex_proc_info, brd);
 
    list_add_tail(&brd->m_list, &device_list);
 
    boards_count++;
 
    dbg_msg(dbg_trace, "%s(): Board %s - setup complete\n", __FUNCTION__, brd->m_name);
 
    mutex_unlock(&pex_mutex);
 
    return error;
 
do_delete_cdev:
    cdev_del(&brd->m_cdev);
 
do_free_irq:
    free_irq(brd->m_Interrupt, brd);
 
do_unmap_bar1:
    iounmap(brd->m_BAR1.virtual_address);
 
do_unmap_bar0:
    iounmap(brd->m_BAR0.virtual_address);
 
 
do_disable_device:
    pci_disable_device(dev);
 
do_free_memory:
    kfree(brd);
 
do_out:
    mutex_unlock(&pex_mutex);
 
    return error;
}
 
//-----------------------------------------------------------------------------
 
static void __devexit pex_device_remove(struct pci_dev *dev)
{
    struct list_head *pos, *n;
    struct pex_device *brd = NULL;
    int i = 0;
 
    dbg_msg(dbg_trace, "%s(): device_id = %x, vendor_id = %x\n", __FUNCTION__, dev->device, dev->vendor);
 
    mutex_lock(&pex_mutex);
 
    list_for_each_safe(pos, n, &device_list) {
 
        brd = list_entry(pos, struct pex_device, m_list);
 
        if(brd->m_pci == dev) {
 
            free_irq(brd->m_Interrupt, brd);
            dbg_msg(dbg_trace, "%s(): free_irq() - complete\n", __FUNCTION__);
            pex_remove_proc(brd->m_name);
            dbg_msg(dbg_trace, "%s(): pex_remove_proc() - complete\n", __FUNCTION__);
            for(i = 0; i < MAX_NUMBER_OF_DMACHANNELS; i++) {
                if(brd->m_DmaChannel[i]) {
                     CDmaChannelDelete(brd->m_DmaChannel[i]);
                     dbg_msg(dbg_trace, "%s(): free DMA channel %d - complete\n", __FUNCTION__, i);
                }
            }
            free_memory(brd);
            dbg_msg(dbg_trace, "%s(): free_memory() - complete\n", __FUNCTION__);
            device_destroy(pex_class, brd->m_devno);
            dbg_msg(dbg_trace, "%s(): device_destroy() - complete\n", __FUNCTION__);
            cdev_del(&brd->m_cdev);
            dbg_msg(dbg_trace, "%s(): cdev_del() - complete\n", __FUNCTION__);
            iounmap(brd->m_BAR1.virtual_address);
            dbg_msg(dbg_trace, "%s(): iounmap() - complete\n", __FUNCTION__);
            iounmap(brd->m_BAR0.virtual_address);
            dbg_msg(dbg_trace, "%s(): iounmap() - complete\n", __FUNCTION__);
            pci_disable_device(dev);
            dbg_msg(dbg_trace, "%s(): pci_disable_device() - complete\n", __FUNCTION__);
            list_del(pos);
            dbg_msg(dbg_trace, "%s(): list_del() - complete\n", __FUNCTION__);
            kfree(brd);
            dbg_msg(dbg_trace, "%s(): kfree() - complete\n", __FUNCTION__);
        }
    }
 
    mutex_unlock(&pex_mutex);
}
 
//-----------------------------------------------------------------------------
 
static struct pci_driver pex_pci_driver = {
 
    .name = PEX_DRIVER_NAME,
    .id_table = pex_device_id,
    .probe = pex_device_probe,
    .remove = pex_device_remove,
};
 
//-----------------------------------------------------------------------------
 
static int __init pex_module_init(void)
{
    int error = 0;
 
    dbg_msg(dbg_trace, "%s()\n", __FUNCTION__);
 
    mutex_init(&pex_mutex);
 
    error = alloc_chrdev_region(&devno, 0, MAX_PEXDEVICE_SUPPORT, PEX_DRIVER_NAME);
    if(error < 0) {
        err_msg(err_trace, "%s(): Erorr allocate char device regions\n", __FUNCTION__);
        goto do_out;
    }
 
    dbg_msg(dbg_trace, "%s(): Allocate %d device numbers. Major number = %d\n", __FUNCTION__, MAX_PEXDEVICE_SUPPORT, MAJOR(devno));
 
    pex_class = class_create(THIS_MODULE, PEX_DRIVER_NAME);
    if(!pex_class) {
        err_msg(err_trace, "%s(): Erorr allocate char device regions\n", __FUNCTION__);
        error = -EINVAL;
        goto do_free_chrdev;
    }
 
    error = pci_register_driver(&pex_pci_driver);
    if(error < 0) {
        err_msg(err_trace, "%s(): Erorr register pci driver\n", __FUNCTION__);
        error = -EINVAL;
        goto do_delete_class;
    }
 
    return 0;
 
do_delete_class:
    class_destroy(pex_class);
 
do_free_chrdev:
    unregister_chrdev_region(devno, MAX_PEXDEVICE_SUPPORT);
 
do_out:
    return error;
}
 
//-----------------------------------------------------------------------------
 
static void __exit pex_module_cleanup(void)
{
    dbg_msg(dbg_trace, "%s()\n", __FUNCTION__);
 
    pci_unregister_driver(&pex_pci_driver);
 
    if(pex_class)
        class_destroy(pex_class);
 
    unregister_chrdev_region(devno, MAX_PEXDEVICE_SUPPORT);
}
 
//-----------------------------------------------------------------------------
 
module_init(pex_module_init);
module_exit(pex_module_cleanup);
 
//-----------------------------------------------------------------------------
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.