Linux设备驱动程序中断的解决方案

时间:2016-12-13 04:46:41

标签: linux-device-driver xilinx pci-e

我正在PCIeFPGA (ML605)之间进行PC通信。

我遇到的问题是:

一旦我发送INTA assert packet然后INTA de-assert packet,我就会发信息,所以我的预期行为是interrupt handler应调用一次, 但正在发生的事情是interrupt handler被连续调用不止一次。

request_irq function如下:

        request_irq(gIrq, &XPCIe_IRQHandler, IRQF_SHARED, gDrvrName, gDev)

handler的结尾处,我提供了一个返回类型IRQ_HANDLED

cat /proc/interrupts 我的handler registeredIO-APIC fasteoi type

所以有人可以说可能是什么问题吗?

还需要进一步澄清。 当我第二次insmod我的模块时,interrupt handler正在运行而没有给出interrupt。 在PCI的内核文档中 在section 3.5 Initialize device registers 它被编写为在调用pending interrupts之前清除request_irq

那么有人可以说怎么做吗?

我的代码是

#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
//#include <linux/pci-aspm.h>
//#include <linux/pci_regs.h>

#include <asm/uaccess.h>   /* copy_to_user */

#include "xbmd.h"

// semaphores
enum  {
        SEM_READ,
        SEM_WRITE,
        SEM_WRITEREG,
        SEM_READREG,
        SEM_WAITFOR,
        SEM_DMA,
        NUM_SEMS
      };

//semaphores
struct semaphore gSem[NUM_SEMS];

MODULE_LICENSE("Dual BSD/GPL");

// Defines the Vendor ID.  Must be changed if core generated did not set the Vendor ID to the same value
#define PCI_VENDOR_ID_XILINX      0x10ee

// Defines the Device ID.  Must be changed if core generated did not set the Device ID to the same value
#define PCI_DEVICE_ID_XILINX_PCIE 0x6018 //0x0007

// Defining
#define XBMD_REGISTER_SIZE        (4*8)    // There are eight registers, and each is 4 bytes wide.
#define HAVE_REGION               0x01     // I/O Memory region
#define HAVE_IRQ                  0x02     // Interupt

//Status Flags: 
//       1 = Resouce successfully acquired
//       0 = Resource not acquired.      

#define HAVE_REGION 0x01                    // I/O Memory region
#define HAVE_IRQ    0x02                    // Interupt
#define HAVE_KREG   0x04                    // Kernel registration

int             gDrvrMajor = 241;           // Major number not dynamic.
unsigned int    gStatFlags = 0x00;          // Status flags used for cleanup.
unsigned long   gBaseHdwr;                  // Base register address (Hardware address)
unsigned long   gBaseLen;                   // Base register address Length
void           *gBaseVirt = NULL;           // Base register address (Virtual address, for I/O).
char            gDrvrName[]= "xbmd";        // Name of driver in proc.
struct pci_dev *gDev = NULL;                // PCI device structure.
int             gIrq;                       // IRQ assigned by PCI system.
char           *gBufferUnaligned = NULL;    // Pointer to Unaligned DMA buffer.
char           *gReadBuffer      = NULL;    // Pointer to dword aligned DMA buffer.
char           *gWriteBuffer     = NULL;    // Pointer to dword aligned DMA buffer.
dma_addr_t      gReadHWAddr;
dma_addr_t      gWriteHWAddr;
unsigned long SA_SHIRQ = 0;
unsigned long SA_SAMPLE_RANDOM = 0;

int pos;

// Struct Used for Writing CFG Register.  Holds value and register to be written
typedef struct cfgwrite {
    int reg;
    int value;
} cfgwr;

// Struct Used for Writing BMD Register.  Holds value and register to be written
typedef struct bmdwrite {
    int reg;
    int value;
} bmdwr;
//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------

void  XPCIe_IRQHandler (int irq, void *dev_id, struct pt_regs *regs);
u32   XPCIe_ReadReg (u32 dw_offset);
void  XPCIe_WriteReg (u32 dw_offset, u32 val);
void  XPCIe_InitCard (void);
void  XPCIe_InitiatorReset (void);
u32 XPCIe_ReadCfgReg (u32 byte);
u32 XPCIe_WriteCfgReg (u32 byte, u32 value);


