关于PCI6503的驱动开发的问题

最近搞PCI6503的QNX驱动,不知道什么地方出了问题,老是驱动不了。经过老板同意,将全部代码贴上,希望能得高手指点。另外在贴的时候,原来都排好的,贴上都歪了,不整齐了,如果有必要还要请重新排一下。

这段代码的执行步骤是:
1 取得root权限
2 取得pci_*服务器的支持
3 查找pci设备时候存在,若存在则继续
4 取得板卡的info信息
5 将info信息打印
6 映射板卡的pci mite地址和pci register地址
7 取得进入BAR1的操作权限
8 通过映射指针对板卡进行操作
9 资源回收

问题现象描述:
打印信息正确,说明代码前面部分问题不大,但是执行完,发现板卡根本就不动。问题应该是打印代码后。

关于pci6503的介绍:
它是美国NI公司的24路DIO板,它上面有一个8255并行扩展芯片,通过它的三个端口PORTA/PORTB/PORTC实现数字量的输入和输出。关于它的寄存器级开发说明如下:
BASE+0: PORTA
BASE+1: PORTB
BASE+2: PORTC
BASE+3: CONFIG REGISTER
另外,在进行操作前,该板卡需要先获得BASE的操作权限,原文如下:
To configure the PCI MITE chip, you must first write an algorithm that finds and stores all configuration information about the board. To do this, use PCIBIOS calls to search PCI configuration space for the National Instruments vendor ID (0x1093) and PCI-DIO-96 device ID (0x0160), PXI-6508 device ID (0x13c0), or PCI-6503 device ID (0x17d0). If a board is found, the algorithm can store all the board’s configuration information into a data structure. Base Address Register 0 (BAR0) corresponds to the base address of the PCI MITE, while Base Address Register1 (BAR1) is the base address of the board registers. The size of each of these windows is 4KB. Both addresses will most likely be mapped above 1MB in the memory map. This means that you must know how to perform memory cycles to extended memory to communicate with the board. The memory map provides information to re-map the board under 1MB, which simplifies communication with the board. To accomplish this, use PCI BIOS read and write calls. Use the pseudocode in this section to re-map the board below 1MB. If you choose not to re-map the board, you must still perform steps4 and5. All values in this example are 32bits.

  1. Write the address to which you want to re-map the PCI MITE to PCI configuration space offset 0x10 (BAR0).
  2. Write the value 0x0000aeae to offset 0x340 from the new PCI MITE address.
  3. Write the address to which you want to re-map the board (other than the PCI MITE) to PCI configuration space offset 0x14 (BAR1).
  4. Create the window data value by masking the new board address:window data value = ((0xffffff00 and new board address) or (0x00000080))If you are not remapping the board, then the new board address is the value in BAR1.
  5. Write the window data value to offset 0xc0 from the new PCI MITE address.
    If you are not remapping the board, then the new PCI MITE address is the value in BAR0.The following pseudocode re-maps the PCI MITE to memory address 0xd0000 and the board to memory address 0xd1000. CWrite(0x10,0x000d0000);
    Write(0xd0340,0x0000aeae);
    CWrite(0x14,0x000d1000);
    Write(0xd00c0,0x000d1080) ;

我的代码如下:
/*******************************************************************************

  • 文件名称:PCI6503_QNX_Driver.C
  • 文件说明:在QNX下,对NI公司24路DI/O板卡PCI6503的底层驱动开发。
  • 运行环境:QNX6.2.1
  • 程序员 :倪启明
  • 时 间:2007年06月26日
    *******************************************************************************/

#include <sys/neutrino.h>
#include <hw/pci.h>
#include <hw/pci_devices.h>
#include <stdio.h>
#include <sys/mman.h>
#include <errno.h>

#define PCI_DEBUG

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
#define PCI_CARD_DEVICE_ID 0x17d0 //设备号
#define PCI_CARD_VENDOR_ID 0x1093 //厂商号

//-----------------------------------------------------------------------------
uint32_t dev_address_value_test;
uint32_t mem_address_value_test;

