// SPDX-License-Identifier: MIT /************************************************************************************** * * Copyright (c) 2024, Hilscher Gesellschaft fuer Systemautomation mbH. All Rights Reserved. * * Description: Implementation of the netX virtual network interface. * * Changes: * * Version Date Author Description * ---------------------------------------------------------------------------------- * 1 02.01.24 SD changed licensing terms * **************************************************************************************/ #ifdef CIFXETHERNET #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Hil_Packet.h" #include "Hil_Results.h" #include "Hil_ApplicationCmd.h" #include "DrvEth_GCI_API.h" #include "cifXUser.h" #include "cifXErrors.h" #include "cifXHWFunctions.h" #include "netx_tap.h" #define TUNTAP_DEVICEPATH "/dev/net/tun" #define SEND_RETRIES 0 #define LINK_STATE_POLL_INTERVAL 5 /* in seconds */ void* g_eth_list_lock; typedef struct NETX_ETH_DEV_Ttag { TAILQ_ENTRY(NETX_ETH_DEV_Ttag) lentry; int eth_fd; char cifxeth_name[CIFX_MAX_FILE_NAME_LENGTH]; char event_path[CIFX_MAX_FILE_NAME_LENGTH]; NETX_ETH_DEV_CFG_T config; CIFXHANDLE cifx_driver; CIFXHANDLE cifx_channel; PDEVICEINSTANCE devinst; uint32_t channel_no; pthread_t eth_to_cifx_thread; pthread_t cifx_to_eth_thread; int stop_to_eth; int stop_to_cifx; void* com_lock; void* send_event; uint32_t active_sends; uint32_t send_packets; uint32_t recv_packets; int link_up; void* link_event; } NETX_ETH_DEV_T; static TAILQ_HEAD(, NETX_ETH_DEV_Ttag) s_DeviceList = TAILQ_HEAD_INITIALIZER( s_DeviceList); static int32_t cifxeth_search_eth_channel ( char* szDeviceName, uint32_t ulSearchIdx, uint32_t* pulChannelNumber); static int cifxeth_allocate_tap ( NETX_ETH_DEV_T* internal_dev, char* prefix); static void cifxeth_free_tap ( NETX_ETH_DEV_T* internal_dev, char* name); static void cifxeth_delete_device ( NETX_ETH_DEV_T* internal_dev); static int32_t cifxeth_register_app ( NETX_ETH_DEV_T* internal_dev, int fRegister); static int cifxeth_create_com_thread ( NETX_ETH_DEV_T* internal_dev); static int cifxeth_create_cifx_thread ( NETX_ETH_DEV_T* internal_dev); static int32_t cifxeth_update_device_config ( NETX_ETH_DEV_T* internal_dev); static int32_t cifxeth_update_link_state ( NETX_ETH_DEV_T* internal_dev); static int32_t cifxeth_get_extended_info ( NETX_ETH_DEV_T* internal_dev, uint32_t ulInformationRequest, void* pvBuffer, uint32_t ulBufLen); static void* eth_to_cifx_thread ( void* arg); static void* cifx_to_eth_thread ( void* arg); static NETX_ETH_DEV_T* find_device ( char* name); struct nl_link_arg { struct rtnl_link *change; struct nl_sock *sock; NETX_ETH_DEV_T* priv; }; static void set_cb(struct nl_object *obj, void *arg) { struct rtnl_link *link = nl_object_priv(obj); struct nl_link_arg *link_arg = arg; if (rtnl_link_change(link_arg->sock, link, link_arg->change, 0)) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( link_arg->priv->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Link-update failed for %s", link_arg->priv->cifxeth_name); } } } void nl_signal_link_change( NETX_ETH_DEV_T* internal_dev, int state) { struct nl_sock *sock; struct nl_cache *link_cache; struct rtnl_link *link, *change; struct nl_link_arg link_arg; if (NULL == (sock = nl_cli_alloc_socket())) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Link-update failed - failed to allocate socket", internal_dev->cifxeth_name); } return; } if (0 == nl_cli_connect(sock, NETLINK_ROUTE)) { link_cache = nl_cli_link_alloc_cache(sock); link = nl_cli_link_alloc(); change = nl_cli_link_alloc(); nl_cli_link_parse_name(link, internal_dev->cifxeth_name); if (state != 0) { rtnl_link_set_flags(change, IFF_UP); } else { rtnl_link_unset_flags(change, IFF_UP); } link_arg.sock = sock; link_arg.change = change; link_arg.priv = internal_dev; nl_cache_foreach_filter(link_cache, OBJ_CAST(link), set_cb, &link_arg); } else { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Link-update failed - failed to connect socket", internal_dev->cifxeth_name); } } } /*****************************************************************************/ /*! This function creates a netX based ethernet interface * \param config pointer to configuration structure * \return != NULL on success */ /*****************************************************************************/ void* cifxeth_create_device(NETX_ETH_DEV_CFG_T* config) { NETX_ETH_DEV_T* internal_dev = NULL; void* ret = NULL; int32_t cifx_error = CIFX_NO_ERROR; int32_t search_error = CIFX_NO_ERROR; uint32_t channel_no = 0; uint32_t eth_no = 0; int err = 0; if (NULL == config) goto exit; if (NULL != find_device( config->cifx_name)) return NULL; /* try to find a channel providing an ethernet interface */ while (CIFX_NO_ERROR == (search_error = cifxeth_search_eth_channel( config->cifx_name, channel_no, &channel_no))) { if(NULL != (internal_dev = (NETX_ETH_DEV_T*)OS_Memalloc( sizeof(*internal_dev)))) { OS_Memset( internal_dev, 0, sizeof(NETX_ETH_DEV_T)); if (NULL == (internal_dev->com_lock = OS_CreateLock())) { OS_Memfree( internal_dev); goto exit; } else if (NULL == (internal_dev->send_event = OS_CreateEvent())) { OS_DeleteLock( internal_dev->com_lock); OS_Memfree( internal_dev); goto exit; } else if (NULL == (internal_dev->link_event = OS_CreateEvent())) { OS_DeleteEvent( internal_dev->send_event); OS_DeleteLock( internal_dev->com_lock); OS_Memfree( internal_dev); goto exit; } internal_dev->channel_no = channel_no; if(CIFX_NO_ERROR != (cifx_error = xDriverOpen( &internal_dev->cifx_driver))) { fprintf(stderr, "Ethernet-IF Error: %s: Error opening cifX Device Driver (Ret=0x%08X)\n", __TIME__, cifx_error); } else if(CIFX_NO_ERROR == (cifx_error = xChannelOpen(internal_dev->cifx_driver, config->cifx_name, internal_dev->channel_no, &internal_dev->cifx_channel))) { char prefix[CIFX_MAX_FILE_NAME_LENGTH]; CIFX_PACKET tDummy; uint32_t i = 0; PCHANNELINSTANCE ptdevice = (PCHANNELINSTANCE)internal_dev->cifx_channel; internal_dev->devinst = (PDEVICEINSTANCE)ptdevice->pvDeviceInstance; /* in case there are remaining packages try to empty mailbox */ for(i=0;i<16;i++) { if(CIFX_NO_ERROR != xChannelGetPacket(internal_dev->cifx_channel, sizeof(tDummy), &tDummy, 50)) break; } OS_Memcpy( &internal_dev->config, config, sizeof(internal_dev->config)); strcpy( internal_dev->config.cifx_name, config->cifx_name); strcpy( prefix, config->cifx_name); for (i=0; icifx_name); i++) prefix[i] = tolower(prefix[i]); internal_dev->eth_fd = -1; if( (internal_dev->eth_fd = cifxeth_allocate_tap( internal_dev, prefix)) < 0) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Error allocating tap device for '%s'. Error=%d", config->cifx_name, internal_dev->eth_fd); } } else { /* signal link down since TAP device is up by default */ nl_signal_link_change( internal_dev, 0); eth_no++; strcpy( config->eth_dev_name, internal_dev->cifxeth_name); if(g_ulTraceLevel & TRACE_LEVEL_INFO) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_INFO, "Ethernet-IF Info: Successfully created '%s' at channel %d on device '%s'", internal_dev->cifxeth_name, channel_no, config->cifx_name); } /* de-register application since may not be de-registered */ cifxeth_register_app( internal_dev, 0); /* Register for ethernet service on device */ if (CIFX_NO_ERROR == (cifxeth_register_app( internal_dev, 1))) { /* Create threads for packet exchange */ if(cifxeth_create_cifx_thread( internal_dev) != 0) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Error creating cifX ethernet channel thread for %s.", internal_dev->cifxeth_name); } } else { /* Set device MAC address from response packet */ //cifxeth_update_device_config( internal_dev); /* Device successfully created */ OS_EnterLock( g_eth_list_lock); TAILQ_INSERT_TAIL( &s_DeviceList, internal_dev, lentry); ret = internal_dev; OS_LeaveLock( g_eth_list_lock); return ret; } } } } else { fprintf(stderr, "Ethernet-IF Error: %s: Error opening cifX Ethernet Channel (Board=%s, Channel=%u, Errror=0x%08X)\n", __TIME__, config->cifx_name, internal_dev->channel_no, cifx_error); } if(NULL == ret) { cifxeth_delete_device( internal_dev); } } else { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Not enough memory to create cifx virtual ethernet interface!"); } } channel_no++; } exit: return ret; } /*****************************************************************************/ /*! This removes a previously with cifxeth_create_device() created netX based ethernet interface * The function requires either a handle or config structure containing the name of the cifX device * \param devicehandle pointer to handle returned by cifxeth_create_device() * \param config pointer to configuration structure * \return != NULL on success */ /*****************************************************************************/ void cifxeth_remove_device( void* devicehandle, NETX_ETH_DEV_CFG_T* config) { NETX_ETH_DEV_T* internal_dev = (NETX_ETH_DEV_T*)devicehandle; if ((NULL == internal_dev) && (NULL == config)) return; if ((NULL != internal_dev) || (NULL != (internal_dev = find_device( config->cifx_name)))) { cifxeth_delete_device( internal_dev); } } /*****************************************************************************/ /*! This function empties mailbox * \param internal_dev pointer to internal netx-ethernet device */ /*****************************************************************************/ void empty_mailbox( NETX_ETH_DEV_T* internal_dev) { uint32_t ulRecvPktCount = 0; uint32_t ulSendPktCount = 0; int32_t lRet = 0; if ((internal_dev == NULL) || (internal_dev->cifx_channel == NULL)) return; lRet = xChannelGetMBXState( internal_dev->cifx_channel, &ulRecvPktCount, &ulSendPktCount); while((lRet == CIFX_NO_ERROR) && (ulRecvPktCount > 0)) { CIFX_PACKET cifx_packet; lRet = xChannelGetPacket( internal_dev->cifx_channel, sizeof(cifx_packet), &cifx_packet, CIFX_TO_CONT_PACKET); if (lRet == CIFX_NO_ERROR) lRet = xChannelGetMBXState( internal_dev->cifx_channel, &ulRecvPktCount, &ulSendPktCount); } } /*****************************************************************************/ /*! This function deletes netX based ethernet interface * \param internal_dev pointer to internal netx-ethernet device * \return CIFX_NO_ERROR on success */ /*****************************************************************************/ static void cifxeth_delete_device( NETX_ETH_DEV_T* internal_dev) { if(NULL != internal_dev) { if (0 != internal_dev->eth_to_cifx_thread) { internal_dev->stop_to_cifx = 1; pthread_join( internal_dev->eth_to_cifx_thread, NULL); } if (0 != internal_dev->cifx_to_eth_thread) { internal_dev->stop_to_eth = 1; pthread_join( internal_dev->cifx_to_eth_thread, NULL); } if (internal_dev->cifx_channel != NULL) cifxeth_register_app( internal_dev, 0); /* remove any pending packets */ empty_mailbox( internal_dev); cifxeth_free_tap(internal_dev,internal_dev->cifxeth_name); if (NULL != internal_dev->link_event) OS_DeleteEvent( internal_dev->link_event); if (NULL != internal_dev->com_lock) OS_DeleteLock( internal_dev->com_lock); if (NULL != internal_dev->send_event) OS_DeleteEvent( internal_dev->send_event); if(NULL != internal_dev->cifx_channel) xChannelClose(internal_dev->cifx_channel); if(NULL != internal_dev->cifx_driver) xDriverClose(internal_dev->cifx_driver); OS_EnterLock( g_eth_list_lock); if (!TAILQ_EMPTY( &s_DeviceList)) { TAILQ_REMOVE( &s_DeviceList, internal_dev, lentry); OS_Memfree(internal_dev); } OS_LeaveLock( g_eth_list_lock); } } /*****************************************************************************/ /*! removes all cifx tap devices reside in /sys/class/net */ /*****************************************************************************/ void cifxeth_sys_cleanup(void) { struct dirent** namelist; int num_virt_eth; num_virt_eth = scandir("/sys/class/net/", &namelist, 0, alphasort); if(num_virt_eth > 0) { int currenteth; for(currenteth = 0; currenteth < num_virt_eth; ++currenteth) { if (0 == strncmp("cifx",namelist[currenteth]->d_name,4)) { cifxeth_free_tap(NULL,namelist[currenteth]->d_name); } free(namelist[currenteth]); } free(namelist); } } static int is_ethernet_channel(HIL_DPM_CHANNEL_INFO_BLOCK_T* ptChannel) { int ret = 0; if (HIL_COMM_CLASS_MESSAGING == ptChannel->tCom.usCommunicationClass) { if (HIL_PROT_CLASS_ETHERNET == ptChannel->tCom.usProtocolClass) { /* old identification */ ret = 1; } else if( (HIL_PROT_CLASS_NETWORK_SERVICES == ptChannel->tCom.usProtocolClass) && (ptChannel->tCom.usProtocolConformanceClass & HIL_CONF_CLASS_FLAG_NDIS_AWARE) ) { /* new identification */ ret = 1; } } return ret; } /*****************************************************************************/ /*! This function searches a cifx device for an existing Ethernet channel * \param szDeviceName name of the cifX device * \param ulSearchIdx Start index for channel information search * \param ulSearchIdx pointer to channel providing ethernet interface * \return CIFX_NO_ERROR on success */ /*****************************************************************************/ int32_t cifxeth_search_eth_channel( char* szDeviceName, uint32_t ulSearchIdx, uint32_t* pulChannelNumber) { int32_t lRet = CIFX_NO_ERROR; CIFXHANDLE hSysdevice = NULL; CIFXHANDLE hDriver = NULL; PCHANNELINSTANCE ptdevice = NULL; PDEVICEINSTANCE ptDevInst = NULL; if (ulSearchIdx>=CIFX_MAX_NUMBER_OF_CHANNELS) return CIFX_INVALID_PARAMETER; if (CIFX_NO_ERROR != xDriverOpen(&hDriver)) { fprintf( stderr, "Ethernet-IF Error: %s: Error opening driver to for ethernet interface (lRet=0x%08X)\n",__TIME__,lRet); /* Check if we have a channel that might be used for Ethernet / NDIS */ } else if(CIFX_NO_ERROR != (lRet = xSysdeviceOpen( hDriver, szDeviceName, &hSysdevice))) { fprintf( stderr, "Ethernet-IF Error: %s: Error opening system device to read channel info block to detect channels usable for ethernet interface (lRet=0x%08X). - %s\n",__TIME__,lRet, szDeviceName); } else { /* Read channel information block */ SYSTEM_CHANNEL_CHANNEL_INFO_BLOCK tChannelInfoBlock = {{{0}}}; ptdevice = (PCHANNELINSTANCE)hSysdevice; ptDevInst = (PDEVICEINSTANCE)ptdevice->pvDeviceInstance; if(CIFX_NO_ERROR != (lRet = xSysdeviceInfo( hSysdevice, CIFX_INFO_CMD_SYSTEM_CHANNEL_BLOCK, sizeof(tChannelInfoBlock), &tChannelInfoBlock))) { /* Error reading system info block */ if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace(ptDevInst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Error reading channel info block to detect channels usable for ethernet interface (lRet=0x%08X).", lRet); } } else { uint32_t ulStartIndex = 0; int fComChannelFound = 0; if (ulSearchIdx>=ptDevInst->ulCommChannelCount) return CIFX_INVALID_PARAMETER; for( ; ulStartIndex < CIFX_MAX_NUMBER_OF_CHANNELS; ++ulStartIndex) { HIL_DPM_CHANNEL_INFO_BLOCK_T* ptChannel = (HIL_DPM_CHANNEL_INFO_BLOCK_T*)&tChannelInfoBlock.abInfoBlock[ulStartIndex][0]; if( (HIL_CHANNEL_TYPE_COMMUNICATION == ptChannel->tHandshake.bChannelType)) { if (DEV_IsReady( ptDevInst->pptCommChannels[ulSearchIdx])) { fComChannelFound = 1; lRet = CIFX_NO_ERROR; break; } else { lRet = CIFX_DEV_NOT_READY; } } } lRet = CIFX_INVALID_BOARD; /* Check system info block entries */ for( ; (fComChannelFound && ((ulSearchIdx + ulStartIndex) < CIFX_MAX_NUMBER_OF_CHANNELS)); ++ulSearchIdx) { HIL_DPM_CHANNEL_INFO_BLOCK_T* ptChannel = (HIL_DPM_CHANNEL_INFO_BLOCK_T*)&tChannelInfoBlock.abInfoBlock[ulSearchIdx + ulStartIndex][0]; if(is_ethernet_channel(ptChannel)) { /* only create a NDIS interface if the interface is not created already */ if (NULL != pulChannelNumber) *pulChannelNumber = ulSearchIdx; lRet = CIFX_NO_ERROR; break; } } } /* Close system device */ xSysdeviceClose(hSysdevice); } xDriverClose(hDriver); return lRet; } /*****************************************************************************/ /*! This function allocates and initializes a tap device * \param internal_dev pointer to internal netx-ethernet device * \param prefix prefix of the device (e.g. "cifX" -> cifX[x]) * \param dev returns the name of created device (-> cifX[x]) * \return a valid file descriptor to the device - on success (>=0) */ /*****************************************************************************/ static int cifxeth_allocate_tap( NETX_ETH_DEV_T* internal_dev, char* prefix) { struct ifreq ifr; int ret; if( (ret = open( TUNTAP_DEVICEPATH, O_RDWR)) >= 0 ) { int err; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = (IFF_TAP | IFF_NO_PI); if(prefix) strncpy( ifr.ifr_name, prefix, IFNAMSIZ); if( (err = ioctl(ret, TUNSETIFF, (void *) &ifr)) < 0 ) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Error creating tap device (TUNSETIFF) '%s'. Error=%d", prefix, errno); } close(ret); ret = err; } else { strcpy( internal_dev->cifxeth_name, prefix); sprintf(internal_dev->event_path,"/sys/class/net/%s/uevent",prefix); internal_dev->eth_fd = ret; /* set temp. since cifxeth_update_device_config() deals with that handle */ cifxeth_update_device_config(internal_dev); } } else { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Error opening tun interface '%s'. Error=%d", TUNTAP_DEVICEPATH, errno); } ret = -errno; } return ret; } /*****************************************************************************/ /*! This function frees a tap device * \param internal_dev pointer to internal netx-ethernet device * \param name name of device in case device is already closed (->link down) */ /*****************************************************************************/ void cifxeth_free_tap(NETX_ETH_DEV_T* internal_dev, char* name) { if (NULL != internal_dev) { if (internal_dev->eth_fd>=0) { ioctl(internal_dev->eth_fd, TUNSETPERSIST, 0); close(internal_dev->eth_fd); internal_dev->eth_fd = -1; } } /* we have also check the name, in case of link down the handle is '-1' */ if (name != NULL) { struct ifreq ifr; int ret; if ((ret = open( TUNTAP_DEVICEPATH, O_RDWR))) { memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = (IFF_TAP | IFF_NO_PI); strncpy( ifr.ifr_name, name, IFNAMSIZ); ioctl(ret, TUNSETIFF, (void *) &ifr); ioctl(ret, TUNSETPERSIST, 0); close(ret); } } } /*****************************************************************************/ /*! This function creates send/receivce thread * \param internal_dev pointer to internal cifx eth channel * \return >0 on success */ /*****************************************************************************/ static int cifxeth_create_cifx_thread(NETX_ETH_DEV_T* internal_dev) { int ret = -1; pthread_attr_t attr; internal_dev->stop_to_eth = 0; if(0 == (ret = pthread_attr_init(&attr))) { ret = pthread_create(&internal_dev->cifx_to_eth_thread, &attr, cifx_to_eth_thread, internal_dev); } return ret; } /*****************************************************************************/ /*! This function creates send/receivce thread * \param internal_dev pointer to internal netx-ethernet device * \return >0 on success */ /*****************************************************************************/ static int cifxeth_create_com_thread(NETX_ETH_DEV_T* internal_dev) { int ret = -1; pthread_attr_t attr; internal_dev->stop_to_cifx = 0; if(0 == (ret = pthread_attr_init(&attr))) { ret = pthread_create(&internal_dev->eth_to_cifx_thread, &attr, eth_to_cifx_thread, internal_dev); } return ret; } /*****************************************************************************/ /*! Send thread: processes eth packets from tapX to cifX device * \return CIFX_NO_ERROR on success */ /*****************************************************************************/ static void* eth_to_cifx_thread(void* arg) { NETX_ETH_DEV_T* internal_dev = (NETX_ETH_DEV_T*)arg; int fd = internal_dev->eth_fd; char buffer[1514]; DRVETH_GCI_SEND_ETH_FRAME_PCK_T cifx_packet; int select_ret = 0; memset(&cifx_packet.tReq.tHead, 0, sizeof(cifx_packet.tReq.tHead)); cifx_packet.tReq.tHead.ulCmd = DRVETH_GCI_CMD_SEND_ETH_FRAME_REQ; cifx_packet.tReq.tHead.ulDest = HIL_PACKET_DEST_DEFAULT_CHANNEL; while(1) { fd_set readfds, exceptfds; struct timeval timeout = {0}; timeout.tv_sec = 0; timeout.tv_usec = 500 * 1000; /* Default wait timeout = 500ms */ FD_ZERO(&readfds); FD_ZERO(&exceptfds); FD_SET(fd, &readfds); FD_SET(fd, &exceptfds); /* check link state */ while (0 == internal_dev->link_up) { OS_WaitEvent( internal_dev->link_event, 10); if (internal_dev->stop_to_cifx == 1) break; } if((select_ret = select( fd+1, &readfds, NULL, &exceptfds, &timeout)) > 0) { if(FD_ISSET(fd, &exceptfds)) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Exception on Ethernet Device file descriptor, exiting thread"); } break; } if(FD_ISSET(fd, &readfds)) { int32_t cifx_error; ssize_t recv_len; int retry = 0; recv_len = read(fd, buffer, sizeof(buffer)); while (internal_dev->active_sends>0x08) { OS_WaitEvent( internal_dev->send_event, 10); if (internal_dev->stop_to_cifx == 1) break; } if (internal_dev->stop_to_cifx == 1) break; if(recv_len > 0) { memcpy( &cifx_packet.tReq.tData, buffer, recv_len); cifx_packet.tReq.tHead.ulLen = recv_len; if (recv_len<60) { memset( (cifx_packet.tReq.tData.abData + recv_len), 0, (60 - recv_len)); cifx_packet.tReq.tHead.ulLen = 60; } retry = SEND_RETRIES; do { cifx_error = xChannelPutPacket( internal_dev->cifx_channel, (CIFX_PACKET*)&cifx_packet, CIFX_TO_CONT_PACKET); if (cifx_error == CIFX_NO_ERROR) { OS_EnterLock( internal_dev->com_lock); internal_dev->active_sends++; OS_LeaveLock( internal_dev->com_lock); } } while ((cifx_error == CIFX_DEV_MAILBOX_FULL) && (retry-->0)); if ((g_ulTraceLevel & TRACE_LEVEL_DEBUG) && ((SEND_RETRIES-retry) != SEND_RETRIES)) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_DEBUG, "Ethernet-IF Error: Sending a packet took %d-%dms)!", (SEND_RETRIES-retry+1)*CIFX_TO_CONT_PACKET, (SEND_RETRIES-retry)*CIFX_TO_CONT_PACKET); } if (CIFX_NO_ERROR != cifx_error) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Error sending packet to cifX Device. (Error=0x%08X)", cifx_error); } } } } } else if (0 == select_ret) { //continue; } else { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Error on select/or stop requested for Ethernet Device file descriptor, exiting thread"); } break; } if (internal_dev->stop_to_cifx == 1) break; } return NULL; } /*****************************************************************************/ /*! send confirmation of the packet * \param internal_dev Pointer to internal device * \param ptPacket Pointer to packet to handle * \param ulTimeout timeout * \return CIFX_NO_ERROR on success */ /*****************************************************************************/ int32_t send_confirmation( NETX_ETH_DEV_T* internal_dev, CIFX_PACKET* ptPacket, uint32_t ulState, uint32_t ulTimeout) { int32_t ret = CIFX_NO_ERROR; if (ptPacket == NULL) return CIFX_NO_ERROR; if ((ptPacket->tHeader.ulCmd & CIFX_MSK_PACKET_ANSWER) == 0) { uint8_t bRetry = 3; ptPacket->tHeader.ulCmd |= CIFX_MSK_PACKET_ANSWER; ptPacket->tHeader.ulState = ulState; ptPacket->tHeader.ulLen = 0; do { ret = xChannelPutPacket(internal_dev->cifx_channel, ptPacket, ulTimeout); } while((ret != CIFX_NO_ERROR) && (bRetry-- > 0)); if ((ret != CIFX_NO_ERROR) && (g_ulTraceLevel & TRACE_LEVEL_ERROR)) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Failed to send confirmation - Error=0x%X (ulCmd=0x%X / ulState=0x%X)!\n", ret, ptPacket->tHeader.ulCmd, ptPacket->tHeader.ulState); } } return ret; } /*****************************************************************************/ /*! handles incoming packets * \param internal_dev Pointer to internal device * \param ptPacket Pointer to packet to handle * \return CIFX_NO_ERROR on success */ /*****************************************************************************/ void handle_incoming_packet( NETX_ETH_DEV_T* internal_dev, CIFX_PACKET* ptPacket) { uint32_t ulState = 0; switch(ptPacket->tHeader.ulCmd) { case DRVETH_GCI_CMD_SEND_ETH_FRAME_CNF: /* Send response */ OS_EnterLock( internal_dev->com_lock); internal_dev->active_sends--; OS_SetEvent(internal_dev->send_event); OS_LeaveLock( internal_dev->com_lock); if (ptPacket->tHeader.ulState != CIFX_NO_ERROR) { if(g_ulTraceLevel & TRACE_LEVEL_WARNING) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_WARNING, "Ethernet-IF Error: Error signaled by confirmation packet (0x%X)\n", ptPacket->tHeader.ulState); } } break; case DRVETH_GCI_CMD_RECV_ETH_FRAME_IND: { if (internal_dev->link_up) { uint32_t data_len = ptPacket->tHeader.ulLen; ssize_t send_res = data_len; int ret = 0; /* New RX packet */ if(send_res != (ret = write(internal_dev->eth_fd, ptPacket->abData, data_len))) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Error sending incoming data to ethernet device (%d)\n", ret); } } } } break; case DRVETH_GCI_CMD_EVENT_IND: { /* ignore package since we poll the link state */ } break; default: { ulState = ERR_HIL_UNKNOWN_COMMAND; if(g_ulTraceLevel & TRACE_LEVEL_INFO) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_INFO, "Ethernet-IF Error: Error receiving unknown packet cmd=0x%X\n", ptPacket->tHeader.ulCmd); } } break; } send_confirmation( internal_dev, ptPacket, ulState, CIFX_TO_CONT_PACKET); } /*****************************************************************************/ /*! Receiver thread: processes eth packets from cifX to tapX device * \return CIFX_NO_ERROR on success */ /*****************************************************************************/ static void* cifx_to_eth_thread(void* arg) { NETX_ETH_DEV_T* internal_dev = (NETX_ETH_DEV_T*)arg; CIFX_PACKET cifx_packet; uint32_t ulRecvPktCount = 0; uint32_t ulSendPktCount = 0; time_t last_update = 0; int32_t lRet; while(1) { if (internal_dev->stop_to_eth == 1) break; ulRecvPktCount = 1; while(ulRecvPktCount > 0) { if (CIFX_NO_ERROR == (lRet = xChannelGetPacket( internal_dev->cifx_channel, sizeof(cifx_packet), &cifx_packet, CIFX_TO_CONT_PACKET))) { handle_incoming_packet( internal_dev, &cifx_packet); } if (lRet == CIFX_DEV_GET_NO_PACKET) { /* in some case the firmware may deliver wrong mailbox state. */ /* In case of false state, break here to update link state or */ /* to be able to interrupt/stop the running thread. */ break; } if (CIFX_NO_ERROR != (lRet = xChannelGetMBXState( internal_dev->cifx_channel, &ulRecvPktCount, &ulSendPktCount))) { break; } } if (difftime( time(NULL), last_update) > LINK_STATE_POLL_INTERVAL) { cifxeth_update_link_state( internal_dev); last_update = time(NULL); } } return NULL; } /*! ************************************************************************** * Function retrieves the device link state and updates the corresponding * tap device * \param internal_dev Pointer to internal device * \return CIFX_NO_ERROR on success */ /*****************************************************************************/ static int32_t cifxeth_update_link_state( NETX_ETH_DEV_T* internal_dev) { LINK_STATE_T tLinkState = {0}; int32_t lRet = CIFX_NO_ERROR; if (CIFX_NO_ERROR == (lRet = cifxeth_get_extended_info( internal_dev, EXT_INFO_LINKSTATE, &tLinkState, sizeof(tLinkState)))) { int fSkipUpdate = 0; if ((internal_dev->link_up > 0) && (tLinkState.bLinkState)) { /* device is already online */ fSkipUpdate = 1; } if ((internal_dev->link_up <= 0) && (tLinkState.bLinkState == 0)) { /* already offline... skip handling */ fSkipUpdate = 1; } if (fSkipUpdate == 0) { if (tLinkState.bLinkState) { if(g_ulTraceLevel & TRACE_LEVEL_DEBUG) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_DEBUG, "Link up on '%s'", internal_dev->cifxeth_name); } /* notify link state change */ internal_dev->link_up = 1; OS_SetEvent( internal_dev->link_event); if (cifxeth_create_com_thread( internal_dev) != 0) { lRet = CIFX_FUNCTION_FAILED; if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Error creating cifX Ethernet communication thread for %s.",internal_dev->cifxeth_name); } } else { FILE *file = NULL; nl_signal_link_change( internal_dev, 1); /* notify link up via uevent */ if (NULL != (file = fopen(internal_dev->event_path,"r+"))) { fprintf(file, "online"); fclose(file); } else { lRet = CIFX_FUNCTION_FAILED; if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Error opening event path of cifX Ethernet IF %s (online event).",internal_dev->cifxeth_name); } } } } else { FILE *file = NULL; internal_dev->link_up = 0; if(g_ulTraceLevel & TRACE_LEVEL_DEBUG) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_DEBUG, "Link down on '%s'", internal_dev->cifxeth_name); } /* stop eth-if to cifx communication since we we will remove the handle */ internal_dev->stop_to_cifx = 1; if (internal_dev->eth_to_cifx_thread != 0) { pthread_join( internal_dev->eth_to_cifx_thread, 0); internal_dev->eth_to_cifx_thread = 0; } nl_signal_link_change( internal_dev, 0); /* notify link down via uevent */ if (NULL != (file = fopen(internal_dev->event_path,"r+"))) { fprintf(file, "offline"); fclose(file); } else { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Error opening event path of cifX Ethernet IF %s (offline event).",internal_dev->cifxeth_name); } lRet = CIFX_FUNCTION_FAILED; } } } } return lRet; } /*! ************************************************************************** * Function retrieves the device configuration and initialize the corresponding * tap device * \param internal_dev Pointer to internal device * \return CIFX_NO_ERROR on success */ /*****************************************************************************/ static int32_t cifxeth_update_device_config( NETX_ETH_DEV_T* internal_dev) { IFCONFIG_T tExtInfo = {0}; int32_t lRet = CIFX_NO_ERROR; if (CIFX_NO_ERROR == (lRet = cifxeth_get_extended_info( internal_dev, EXT_INFO_INTF_CONFIG, &tExtInfo, sizeof(tExtInfo)))) { struct ifreq ifr; memset( &ifr, 0, sizeof(ifr)); memcpy( ifr.ifr_hwaddr.sa_data, tExtInfo.abEthernetMACAddr, 6); ifr.ifr_hwaddr.sa_family = 1; if( (ioctl( internal_dev->eth_fd, SIOCSIFHWADDR, (void *) &ifr)) < 0 ) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Failed to set MAC address %02x:%02x:%02x:%02x:%02x:%02x of %s (%d)", tExtInfo.abEthernetMACAddr[0], tExtInfo.abEthernetMACAddr[1], tExtInfo.abEthernetMACAddr[2], tExtInfo.abEthernetMACAddr[3], tExtInfo.abEthernetMACAddr[4], tExtInfo.abEthernetMACAddr[5], internal_dev->cifxeth_name, errno); } } else { if(g_ulTraceLevel & TRACE_LEVEL_DEBUG) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_DEBUG, "Ethernet-IF: Successfully set MAC address to %02x:%02x:%02x:%02x:%02x:%02x on %s", tExtInfo.abEthernetMACAddr[0], tExtInfo.abEthernetMACAddr[1], tExtInfo.abEthernetMACAddr[2], tExtInfo.abEthernetMACAddr[3], tExtInfo.abEthernetMACAddr[4], tExtInfo.abEthernetMACAddr[5], internal_dev->cifxeth_name); } } } return lRet; } /*! ************************************************************************* * Receive callback function. requried to handle incoming packets during admin * communication * \param ptRecvPkt * \param pvUser */ /****************************************************************************/ void PacketRecvCallBack( CIFX_PACKET* ptRecvPkt, void* pvUser) { NETX_ETH_DEV_T* internal_dev = (NETX_ETH_DEV_T*)pvUser; handle_incoming_packet( internal_dev, ptRecvPkt); } /*! ************************************************************************* * Register an application, so the Ethernet Stack will send indications * \param internal_dev Pointer to internal device * \param fRegister 1= register, 0 = unregister application */ /****************************************************************************/ static int32_t cifxeth_register_app( NETX_ETH_DEV_T* internal_dev, int fRegister) { uint32_t lRet = CIFX_NO_ERROR; CIFXHANDLE hChannel = internal_dev->cifx_channel; HIL_REGISTER_APP_REQ_T tSendPkt = {{0}}; CIFX_PACKET tRecvPkt = {{0}}; tSendPkt.tHead.ulDest = HIL_PACKET_DEST_DEFAULT_CHANNEL; /* Destination of packet, process queue */ tSendPkt.tHead.ulSrc = 0; /* Source of packet, process queue */ tSendPkt.tHead.ulLen = 0; tSendPkt.tHead.ulId = 0x00; /* Identification handle of sender */ if (fRegister) { tSendPkt.tHead.ulCmd = HIL_REGISTER_APP_REQ; /* Packet command */ }else { tSendPkt.tHead.ulCmd = HIL_UNREGISTER_APP_REQ; /* Packet command */ } lRet = DEV_TransferPacket( hChannel, (CIFX_PACKET*)&tSendPkt, &tRecvPkt, sizeof(tRecvPkt), CIFX_TO_SEND_PACKET, PacketRecvCallBack, internal_dev); if( CIFX_NO_ERROR != lRet) { /* This is a transport error */ if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { /* This is a transport error */ USER_Trace(internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Error in cifXEthTransferPacket()(lRet=0x%08X).", lRet); } } else { /* in case of de-register ignore return value since we might not be registered */ if (fRegister) { /* Check if we have a state error from the stack */ if(SUCCESS_HIL_OK != (lRet = tRecvPkt.tHeader.ulState)) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace(internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Error sending Register-Application-Request (lRet=0x%08X).", lRet); } } } } return lRet; } /*! ************************************************************************** * Function reads extended status block an returns requested information * \param internal_dev Pointer to internal device * \param ulInformationRequest Command which information is retrieved * \param pvBuffer Pointer to information buffer * \param ulBufLen Size of pvBuffer * \return CIFX_NO_ERROR on success */ /*****************************************************************************/ static int32_t cifxeth_get_extended_info( NETX_ETH_DEV_T* internal_dev, uint32_t ulInformationRequest, void* pvBuffer, uint32_t ulBufLen) { int32_t lRet = CIFX_NO_ERROR; CIFXHANDLE hChannel = internal_dev->cifx_channel; DRVETH_GCI_EXTENDED_STATE_T tExtStatusInfo; /* check parameter */ if ((pvBuffer == NULL) || (ulBufLen == 0)) return CIFX_INVALID_PARAMETER; lRet = xChannelExtendedStatusBlock( hChannel, CIFX_CMD_READ_DATA, 0, sizeof(tExtStatusInfo), &tExtStatusInfo); if( CIFX_NO_ERROR == lRet) { /* check which information is requested */ switch (ulInformationRequest) { /* return MAC address */ case EXT_INFO_INTF_CONFIG: { if ( sizeof(IFCONFIG_T) != ulBufLen) { lRet = CIFX_INVALID_BUFFERSIZE; } else { PIFCONFIG_T ptIPconfig = (PIFCONFIG_T)pvBuffer; OS_Memcpy( (void*)ptIPconfig->abEthernetMACAddr, (void*)tExtStatusInfo.abMacAddress, DRVETH_GCI_ETH_ADDR_SIZE); lRet = CIFX_NO_ERROR; } } break; /* return Link State */ case EXT_INFO_LINKSTATE: { if ( sizeof(LINK_STATE_T) != ulBufLen) { lRet = CIFX_INVALID_BUFFERSIZE; } else { PLINK_STATE_T ptLinkState = (PLINK_STATE_T)pvBuffer; ptLinkState->bLinkState = tExtStatusInfo.bMautype; lRet = CIFX_NO_ERROR; } } break; case EXT_STATISTICS: { if ( sizeof(STATISTIC_T) != ulBufLen) { lRet = CIFX_INVALID_BUFFERSIZE; } else { STATISTIC_T* ptStatistic = (STATISTIC_T*)pvBuffer; ptStatistic->ullIfInPkts = tExtStatusInfo.ullIfInPkts; ptStatistic->ullIfInDiscards = tExtStatusInfo.ullIfInDiscards; ptStatistic->ullIfOutPkts = tExtStatusInfo.ullIfOutPkts; ptStatistic->ullIfOutDiscards = tExtStatusInfo.ullIfOutDiscards; ptStatistic->ullIfInBytes = tExtStatusInfo.ullIfInBytes; ptStatistic->ullIfOutBytes = tExtStatusInfo.ullIfOutBytes; lRet = CIFX_NO_ERROR; } } break; case EXT_INFO_MACADDR: case EXT_INFO_IPADDR: case EXT_INFO_NETMASK: case EXT_INFO_GATEWAY: case EXT_INFO_NO_RECVPKT: case EXT_INFO_NO_RCVPKT_DROP: case EXT_INFO_NO_SENDPKT: case EXT_INFO_NO_SENDPKT_DROP: default: lRet = CIFX_INVALID_COMMAND; break; } } if (CIFX_NO_ERROR != lRet) { if(g_ulTraceLevel & TRACE_LEVEL_ERROR) { USER_Trace( internal_dev->devinst, TRACE_LEVEL_ERROR, "Ethernet-IF Error: Failed to retrieve extended info of %s (0x%X)", internal_dev->cifxeth_name, lRet); } } return lRet; } /*****************************************************************************/ /*! Searchs for internal device structure of the device given by name * \param name Name of the requested cifX device * \return pointer to internal device (!= NULL on success) */ /*****************************************************************************/ static NETX_ETH_DEV_T* find_device(char* name) { NETX_ETH_DEV_T* internal_dev = NULL; OS_EnterLock( g_eth_list_lock); if (!TAILQ_EMPTY( &s_DeviceList)) { NETX_ETH_DEV_T* item; TAILQ_FOREACH( item, &s_DeviceList, lentry) { if (0 == strcmp( item->config.cifx_name, name)) { internal_dev = item; break; } } } OS_LeaveLock( g_eth_list_lock); return internal_dev; } /*****************************************************************************/ /*! NOTE: xSysdeviceReset() is a cifx toolkit function. In case of an ethernet * interface a function wrapper is required since xSysdeviceReset() will reset * the whole device including the raw ethernet channel. So we need a wrap * around to remove all previously registered ethernet interfaces. */ /*****************************************************************************/ extern int32_t APIENTRY xSysdeviceResetTK( CIFXHANDLE hSysdevice, uint32_t ulTimeout); /*****************************************************************************/ /*! Hard resets a complete device via system channel * \param hSysdevice Handle to system device * \param ulTimeout Timeout to wait for card to finish reset * \return CIFX_NO_ERROR on success */ /*****************************************************************************/ int32_t APIENTRY xSysdeviceReset( CIFXHANDLE hSysdevice, uint32_t ulTimeout) { int32_t ret = CIFX_NO_ERROR; PCHANNELINSTANCE ptdevice = (PCHANNELINSTANCE)hSysdevice; PDEVICEINSTANCE ptDevInst = (PDEVICEINSTANCE)ptdevice->pvDeviceInstance; NETX_ETH_DEV_T* internal_dev = find_device( ptDevInst->szName); NETX_ETH_DEV_CFG_T config = {{0}}; int ethdevice = 0; if (NULL != internal_dev) { ethdevice = 1; strcpy( config.cifx_name, internal_dev->config.cifx_name); cifxeth_delete_device( internal_dev); } if (CIFX_NO_ERROR == (ret = xSysdeviceResetTK( hSysdevice, ulTimeout))) { if (1 == ethdevice) cifxeth_create_device( &config); } return ret; } extern int32_t APIENTRY xSysdeviceResetExTK(CIFXHANDLE hSysdevice, uint32_t ulTimeout, uint32_t ulMode); /*****************************************************************************/ /*! Hard resets a complete device via system channel with reset parameter * \param hSysdevice Handle to system device * \param ulTimeout Timeout to wait for card to finish reset * \param ulMode Reset mode with parameter * \return CIFX_NO_ERROR on success */ /*****************************************************************************/ int32_t APIENTRY xSysdeviceResetEx(CIFXHANDLE hSysdevice, uint32_t ulTimeout, uint32_t ulMode) { int32_t ret = CIFX_NO_ERROR; PCHANNELINSTANCE ptdevice = (PCHANNELINSTANCE)hSysdevice; PDEVICEINSTANCE ptDevInst = (PDEVICEINSTANCE)ptdevice->pvDeviceInstance; NETX_ETH_DEV_T* internal_dev = find_device( ptDevInst->szName); NETX_ETH_DEV_CFG_T config = {{0}}; int ethdevice = 0; switch(ulMode) { case CIFX_RESETEX_SYSTEMSTART: case CIFX_RESETEX_BOOTSTART: case CIFX_RESETEX_UPDATESTART: { if (NULL != internal_dev) { ethdevice = 1; strcpy( config.cifx_name, internal_dev->config.cifx_name); cifxeth_delete_device( internal_dev); } if (CIFX_NO_ERROR == (ret = xSysdeviceResetExTK( hSysdevice, ulTimeout, ulMode))) { if ((1 == ethdevice) && (ulMode != CIFX_RESETEX_BOOTSTART)) cifxeth_create_device( &config); } } break; default: { ret = CIFX_INVALID_PARAMETER; } break; } return ret; } extern int32_t APIENTRY xSysdeviceBootstartTK(CIFXHANDLE hSysdevice, uint32_t ulTimeout); /*****************************************************************************/ /*! Boot start reset to via system channel * \param hSysdevice Handle to system device * \param ulTimeout Timeout to wait for card to finish reset * \return CIFX_NO_ERROR on success */ /*****************************************************************************/ int32_t APIENTRY xSysdeviceBootstart(CIFXHANDLE hSysdevice, uint32_t ulTimeout) { int32_t ret = CIFX_NO_ERROR; PCHANNELINSTANCE ptdevice = (PCHANNELINSTANCE)hSysdevice; PDEVICEINSTANCE ptDevInst = (PDEVICEINSTANCE)ptdevice->pvDeviceInstance; NETX_ETH_DEV_T* internal_dev = find_device( ptDevInst->szName); NETX_ETH_DEV_CFG_T config = {{0}}; if (NULL != internal_dev) { strcpy( config.cifx_name, internal_dev->config.cifx_name); cifxeth_delete_device( internal_dev); } return xSysdeviceBootstartTK( hSysdevice, ulTimeout); } /*****************************************************************************/ /*! NOTE: xChannelReset() is a cifx toolkit function. In case of an ethernet * interface a function wrapper is required since xChannelReset() (with mode set * to CIFX_SYSTEMSTART) will reset the whole device including the raw ethernet * channel. So we need a wrap around to remove all previously registered ethernet * interfaces. */ /*****************************************************************************/ extern int32_t APIENTRY xChannelResetTK( CIFXHANDLE hChannel, uint32_t ulResetMode, uint32_t ulTimeout); /*****************************************************************************/ /*! Hard resets a complete device via system channel * \param hChannel Handle to system device * \param ulResetMode Reset Mode * \param ulTimeout Timeout to wait for card to finish reset * \return CIFX_NO_ERROR on success */ /*****************************************************************************/ int32_t APIENTRY xChannelReset(CIFXHANDLE hChannel, uint32_t ulResetMode, uint32_t ulTimeout) { int32_t ret = CIFX_NO_ERROR; PCHANNELINSTANCE ptdevice = (PCHANNELINSTANCE)hChannel; PDEVICEINSTANCE ptDevInst = (PDEVICEINSTANCE)ptdevice->pvDeviceInstance; NETX_ETH_DEV_T* internal_dev = find_device( ptDevInst->szName); NETX_ETH_DEV_CFG_T config = {{0}}; int ethdevice = 0; switch(ulResetMode) { case CIFX_SYSTEMSTART: { if (NULL != internal_dev) { ethdevice = 1; strcpy( config.cifx_name, internal_dev->config.cifx_name); cifxeth_delete_device( internal_dev); } if (CIFX_NO_ERROR == (ret = xChannelResetTK( hChannel, ulResetMode, ulTimeout))) { if (1 == ethdevice) cifxeth_create_device( &config); } } break; default: { ret = xChannelResetTK( hChannel, ulResetMode, ulTimeout); } break; } return ret; } #endif //CIFXETHERNET