int XPCIe_Open(struct inode *inode, struct file *filp)
{
  printk(KERN_INFO"%s: Open: module opened\n",gDrvrName);
  return SUCCESS;
}


int XPCIe_Release(struct inode *inode, struct file *filp)
{
  printk(KERN_INFO"%s: Release: module released\n",gDrvrName);
  return(SUCCESS);
}


ssize_t XPCIe_Write(struct file *filp, const char *buf, size_t count,
                       loff_t *f_pos)
{
  int ret = SUCCESS;
//  memcpy((char *)gWriteBuffer, buf, count);
// printk(KERN_INFO"%s: XPCIe_Write: %d bytes have been written...\n", gDrvrName, count);
  memcpy((char *)gReadBuffer, buf, count);
  printk(KERN_INFO"%s: XPCIe_Write: %d bytes have been written...\n", gDrvrName, count);
  return (ret);
}



ssize_t XPCIe_Read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
  memcpy(buf, (char *)gWriteBuffer, count);
  printk(KERN_INFO"%s: XPCIe_Read: %d bytes have been read...\n", gDrvrName, count);
  return (0);
}


int XPCIe_Ioctl(//struct inode *inode, 
                struct file *filp, 
                unsigned int cmd,
                unsigned long arg)
{

  u32 regx;
  int ret = SUCCESS;

  switch (cmd) {

    case INITCARD:                    // Initailizes XBMD application
      XPCIe_InitCard();
      break;
    case INITRST:                     // Resets XBMD applications
      XPCIe_InitiatorReset();
      break;
    case DISPREGS:
      break;
    case RDDCSR:                     // Read: Device Control Status Register
      regx = XPCIe_ReadReg(0);  
      *((u32 *)arg) = regx; 
      break;
    case RDDDMACR:                   // Read: DMA Control Status Register
      regx = XPCIe_ReadReg(1);  
      *((u32 *)arg) = regx; 
      break;
    case RDWDMATLPA:                 // Read: Write DMA TLP Address Register
      regx = XPCIe_ReadReg(2);  
      *((u32 *)arg) = regx; 
      break;
    case RDWDMATLPS:                 // Read: Write DMA TLP Size Register
      regx = XPCIe_ReadReg(3);  
      *((u32 *)arg) = regx; 
      break;
    case RDWDMATLPC:                 // Read: Write DMA TLP Count Register
      regx = XPCIe_ReadReg(4);  
      *((u32 *)arg) = regx; 
      break;
    case RDWDMATLPP:                 // Read: Write DMA TLP Pattern Register
      regx = XPCIe_ReadReg(5);  
      *((u32 *)arg) = regx; 
      break;
    case RDRDMATLPP:                 // Read: Read DMA TLP Pattern Register
      regx = XPCIe_ReadReg(6);  
      *((u32 *)arg) = regx; 
      break;
    case RDRDMATLPA:                 // Read: Read DMA TLP Address Register
      regx = XPCIe_ReadReg(7);  
      *((u32 *)arg) = regx; 
      break;
    case RDRDMATLPS:                 // Read: Read DMA TLP Size Register
      regx = XPCIe_ReadReg(8);  
      *((u32 *)arg) = regx; 
      break;
    case RDRDMATLPC:                 // Read: Read DMA TLP Count Register
      regx = XPCIe_ReadReg(9);  
      *((u32 *)arg) = regx; 
      break;
    case RDWDMAPERF:                 // Read: Write DMA Performance Register
      regx = XPCIe_ReadReg(10);  
      *((u32 *)arg) = regx; 
      break;
    case RDRDMAPERF:                 // Read: Read DMA Performance Register
      regx = XPCIe_ReadReg(11);  
      *((u32 *)arg) = regx; 
      break;
    case RDRDMASTAT:                 // Read: Read DMA Status Register
      regx = XPCIe_ReadReg(12);  
      *((u32 *)arg) = regx; 
      break;
    case RDNRDCOMP:                  // Read: Number of Read Completion w/ Data Register
      regx = XPCIe_ReadReg(13);  
      *((u32 *)arg) = regx; 
      break;
    case RDRCOMPDSIZE:               // Read: Read Completion Size Register
      regx = XPCIe_ReadReg(14);  
      *((u32 *)arg) = regx; 
      break;
    case RDDLWSTAT:                  // Read: Device Link Width Status Register
      regx = XPCIe_ReadReg(15);  
      *((u32 *)arg) = regx; 
      break;
    case RDDLTRSSTAT:                // Read: Device Link Transaction Size Status Register
      regx = XPCIe_ReadReg(16);  
      *((u32 *)arg) = regx; 
      break;
    case RDDMISCCONT:                // Read: Device Miscellaneous Control Register
      regx = XPCIe_ReadReg(17);  
      *((u32 *)arg) = regx; 
      break;
    case RDDMISCONT:                // Read: Device MSI Control
      regx = XPCIe_ReadReg(18);  
      *((u32 *)arg) = regx; 
      break;
    case RDDLNKC:                   // Read: Device Directed Link Change Register
      regx = XPCIe_ReadReg(19);  
      *((u32 *)arg) = regx; 
      break;
    case DFCCTL:                    // Read: Device FC Control Register
      regx = XPCIe_ReadReg(20);  
      *((u32 *)arg) = regx; 
      break;
    case DFCPINFO:                  // Read: Device FC Posted Information
      regx = XPCIe_ReadReg(21);  
      *((u32 *)arg) = regx; 
      break;
    case DFCNPINFO:                 // Read: Device FC Non Posted Information
      regx = XPCIe_ReadReg(22);  
      *((u32 *)arg) = regx; 
      break;
    case DFCINFO:                  // Read: Device FC Completion Information
      regx = XPCIe_ReadReg(23);  
      *((u32 *)arg) = regx; 
      break;
    case WRDDMACR:                 // Write: DMA Control Status Register
      XPCIe_WriteReg(1, arg);
      break;
    case WRWDMATLPS:               // Write: Write DMA TLP Size Register
      XPCIe_WriteReg(3, arg);
      break;
    case WRWDMATLPC:               // Write: Write DMA TLP Count Register
      XPCIe_WriteReg(4, arg);
      break;
    case WRWDMATLPP:               // Write: Write DMA TLP Pattern Register
      XPCIe_WriteReg(5, arg);
      break;
    case WRRDMATLPS:               // Write: Read DMA TLP Size Register
      XPCIe_WriteReg(8, arg);
      break;
    case WRRDMATLPC:               // Write: Read DMA TLP Count Register
      XPCIe_WriteReg(9, arg);
      break;
    case WRRDMATLPP:               // Write: Read DMA TLP Pattern Register
      XPCIe_WriteReg(6, arg);
      break;
    case WRDMISCCONT:              // Write: Device Miscellaneous Control Register
      XPCIe_WriteReg(18, arg);
      break;
    case WRDDLNKC:                 // Write: Device Directed Link Change Register
      XPCIe_WriteReg(19, arg);
      break;
    case RDBMDREG:                 // Read: Any XBMD Reg.  Added generic functionality so all register can be read
      regx = XPCIe_ReadReg(*(u32 *)arg);  
      *((u32 *)arg) = regx; 
      break; 
    case RDCFGREG:                 // Read: Any CFG Reg.  Added generic functionality so all register can be read
      regx = XPCIe_ReadCfgReg(*(u32 *)arg);  
      *((u32 *)arg) = regx; 
      break;
   case WRBMDREG:                 // Write: Any BMD Reg.  Added generic functionality so all register can be read
        XPCIe_WriteReg((*(bmdwr *)arg).reg,(*(bmdwr *)arg).value);
    printk(KERN_WARNING"%d: Write Register.\n", (*(bmdwr *)arg).reg);
    printk(KERN_WARNING"%d: Write Value\n", (*(bmdwr *)arg).value);
    break;
   case WRCFGREG:                 // Write: Any CFG Reg.  Added generic functionality so all register can be read
        regx = XPCIe_WriteCfgReg((*(cfgwr *)arg).reg,(*(cfgwr *)arg).value);
    printk(KERN_WARNING"%d: Write Register.\n", (*(cfgwr *)arg).reg);
    printk(KERN_WARNING"%d: Write Value\n", (*(cfgwr *)arg).value);
    break;
    default:
      break;
  }

  return ret;
}


