Help with mmap_device_memory call

OK, today my troubles are with the mmap_device_memory() call.

Here’s the quick background again. Currently this code is working just fine running under QNX 6.1. I’m in the process of upgrading everything to QNX 6.3. The code in question below is trying to map a stepper card but failing. I’ve cut out parts below where it fails because those aren’t important since the code doesn’t reach that point (BTW, this code was written several years ago by someone who is no longer here).

int StepperCardMemory::Open(int CardNumber)
{
int status;

assert(CardNumber >= 1);

// close any previous connection to stepper
Close();

m_CardNumber = CardNumber;

// Connect to the PCI server, refer to [2] for details
m_PciHandle = pci_attach( 0 );
if (m_PciHandle == -1)
{
ReportError(“pci_attach”, errno);
return 0;
}

// determine if pci present, refer to [2] for details
unsigned int lastbus, version, hardware;
status = pci_present(&lastbus, &version, &hardware);
if (status != PCI_SUCCESS)
{
ReportError(“pci_present”, status);
return 0;
}

// find the first card of base class 0x0b, subclass 0x40, interface 0x00
// refer to [2] for details of this call
// refer to [1] for details of pci class numbers
// refer to [3] for details of class code provided by the 2040
// show_pci can be used to display details of all pci cards present
unsigned bus, dev_func;
status = pci_find_class(0xb4000, m_CardNumber-1, &bus, &dev_func);
if (status != PCI_SUCCESS)
{
ReportError(“pci_find_class”, status);
return 0;
}

// read the physical address of the HPI CSR data area
// refer to [2] for details of pci_read_config32
// refer to [3] for details about the data area contents
status = pci_read_config32(bus, dev_func, 0x10, 1, &m_HPI_CSR_BaseAddress);
if (status != PCI_SUCCESS)
{
ReportError(“pci_read_config32”, status);
return 0;
}

// read the physical address of the HPI control space
// refer to [2] for details of pci_read_config32
// refer to [3] for details about the data area contents
status = pci_read_config32(bus, dev_func, 0x14, 1, &m_ControlSpaceBaseAddress);
if (status != PCI_SUCCESS)
{
ReportError(“pci_read_config32”, status);
return 0;
}

// map the HPI CSR data space into this tasks logical memory space
// refer to [2] for details of mmap_device_memory

/* Original 6.1 mmap call here */

// m_pHPI_CSR = (unsigned char*)mmap_device_memory((void*)0x10000000, 4096, PROT_NOCACHE | PROT_READ | PROT_WRITE, MAP_TYPE, m_HPI_CSR_BaseAddress);

/* Updated 6.3 call here */

m_pHPI_CSR = (unsigned char*)mmap_device_memory(0, 4096, PROT_NOCACHE | PROT_READ | PROT_WRITE, 0, HPI_CSR_BaseAddress);

if (m_pHPI_CSR == MAP_FAILED)
{
    m_pHPI_CSR = 0;
    ReportError("mmap_device_memory", errno);
    cout << "StepperCardMemory::Open() - mmap_device_memory failed with MAP_FAILED and errno " << errno << " for address " << m_HPI_CSR_BaseAddress << endl; 
    return 0;
}

SNIP LOTS OF CODE HERE

}

So what happens is that the call to mmap_device_memory fails with (22) EINVAL at address D7028000.

You can see the original 6.1 call with the address specified and flags set to MAP_TYPE. I took a look at the mmap_device_memory call and it should only generate EINVAL is the ‘flag’ arguments are incorrect. Since MAP_TYPE isn’t supported under 6.3 I decided to just replace it and the address with 0 and see what happened. But it still generates the EINVAL error even though I am clearly using valid flag arguments. I’m not sure why this can be unless the D7028000 address is somehow out of range for the PCI bus (I’m not a hardware expert so it could well be which is why I included it above).

I also tried several varriations on the above code such as:

m_pHPI_CSR = (unsigned char*)mmap_device_memory((void*)0x10000000, 4096, PROT_NOCACHE | PROT_READ | PROT_WRITE, MAP_FIXED, m_HPI_CSR_BaseAddress);

m_pHPI_CSR = (unsigned char*)mmap_device_memory((void*)0x10000000, 4096, PROT_NOCACHE | PROT_READ | PROT_WRITE, 0, m_HPI_CSR_BaseAddress);

m_pHPI_CSR = (unsigned char*)mmap_device_memory(0, 4096, PROT_NOCACHE | PROT_READ | PROT_WRITE, MAP_FIXED, m_HPI_CSR_BaseAddress);

but all give me the same EINVAL error.

It’s been a while since I have done anything with mmap in general (back in QNX 4.25 days) and never with mmap_device_memory so I’m really lost as to what to attempt next.

TIA,

Tim

P.S. Here are the original references from the file header referred to by [] in the comments in the routine. They mean little to me but might help someone trying to diagnose the problem.

// References:
// [1] PCI Local Bus Specification Revision 2.1
// hard copy
// - describes general PCI details.
// - Most useful part was Chapter 6 containing details of the PCI
// configuration space.
// [2] qnx library functions pci_xxxxx
// support.qnx.com/support/docs/qnx … xxxxx.html
// - describes the pci_xxxxx functions used to find a pci card and read its
// configuration area.
// - describes the mmap_device_memory and munmap_device_memory used to
// map physical to logical memory
// - describes the delay function
// [3] TI PCI2040 PCI-DSP Bridge Controller Data Manual
// scps048.pdf and hard copy
// - describes the contents of the pci memory areas mapped into the
// PC memory space
// [4] TI TMS32054x DSP Enhanced Peripherals Reference Set Volume 5
// hard copy and on Code Composer Studio CD
// - Chapter 4 describes the Enhanced Host Port Interface
// This supercedes some of the information given in [3] about the
// HPI interface.
// Also contains some info about HPI use during initial DSP reset
// [5] TI TMS320VC5410 Bootloader
// spra609a.pdf
// - contains details on how to load a program to the 5410 using HPI
// [6] TI 2040 EVM Hardware Guide Users Guide
// describes EVM card details relevent to the bootload process
// pci 2040 EVM hardware guide.pdf
// [7] TI TMS320VC5402 and TMS320UC5402 Bootloaders
// hard copy
// - contains details on how to load a program to the 5402 using HPI.
// It is not needed for the EVM board but will be required for the
// final stepper board
// [8] TI TMS32054x Assembly Language Tools Users Guide
// hard copy and on Code Composer Studio CD
// - Chapter 10 describes how to create and the format of the hex program
// file created
// [9] show_pci.c
// example neutrino program for displaying pci card details
// converted to neutrino from qnx by ZPZ 20/7/00
// [10] TI TMS320UC5402/TMS320VC5402 Digital Signal Processor Silicon Advisories SPRZ155B
// contains details of bug where HPI may lock up if both DSP and host write 1 to HINT

Just an off the cuff thought - is m_HPI_CSR_BaseAddress a unsigned long long? - that is what mmap_device_memory() expects - but you gave it’s address to a function pci_read_config32() which wanted a 32 bit value - I suspect the address is getting mangled by a 32-64 bit issue.

This is very possible if you are declaring m_HPI_CSR_BaseAddress as a 64 bit value. If it is 32 bit, that is probably not the problem.

Rick…

Rick,

From the header file:

long m_HPI_CSR_BaseAddress; // HPI CSR Memory Base Addresss - physical address

It’s a long (32 bit number).

So I guess I need to put this into a U64 (low bytes I assume) and then pass it to mmap.

One question, since I am translating from a 32 bit number to a 64 bit one and the number is large (high order bit sign) will I need to do any re-ordering of the bytes for MSB/LSB or can it go directly as is?

Tim

It’s probably not the problem as I suspect the compiler will silently promote to 64 bits as required.

