none
XXX_Read help RRS feed

  • Question

  • Hello,

    I believe I am having trouble with my OCT_Read function.  This is what it looks like:

    DWORD OCT_Read(DWORD hOpenContext, LPVOID pBuffer, DWORD Count)
    {
    	unsigned char * dpr = (unsigned char *)0xd9200000;
    
    	// Implement the code to read from the stream device here.
    	printf("OCT_Read()\n");
    
    	//As a hack, lets see if I can read from d920 0000.
    	((unsigned char *)pBuffer)[0] = dpr[0];
        return Count;
    }

    I am using 0xd9200000 because that is what is in the registry at

    Drivers\BuiltIn\PCI\Instance\OCT1\MemBase

    (I know for production I will have to read it each time, but for testing I am hard-coding it.  I verified that this was the address being used.)

    This is how my subproject uses the driver:

    hDriver = CreateFile(L"OCT1:",
                    GENERIC_READ| GENERIC_WRITE,
                    0,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL,
                    NULL);
    
    if (hDriver == INVALID_HANDLE_VALUE) {
        printf("Unable to open driver\n");
    
    	dwError = GetLastError();
    	FormatMessage(	FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                    NULL,
                    dwError,
                    0,
                    (LPTSTR)&gleBuf,
                    100,	//want 100 chars minimum
                    NULL);
    
    	printf("Failed with error %d: %s\n", dwError, gleBuf);
    	return -1;
    }
    
    //Insert code that uses the driver here
    printf("We can use the driver.\n\n");
    
    //Calling ReadFile will cause the driver's XXX_Read()
    //function to be called.
    bRet = ReadFile(hDriver,
                rBuf,
                numReadBytes,
                &numActuallyRead,
                NULL);
    if (bRet) {
    	printf("ReadFile succeeded!\n");
    	printf("\t0x%02x\n", rBuf[0]);
    	printf("\t0x%02x\n", rBuf[1]);
    	printf("\t0x%02x\n", rBuf[2]);
    	printf("\t0x%02x\n", rBuf[3]);
    	printf("\t0x%02x\n", rBuf[4]);
    	printf("\t0x%02x\n", rBuf[5]);
    	printf("\t0x%02x\n", rBuf[6]);
    	printf("\t0x%02x\n", rBuf[7]);
    	printf("\t0x%02x\n", rBuf[8]);
    	printf("\t0x%02x\n", rBuf[9]);
    }
    else {
    	printf("Unable to read from device.\n");
    
    	dwError = GetLastError();
    	FormatMessage(	FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                    NULL,
                    dwError,
                    0,
                    (LPTSTR)&gleBuf,
                    100,	//want 100 chars minimum
                    NULL);
    
    	printf("Failed with error %d: %s\n", dwError, gleBuf);
    	return -1;
    }

    What I get printed is this:

    We can use the driver.
    
    Unable to read from device.
    Failed with error 87: (null)

    Error 87 is "The parameter is incorrect."

    But if I comment out

    ((unsigned char *)pBuffer)[0] = dpr[0];
    then I don't get the error.  Of course, then nothing useful is returned.

    How am I supposed to write anything to pBuffer?

     

    Thanks.

    Friday, July 2, 2010 5:38 PM

Answers

  • Some of the PCI devices may have more than one BAR (Base Address Register), you could check the DDKWINDOWINFO::dwNumMemWindows to determine how many valid memory base address. Also the RAM on the device may need to initialized before use, you should be able to figure out these information in the datasheet of the chip/device.
    • Marked as answer by mr_jeff Wednesday, July 7, 2010 2:02 PM
    Wednesday, July 7, 2010 5:41 AM