// Aliasing write, read, ioctl, etc...
struct file_operations XPCIe_Intf = {
    read:       XPCIe_Read,
    write:      XPCIe_Write,
    unlocked_ioctl:      XPCIe_Ioctl,
    open:       XPCIe_Open,
    release:    XPCIe_Release,
};


static int XPCIe_init(void)
{
  // Find the Xilinx EP device.  The device is found by matching device and vendor ID's which is defined
  // at the top of this file.  Be default, the driver will look for 10EE & 0007.  If the core is generated 
  // with other settings, the defines at the top must be changed or the driver will not load
  gDev = pci_get_device (PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILINX_PCIE, gDev); 
// gDev = pci_find_device (PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILINX_PCIE, gDev);
  if (NULL == gDev) {

    // If a matching device or vendor ID is not found, return failure and update kernel log. 
    // NOTE: In fedora systems, the kernel log is located at: /var/log/messages
    printk(KERN_WARNING"%s: Init: Hardware not found.\n", gDrvrName);
    return (CRIT_ERR);
  }

  // Get Base Address of registers from pci structure. Should come from pci_dev
  // structure, but that element seems to be missing on the development system.
  gBaseHdwr = pci_resource_start (gDev, 0);

  if (0 > gBaseHdwr) {
    printk(KERN_WARNING"%s: Init: Base Address not set.\n", gDrvrName);
    return (CRIT_ERR);
  } 

  // Print Base Address to kernel log
  printk(KERN_INFO"%s: Init: Base hw val %X\n", gDrvrName, (unsigned int)gBaseHdwr);

  // Get the Base Address Length
  gBaseLen = pci_resource_len (gDev, 0);

  // Print the Base Address Length to Kernel Log
  printk(KERN_INFO"%s: Init: Base hw len %d\n", gDrvrName, (unsigned int)gBaseLen);

  // Remap the I/O register block so that it can be safely accessed.
  // I/O register block starts at gBaseHdwr and is 32 bytes long.
  // It is cast to char because that is the way Linus does it.
  // Reference "/usr/src/Linux-2.4/Documentation/IO-mapping.txt".
  gBaseVirt = ioremap(gBaseHdwr, gBaseLen);
  if (!gBaseVirt) {
    printk(KERN_WARNING"%s: Init: Could not remap memory.\n", gDrvrName);
    return (CRIT_ERR);
  } 

  // Print out the aquired virtual base addresss
  printk(KERN_INFO"%s: Init: Virt HW address %X\n", gDrvrName, (unsigned int)gBaseVirt);

  // Get IRQ from pci_dev structure. It may have been remapped by the kernel,
  // and this value will be the correct one.
  gIrq = gDev->irq;
  printk(KERN_INFO"%s: Init: Device IRQ: %X\n",gDrvrName, gIrq);


  //---START: Initialize Hardware

  // Check the memory region to see if it is in use
  if (0 > check_mem_region(gBaseHdwr, XBMD_REGISTER_SIZE)) {
    printk(KERN_WARNING"%s: Init: Memory in use.\n", gDrvrName);
    return (CRIT_ERR);
  }

  // Try to gain exclusive control of memory for demo hardware.
  request_mem_region(gBaseHdwr, XBMD_REGISTER_SIZE, "3GIO_Demo_Drv");
  // Update flags
  gStatFlags = gStatFlags | HAVE_REGION;

  printk(KERN_INFO"%s: Init: Initialize Hardware Done..\n",gDrvrName);

  // Request IRQ from OS.
  // In past architectures, the SHARED and SAMPLE_RANDOM flags were called: SA_SHIRQ and SA_SAMPLE_RANDOM
  // respectively.  In older Fedora core installations, the request arguments may need to be reverted back.
  // SA_SHIRQ | SA_SAMPLE_RANDOM
  printk(KERN_INFO"%s: ISR Setup..\n", gDrvrName);
   if (0 > request_irq(gIrq, (irq_handler_t)XPCIe_IRQHandler, IRQF_SHARED, gDrvrName, gDev)) {
// if (0 > request_irq(gIrq, &XPCIe_IRQHandler, IRQF_SHARED | IRQF_SAMPLE_RANDOM, gDrvrName, gDev)) {
    printk(KERN_WARNING"%s: Init: Unable to allocate IRQ",gDrvrName);
    return (CRIT_ERR);
  }
  // Update flags stating IRQ was successfully obtained
  gStatFlags = gStatFlags | HAVE_IRQ;

  // Bus Master Enable
  if (0 > pci_enable_device(gDev)) {
    printk(KERN_WARNING"%s: Init: Device not enabled.\n", gDrvrName);
    return (CRIT_ERR);
  }

  //--- END: Initialize Hardware

  //--- START: Allocate Buffers

  // Allocate the read buffer with size BUF_SIZE and return the starting address
  gReadBuffer = pci_alloc_consistent(gDev, BUF_SIZE, &gReadHWAddr);
  if (NULL == gReadBuffer) {
    printk(KERN_CRIT"%s: Init: Unable to allocate gBuffer.\n",gDrvrName);
    return (CRIT_ERR);
  }
  // Print Read buffer size and address to kernel log
  printk(KERN_INFO"%s: Read Buffer Allocation: %X->%X\n", gDrvrName, (unsigned int)gReadBuffer, (unsigned int)gReadHWAddr);

  // Allocate the write buffer with size BUF_SIZE and return the starting address
  gWriteBuffer = pci_alloc_consistent(gDev, BUF_SIZE, &gWriteHWAddr);
  if (NULL == gWriteBuffer) {
    printk(KERN_CRIT"%s: Init: Unable to allocate gBuffer.\n",gDrvrName);
    return (CRIT_ERR);
  }
  // Print Write buffer size and address to kernel log  
  printk(KERN_INFO"%s: Write Buffer Allocation: %X->%X\n", gDrvrName, (unsigned int)gWriteBuffer, (unsigned int)gWriteHWAddr);

  //--- END: Allocate Buffers

  //--- START: Register Driver

  // Register with the kernel as a character device.
  if (0 > register_chrdev(gDrvrMajor, gDrvrName, &XPCIe_Intf)) {
    printk(KERN_WARNING"%s: Init: will not register\n", gDrvrName);
    return (CRIT_ERR);
  }
  printk(KERN_INFO"%s: Init: module registered\n", gDrvrName);
  gStatFlags = gStatFlags | HAVE_KREG;

  //--- END: Register Driver

  // The driver is now successfully loaded.  All HW is initialized, IRQ's assigned, and buffers allocated
  printk("%s driver is loaded\n", gDrvrName);


  // Initializing card registers
  XPCIe_InitCard();

  return 0;
}

