// SPDX-License-Identifier: MIT /************************************************************************************** * * Copyright (c) 2024, Hilscher Gesellschaft fuer Systemautomation mbH. All Rights Reserved. * * Description: Linux specific implementation for finding firmware files, etc. * * Changes: * * Version Date Author Description * ---------------------------------------------------------------------------------- * 1 02.01.24 SD changed licensing terms * **************************************************************************************/ #include "cifXToolkit.h" #include #include #include #include #include #include #include #include #include #include #include #include "WarmstartFile.h" #include "cifxlinux_internal.h" char* g_szDriverBaseDir = NULL; /*!< Global base path to driver/cifX configuration data */ static const char* DEVICE_CONF_ALIAS_KEY = "alias="; static const char* DEVICE_CONF_IRQ_KEY = "irq="; static const char* DEVICE_CONF_IRQPRIO_KEY = "irqprio="; static const char* DEVICE_CONF_IRQSCHED_KEY = "irqsched="; #ifdef CIFX_TOOLKIT_DMA static const char* DEVICE_CONF_DMA = "dma="; #endif #ifdef CIFXETHERNET static const char* DEVICE_CONF_ETH = "eth="; #endif uint8_t severity_mapping[] = { 0xff, /* not used */ 1, /* TRACE_LEVEL_DEBUG */ 2, /* TRACE_LEVEL_INFO */ 0xff, /* not used */ 4, /* TRACE_LEVEL_WARNING */ 0xff, /* not used */ 0xff, /* not used */ 0xff, /* not used */ 8, /* TRACE_LEVEL_ERROR */ }; /*****************************************************************************/ /*! Print a trace message from cifX toolkit * \param ptDevInstance Device instance the trace is coming from * \param ulTraceLevel see TRACE_LVL_XXX defines * \param szFormat printf style format string * \param ... printf arguments */ /*****************************************************************************/ void USER_Trace(PDEVICEINSTANCE ptDevInstance, uint32_t ulTraceLevel, const char* szFormat, ...) { PCIFX_DEVICE_INTERNAL_T internaldev = (PCIFX_DEVICE_INTERNAL_T)ptDevInstance->pvOSDependent; va_list vaList; struct timeval time; struct tm *local_tm; gettimeofday(&time, NULL); local_tm = localtime(&time.tv_sec); va_start(vaList, szFormat); if(NULL != internaldev->log_file) { fprintf(internaldev->log_file, "<%u> %.2d.%.2d.%.4d %.2d:%.2d:%.2d.%.3ld.%.3ld: ", severity_mapping[ulTraceLevel], local_tm->tm_mday, local_tm->tm_mon + 1, local_tm->tm_year + 1900, local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec, time.tv_usec / 1000, time.tv_usec % 1000); /* log file is given, so add this trace to our logfile */ vfprintf(internaldev->log_file, szFormat, vaList); fprintf(internaldev->log_file, "\n"); fflush(internaldev->log_file); } else { printf("<%u> %.2d.%.2d.%.4d %.2d:%.2d:%.2d.%.3ld.%3ld: ", severity_mapping[ulTraceLevel], local_tm->tm_mday, local_tm->tm_mon + 1, local_tm->tm_year + 1900, local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec, time.tv_usec / 1000, time.tv_usec % 1000); /* No logfile, so print to console */ vprintf(szFormat, vaList); printf("\n"); } UNREFERENCED_PARAMETER(ulTraceLevel); } #define PARSER_BUFFER_SIZE 1024 char* ToLowerCase( char* str, int len) { int i = 0; if (str != NULL) { while ((str[i] != 0) && (iptDeviceInstance->ulSlotNumber; /* if the rotary switch is set != 0 */ if (ulSlotNr) { snprintf(szPath, iPathLen, "%s/deviceconfig/Slot_%d/channel%d/", g_szDriverBaseDir, (unsigned int)ulSlotNr, (unsigned int)ptDevInfo->ulChannel); if (path_exists(szPath)==0) goto exit; } snprintf(szPath, iPathLen, "%s/deviceconfig/%d/%d/channel%d/", g_szDriverBaseDir, (unsigned int)ptDevInfo->ulDeviceNumber, (unsigned int)ptDevInfo->ulSerialNumber, (unsigned int)ptDevInfo->ulChannel); if (path_exists(szPath)==0) goto exit; snprintf(szPath, iPathLen, "%s/deviceconfig/%s/channel%d/", g_szDriverBaseDir, ptDevInfo->ptDeviceInstance->szName, (unsigned int)ptDevInfo->ulChannel); if (path_exists(szPath)==0) goto exit; snprintf(szPath, iPathLen, "%s/deviceconfig/FW/channel%d/", g_szDriverBaseDir, (unsigned int)ptDevInfo->ulChannel); exit: return; } /*****************************************************************************/ /*! Internal helper function returning the path to a channel directory * on the given device (e.g. /opt/cifx/deviceconfig/1250100/20004/) * \param szPath Pointer to returned path * \param iPathLen Length of the buffer passed in szPath * \param ptDevInfo Device information (DeviceNr, SerialNr, ChannelNr)*/ /*****************************************************************************/ static void GetDeviceDir(char* szPath, size_t iPathLen,PCIFX_DEVICE_INFORMATION ptDevInfo) { uint32_t ulSlotNr = ptDevInfo->ptDeviceInstance->ulSlotNumber; /* if the rotary switch is set != 0 */ if (ulSlotNr) { snprintf(szPath, iPathLen, "%s/deviceconfig/Slot_%d/", g_szDriverBaseDir, (unsigned int)ulSlotNr); if (path_exists(szPath)==0) goto exit; } snprintf(szPath, iPathLen, "%s/deviceconfig/%d/%d/", g_szDriverBaseDir, (unsigned int)ptDevInfo->ulDeviceNumber, (unsigned int)ptDevInfo->ulSerialNumber); if (path_exists(szPath)==0) goto exit; snprintf(szPath, iPathLen, "%s/deviceconfig/%s/", g_szDriverBaseDir, ptDevInfo->ptDeviceInstance->szName); if (path_exists(szPath)==0) goto exit; snprintf(szPath, iPathLen, "%s/deviceconfig/FW/", g_szDriverBaseDir); exit: return; } /*****************************************************************************/ /*! Returns the number of firmware files to be downloaded on the given * device/channel * \param ptDevInfo Device information (DeviceNr, SerialNr, ChannelNr) * \return Number of files (used for USER_GetFirmwareFile() */ /*****************************************************************************/ uint32_t USER_GetFirmwareFileCount(PCIFX_DEVICE_INFORMATION ptDevInfo) { char szPath[CIFX_MAX_FILE_NAME_LENGTH]; unsigned long ulRet = 0; DIR* dir; GetChannelDir(szPath, sizeof(szPath), ptDevInfo); if(NULL != (dir = opendir(szPath))) { struct dirent* dirent; while(NULL != (dirent = readdir(dir))) { char* szExt = strstr(dirent->d_name, "."); if(NULL != szExt) { if( (0 == strncasecmp(szExt, HIL_FILE_EXTENSION_FIRMWARE, 4)) || (0 == strncasecmp(szExt, HIL_FILE_EXTENSION_OPTION, 4)) ) { ++ulRet; } } } closedir(dir); } return ulRet; } /*****************************************************************************/ /*! Returns filename of the file to be downloaded on the given device/channel * \param ptDevInfo Device information (DeviceNr, SerialNr, ChannelNr) * \param ulIdx Number of the file (0..USER_GetFirmwareFileCount) * \param ptFileInfo Full and short filename of the file * \return !=0 on success */ /*****************************************************************************/ int USER_GetFirmwareFile(PCIFX_DEVICE_INFORMATION ptDevInfo, uint32_t ulIdx, PCIFX_FILE_INFORMATION ptFileInfo) { char szPath[CIFX_MAX_FILE_NAME_LENGTH]; int ret = 0; DIR* dir; GetChannelDir(szPath, sizeof(szPath), ptDevInfo); if(NULL != (dir = opendir(szPath))) { struct dirent* dirent; unsigned long ulFile = 0; while(NULL != (dirent = readdir(dir))) { char* szExt = strstr(dirent->d_name, "."); if(NULL != szExt) { if( (0 == strncasecmp(szExt, HIL_FILE_EXTENSION_FIRMWARE, 4)) || (0 == strncasecmp(szExt, HIL_FILE_EXTENSION_OPTION, 4)) ) { if(ulFile++ == ulIdx) { snprintf(ptFileInfo->szFullFileName, sizeof(ptFileInfo->szFullFileName), "%s/%s", szPath, dirent->d_name); strncpy(ptFileInfo->szShortFileName, dirent->d_name, sizeof(ptFileInfo->szShortFileName)); ret = 1; break; } } } } closedir(dir); } return ret; } /*****************************************************************************/ /*! Returns the number of configuration files to be downloaded on the given * device/channel * \param ptDevInfo Device information (DeviceNr, SerialNr, ChannelNr) * \return Number of files (used for USER_GetConfigurationFile() */ /*****************************************************************************/ uint32_t USER_GetConfigurationFileCount(PCIFX_DEVICE_INFORMATION ptDevInfo) { char szPath[CIFX_MAX_FILE_NAME_LENGTH]; unsigned long ulRet = 0; DIR* dir; GetChannelDir(szPath, sizeof(szPath), ptDevInfo); if(NULL != (dir = opendir(szPath))) { struct dirent* dirent; while(NULL != (dirent = readdir(dir))) { char* szExt = strstr(dirent->d_name, "."); if(NULL != szExt) { if(0 == strncasecmp(szExt, HIL_FILE_EXTENSION_DATABASE, 4)) { ++ulRet; } } } closedir(dir); } return ulRet; } /*****************************************************************************/ /*! Returns filename of the file to be downloaded on the given device/channel * \param ptDevInfo Device information (DeviceNr, SerialNr, ChannelNr) * \param ulIdx Number of the file (0..USER_GetConfigurationFileCount) * \param ptFileInfo Full and short filename of the file * \return !=0 on success */ /*****************************************************************************/ int USER_GetConfigurationFile(PCIFX_DEVICE_INFORMATION ptDevInfo, uint32_t ulIdx, PCIFX_FILE_INFORMATION ptFileInfo) { char szPath[CIFX_MAX_FILE_NAME_LENGTH]; int ret = 0; DIR* dir; GetChannelDir(szPath, sizeof(szPath), ptDevInfo); if(NULL != (dir = opendir(szPath))) { struct dirent* dirent; unsigned long ulFile = 0; while(NULL != (dirent = readdir(dir))) { char* szExt = strstr(dirent->d_name, "."); if(NULL != szExt) { if(0 == strncasecmp(szExt, HIL_FILE_EXTENSION_DATABASE, 4)) { if(ulFile++ == ulIdx) { snprintf(ptFileInfo->szFullFileName, sizeof(ptFileInfo->szFullFileName), "%s/%s", szPath, dirent->d_name); strncpy(ptFileInfo->szShortFileName, dirent->d_name, sizeof(ptFileInfo->szShortFileName)); ret = 1; break; } } } } closedir(dir); } return ret; } /*****************************************************************************/ /*! Get warmstart parameters (for slave devices to be configured at startup) * This is usually done by the user application, and not the toolkit * \param ptDevInfo Device information (DeviceNr, SerialNr, ChannelNr) * \param ptPacket Packet to send after starting the firmware * \return !=0 if a packet is available */ /*****************************************************************************/ int USER_GetWarmstartParameters(PCIFX_DEVICE_INFORMATION ptDevInfo, CIFX_PACKET* ptPacket) { int ret = 0; char szFile[CIFX_MAX_FILE_NAME_LENGTH]; void* pvFile; uint32_t ulFileLen; GetChannelDir(szFile, sizeof(szFile), ptDevInfo); strcat(szFile, "warmstart.dat"); pvFile = OS_FileOpen(szFile, &ulFileLen); if ( (pvFile == NULL) || (ulFileLen < sizeof(CIFX_WS_FILEHEADER)) ) { if (errno != ENOENT) /* do not print an error if file does not exists */ { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace(ptDevInfo->ptDeviceInstance, TRACE_LEVEL_ERROR, "Error opening warm start file! %s", strerror(errno)); } } } else { CIFX_WS_FILEHEADER tHeader = {0}; /* Read file header */ if (OS_FileRead( pvFile, 0, sizeof(tHeader), &tHeader) != sizeof(tHeader)) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace(ptDevInfo->ptDeviceInstance, TRACE_LEVEL_ERROR, "Error reading from warm start file!"); } } else if( tHeader.ulCookie != CIFX_WS_WARMSTART_FILE_COOKIE) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace(ptDevInfo->ptDeviceInstance, TRACE_LEVEL_ERROR, "Invalid warm start file cookie!"); } } else if( tHeader.ulDataLen > sizeof(*ptPacket)) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace(ptDevInfo->ptDeviceInstance, TRACE_LEVEL_ERROR, "Invalid warm start file length!"); } } else { /* Read file data */ if (OS_FileRead( pvFile, sizeof(tHeader), tHeader.ulDataLen, (void*)ptPacket) != tHeader.ulDataLen) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace(ptDevInfo->ptDeviceInstance, TRACE_LEVEL_ERROR, "Error reading user data from warm start file!"); } } else { ret = 1; } } if( NULL != pvFile) OS_FileClose(pvFile); } return ret; } /*****************************************************************************/ /*! Retrieve the full and short filename of a bootloader file to download to * the given device * \param ptDevInstance Device Instance containing all device data * \param ptFileInfo Pointer to returned file information */ /*****************************************************************************/ void USER_GetBootloaderFile(PDEVICEINSTANCE ptDevInstance, PCIFX_FILE_INFORMATION ptFileInfo) { switch(ptDevInstance->eChipType) { case eCHIP_TYPE_NETX500: case eCHIP_TYPE_NETX100: { FILE* fd; snprintf(ptFileInfo->szFullFileName, sizeof(ptFileInfo->szFullFileName), "%s/%s", g_szDriverBaseDir, "NETX100-BSL.bin"); strncpy(ptFileInfo->szShortFileName, "NETX100-BSL.bin", sizeof(ptFileInfo->szShortFileName)); if(NULL != (fd = fopen(ptFileInfo->szFullFileName, "r"))) { /* New bootloader found */ fclose(fd); } else { /* new bootloader not found. Assume user still has the "old" cifX bootloader */ snprintf(ptFileInfo->szFullFileName, sizeof(ptFileInfo->szFullFileName), "%s/%s", g_szDriverBaseDir, "NXCIF50-RTE.bin"); strncpy(ptFileInfo->szShortFileName, "NXCIF50-RTE.bin", sizeof(ptFileInfo->szShortFileName)); } } break; case eCHIP_TYPE_NETX52: snprintf(ptFileInfo->szFullFileName, sizeof(ptFileInfo->szFullFileName), "%s/%s", g_szDriverBaseDir, "NETX52-BSL.bin"); strncpy(ptFileInfo->szShortFileName, "NETX52-BSL.bin", sizeof(ptFileInfo->szShortFileName)); break; case eCHIP_TYPE_NETX51: snprintf(ptFileInfo->szFullFileName, sizeof(ptFileInfo->szFullFileName), "%s/%s", g_szDriverBaseDir, "NETX51-BSL.bin"); strncpy(ptFileInfo->szShortFileName, "NETX51-BSL.bin", sizeof(ptFileInfo->szShortFileName)); break; case eCHIP_TYPE_NETX50: /* new bootloader for netX50 will default to NETX50-BSL.bin (which is the default toolkit delivery) */ snprintf(ptFileInfo->szFullFileName, sizeof(ptFileInfo->szFullFileName), "%s/%s", g_szDriverBaseDir, "NETX50-BSL.bin"); strncpy(ptFileInfo->szShortFileName, "NETX50-BSL.bin", sizeof(ptFileInfo->szShortFileName)); break; default: /* This should never happen */ break; } } /*****************************************************************************/ /*! Retrieve the alias name of a card. This name can be alternatively used * by a application in a call to xChannelOpen. This name must be unique * \param ptDevInfo Device information (DeviceNr, SerialNr) * \param ulMaxLen Maximum Length of alias * \param szAlias Pointer to returned alias name (empty = no alias) */ /*****************************************************************************/ void USER_GetAliasName(PCIFX_DEVICE_INFORMATION ptDevInfo, uint32_t ulMaxLen, char* szAlias) { char szFile[CIFX_MAX_FILE_NAME_LENGTH]; char* szTempAlias = NULL; GetDeviceDir(szFile, sizeof(szFile), ptDevInfo); strcat(szFile, "/device.conf"); /* Read alias from file */ if(GetDeviceConfigString(szFile, DEVICE_CONF_ALIAS_KEY, &szTempAlias)) { strncpy(szAlias, szTempAlias, ulMaxLen); free(szTempAlias); } } /*****************************************************************************/ /*! Returns OS file information for the given device/channel and Idx * passed as argument. * \param ptDevInfo DeviceInfo including channel number, for which the * firmware file should be delivered * \param ptFileInfo Short and full file name of the firmware. Used in * calls to OS_OpenFile() * \return != 0 on success */ /*****************************************************************************/ int USER_GetOSFile(PCIFX_DEVICE_INFORMATION ptDevInfo, PCIFX_FILE_INFORMATION ptFileInfo) { char szPath[CIFX_MAX_FILE_NAME_LENGTH]; int ret = 0; DIR* dir; GetDeviceDir(szPath, sizeof(szPath), ptDevInfo); if(NULL != (dir = opendir(szPath))) { struct dirent* dirent; while(NULL != (dirent = readdir(dir))) { char* szExt = strstr(dirent->d_name, "."); if(NULL != szExt) { if(0 == strncasecmp(szExt, HIL_FILE_EXTENSION_FIRMWARE, 4)) { snprintf(ptFileInfo->szFullFileName, sizeof(ptFileInfo->szFullFileName), "%s/%s", szPath, dirent->d_name); strncpy(ptFileInfo->szShortFileName, dirent->d_name, sizeof(ptFileInfo->szShortFileName)); ret = 1; break; } } } closedir(dir); } return ret; } /*****************************************************************************/ /*! Check if the interrupts are to be enabled on this device * \param ptDevInstance Device Instance containing all device data * \return !=0 to enable IRQs */ /*****************************************************************************/ int USER_GetInterruptEnable(PCIFX_DEVICE_INFORMATION ptDevInfo) { char szFile[CIFX_MAX_FILE_NAME_LENGTH]; char* szTempIrq = NULL; int ret = 0; GetDeviceDir(szFile, sizeof(szFile), ptDevInfo); strcat(szFile, "/device.conf"); /* Read IRQ enable from file */ if(GetDeviceConfigString(szFile, DEVICE_CONF_IRQ_KEY, &szTempIrq)) { if(0 == strcasecmp("yes", szTempIrq)) { PCIFX_DEVICE_INTERNAL_T internaldev = (PCIFX_DEVICE_INTERNAL_T)ptDevInfo->ptDeviceInstance->pvOSDependent; /* Check if we have a uio handle. If not, we cannot handle IRQs */ if(-1 == internaldev->userdevice->uio_fd) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace(ptDevInfo->ptDeviceInstance, TRACE_LEVEL_ERROR, "Trying to activate IRQ's on a device, that does not have a uio connection. Fallback to polling mode!"); } } else { char* szTempData = NULL; uint32_t ulEnableInt = 0; /* NOTE: We don't want do enable interrupts here! We just want to check if the device provides interrupt support! */ /* ret = ENOSYS => !to be backwards compatible! - verification not provided but device supports interrupt */ /* ret = EIO => interrupt not supported, the device did not register for any interrupt */ /* NOTE: For SPIDEV devices, the IRQ trigger file is opened read only! In this case the uio_num is -1. */ if ((internaldev->userdevice->uio_num >= 0) && ((ret=write(internaldev->userdevice->uio_fd, (void*)&ulEnableInt, sizeof(uint32_t))) < 0) && (errno != ENOSYS)) { ret = 0; if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace(ptDevInfo->ptDeviceInstance, TRACE_LEVEL_ERROR, "Error enabling interrupts (%s), fallback to polling mode!", strerror(errno)); if (errno == EIO) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace(ptDevInfo->ptDeviceInstance, TRACE_LEVEL_ERROR, "No irq for device registered!"); } } } } else { /* Check for custom priority and set them in CIFX_DEVICE_INTERNAL_T to use them in OS_EnableInterrupts() */ if(GetDeviceConfigString(szFile, DEVICE_CONF_IRQPRIO_KEY, &szTempData)) { internaldev->set_irq_prio = 1; internaldev->irq_prio = atoi(szTempData); if(g_ulTraceLevel & TRACE_LEVEL_INFO) { USER_Trace(ptDevInfo->ptDeviceInstance, TRACE_LEVEL_INFO, "Using custom IRQ thread priority (%d).", internaldev->irq_prio); } free(szTempData); } else { internaldev->set_irq_prio = 0; } /* Check for custom scheduling policy */ if(GetDeviceConfigString(szFile, DEVICE_CONF_IRQSCHED_KEY, &szTempData)) { internaldev->set_irq_scheduler_algo = 1; if(strcasecmp("fifo", szTempData) == 0) { /* SCHED_FIFO */ internaldev->irq_scheduler_algo = SCHED_FIFO; if(g_ulTraceLevel & TRACE_LEVEL_INFO) { USER_Trace(ptDevInfo->ptDeviceInstance, TRACE_LEVEL_INFO, "Using custom IRQ thread scheduling algorithm (SCHED_FIFO)."); } } else if(strcasecmp("rr", szTempData) == 0) { /* SCHED_RR */ internaldev->irq_scheduler_algo = SCHED_RR; if(g_ulTraceLevel & TRACE_LEVEL_INFO) { USER_Trace(ptDevInfo->ptDeviceInstance, TRACE_LEVEL_INFO, "Using custom IRQ thread scheduling algorithm (SCHED_RR)."); } } else { internaldev->set_irq_scheduler_algo = 0; if(g_ulTraceLevel & TRACE_LEVEL_INFO) { USER_Trace(ptDevInfo->ptDeviceInstance, TRACE_LEVEL_INFO, "Trying to override IRQ thread scheduling policy, but unknown policy was given (%s)", szTempData); } } free(szTempData); } else { internaldev->set_irq_scheduler_algo = 0; } ret = 1; } } } free(szTempIrq); } if(g_ulTraceLevel & TRACE_LEVEL_INFO) { USER_Trace( ptDevInfo->ptDeviceInstance, TRACE_LEVEL_INFO, "%s", (ret)?"IRQ-Mode enabled!":"Polling Mode enabled!"); } return ret; } #ifdef CIFX_TOOLKIT_DMA /*****************************************************************************/ /*! Check if the DMA mode is enabled * \param ptDevInstance Device Instance containing all device data * \return CIFX_TOOLKIT_DMA_MODE_E */ /*****************************************************************************/ int USER_GetDMAMode(PCIFX_DEVICE_INFORMATION ptDevInfo) { char szFile[CIFX_MAX_FILE_NAME_LENGTH]; char* szTempDMA = NULL; GetDeviceDir(szFile, sizeof(szFile), ptDevInfo); strcat(szFile, "/device.conf"); if(GetDeviceConfigString(szFile, DEVICE_CONF_DMA, &szTempDMA)) { if(0 == strcasecmp("yes", szTempDMA)) { if(g_ulTraceLevel & TRACE_LEVEL_INFO) { USER_Trace(ptDevInfo->ptDeviceInstance, 0, "DMA mode enabled!"); } return eDMA_MODE_ON; } free(szTempDMA); } if(g_ulTraceLevel & TRACE_LEVEL_INFO) { USER_Trace(ptDevInfo->ptDeviceInstance, 0, "No DMA support!"); } return eDMA_MODE_OFF; } #endif #ifdef CIFXETHERNET /*****************************************************************************/ /*! Check if the ethernet support is enabled * \param ptDevInstance Device Instance containing all device data * \return CIFX_TOOLKIT_DMA_MODE_E */ /*****************************************************************************/ int USER_GetEthernet(PCIFX_DEVICE_INFORMATION ptDevInfo) { char szFile[CIFX_MAX_FILE_NAME_LENGTH]; char* szTempEth = NULL; int ret = 0; GetDeviceDir(szFile, sizeof(szFile), ptDevInfo); strcat(szFile, "/device.conf"); if(GetDeviceConfigString(szFile, DEVICE_CONF_ETH, &szTempEth)) { if(0 == strcasecmp("yes", szTempEth)) { if(g_ulTraceLevel & TRACE_LEVEL_INFO) { USER_Trace(ptDevInfo->ptDeviceInstance, 0, "Ethernet support enabled!"); } ret = 1; } free(szTempEth); } else { if(g_ulTraceLevel & TRACE_LEVEL_INFO) { USER_Trace(ptDevInfo->ptDeviceInstance, 0, "No ethernet support!"); } } return ret; } #endif /*****************************************************************************/ /*! Get actual I/O buffer caching mode for the given device * \param ptDevInfo Device Information * \return eCACHED_MODE_ON to enable caching */ /*****************************************************************************/ int USER_GetCachedIOBufferMode(PCIFX_DEVICE_INFORMATION ptDevInfo) { return eCACHED_MODE_OFF; }