Expanded Memory Manager

Exist in QNX an application like dos EMM386?

otherwise, how can I exclude a region of memory from the list of available mapping regions?

I intalled a PCI card on my sys and everything seems to work. but when I try to inizlize the board It displays some errors.

I read (about 1000 times) the User’s Manual and in a invisible part it says that “selecting a base memory address (via Hardwere Jumpers) be aware of potential conflict with apps also using the UMB”

How can I solve this?

The concept of Expanded memory manager does exits. This is a 32 bit OS. The memory on your PCI card is NOT mapped as normal memory. There is no need to exclude it.

Something is probably wrong in the way you access the board. Care to post some code?

mmm…the way I access the board is very easy… i use the dlopen on “libcei22032.so” and with dlsym i call “ar_loadslv”.

this function is the first I must run because it get access to the board.
the porototype is:

short ar_loadslv(u16bit BOARD_NUMBER, unsigned long BASE_SEGMENT,
int BASE_PORT, unsigned short RAM_SIZE);


BOARD_NUMBER is the ID of the board and I use it to reference to it (it must be between 0 and 15)

BASE_SEGMENT is the address of memory where the board is loaded. it must be according to the jumpers conf.

BASE_PORT is the address of the base port that the board is jumped for.in my type of card it must be set to 0.

RAM_SIZE is the memory space in Kb. my card have 4Kb of memory space but the guide says that it must be set to 0. (mah!!)

now the call of this function is:

ar_loadslv(0, 0xD000, 0, 0);

I’ve tryed the same call with different args but I obtain the same “Function not supported by the current hardware”. the guide says that this kind of error is generated by the wrong parameter passed to the function that are not according with the HW settings of the jumpers. Otherwise, there are memory conflicts when a 16-bit memory interface (such as my board) is placed in the same 128Kb region as an 8-bit memory interface, and now the guide says that this can be solved by using UMB manager.

mm…probabily i’m gone OT…I’m sorry, but I’m going crazy with this. I’ve tryed all the possible ways and it does’n work…sigh!

If it’s a PCI card then the address shouldn’t be the 0xd000 range. 0xD000 is an address range that correspond to the ISA bus not the PCI bus. It’s the first time I ever heard of a PCI card with jumpers to specify the address.

Since the doc talks about UBM manager it was definitely written with DOS in mind. Now I wonder if the source code use to create this dll was ported as is or adapted to QNX. Do you have to source to this, can you post the code of ar_loadslv?

it’s strange bu true… I have to set jumpers for the 2 MSB of the memspace… on the guide it says that the default address is 0xd000 corrisponding to a physical address of 0xd0000. (so I can only set 8 humpers for the firt d0)

I’ve tried other addresses but with the same result…

ar_loadslv is build inside the libcei22032.so, and I’ve only the entry point (and all I need to know is on the guide - just not ALL but a part of it :smiley: )


founded a very big fault!!!

this is NOT a PCI card (as writed inthe guide or on the website) but a ISA card with a PCI Pass Through (I’ve just phoned to the support!!)

the problem now is: WHERE qnx map this card?! however, in 0xd000 is not present, but right now i’ve tryed it with a dos-app and all works right!!

First time I heard of that PCI Pass Through. Assuming this works tranparently, here are a few though.

QNX uses virtual addressing that means if you do:

char *p = (char *) 0x1234;
*p = 0x1234;

This will NOT write to the physical address 0x1234 but to the virtual address 0x1234 of that perticular process. Each process get its own virtual address mapping. To get access to a specific physical address you must use the mmap familly of fonctions. These fonctions will map a physical block of memory into the virtual memory of the process.

I asked for the source of the ar_load call to determine how does the function deal with the physical/virtual issue. It seems the fonction is trying to keep its DOS heritage, because under QNX the base_segment variable doesn’t really make sense. There is no real concept of segment under QNX (well there is but you don’t deal with it in most cases). Hence under QNX you usually express the address by its linear address. Segment 0xd000 as understood under DOS is in reality address 0xd0000.

It’s possible the function translates the “DOS segment” into a QNX physical address, then map that address into the virtual address space for latter access.

It’s also possible the fonction needs the physical address, and it will take care of the mappaing.

It’s also possible the function assume you’ve done all the mapping yourself and thus base_segment specifies the virtual address.

Then last it’s possible the source code was ported by someone that didn’t know what they were doing and that’s it’s totaly broken ;-)

I think you solved all my problems (or maybe, a big part of them!)

If the board is loaded in a virtual address, for sure I can’t find it in the base segment.

the problem is that I must set the address setting the jumper on the board, so i MUST load my board in the same address, and not in a viartual address.