All replies

  • Firstly, I recommend to use RETAILMSG or other equivalent Debug Message macros instead of using printf.
    And for the problem, the 0xd9200000 is a physical address but in CE, you need to access memory via Virtual Address. You need to map the PA to VA first and use the mapped VA for accessing. To map a PA, you can use MmMapIoSpace API (http://msdn.microsoft.com/en-us/library/ee481674.aspx)
    Friday, July 2, 2010 8:41 PM
  • The device manager will set ERROR_INVALID_PARAMETER if there's an exception in your XXX_Read function, which is what is happening due to the physical/virtual address mistranslation as explained by KMOS

    --
    Luca Calligaris (MVP-Windows Embedded)
    lucaDOTcalligarisATeurotechDOTcom
    www.eurotech.com
     
     
    "K M O S" <=?utf-8?B?SyBNIE8gUw==?=> ha scritto nel messaggio news:2a0d8002-2bbf-43d6-b7bd-c7e71cc078ff...
    Firstly, I recommend to use RETAILMSG or other equivalent Debug Message macros instead of using printf.
    And for the problem, the 0xd9200000 is a physical address but in CE, you need to access memory via Virtual Address. You need to map the PA to VA first and use the mapped VA for accessing. To map a PA, you can use MmMapIoSpace API (http://msdn.microsoft.com/en-us/library/ee481674.aspx)

    Luca Calligaris (MVP-Windows Embedded) lucaDOTcalligarisATeurotechDOTcom www.eurotech.com
    Tuesday, July 6, 2010 8:57 AM
  • Firstly, I recommend to use RETAILMSG or other equivalent Debug Message macros instead of using printf.
    And for the problem, the 0xd9200000 is a physical address but in CE, you need to access memory via Virtual Address. You need to map the PA to VA first and use the mapped VA for accessing. To map a PA, you can use MmMapIoSpace API (http://msdn.microsoft.com/en-us/library/ee481674.aspx )

    Thanks.  I think I am headed in the right direction now.  My code looks like this:

    //What I'd like to do here is store MemBase and MemLen. I can
    //figure these out in OCT_Init and then I'd like to use them in
    //OCT_Read.
    long gMemBase;   //global variable
    
    
    ////////////////////////////////////////////////////////////////
    // OCT_Init function (code fragment from it)
    ///////////////////////////////////////////////////////////////
    DWORD OCT_Init(LPCTSTR pContext, LPCVOID lpvBusContext)
    {
    	HKEY hKey;
    	DDKWINDOWINFO wi;
    	PHYSICAL_ADDRESS PortAddress;
    	HANDLE hBusAccess;
    	WCHAR dbgBuf[100];
    	DWORD inIoSpace = 0;
    	PVOID pvRegisters;
    
    	hKey = OpenDeviceKey(pContext);
    
    	//What I am trying to do now is set things up for the
    	//OCT_Read function. I think the first step is to call 
    	//DDKReg_GetWindowInfo to get information about
    	//mapping my memory windows.
    	wi.cbSize = sizeof(wi);
    	retCode = DDKReg_GetWindowInfo(hKey, &wi);
    	if (retCode == ERROR_SUCCESS) {
    		OutputDebugStringW(_T("DDKWINDOWINFO structure successfully populated.\n"));
    		wsprintf(dbgBuf, _T("There are %d mem windows.\n"), wi.dwNumMemWindows);
    		OutputDebugStringW(dbgBuf);
    	}
    
    	//The next step would be to create a handle.
    	hBusAccess = CreateBusAccessHandle(pContext);
    
    	//Am I now ready to translate a bus address to a virtual
    	//system address? The value 0xd9200000 in MemBase
    	//(in registry) is a physical address, which I can't use. I
    	//need a virtual address.
    	PortAddress.LowPart = (wi.memWindows[0]).dwBase;
        PortAddress.HighPart = 0;
    	bRetVal = BusTransBusAddrToVirtual(	hBusAccess,
    								(INTERFACE_TYPE) wi.dwInterfaceType,
    								wi.dwBusNumber,
    								PortAddress,
    								(wi.memWindows[0]).dwLen,
    								&inIoSpace,
    								&pvRegisters);
    
    	if (bRetVal) {
    		OutputDebugStringW(_T("BusTransBusAddrToVirtual SUCCESSFUL!\n"));
    
    		//Let me make sure CE thinks this is memory and
    		//not I/O space.
    		if (inIoSpace == 1)
    			OutputDebugStringW(_T("I/O space."));
    		if (inIoSpace == 0) {
    			wsprintf(dbgBuf, _T("Memory starts at %d"), (unsigned long)(pvRegisters));
    			OutputDebugStringW(dbgBuf);
    
    			//I'll save this virtual address so that I can use it 
    			//from OCT_Read.
    			gMemBase = (unsigned long)(pvRegisters);
    		}
    	}
    }
    
    //////////////////////////////////////////////////////////////
    //OCT_Read
    //////////////////////////////////////////////////////////////
    DWORD OCT_Read(DWORD hOpenContext, LPVOID pBuffer, DWORD Count)
    {
    	unsigned char * w_dpr = (unsigned char *)gMemBase;
    	unsigned char * dpr = (unsigned char *)gMemBase;
    
    	// Implement the code to read from the stream device here.
    	printf("OCT_Read()\n");
    
    	//If I write this here, I can see it from my subproject1.exe
    	//application.
    	w_dpr[0] = 'j';
    
    	((unsigned char *)pBuffer)[0] = dpr[0];
    
    	return Count;
    }
    Now if I use one array to write a value to the beginning of my memory, I can read it back just fine.  However, I am not sure this is a good test.  I know for a fact that my memory is getting initialized (by a chip on the board) to a certain value.  For example, at offset 1 into my memory I should see 'R' and I never do.  So I am still missing something, but am I on the right track?

    Tuesday, July 6, 2010 3:29 PM
  • Is this memory address map to a RAM like memory in your PCI device?
    If that is a register (flip-flop), it is possible some of the register is read-only.
    But assume it is writable and just like RAM, you should consider to declare the pointer with volatile keyword or use READ_REGISTER_UCHAR http://msdn.microsoft.com/en-us/library/ee483124.aspx to access the memory.

    Tuesday, July 6, 2010 7:29 PM
  • Is this memory address map to a RAM like memory in your PCI device?
    If that is a register (flip-flop), it is possible some of the register is read-only.
    But assume it is writable and just like RAM, you should consider to declare the pointer with volatile keyword or use READ_REGISTER_UCHAR http://msdn.microsoft.com/en-us/library/ee483124.aspx  to access the memory.


    Yes, the memory on my device is a type of RAM.  In my target PC I have a DOS application that reads and writes to it.  I use this to verify it is in the machine ok, etc.  Also, we have a driver for this in 2000/XP and we can do reads and writes to the RAM.

    I tried adding the keyword "volatile" to PVOID pvRegisters but that didn't change anything.

     

    I also did the READ_REGISTER_UCHAR suggestion.  I changed the code like this:

    UCHAR * gMemBase;      //global variable
    
    gMemBase = (UCHAR*) pvRegisters;  //line from OCT_Init
    
    DWORD OCT_Read(DWORD hOpenContext, LPVOID pBuffer, DWORD Count)
    {
    	unsigned char * dpr = (unsigned char *)gMemBase;
    
    	// Implement the code to read from the stream device here.
    	printf("OCT_Read()\n");
    
    	((unsigned char *)pBuffer)[0] = READ_REGISTER_UCHAR(gMemBase);
    	((unsigned char *)pBuffer)[1] = READ_REGISTER_UCHAR(gMemBase++);
    	((unsigned char *)pBuffer)[2] = READ_REGISTER_UCHAR(gMemBase++);
    	((unsigned char *)pBuffer)[3] = READ_REGISTER_UCHAR(gMemBase++);
    	((unsigned char *)pBuffer)[4] = READ_REGISTER_UCHAR(gMemBase++);
    	((unsigned char *)pBuffer)[5] = READ_REGISTER_UCHAR(gMemBase++);
    	((unsigned char *)pBuffer)[6] = READ_REGISTER_UCHAR(gMemBase++);
    	((unsigned char *)pBuffer)[7] = READ_REGISTER_UCHAR(gMemBase++);
    	((unsigned char *)pBuffer)[8] = READ_REGISTER_UCHAR(gMemBase++);
    
    	return Count;
    }
    But alas, I still get 0x00 when reading.  Is there anything else I can check?  Maybe something is wrong with gMemBase?

    Tuesday, July 6, 2010 8:04 PM
  • You can't add volatile when you use PVOID and pvRegisters for three reasons; it is in the wrong place, so it makes the variable a volitle pointer to static memory instead of a static pointer to volatile memory (see http://geekswithblogs.net/BruceEitman/archive/2008/06/30/windows-ce-volatile-and-pbyte-pdword.aspx), volatile and void don't make much sense together and finally you aren't reading using pvRegisters so again doesn't make sense.

    Now volatile UCHAR *gMemBase makes a lot of sense.  So does volatile unsigned char *dpr in your original code.

    Are you sure that the memory map of your device has this RAM at the base of the PCI address?  It isn't offset any?

    Have you tried scoping the address/data lines to see if anything is happening when you call ReadFile?


    Bruce Eitman (eMVP)
    Senior Engineer
    Bruce.Eitman AT Eurotech DOT com
    My BLOG http://geekswithblogs.net/bruceeitman

    Eurotech Inc.
    www.Eurotech.com
    Tuesday, July 6, 2010 9:31 PM
    Moderator
  • Are you sure that the memory map of your device has this RAM at the base of the PCI address?  It isn't offset any?

    Excellent point.  It probably is offset.  I see that the DOS code that has success with this memory is working with an offset, but it manages to figure it out by reading it from somewhere.  Where would I get this offset value from?  Also, is there anything wrong with the registry entry MemLen being 80?  I know I have much more ram than that.

     

    Thanks,

     

     

    Jeff

    Tuesday, July 6, 2010 9:52 PM
  • Some of the PCI devices may have more than one BAR (Base Address Register), you could check the DDKWINDOWINFO::dwNumMemWindows to determine how many valid memory base address. Also the RAM on the device may need to initialized before use, you should be able to figure out these information in the datasheet of the chip/device.
    • Marked as answer by mr_jeff Wednesday, July 7, 2010 2:02 PM
    Wednesday, July 7, 2010 5:41 AM
  • Some of the PCI devices may have more than one BAR (Base Address Register), you could check the DDKWINDOWINFO::dwNumMemWindows to determine how many valid memory base address.

    Yes, there are two memory windows.  I marked your response as Answer because I can now read what I am expecting to read.  But as a general comment/question, can someone explain why the registry doesn't have more than one MemBase?  It looks like the first memory window, wi.memWindows[0], is not the RAM I was looking for.  Is it possible then that this is of size 80 and is what is being referred to by the registry keys

    HKLM->Drivers->BuiltIn->PCI->Instance->OCT1

                  MemBase: d9200000

                  MemLen: 80

    And since the RAM I really want is wi.memWindows[1 ], does information about this ram NOT appear in the registry?

    Wednesday, July 7, 2010 2:15 PM
  • The first question is how are your checking the registry?  MemBase is a REG_MULTISZ which can have multiple strings seperated by a NULL character.

    Next, does that key have an IOBase also?

    BTW, the the fact that DDKReg_GetWindowInfo() returns multiple windows indicates that the data is in the registry.  You can check the source code for DDKReg_GetWindowInfo(), it is under the CE 6.0 PRIVATE tree in private\winceos\drivers\devload\memcfg.cpp


    Bruce Eitman (eMVP)
    Senior Engineer
    Bruce.Eitman AT Eurotech DOT com
    My BLOG http://geekswithblogs.net/bruceeitman

    Eurotech Inc.
    www.Eurotech.com
    Wednesday, July 7, 2010 2:48 PM
    Moderator
  • I am using the Remote Registry Viewer to check the registry.  And now I just noticed that by right-clicking and selecting Modify, I can see both values.  (I didn't know you could separate MULTISZ's with NULL.  I've made them before but used commas to separate.  Like, with the DeviceID you can separate many by commas.)

    There is no IOBase in this key.

    I guess things make sense now.  Thanks for your reply as well.

    Wednesday, July 7, 2010 4:56 PM
  • Don't confuse your input with how the data is actually stored.
    Bruce Eitman (eMVP)
    Senior Engineer
    Bruce.Eitman AT Eurotech DOT com
    My BLOG http://geekswithblogs.net/bruceeitman

    Eurotech Inc.
    www.Eurotech.com
    Wednesday, July 7, 2010 5:05 PM
    Moderator