Exemplo n.º 1
0
//------------------------------------------------------------------------------
//
//  Function:  CS8900AMulticastList
//
BOOL
CS8900AMulticastList(UINT8 *pAddresses, UINT32 count)
{
    UINT32 i, j, crc, data, bit;

    OALMSGS(OAL_ETHER && OAL_FUNC, (
        L"+RTL8139MulticastList(0x%08x, %d)\r\n", pAddresses, count
    ));

    g_hash[0] = g_hash[1] = g_hash[2] = g_hash[3] = 0;
    for (i = 0; i < count; i++) {
        data = crc = ComputeCRC(pAddresses, 6);
        for (j = 0, bit = 0; j < 6; j++) {
            bit <<= 1;
            bit |= (data & 1);
            data >>= 1;
        }
        g_hash[bit >> 4] |= 1 << (bit & 0x0f);
        pAddresses += 6;
    }

    // But update only if all multicast mode isn't active
    if ((g_filter & PACKET_TYPE_ALL_MULTICAST) == 0) {        
        WritePacketPage(LOGICAL_ADDR_FILTER_BASE + 0, g_hash[0]);
        WritePacketPage(LOGICAL_ADDR_FILTER_BASE + 2, g_hash[1]);
        WritePacketPage(LOGICAL_ADDR_FILTER_BASE + 4, g_hash[2]);
        WritePacketPage(LOGICAL_ADDR_FILTER_BASE + 6, g_hash[3]);
    }

    OALMSGS(OAL_ETHER && OAL_FUNC, (L"-CS8900AMulticastList(rc = 1)\r\n"));

    return (TRUE);
}
Exemplo n.º 2
0
//------------------------------------------------------------------------------
//
//  Function:  CS8900ASendFrame
//
UINT16 CS8900ASendFrame(UINT8 *pData, UINT32 length)
{
    BOOL rc = FALSE;
    UINT32 count;

    OALMSGS(OAL_ETHER&&OAL_VERBOSE, (
        L"+CS8900ASendFrame(0x%08x, %d)\r\n", pData, length
    ));

    // Send Command
    OUTPORT16(&g_pCS8900->TXCMD, TX_CMD_START_ALL);
    OUTPORT16(&g_pCS8900->TXLENGTH, length);

    count = RETRY_COUNT;
    while (count-- > 0) {
        if ((ReadPacketPage(BUS_ST) & BUS_ST_TX_RDY) != 0) break;
    }
    if (count == 0) goto cleanUp;

    length = (length + 1) >> 1;
    while (length-- > 0) {
        OUTPORT16(&g_pCS8900->DATA0, *(UINT16*)pData);
        pData += sizeof(UINT16);
    }

    rc = TRUE;

cleanUp:
    OALMSGS(OAL_ETHER&&OAL_VERBOSE, (L"-CS8900ASendFrame(rc = %d)\r\n", !rc));
    return !rc;
}
Exemplo n.º 3
0
//------------------------------------------------------------------------------
//
//  Function:  CS8900ADisableInts
//
VOID CS8900ADisableInts()
{
    UINT16 data;

    OALMSGS(OAL_ETHER&&OAL_FUNC, (L"+CS8900ADisableInts\r\n"));
    data = ReadPacketPage(BUS_CTL);
    WritePacketPage(BUS_CTL, data & ~BUS_CTL_ENABLE_IRQ);
    OALMSGS(OAL_ETHER&&OAL_FUNC, (L"-CS8900ADisableInts\r\n"));
}
Exemplo n.º 4
0
//------------------------------------------------------------------------------
//
//  Function:  CS8900AGetFrame
//
UINT16 CS8900AGetFrame(UINT8 *pData, UINT16 *pLength)
{
    UINT16 isq, length, status, count, data;

    OALMSGS(OAL_ETHER&&OAL_VERBOSE, (
        L"+CS8900AGetFrame(0x%08x, %d)\r\n", pData, *pLength
    ));

    length = 0;
    isq = INPORT16(&g_pCS8900->ISQ);
    if ((isq & ISQ_ID_MASK) == RX_EVENT_ID && (isq & RX_EVENT_RX_OK) != 0) {

        // Get RxStatus and length
        status = INPORT16(&g_pCS8900->DATA0);
        length = INPORT16(&g_pCS8900->DATA0);

        if (length > *pLength) {
            // If packet doesn't fit in buffer, skip it
            data = ReadPacketPage(RX_CFG);
            WritePacketPage(RX_CFG, data | RX_CFG_SKIP_1);
            length = 0;
        } else {
            // Read packet data
            count = length;
            while (count > 1) {
                data = INPORT16(&g_pCS8900->DATA0);
                *(UINT16*)pData = data;
                pData += sizeof(UINT16);
                count -= sizeof(UINT16);
            }

            // Read last one byte
            if (count > 0) {
                data = INPORT16(&g_pCS8900->DATA0);
                *pData = (UINT8)data;
            }
        }
    }

    // Return packet size
    *pLength = length;

    OALMSGS(OAL_ETHER&&OAL_VERBOSE, (
        L"-CS8900AGetFrame(length = %d)\r\n", length
    ));
    return length;
}
Exemplo n.º 5
0
//------------------------------------------------------------------------------
//
//  Function:  CS8900ACurrentPacketFilter
//
VOID
CS8900ACurrentPacketFilter(UINT32 filter)
{
    UINT16 rxCtl;

    OALMSGS(OAL_ETHER && OAL_FUNC, (
        L"+CS8900ACurrentPacketFilter(0x%08x)\r\n", filter
    ));

    // Read current filter
    rxCtl = ReadPacketPage(RX_CTL);

    if ((filter & PACKET_TYPE_ALL_MULTICAST) != 0) {
        WritePacketPage(LOGICAL_ADDR_FILTER_BASE + 0, 0xFFFF);
        WritePacketPage(LOGICAL_ADDR_FILTER_BASE + 2, 0xFFFF);
        WritePacketPage(LOGICAL_ADDR_FILTER_BASE + 4, 0xFFFF);
        WritePacketPage(LOGICAL_ADDR_FILTER_BASE + 6, 0xFFFF);
    } else {
        WritePacketPage(LOGICAL_ADDR_FILTER_BASE + 0, g_hash[0]);
        WritePacketPage(LOGICAL_ADDR_FILTER_BASE + 2, g_hash[1]);
        WritePacketPage(LOGICAL_ADDR_FILTER_BASE + 4, g_hash[2]);
        WritePacketPage(LOGICAL_ADDR_FILTER_BASE + 6, g_hash[3]);
    }

    if ((filter & PACKET_TYPE_MULTICAST)
    ||  (filter & PACKET_TYPE_ALL_MULTICAST))
    {
        rxCtl |= RX_CTL_MULTICAST;
    } else {
        rxCtl &= ~RX_CTL_MULTICAST;
    }

    if (filter & PACKET_TYPE_PROMISCUOUS)
    {
        rxCtl |= RX_CTL_PROMISCUOUS;
    } else {
        rxCtl &= ~RX_CTL_PROMISCUOUS;
    }

    WritePacketPage(RX_CTL, rxCtl);

    // Save actual filter
    g_filter = filter;

    OALMSGS(OAL_ETHER && OAL_FUNC, (L"-CS8900ACurrentPacketFilter\r\n"));
}
Exemplo n.º 6
0
//------------------------------------------------------------------------------
//
//  Function:  OEMMapMemAddr
//
//  This function maps image relative address to memory address. It is used
//  by boot loader to verify some parts of downloaded image.
//
//  EBOOT mapping depends on download type. Download type is
//  set in OMEMultiBinNotify.
//
UINT8* 
OEMMapMemAddr(
    DWORD image,
    DWORD address
    )
{
    UINT8 *pAddress = NULL;

    OALMSG(OAL_FUNC, (L"+OEMMapMemAddr(0x%08x, 0x%08x)\r\n", image, address));
    
    switch (g_eboot.type) {

        
    case DOWNLOAD_TYPE_XLDR:
    case DOWNLOAD_TYPE_EBOOT:   
	case DOWNLOAD_TYPE_LOGO:
        //  Map to scratch RAM prior to flashing
        pAddress = (UINT8*)g_eboot.region[0].base + (address - image);
        break;

    case DOWNLOAD_TYPE_RAM:            
        //  RAM based NK.BIN and EBOOT.BIN files are given in virtual memory addresses
        pAddress = (UINT8*)address;
        break;

    case DOWNLOAD_TYPE_FLASHNAND:
        pAddress = (UINT8*) (address - NAND_ROMOFFSET);
        break;

	case DOWNLOAD_TYPE_FLASHNOR:
        pAddress = (UINT8*) (address - NOR_ROMOFFSET);
        break;
		
#ifdef IMGMULTIXIP
	case DOWNLOAD_TYPE_EXT:
        pAddress = (UINT8*) (address);
        break;
#endif

    default:
        OALMSG(OAL_ERROR, (L"ERROR: OEMMapMemAddr: "
            L"Invalid download type %d\r\n", g_eboot.type
        ));

    }


    OALMSGS(OAL_FUNC, (L"-OEMMapMemAddr(pAddress = 0x%08x)\r\n", pAddress));
    return pAddress;
}
Exemplo n.º 7
0
//------------------------------------------------------------------------------
//
//  Function:  CS8900ASendFrame
//
UINT16
CS8900ASendFrame(UINT8 *pData, UINT32 length)
{
    UINT32 NumRetries;
    BOOL   rc = TRUE;	// Default to failure code

    OALMSGS(OAL_ETHER && OAL_VERBOSE, (
        L"+CS8900ASendFrame(0x%08x, %d)\r\n", pData, length
    ));

    // Send Command
    OUTPORT16(&g_pCS8900->TXCMD, TX_CMD_START_ALL);
    OUTPORT16(&g_pCS8900->TXLENGTH, length);

    NumRetries = 0;
    while ((ReadPacketPage(BUS_ST) & BUS_ST_TX_RDY) == 0)
    {
        if (NumRetries++ >= RETRY_COUNT)
        {
            OALMSGS(OAL_ERROR, (L"ERROR: CS8900AInit: Not ready for TX\r\n"));
            goto Exit;
        }
    }

    length = (length + 1) >> 1;
    while (length-- > 0)
    {
        OUTPORT16(&g_pCS8900->DATA0, *(UINT16*)pData);
        pData += sizeof *(UINT16*)pData;
    }

    rc = FALSE;		// Success

Exit:
    OALMSGS(OAL_ETHER && OAL_VERBOSE, (L"-CS8900ASendFrame(rc = %d)\r\n", rc));
    return (rc);
}
Exemplo n.º 8
0
//------------------------------------------------------------------------------
//
//  Function:   OEMMultiBinNotify
//
VOID
OEMMultiBinNotify(
    MultiBINInfo *pInfo
    )
{
    BOOL rc = FALSE;
    UINT32 base = OALVAtoPA((UCHAR*)IMAGE_WINCE_CODE_CA);
    UINT32 start, length;
    UINT32 ix;


    OALMSGS(OAL_FUNC, (
        L"+OEMMultiBinNotify(0x%08x -> %d)\r\n", pInfo, pInfo->dwNumRegions
        ));
    OALMSG(OAL_INFO, (
        L"Download file information:\r\n"
        ));
    OALMSG(OAL_INFO, (
        L"-----------------------------------------------------------\r\n"
        ));

    // Copy information to EBOOT structure and set also save address
    g_eboot.numRegions = pInfo->dwNumRegions;
    for (ix = 0; ix < pInfo->dwNumRegions; ix++)
        {
        g_eboot.region[ix].start = pInfo->Region[ix].dwRegionStart;
        g_eboot.region[ix].length = pInfo->Region[ix].dwRegionLength;
        g_eboot.region[ix].base = base;
        base += g_eboot.region[ix].length;
        OALMSG(OAL_INFO, (
            L"[%d]: Address=0x%08x  Length=0x%08x  Save=0x%08x\r\n",
            ix, g_eboot.region[ix].start, g_eboot.region[ix].length,
            g_eboot.region[ix].base
            ));
        }
    OALMSG(OAL_INFO, (
        L"-----------------------------------------------------------\r\n"
        ));

    // Determine type of image downloaded
    if (g_eboot.numRegions > 1) 
        {
        OALMSG(OAL_ERROR, (L"ERROR: MultiXIP image is not supported\r\n"));
        goto cleanUp;
        }

    base = g_eboot.region[0].base;
    start = g_eboot.region[0].start;
    length = g_eboot.region[0].length; 
    
    if (start == IMAGE_XLDR_CODE_PA)
        {
        g_eboot.type = DOWNLOAD_TYPE_XLDR;
        memset(OALPAtoCA(base), 0xFF, length);
        } 
    else if (start == IMAGE_EBOOT_CODE_CA)
        {
        g_eboot.type = DOWNLOAD_TYPE_EBOOT;
        memset(OALPAtoCA(base), 0xFF, length);
        }
    else if (start == (IMAGE_WINCE_CODE_CA + NAND_ROMOFFSET))
        {
        g_eboot.type = DOWNLOAD_TYPE_FLASHNAND;
        memset(OALPAtoCA(base), 0xFF, length);
        } 
	else if (start == (IMAGE_WINCE_CODE_CA + NOR_ROMOFFSET))
        {
        g_eboot.type = DOWNLOAD_TYPE_FLASHNOR;
        memset(OALPAtoCA(base), 0xFF, length);
        } 
	else if (start == 0) // Probably a NB0 file, let's fint out
		{
		// Convert the file name to lower case
		CHAR szFileName[MAX_PATH];
		int i = 0;
		int fileExtPos = 0;

		while ((pInfo->Region[0].szFileName[i] != '\0') && (i < MAX_PATH))
		{
			if((pInfo->Region[0].szFileName[i] >= 'A') && (pInfo->Region[0].szFileName[i] <= 'Z')) 
			{
				szFileName[i] = (pInfo->Region[0].szFileName[i] - 'A' + 'a'); 
			}
			else
			{
				szFileName[i] = pInfo->Region[0].szFileName[i];
			}

			// Keep track of file extension position
			if (szFileName[i] == '.')
			{
				fileExtPos = i;
			}
			i++;
		}

		// Copy string terminator as well
		szFileName[i] = pInfo->Region[0].szFileName[i];

		// Check if we support this file
		if (strncmp(szFileName, LOGO_NB0_FILE, LOGO_NB0_FILE_LEN) == 0)
		{
			// Remap the start address to the correct NAND location of the logo
			g_eboot.region[0].start = IMAGE_XLDR_BOOTSEC_NAND_SIZE + IMAGE_EBOOT_BOOTSEC_NAND_SIZE;
			g_eboot.type = DOWNLOAD_TYPE_LOGO;
		}
		else
		{
		    OALMSG(OAL_ERROR, (L"Unsupported downloaded file\r\n"));
			goto cleanUp;
		}
		}
    else 
        {
        g_eboot.type = DOWNLOAD_TYPE_RAM;
        }

    OALMSG(OAL_INFO, (
        L"Download file type: %d\r\n", g_eboot.type
    ));
    
    rc = TRUE;

cleanUp:
    if (!rc) 
        {
        OALMSG(OAL_ERROR, (L"Spin for ever...\r\n"));
        for(;;);
        }
    OALMSGS(OAL_FUNC, (L"-OEMMultiBinNotify\r\n"));
}
Exemplo n.º 9
0
//------------------------------------------------------------------------------
//
//  Function:  CS8900AGetFrame
//
UINT16
CS8900AGetFrame(UINT8 *pData, UINT16 *pLength)
{
    UINT16 isq, length, status, count, data;
    UINT16 DeviceInterrupt;
    BOOL   GlobalInterrupt;

    OALMSGS(OAL_ETHER && OAL_VERBOSE, (
        L"+CS8900AGetFrame(0x%08x, %d)\r\n", pData, *pLength
    ));

    //
    // This routine could be interrupt driven, or polled.
    // When polled, it keeps the emulator so busy that it
    // starves other threads. The emulator already has the
    // ability to recognize the polling in the bootloader
    // by noting whether the interrupt for this device is
    // disabled. In order to make the emulator recognize it
    // here as well, we need to detect the polling by noting
    // that *global* interrupts are disabled, then disable at
    // the device level as well if so.
    //
    GlobalInterrupt = INTERRUPTS_ENABLE(FALSE);	// Disable to obtain state
    INTERRUPTS_ENABLE(GlobalInterrupt);          // Immediately restore it.

    // Mimic the global interrupt state at the device level too.
    if (!GlobalInterrupt)	// If polled
    {
        // Disable at the device level too so the emulator knows we're polling.
        // Now it can yeild to other threads on the host side.
        DeviceInterrupt = ReadPacketPage(BUS_CTL);
        WritePacketPage(BUS_CTL, DeviceInterrupt & ~BUS_CTL_ENABLE_IRQ);
        DeviceInterrupt &= BUS_CTL_ENABLE_IRQ;
    }
        
    length = 0;
    isq = INPORT16(&g_pCS8900->ISQ);

    if ((isq & ISQ_ID_MASK) == RX_EVENT_ID
    &&  (isq & RX_EVENT_RX_OK) != 0)
    {
        // Get RxStatus and length
        status = INPORT16(&g_pCS8900->DATA0);
        length = INPORT16(&g_pCS8900->DATA0);

        if (length > *pLength)
        {
            // If packet doesn't fit in buffer, skip it
            data = ReadPacketPage(RX_CFG);
            WritePacketPage(RX_CFG, data | RX_CFG_SKIP_1);
            length = 0;
        } else {
            // Read packet data
            count = length;
            while (count > 1)
            {
                data = INPORT16(&g_pCS8900->DATA0);
                *(UINT16*)pData = data;
                pData += sizeof *(UINT16*)pData;
                count -= sizeof *(UINT16*)pData;
            }

            // Read last one byte
            if (count > 0)
            {
                data = INPORT16(&g_pCS8900->DATA0);
                *pData = (UINT8)data;
            }
        }
    }

    if (!GlobalInterrupt)	// If polled
    {
        if (DeviceInterrupt)    // If it was enabled at the device level,
        {
            // Re-enable at the device level.
            CS8900AEnableInts();
        }
    }

    // Return packet size
    *pLength = length;

    OALMSGS(OAL_ETHER && OAL_VERBOSE, (
        L"-CS8900AGetFrame(length = %d)\r\n", length
    ));

    return (length);
}
Exemplo n.º 10
0
//------------------------------------------------------------------------------
//
//  Function:  CS8900AInit
//
BOOL
CS8900AInit(UINT8 *pAddress, UINT32 offset, UINT16 mac[3])
{
    UINT32 NumRetries;
    PBYTE  pBaseIOAddress = NULL;
    UINT32 MemoryBase = 0;  
    BOOL   rc = FALSE;

    OALMSGS(OAL_ETHER && OAL_FUNC, (
        L"+CS8900AInit(0x%08x, 0x%08x, %02x:%02x:%02x:%02x:%02x:%02x)\r\n",
        pAddress, offset, mac[0]&0xFF, mac[0]>>8, mac[1]&0xFF, mac[1]>>8,
        mac[2]&0xFF, mac[2]>>8
    ));

    // Save address
    g_pCS8900 = (CS8900A_REGS*)pAddress;

    // First check if there is chip
    if (ReadPacketPage(EISA_NUMBER) != CS8900A_EISA_NUMBER)
    {
        OALMSGS(OAL_ERROR, (L"ERROR: CS8900AInit: Failed detect chip\r\n"));
        goto Exit;
    }

    OALMSGS(OAL_INFO, (L"INFO: CS8900AInit chip detected\r\n"));

    // Initiate a reset sequence in software.
    WritePacketPage(SELF_CTL, SELF_CTL_RESET);

    // Wait for status to indicate initialization is complete.
    NumRetries = 0;
    while ((ReadPacketPage(SELF_ST) & SELF_ST_INITD) == 0)
    {
        if (NumRetries++ >= RETRY_COUNT)
        {
            OALMSGS(OAL_ERROR, (L"ERROR: CS8900AInit: Timed out during initialization\r\n"));
            goto Exit;
        }
    }

    // Wait for status to indicate the EEPROM is done being accessed/programmed.
    NumRetries = 0;
    while ((ReadPacketPage(SELF_ST) & SELF_ST_SIBUSY) != 0)
    {
        if (NumRetries++ >= RETRY_COUNT)
        {
            OALMSGS(OAL_ERROR, (L"ERROR: CS8900AInit: Timed out (EEPROM stayed busy)\r\n"));
            goto Exit;
        }
    }

    pBaseIOAddress   = (PBYTE)OALPAtoVA(pBSPArgs->kitl.devLoc.LogicalLoc, FALSE);
    MemoryBase       = (UINT32)OALPAtoVA(BSP_BASE_REG_PA_CS8900A_MEMBASE, FALSE);
    
    // Initialize the Ethernet controller, set MAC address
    //
    if (!CS8900DBG_Init((PBYTE)pBaseIOAddress, MemoryBase, mac))
    {
        OALMSG(OAL_ERROR, (TEXT("ERROR: InitEthDevice: Failed to initialize Ethernet controller.\r\n")));
        goto Exit;
    }

    // Enable receive
    WritePacketPage(RX_CTL, RX_CTL_RX_OK | RX_CTL_INDIVIDUAL | RX_CTL_BROADCAST);

    // Enable interrupt on receive
    WritePacketPage(RX_CFG, RX_CFG_RX_OK_IE);

    // Configure the hardware to use INTRQ0
    WritePacketPage(INTERRUPT_NUMBER, 0);

    // Enable
    WritePacketPage(LINE_CTL, LINE_CTL_RX_ON | LINE_CTL_TX_ON);

    // Make sure MAC address has been programmed.
    //
    if (!pBSPArgs->kitl.mac[0] && !pBSPArgs->kitl.mac[1] && !pBSPArgs->kitl.mac[2])
    {
        OALMSG(OAL_ERROR, (TEXT("ERROR: InitEthDevice: Invalid MAC address.\r\n")));
        goto Exit;
    }

    // Done
    rc = TRUE;

Exit:
    OALMSGS(OAL_ETHER && OAL_FUNC, (L"-CS8900AInit(rc = %d)\r\n", rc));
    return rc;
}
Exemplo n.º 11
0
//------------------------------------------------------------------------------
//
//  Function:  CS8900AInit
//
BOOL CS8900AInit(UINT8 *pAddress, UINT32 offset, UINT16 mac[3])
{
    BOOL rc = FALSE;
    UINT32 count;

    OALMSGS(OAL_ETHER&&OAL_FUNC, (
        L"+CS8900AInit(0x%08x, 0x%08x, %02x:%02x:%02x:%02x:%02x:%02x)\r\n",
        pAddress, offset, mac[0]&0xFF, mac[0]>>8, mac[1]&0xFF, mac[1]>>8,
        mac[2]&0xFF, mac[2]>>8
    ));

    // Save address
    g_pCS8900 = (CS8900A_REGS*)pAddress;

    // First check if there is chip
    if (ReadPacketPage(EISA_NUMBER) != CS8900A_EISA_NUMBER) {
        OALMSGS(OAL_ERROR, (L"ERROR: CS8900AInit: Failed detect chip\r\n"));
        goto cleanUp;
    }

    OALMSGS(OAL_INFO, (L"INFO: CS8900AInit chip detected\r\n"));

    // Reset chip
    WritePacketPage(SELF_CTL, SELF_CTL_RESET);
    count = RETRY_COUNT;
    while (count-- > 0) {
        if ((ReadPacketPage(SELF_ST) & SELF_ST_INITD) != 0) break;
    }
    if (count == 0) {
        OALMSGS(OAL_ERROR, (L"ERROR: CS8900AInit: Failed to reset card\r\n"));
        goto cleanUp;
    }
    count = RETRY_COUNT;
    while (count-- > 0) {
        if ((ReadPacketPage(SELF_ST) & SELF_ST_SIBUSY) != 0) break;
    }
    if (count == 0) {
        OALMSGS(OAL_ERROR, (L"ERROR: CS8900AInit: Failed to reset card\r\n"));
        goto cleanUp;
    }

    // Set MAC address
    WritePacketPage(INDIVIDUAL_ADDRESS + 0, mac[0]);
    WritePacketPage(INDIVIDUAL_ADDRESS + 2, mac[1]);
    WritePacketPage(INDIVIDUAL_ADDRESS + 4, mac[2]);

    // Enable receive
    WritePacketPage(RX_CTL, RX_CTL_RX_OK|RX_CTL_INDIVIDUAL|RX_CTL_BROADCAST);

    // Enable interrupt on receive
    WritePacketPage(RX_CFG, RX_CFG_RX_OK_IE);

    // Let assume that hardware is using INTRQ0
    WritePacketPage(INTERRUPT_NUMBER, 0);

    // Enable
    WritePacketPage(LINE_CTL, LINE_CTL_RX_ON|LINE_CTL_TX_ON);

    // Done
    rc = TRUE;

cleanUp:
    OALMSGS(OAL_ETHER&&OAL_FUNC, (L"-CS8900AInit(rc = %d)\r\n", rc));
    return rc;
}