/********************************************************************************

  • 函数名称:pci_printf
  • 函数说明:打印PCI设备信息
  • 程序员 :倪启明
  • 日 期:2007年06月18日
    ********************************************************************************/
    void pci_printf(struct pci_dev_info *inf)
    {
    uint32_t i;

fprintf (stdout, “\n device id = %x”, inf->DeviceId);
fprintf (stdout, “\n Vendo id = %x”, inf->VendorId);
fprintf (stdout, “\n BusNumber = %x”, inf->BusNumber);
fprintf (stdout, “\n device number = %x”, ((inf->DevFunc)>>3)&0x1F);
fprintf (stdout, “\n Function number = %x”, (inf->DevFunc)&0x07);
fprintf (stdout, “\n Revision = %x”, inf->Revision);
fprintf (stdout, “\n device class = %x”, inf->Class);
fprintf (stdout, “\n device irq = %x”, inf->Irq);
fprintf (stdout, “\n pcibase address = %x”, ((inf->CpuBaseAddress)));
fprintf (stdout, “\n base address size = %x”, (
(inf->BaseAddressSize)));

pci_read_config32 (inf->BusNumber, inf->DevFunc, 0x10, 1, &i);
fprintf (stdout, “\n Base address 0 = %x”, i);

pci_read_config32 (inf->BusNumber, inf->DevFunc, 0x14, 1, &i);
fprintf (stdout, “\n Base address 1 = %x”, i);
}

/********************************************************************************

  • 函数名称:pci_card_detect
  • 函数说明:对PCI设备进行探测
  • 函数输入:devcieNo 设备号,
  •       vendor   厂商号,
    
  •       index    同类型板卡标识(从0开始)
    
  • 函数输出:pci_handle PCI句柄
  •       inf         设备信息
    
  •       dev_handle  设备句柄
    
  • 返回值 :PCI_YES 表示存在
  • 程序员 :倪启明
  • 日 期:2007年06月18日
    ********************************************************************************/
    #define PCI_YES 1
    #define ATTACH_PCI_FAILURE -1
    #define FIND_FAILURE -2
    #define ADDR_ERROR -3
    #define ATTACH_DEVICE_FAILURE -4

int pci_card_detect(unsigned index,
unsigned *pci_handle,
struct pci_dev_info *inf,
uint32_t *dev_handle,
uint32_t *mem_handle)
{
uint32_t addr_handle;
uint32_t io_addr;

unsigned bus;
unsigned dev;
unsigned func;
unsigned dev_func;

uint8_t* x;
uint32_t *p;
uint32_t value;

uint32_t BAR0, BAR1;

uint32_t *addr[2];

//取得root操作权限
ThreadCtl (_NTO_TCTL_IO, 0);

//将程序连接到PCI server上,使得整个程序可以使用QNX系统提供的pci_*()函数
*pci_handle = pci_attach(0);
if (*pci_handle == -1)
return ATTACH_PCI_FAILURE;

//PCI设备检测
if (pci_find_device (inf->DeviceId,
inf->VendorId,
index,
&bus,
&dev_func) != PCI_SUCCESS)
return FIND_FAILURE;

//将inf与PCI设备相连,同时取得设备信息
dev_handle = pci_attach_device (NULL,
(PCI_SHARE|PCI_SEARCH_VENDEV|PCI_INIT_ALL),
0,
inf);
if (dev_handle == NULL)
return ATTACH_DEVICE_FAILURE;

//print some information which have been found.
pci_printf(inf);

//取得PCI MITE基地址
if ( pci_read_config32 (inf->BusNumber,
inf->DevFunc,
0x10,
1,
&BAR0)!= PCI_SUCCESS)
return ADDR_ERROR;

//取得PCI板卡寄存器基地址
if ( pci_read_config32 (inf->BusNumber,
inf->DevFunc,
0x14,
1,
&BAR1)!= PCI_SUCCESS)
return ADDR_ERROR;

//板卡pci mite映射
addr_handle = PCI_IO_ADDR(BAR0); //PCI_MEM_ADDR(BAR0);

fprintf (stdout, “\n 1 board mite addr: %x”, addr_handle);

addr[0] = mmap_device_memory (NULL,
*(inf->BaseAddressSize),
(PROT_READ|PROT_WRITE),
0,
(uint64_t)addr_handle);

fprintf (stdout, “\n 1 board mite new : %x”, addr[0]);

//板卡寄存器地址映射
addr_handle = PCI_IO_ADDR(BAR1);//PCI_MEM_ADDR(BAR1);

fprintf (stdout, “\n 2 board reg addr : %x”, addr_handle);

addr[1] = mmap_device_memory (NULL,
*(inf->BaseAddressSize),
(PROT_READ|PROT_WRITE),
0,
(uint64_t)addr_handle);
fprintf (stdout, “\n 2 board reg new : %x”, addr[1]);

mem_handle = addr[1];
if (mem_handle == MAP_FAILED)
return;

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
fprintf (stdout, “\n 1 dev_handle: %x”, dev_handle);
fprintf (stdout, “\n 1 mem_handle: %x”, mem_handle);

dev_address_value_test = (uint32_t)dev_handle;
mem_address_value_test = (uint32_t)mem_handle;
//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

// access to BAR1
pci_write_config32 (inf->BusNumber,
inf->DevFunc,
0x10, //BAR0
1,
&addr[0]);//set the new pci mite value to BAR0

((uint32_t)addr[0] + 0x340) = 0xaeae;

pci_write_config32 (inf->BusNumber,
inf->DevFunc,
0x14,//BAR1
1,
&addr[1]);//set the new pci board reg value to BAR1

((uint32_t)addr[0] + 0xc0) = ((uint32_t)addr[1]& 0xffffff00)|0x80;

//Set Configer Register (all is output port)
x = (uint8_t*)mem_handle+0x03;
*x = 0x80;

fprintf (stdout, “\n Configer register addr = %x”, x);

fprintf (stdout, “\n Configer register value = %x\n”, *x);

//Port A output
x = (uint8_t*)mem_handle+0x0000;
*x = 0xaa;
fprintf (stdout, “\n Port A address = %x”, x);
fprintf (stdout, “\n Port A value = %x\n”, *x);

//Port B output
x = (uint8_t*)mem_handle+0x0001;
*x = 0xaa;
fprintf (stdout, “\n Port B address = %x”, x);
fprintf (stdout, “\n Port B value = %x\n”, *x);

//Port C output
x = (uint8_t*)mem_handle+0x0002;
*x = 0xaa;
fprintf (stdout, “\n Port C address = %x”, x);
fprintf (stdout, “\n Port C value = %x”, *x);

return PCI_YES;
}


/********************************************************************************

  • 函数名称:close_card
  • 函数说明:驱动退出时的资源释放处理
  • 程序员 :倪启明
  • 日 期:2007年06月18日
    ********************************************************************************/
    void close_card (struct pci_dev_info *inf,
    int pci_handle,
    void *dev_handle,
    void *mem_handle)
    {
    //判断是否已经成功内存映射
    if (mem_handle == MAP_FAILED)
    return;

//收回映射内存
if (munmap_device_memory (mem_handle, (*(inf->BaseAddressSize))) == -1)
fprintf (stdout, “\n Error:munmap failed!”);

//收回与设备的关联
if (pci_detach_device(dev_handle) != PCI_SUCCESS)
fprintf(stdout, “\n Error:detach device failed status!\n”);

//收回与PCI server的关联
if (pci_detach (pci_handle) != PCI_SUCCESS)
fprintf (stdout, “\n Error:detach pci sever failed status!!%s\n”, strerror (errno));
return;
}

/*******************************************************************************/
int main(int argc, char *argv)
{
struct pci_dev_info inf;

int pci_handle; //PCI函数使用句柄
uint32_t addr_handle;
uint32_t *mem_handle;
uint32_t *dev_handle;
unsigned index = 0;

fprintf (stdout, “======================================================”);

//初始化
memset (&inf, 0, sizeof (struct pci_dev_info));
inf.VendorId = PCI_CARD_VENDOR_ID;
inf.DeviceId = PCI_CARD_DEVICE_ID;

//PCI设备探测
if (pci_card_detect (index,
&pci_handle, //pci句柄
&inf, //设备信息
dev_handle, //设备句柄
mem_handle) == PCI_YES) //内存映射句柄
{
dev_handle = (uint32_t*)dev_address_value_test;
mem_handle = (uint32_t*)mem_address_value_test;

close_card(&inf, pci_handle, dev_handle, mem_handle);
}

fprintf (stdout, “\n======================================================\n”);
return 0;
}
/*******************************************************************************/

MITE and register should be mapped with NOCACHE option,
(PROT_READ | PROT_WRITE | PROT_NOCACHE)

我把两个映射都改过来了
mmap_device_memory (NULL,
*(inf->BaseAddressSize),
(PROT_READ|PROT_WRITE| PROT_NOCACHE),
0,
(uint64_t)addr_handle);
可还是不行。

另外,"pci -v"检测到的信息:
Class = Unknown (Unknown)
Vendor ID = 1093h, National Instruments
Device ID = 17d0h, PCI-6503
PCI index = 0h
Class Codes = ff0000h
Revision ID = 0h
Bus number = 1
Device number = 12
Function num = 0
Status Reg = 280h
Command Reg = 7h
Header type = 0h Single-function
BIST = 0h Build-in-self-test not supported
Latency Timer = 20h
Cache Line Size= 8h un-cacheable
PCI Mem Address = de021000h 32bit length 4096 enabled
PCI Mem Address = de022000h 32bit length 4096 enabled
Max Lat = 0ns
Min Gnt = 0ns
PCI Int Pin = INT A
Interrupt line = 5

程序输入信息:

device id = 17d0
Vendo id = 1093
BusNumber = 1
device number = c
Function number = 0
Revision = 0
device class = ff0000
device irq = 5
pcibase address = de021000
base address size = 1000
Base address 0 = de021000
Base address 1 = de022000
1 board mite addr: de021000
1 board mite new : 40100000
2 board reg addr : de022000
2 board reg new : 40101000
1 dev_handle: 8057018
1 mem_handle: 40101000
Configer register addr = 40101003
Configer register value = ff

Port A address = 40101000
Port A value = ff

Port B address = 40101001
Port B value = ff

Port C address = 40101002
Port C value = ff

There are many potential problems in your code.

  1. register/device memory access need to be volatile, so variable x has to be defined as “volatile char *x”
  2. your mapped in register, and put virtual address back to BAR0/BAR1, that’s wrong, don’t modify BARs.

I have changed the variable x “volatile uint8_t* x”, and delete the code of modifing the BAR0 and BAR1. But the problem is also alive.

我现在将程序重新整理了一遍,编译和运行都没有问题,但是仍然不能控制硬件工作:

/*******************************************************************************

  • file name :PCI6503_QNX_Driver.C
  • description :the driver for NI’s PCI6503
  • enviroment :QNX6.2.1
  • programmer :Qiming Ni
  • date :06/26/2007
    *******************************************************************************/

#include <sys/neutrino.h>
#include <hw/pci.h>
#include <hw/pci_devices.h>
#include <stdio.h>
#include <sys/mman.h>
#include <errno.h>

#define PCI_DEBUG

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
#define PCI_CARD_DEVICE_ID 0x17d0
#define PCI_CARD_VENDOR_ID 0x1093

/********************************************************************************

  • function :pci_printf
  • description :printf the pci info
  • programmer :Qiming Ni
  • date :06/18/2007
    ********************************************************************************/
    void pci_printf(struct pci_dev_info *inf)
    {
    uint32_t i;

fprintf (stdout, “\n device id = %x”, inf->DeviceId);
fprintf (stdout, “\n Vendo id = %x”, inf->VendorId);
fprintf (stdout, “\n BusNumber = %x”, inf->BusNumber);
fprintf (stdout, “\n device number = %x”, ((inf->DevFunc)>>3)&0x1F);
fprintf (stdout, “\n Function number = %x”, (inf->DevFunc)&0x07);
fprintf (stdout, “\n Revision = %x”, inf->Revision);
fprintf (stdout, “\n device class = %x”, inf->Class);
fprintf (stdout, “\n device irq = %x”, inf->Irq);
fprintf (stdout, “\n pcibase address = %x”, ((inf->CpuBaseAddress)));
fprintf (stdout, “\n base address size = %x”, (
(inf->BaseAddressSize)));

pci_read_config32 (inf->BusNumber, inf->DevFunc, 0x10, 1, &i);
fprintf (stdout, “\n Base address 0 = %x”, i);

pci_read_config32 (inf->BusNumber, inf->DevFunc, 0x14, 1, &i);
fprintf (stdout, “\n Base address 1 = %x”, i);
}

/*******************************************************************************/
int main(int argc, char *argv)
{
struct pci_dev_info inf;

int pci_handle;
uint32_t addr_handle;
uint32_t io_addr;
void *addr[2];
void *dev_handle;
unsigned index = 0;
unsigned bus;
unsigned dev;
unsigned func;
unsigned dev_func;

volatile uint8_t* x;
uint32_t value;

uint32_t BAR0, BAR1;

fprintf (stdout, “======================================================”);

//intial some variable
memset (&inf, 0, sizeof (struct pci_dev_info));
inf.VendorId = PCI_CARD_VENDOR_ID;
inf.DeviceId = PCI_CARD_DEVICE_ID;

//get the hight provillege
ThreadCtl (_NTO_TCTL_IO, 0);

//open PCI server, so that you can use pci_*()
pci_handle = pci_attach(0);
if (pci_handle == -1)
return 0;

//detect pci device
if (pci_find_device (inf.DeviceId,
inf.VendorId,
index,
&bus,
&dev_func) != PCI_SUCCESS)
return 0;

//get the device information
dev_handle = pci_attach_device (NULL,
(PCI_SHARE|PCI_SEARCH_VENDEV|PCI_INIT_ALL),
0,
&inf);
if (dev_handle == NULL)
return 0;

//print some information which have been found.
pci_printf (&inf);

//get pci mite address
if ( pci_read_config32 (inf.BusNumber,
inf.DevFunc,
0x10,
1,
&BAR0)!= PCI_SUCCESS)
return 0;


//get pci board register address
if ( pci_read_config32 (inf.BusNumber,
inf.DevFunc,
0x14,
1,
&BAR1)!= PCI_SUCCESS)
return 0;

//remap pci mite address
if (PCI_IS_MEM (BAR0))
addr_handle = PCI_MEM_ADDR (BAR0);
else
addr_handle = PCI_IO_ADDR (BAR0);

addr[0] = mmap_device_memory (NULL,
*(inf.BaseAddressSize),
(PROT_READ | PROT_WRITE | PROT_NOCACHE),//must NOCACHE
0,
(uint64_t)addr_handle);

if (addr[0] == MAP_FAILED)
return 0;

//remap pci board address
if (PCI_IS_MEM (BAR1))
addr_handle = PCI_MEM_ADDR (BAR1);
else
addr_handle = PCI_IO_ADDR (BAR1);

addr[1] = mmap_device_memory (NULL,
*(inf.BaseAddressSize),
(PROT_READ | PROT_WRITE | PROT_NOCACHE),
0,
(uint64_t)addr_handle);

if (addr[1] == MAP_FAILED)
return 0;

//====================================================================================
//====================================================================================
// set the windows date to new pci mite
x = (uint8_t*)addr[0] + 0xc0;
((uint32_t)x) = ((uint32_t)addr[1] & 0xffffff00)|0x80;

//Set Configer Register (all is output port)
x = (uint8_t*)addr[1]+0x03;
*x = 0x80;

//Port A output
x = (uint8_t*)addr[1]+0x0000;
*x = 0xaa;
fprintf (stdout, “\n Port A address = %x”, x);
fprintf (stdout, “\n Port A value = %x\n”, *x);

//Port B output
x = (uint8_t*)addr[1]+0x0001;
*x = 0xaa;
fprintf (stdout, “\n Port B address = %x”, x);
fprintf (stdout, “\n Port B value = %x\n”, *x);

//Port C output
x = (uint8_t*)addr[1]+0x0002;
*x = 0xaa;
fprintf (stdout, “\n Port C address = %x”, x);
fprintf (stdout, “\n Port C value = %x”, *x);

//====================================================================================
//====================================================================================
//release re-map memory
if (munmap_device_memory (addr[0], (*(inf.BaseAddressSize))) == -1)
fprintf (stdout, “\n Error:munmap ADDR0 failed!”);

if (munmap_device_memory (addr[1], (*(inf.BaseAddressSize))) == -1)
fprintf (stdout, “\n Error:munmap ADDR1 failed!”);

//release the relation with the device
if (pci_detach_device(dev_handle) != PCI_SUCCESS)
fprintf(stdout, “\n Error:detach device failed status!\n”);

//release pci server
if (pci_detach (pci_handle) != PCI_SUCCESS)
fprintf (stdout, “\n Error:detach pci sever failed status!!%s\n”, strerror (errno));

fprintf (stdout, “\n======================================================\n”);
return 0;
}
/*******************************************************************************/

  1. Don’t read BARs by yourself, structure pci_dev_info already has IO/memory address in it(info.CpuBaseAddress[6]/info.BaseAddressSize[6])
  2. If you’are running on x86, memory space has to be mapped by mmap_device_memory() and accessed like *((volatile char *)x) = data;
    IO spave has to be mapped by mmap_device_io and accessed by using in/out instruction.
    You’re luky because looks like both MITE and registers are in memory space.
    mapping example
    mmapAddr = mmap_device_memory( NULL, info.BaseAddressSize[0],
    PROT_NOCACHE|PROT_READ|PROT_WRITE, MAP_SHARED|MAP_PHYS, info.CpuBaseAddress[0]);
    Your munmap_device_memory calls are also wrong (size).
  3. For the next code, you are writing virtual address to offset 0xc0 of addr[0], I am not familiar withf the board, but I don’t think it’s right.
    // set the windows date to new pci mite
    x = (uint8_t*)addr[0] + 0xc0;
    ((uint32_t)x) = ((uint32_t)addr[1] & 0xffffff00)|0x80;
    Probably should be like this.
    ((volatile uint32_t)x) = ((uint32_t)BAR1 & 0xffffff00)|0x80;