I hope it can be made possible.
how can I do tis?

I’m gonna test it…thanks a lot!!

for the PCI Pass through connector…I’m working not with a standard pc but with a series called PC-104 / PC-104Plus. The 104 version use only an isaconnector to connect the variuous boards. the 104 plus use both an Isa and a PCI connector. My Board have Both the connectors but is a pc104 NOT plus becase the PCI connector doen’t really work, but is used to bring connection to other boards (I’m not sure you really understand me, so you chekin’ out the pc104 it’s really a better thing!!)

I know exactly what you are talking about. We use PC-104+ here.

What you want is to investigate is mmap_device_memory(), in the documentation. This will map the physical address of the dual ported memory into your processes virtual address space.


yes, you’re right…i’ve done that but seems it doesn’t work… :frowning: I wonder what I’m missing…

because I’ve got this function:

short ar_loadslv(unsigned int board, unsigned long base_seg, int base_port, unsigned short ram_size);

and this is the call:

status = ar_loadslv(0, 0xd000, 0, 0);

in base_seg i must set the address of the device, in my case 0xd000, but in this way it doesn’t work under qnx.

so I tryed to use the mmap function:

addr = mmap_device_memory(NULL, 4096, PROT_READ|PROT_WRITE|PROT_NOCACHE, 0, 0xd0000);

and so:

status = ar_loadslv(0, addr, 0, 0);

but situation doesn’t change…

at last I tryed to force the address mapped by mmap to the address that I needded in this way:

addr = mmap_device_memory(0xd000, 4096, PROT_READ|PROT_WRITE|PROT_NOCACHE, MAP_FIXED, 0xd0000);

and of course…it doesn’t work…[/code]

Maybe call “ThreadCtl(_NTO_TCTL_IO, 0);” before you call the function ?

short ar_loadslv(unsigned int board, unsigned long base_seg, int base_port, unsigned short ram_size);

This function is expecting a segment. A virtual address is not a segment, and it is easy to imagine code in ar_loadslv that would not work if passed a virtual address in place of a segment.

Do you have the source for ar_loadslv ? If not, you are probably out of luck, since (unless the author of ar_loadslv mis-prototyped/documented the function) it is incapable (as written/documented) of working under a vm system, and must be ported.

If you do have the source to ar_loadslv,you should post that.


[for xtang] No, I never used that… what is?

[for rgallen] I’m not sure i’ve understan all you said but…i’ve not the code for loadslv (too easy to work in this way - Damn!)

but, and if I can’t pass a virtual address, how can I load the board? I mean, how can I know what address pass to the ar_loadslv?

I’ve tried with mmap() but is teh same…

Read the doc on the fonction ThreadCtl()

If you don’t have the source to ar_loadslv, and if it is actually expecting a segment, then you can’t make it work.

If ar_loadslv truly expects a segment then it is not capable of working in an operating system that provides each process a flat memory space. Essentially the concept of segments (selectors) is abstracted out of existance for applications in QNX (although the x86 kernel will be aware of them). Your process is simply presented with a 4GB flat virtual memory space accessed through linear 32 bit pointers.

A thread in a processes can request that some range of this virtual space be backed with some memory at a physical address using mmap_device_memory(), and a virtual address indicating the beginning of this range will be returned. ar_loadslv must be perpared to take an address which is a linear 32 bit pointer.

Without the source (or documentation) it is impossible to know whether ar_loadslv will treat that argument as a simple linear 32 bit pointer, or attempt to manufacture what used to be called a FAR pointer, and (in the process) mangle the address that you are passing in.

If the problem you are having is that mmap_device_memory fails, then (as xtang suggested) make sure that IO privity is enabled for the thread making the call.