//--- XPCIe_InitiatorReset(): Resets the XBMD reference design
//--- Arguments: None
//--- Return Value: None
//--- Detailed Description: Writes a 1 to the DCSR register which resets the XBMD design
void XPCIe_InitiatorReset()
{
  XPCIe_WriteReg(0, 1);                   // Write: DCSR (offset 0) with value of 1 (Reset Device)
  XPCIe_WriteReg(0, 0);                   // Write: DCSR (offset 0) with value of 0 (Make Active)
}


//--- XPCIe_InitCard(): Initializes XBMD descriptor registers to default values
//--- Arguments: None
//--- Return Value: None
//--- Detailed Description: 1) Resets device
//---                       2) Writes specific values into the XBMD registers inside the EP 
void XPCIe_InitCard()
{

  XPCIe_WriteReg(0, 1);                   // Write: DCSR (offset 0) with value of 1 (Reset Device)
  XPCIe_WriteReg(0, 0);                   // Write: DCSR (offset 0) with value of 0 (Make Active)

  XPCIe_WriteReg(2, gWriteHWAddr);        // Write: Write DMA TLP Address register with starting address
  XPCIe_WriteReg(3, 0x20);                // Write: Write DMA TLP Size register with default value (32dwords)
  XPCIe_WriteReg(4, 0x2000);              // Write: Write DMA TLP Count register with default value (2000)
  XPCIe_WriteReg(5, 0x00000000);          // Write: Write DMA TLP Pattern register with default value (0x0)

  XPCIe_WriteReg(6, 0xfeedbeef);          // Write: Read DMA Expected Data Pattern with default value (feedbeef)
  XPCIe_WriteReg(7, gReadHWAddr);         // Write: Read DMA TLP Address register with starting address.
  XPCIe_WriteReg(8, 0x20);                // Write: Read DMA TLP Size register with default value (32dwords)
  XPCIe_WriteReg(9, 0x2000);              // Write: Read DMA TLP Count register with default value (2000)
}