Also I assume that it is a type in your example that the 6.3 code uses HPI_CSR_BaseAddress and not m_HPI_CSR_BaseAddress.

I will keep looking and see if I can see anything…

Rick…

Aha… If it is declared as a long, when the compiler sign extends it, it will be a big negative number, then it may fail to convert it to an unsigned long long (or some like that). It may be interesting to convert it to an unsigned long long and see what value it has - that should tell you whether you have to hand convert it to 64 bits.

Rick…

Rick,

The code uses m_HPI_CSR_BaseAddress. I accidently erased the m_ parth when I was trying paste and format into my post.

OK. I added code to do the following:

uint64 addr64 = m_HPI_CSR_BaseAddress;
cout << "64bit value is " << addr64 << endl;

m_pHPI_CSR = (unsigned char*)mmap_device_memory(0, 4096, PROT_NOCACHE | PROT_READ | PROT_WRITE, 0, addr64);

But the problem remainds. The value of addr64 is 18446744073021849600 which converts to 0xFFFFFFFFD7028000 using the built in calculator on XP (the QNX one apparently can’t handle 64 bit arithmetic :frowning:).

So there is a conversion problem here. Now tomorrow I just have to figure out exactly how to do the hand conversion in a little test program.

Tim

Did you call ThreadCtl() ? I suspect you do cause I think you should get a different error code then EINVAL.

Yes passing a int to an unsigned long long will cause sign extension.

Mario,

Yes, this code is spun off into one of the many threads running on the system. So while there is no call to ThreadCtrl() inside this source file I am sure if I traced around I would find one someplace. What does that have to do with the error codes?

I find it strange that I get EINVAL when I expected to get ENXIO or ENOMEM which would be more appropriate if I were trying to map something outside the physical address space which it now looks like is happening due to the sign extention when promotion to uint64.

Tim

About ThreadCtl I was just thinking out loud ;)

As for EINVAL I makes sense to me (the argument you provided is illegal). ENOMEM is when you run out of memory or don’t have enough to perform an operation. ENXIO I guess could be applicable and probably better.

I searched the release notes since 6.1 for references to mmap_device_memory(), and here’s what I found in the “Changes & fixes” section of the 6.2.1A release notes:

mmap()
mmap64()
mmap_device_io()
mmap_device_memory()
If you specify a length less than or equal to 0, these functions return
MAP_FAILED and set errno to EINVAL. (Ref# 11176)


Note: For mmap(), the offset is of type off_t, which by default is a signed
32-bit quantity. If the offset is greater than 0x80000000, it’s treated
as a negative number, and the function fails. If you define
_FILE_OFFSET_BITS to be 64 when you compile your code, the file offsets
automatically become 64-bit values.

The mmap64(), mmap_device_io(), and mmap_device_memory() functions 
always take 64-bit offsets. 

Steve,

Thanks for looking up this for me. I don’t think I have the 6.1 release notes other than the help files that would be available under Photon.

However that said, I was looking again at the helpviewer files under 6.3 for mmap_device_memory and it says:

EINVAL - invalid ‘flags’ argument or ‘len’ is 0 (what you noted above)
ENXIO - the address from physical for len bytes is invalid for the requested object or MAP_FIXED was requested and (snipped here about invalid request)

In my case I requested a length of 4096 bytes which is legit AND I was using a legit flags settings of 0. But my physical value was invalid so I should have gotten ENXIO. Had I gotten the ENXIO right away I would have looked at the physical argument immediately instead of focusing on the flags one.

So either the doc’s need to be slightly updated or the O/S is not behaving as intended…


Note: For mmap(), the offset is of type off_t, which by default is a signed
32-bit quantity. If the offset is greater than 0x80000000, it’s treated
as a negative number, and the function fails. If you define
_FILE_OFFSET_BITS to be 64 when you compile your code, the file offsets
automatically become 64-bit values.

The mmap64(), mmap_device_io(), and mmap_device_memory() functions 
always take 64-bit offsets. 

Wish I understood the ramifications of the above better.

Tim