sincerly I found the ar_loadslv code but it make no really help because it make calls to function with no code. if you want it i can post it…

 * ENTRY POINT:          A R _ L O A D S L V
 * FUNCTION:    Load and initialize the CEI-x20 dual-port RAM and i960 processor.
 * RETURN VAL:  short -- ARS_NORMAL    Success.
 *                    -- ARS_INVBOARD  The board segment was out of range.
 *                    -- ARS_BADLOAD   The memory test failed and the board was
 *                                     not loaded.
 *                    -- ARS_BAD_FPGA  Bad number of transmitters or receivers,
 *                                     no FPGA support for this config.
 * DESCRIPTION: This routine loads the Slave Control Program into dual-port
 *              RAM.  (The i960 runs it's firmware out of dual-port RAM).
 *              This must be the first utility subroutine executed.
 *      The routine resets the slave then performs a memory check on the dual-
 *      port memory.  If the memory test passes, the slave program is loaded
 *      and started.
 *      Upon successful completion of this routine, the following ARINC defaults
 *      are in effect:  Transmit and receive bit rates are 100K; Parity ODD;
 *      SDI and label filters disabled; Timer rate 5 milliseconds.
#ifdef __TURBOC__
#pragma argsused
EXPORT32 short DLL_EXPORTED ar_loadslv(
   short cardnum,           // (i) Board number associated with this and all
                            //     future references to the board.  Since up to
                            //     MAX_BOARDS are supported simultaneously in one
                            //     host, each board has a unique number.
                            //     Under WIN32 this is the WinRT device number
                            //     and the base_seg is ignored.
   unsigned long base_seg,  // (i) Ignored in WIN32, else is the Base Address of
                            //     the board memory space.
   int base_port,           // (i) Ignored in WIN32, or if non-zero used to
                            //     enter a special FACTORY TEST mode.
   unsigned short ram_size) // (i) unused, should be set to zero.  If non-zero
                            //     used to enter a special FACTORY TEST mode.
   struct CONTROL_DPRAM volatile *ar_base; // Current board pointer.
   int    channel;
   int    status;                  // Device driver and loader function status
#if defined(__WIN32__)
   char      szINIFileName[260];   // Name and path to our .ini file.
   char     *lp;                   // Pointer to .ini file extension.
   char      szSectionInfo[16];    // Lookup board type.
   short dpmi_err_flag = 0;        // Status from ar_map_dualportram()
   //  Check validity of board number.
   if ( (unsigned)cardnum >= MAX_BOARDS )
      return ARS_INVBOARD;

#if defined(__WIN32__) || defined(_UNIX_)
   // Open CEI-x20 low-level driver to map board into our address space.
   //  Since this is a WIN32 application, we will call the kernel driver
   //  open function here.  Check to see if driver is already opened,
   //  then map the memory region and save the memory region pointer.V3.22
   if ( ar_board[cardnum].board_mapped == 0 )
      // If this is a CEI-520 board, it has two mapping regions
      ar_board[cardnum].pMap.flagMapToHost[0] = 1;  // Map the first
      ar_board[cardnum].pMap.flagMapToHost[1] = 1;  //  two memory regions.
      status = (short)vbtMapBoardAddresses(cardnum, &ar_board[cardnum].pMap);
      // CEI-520/620 is not supported by the standard API, see the Enhanced
      //  API ("API520.C") for support.
      if ( ar_board[cardnum].pMap.flagMapToHost[1] )
         return ARS_NO_HW_SUPRT;   // Only the CEI-520/620 maps two regions
         ar_static_base[cardnum] = (void *)ar_board[cardnum].pMap.memHostBase[0];
      if ( status != BTD_OK )
         sprintf(last_error, "Failed to map the CEI-x20 board, "
                             "error %d", status);
         return (short)status;   // Return LOWLEVEL.C error code.
#ifndef _UNIX_
      // Initialize a Critical Section to protect the frame register.

   //  16-bit Windows and DOS applications map board RAM into memory.
   base_seg = ar_map_dualportram (cardnum, base_seg, &dpmi_err_flag, ram_size);
   if ( dpmi_err_flag != 0 )
      return ARS_MEMADERR;
   // Create and save the 16-bit land-style pointer.
   ar_static_base[cardnum] = MK_FP(base_seg,0);
#endif  // endif __WIN32__

   *  Determine the name of our .INI file, by getting from WINDOWS
   *   the name of this .dll file, and replacing the ".DLL" with ".INI"
   * The purpose of this code is to set the factory debug flag during
   *   factory test.  This entire section can be safely discarded when
   *   building the API for non-Condor-factory applications!
#if defined(__WIN32__)
                     (LPSTR)szINIFileName, sizeof(szINIFileName));
   //MessageBox(NULL, szINIFileName, "vbtSetup()", MB_OK | MB_SYSTEMMODAL);
   lp = strrchr(szINIFileName, '.');   // Find the last '.' in the string.
   if ( lp )
      strcpy(lp, ".INI");
   //MessageBox(NULL, szINIFileName, "vbtSetup()", MB_OK | MB_SYSTEMMODAL);

   *  Setup a factory test only test flag.  If the .ini file does not
   *  exist, or this entry does not exist, pass through the callers flag.
   strcpy(szSectionInfo, "Setup Info0");   // Lookup card/carrier type info
   szSectionInfo[10] += (char)cardnum;     //  for the specified card number.
   base_port = GetPrivateProfileInt(szSectionInfo, "EnableTestMode",
                                    base_port, szINIFileName);

   *  This is the end of the section that can be safely discarded when
   *   building the API for non-Condor-factory applications!
   // Board has been commanded to not "GO"
   ar_board[cardnum].board_ar_go = 0;

   // Get the board type (either a CEI-220, CEI-420 or a CEI-420A).
   ar_board[cardnum].board_type = 0;  // Clear the board type
   ar_board[cardnum].ar_inited  = 1;  // So we can read board type
   ar_board[cardnum].board_type = ar_get_boardtype(cardnum);
   if ( ar_board[cardnum].board_type == 0 )
      return ARS_HW_DETECT;

   // W A R N I N G !!  Ugly Code Follows:                                +
   //  Modify the i960 load image...at offset ff90 into dual port.        +
   //  Dual port on the CEI-220/420/420A is 16-bits wide.                 +