//--- XPCIe_exit(): Performs any cleanup required before releasing the device
//--- Arguments: None
//--- Return Value: None
//--- Detailed Description: Performs all cleanup functions required before releasing device
static void XPCIe_exit(void)
{
    printk("N_TEST:in exit\n");
  // Check if we have a memory region and free it
  if (gStatFlags & HAVE_REGION) {
     (void) release_mem_region(gBaseHdwr, XBMD_REGISTER_SIZE);}
    printk("N_TEST:rel reg cmp\n");
    // Check if we have an IRQ and free it
    if (gStatFlags & HAVE_IRQ) {
        (void) free_irq(gIrq, gDev);
    }
    printk("N_TEST:free irq cmp\n");
    // Free Write and Read buffers allocated to use
//    if (NULL != gReadBuffer)
//        (void) kfree(gReadBuffer);
//    if (NULL != gWriteBuffer)
//        (void) kfree(gWriteBuffer);
//  printk("N_TEST:free buff cmp\n");
    // Free memory allocated to our Endpoint
    pci_free_consistent(gDev, BUF_SIZE, gReadBuffer, gReadHWAddr);
    pci_free_consistent(gDev, BUF_SIZE, gWriteBuffer, gWriteHWAddr);
    printk("N_TEST:free consitent mem\n");
    gReadBuffer = NULL;
    gWriteBuffer = NULL;

    // Free up memory pointed to by virtual address
    if (gBaseVirt != NULL) {
        iounmap(gBaseVirt);
     }
    printk("N_TEST:io unmap cmp\n");   
    gBaseVirt = NULL;

    // Unregister Device Driver
    if (gStatFlags & HAVE_KREG) {
      unregister_chrdev(gDrvrMajor, gDrvrName);
    }
    printk("N_TEST:un reg chr dev cmp\n");
    gStatFlags = 0;

    // Update Kernel log stating driver is unloaded
    printk(KERN_ALERT"%s driver is unloaded\n", gDrvrName);
}

// Driver Entry Point
module_init(XPCIe_init);

// Driver Exit Point
module_exit(XPCIe_exit);

static irq_return_t XPCIe_IRQHandler(int irq,void * dev_id,struct pt_regs * regs)     {       u32 i,regx;

  printk(KERN_WARNING"%s: Interrupt Handler Start ..",gDrvrName);

  for (i = 0; i < 32; i++) {
     regx = XPCIe_ReadReg(i);
     printk(KERN_WARNING"%s : REG<%d> : 0x%X\n", gDrvrName, i, regx);
  }

  printk(KERN_WARNING"%s Interrupt Handler End ..\n", gDrvrName);

返回IRQ_HANDLED;
 }

u32 XPCIe_ReadReg (u32 dw_offset)
{
        u32 ret = 0;
        //u32 reg_addr = (u32)(gBaseVirt + (4 * dw_offset));
        //ret = readl(reg_addr);  
        ret = readl(gBaseVirt + (4 * dw_offset));  

        return ret; 
}

void XPCIe_WriteReg (u32 dw_offset, u32 val)
{printk("Nag:offset=%d\tval=%x\n",dw_offset,val);
        //u32 reg_addr = (u32)(gBaseVirt + (4 * dw_offset));
        writel(val, (gBaseVirt + (4 * dw_offset)));
}

ssize_t* XPCIe_ReadMem(char *buf, size_t count)
{

    int ret = 0;
    dma_addr_t dma_addr;

    //make sure passed in buffer is large enough
    if ( count < BUF_SIZE )  {
      printk("%s: XPCIe_Read: passed in buffer too small.\n", gDrvrName);
      ret = -1;
      goto exit;
    }

    down(&gSem[SEM_DMA]);

    // pci_map_single return the physical address corresponding to 
    // the virtual address passed to it as the 2nd parameter

    dma_addr = pci_map_single(gDev, gReadBuffer, BUF_SIZE, PCI_DMA_FROMDEVICE);
    if ( 0 == dma_addr )  {
        printk("%s: XPCIe_Read: Map error.\n",gDrvrName);
        ret = -1;
        goto exit;
    }

    // Now pass the physical address to the device hardware. This is now
    // the destination physical address for the DMA and hence the to be
    // put on Memory Transactions

    // Do DMA transfer here....

    printk("%s: XPCIe_Read: ReadBuf Virt Addr = %x Phy Addr = %x.\n", 
            gDrvrName, (unsigned int)gReadBuffer, (unsigned int)dma_addr);

    // Unmap the DMA buffer so it is safe for normal access again.
    pci_unmap_single(gDev, dma_addr, BUF_SIZE, PCI_DMA_FROMDEVICE);

    up(&gSem[SEM_DMA]);

    // Now it is safe to copy the data to user space.
    if ( copy_to_user(buf, gReadBuffer, BUF_SIZE) )  {
        ret = -1;
        printk("%s: XPCIe_Read: Failed copy to user.\n",gDrvrName);
        goto exit;
    }
    exit:
      return ret;
}

ssize_t XPCIe_WriteMem(const char *buf, size_t count) {
    int ret = 0;
    dma_addr_t dma_addr;

    if ( (count % 4) != 0 )  {
       printk("%s: XPCIe_Write: Buffer length not dword aligned.\n",gDrvrName);
       ret = -1;
       goto exit;
    }

    // Now it is safe to copy the data from user space.
    if ( copy_from_user(gWriteBuffer, buf, count) )  {
        ret = -1;
        printk("%s: XPCIe_Write: Failed copy to user.\n",gDrvrName);
        goto exit;
    }

    //set DMA semaphore if in loopback
    down(&gSem[SEM_DMA]);

    // pci_map_single return the physical address corresponding to 
    // the virtual address passed to it as the 2nd parameter

    dma_addr = pci_map_single(gDev, gWriteBuffer, BUF_SIZE, PCI_DMA_FROMDEVICE);
    if ( 0 == dma_addr )  {
        printk("%s: XPCIe_Write: Map error.\n",gDrvrName);
        ret = -1;
        goto exit;
    }

    // Now pass the physical address to the device hardware. This is now
    // the source physical address for the DMA and hence the to be
    // put on Memory Transactions

    // Do DMA transfer here....

    printk("%s: XPCIe_Write: WriteBuf Virt Addr = %x Phy Addr = %x.\n",  
           gDrvrName, (unsigned int)gReadBuffer, (unsigned int)dma_addr);

    // Unmap the DMA buffer so it is safe for normal access again.
    pci_unmap_single(gDev, dma_addr, BUF_SIZE, PCI_DMA_FROMDEVICE);

    up(&gSem[SEM_DMA]);

    exit:
      return (ret);
}

u32 XPCIe_ReadCfgReg (u32 byte) {
   u32 pciReg;
   if (pci_read_config_dword(gDev, byte, &pciReg) < 0) {
        printk("%s: XPCIe_ReadCfgReg: Reading PCI interface failed.",gDrvrName);
        return (-1);
   }
   return (pciReg);
}

u32 XPCIe_WriteCfgReg (u32 byte, u32 val) {
   if (pci_write_config_dword(gDev, byte, val) < 0) {
        printk("%s: XPCIe_Read Device Control: Reading PCI interface failed.",gDrvrName);
        return (-1);
   }
   return 1;
}

0 个答案:

没有答案