#define IBR_BASE  (0xF000/2)
   // Modify the i960 image for CEI-220/420/420A processors.
   i960_data[IBR_BASE+0x0F30/2+4] = 0x0040;
   i960_data[IBR_BASE+0x0F80/2+5] = 0x0040;    // 16-bit wide
   i960_data[IBR_BASE+0x0FB0/2+5] = 0x0040;

   //ar_board[cardnum].ar_inited = 0; // We are not done yet...

   //  Initialize some global info for this board and then load and start it.
   //  i960 comes up in "ar_reset" mode, it does NOT process ARINC data until
   //   "ar_go" is called, but it DOES process the discretes...
   ar_board[cardnum].board_mapped = 1;
   status = loader(cardnum, base_port, ram_size);
   if ( status )
      return (short)status;    // last_error has already been setup...

   //  Initialize i960 receiver and transmitter buffer data structures.     +
   //  The i960 is running at this point, but it is not ar_go()...          +
   ar_board[cardnum].board_mapped = 2;
   ar_board[cardnum].ar_inited = 1;
   ar_base = ar_set_cpage(cardnum);    // Go to control page zero.
   // Clear the timer tick counter.  Firmware will load it on the next ar_go().
   ar_base->TickTimer = 0;
   for ( channel = 0; channel < MAX_CHAN; channel++ )
      ar_base->rcvr[channel].head_ptr = 0;
      ar_base->rcvr[channel].tail_ptr = 0;
      ar_base->rcvr[channel].mask_ptr = ARINC_RX_BUFFER_MASK;
      ar_base->rcvr[channel].num_scheduled = 0;

      ar_base->xmtr[channel].head_ptr = 0;
      ar_base->xmtr[channel].tail_ptr = 0;
      ar_base->xmtr[channel].mask_ptr = ARINC_TX_BUFFER_MASK;
      ar_base->xmtr[channel].num_scheduled = 0;
   return ARS_NORMAL;

OK, I have to assume that since there is no QNX poundef, that UNIX is being defined, and that vbtMapBoardAddresses() is essentially a cover for mmap that performs multiple mappings. It appears that the segment argument is unused in QNX (UNIX), and instead there is a array of structures called ar_board[] that contain this information. It appears that this must be setup prior to calling ar_loadslv(). Could it be that you need to call some other API function ahead of this in a W32 or UNIX environment ?


sincerly I don’t know…I think I’ve try everything is possible…the point is that the code I posted is compiled without errors…i can make the api lib without problems and I can call alle the function inside and everything work…the real problem is that when i call ar_loadslv it says “this function is not supported for this hardware” (or something similar) and it mean that the call of ar_loadslv is not with right args(or better, the args such as base_seg ecc, are not concording with the hardware settings of the jumpers)

so I think is not a problem of the sw but of qnx that load the board in a different memory space which I don’t know how to find via software…

You got it all wrong. QNX doesnt load the board in memory, that concept doesnt exists. Its up to YOUR software to map the board memory into your process space. If you dont tell QNX to do it, it totaly ignores the hardware.

Hope I`m not sounding to rude, but I think you should get some external help. I get the impression this is a little bit above your level of expertise.

Good luck.

mmm I admit, this is my firs work on qnx (and before this, I’ve never work on qnx) so I can’t know much things expecially of the qnx structure (because someone said to me “this is your work. DO IT!!” and nothing more, so I must do it in the way I know)

at this point the problem is that through this forum it’s very hard make you understand (because of my not-right english too) and share useful informations without waste a lot of time.

after what you said, i think it make no sense continue this conversation because the things you might explain to me are too much. :frowning: or better, to make you undestand my problem I might talk to you a lo, explaining everything from the biginning 'cause my prblem is more easy than you think…

sadness… :cry: