/****************************************************************************** 版权所有: 文件名称: net_104.c 文件版本: 01.01 创建作者: sunxi 创建日期: 2012-11-07 功能说明: 网络104接口 其它说明: 修改记录: */ #ifdef CPU_FUXI /*------------------------------- 头文件 -------------------------------------- */ #include "head.h" #include #include #include /*------------------------------- 宏定义 -------------------------------------- */ #define NET_104_BUF_SIZE 16384 /*------------------------------ 类型结构 ------------------------------------- */ /*------------------------------ 全局变量 ------------------------------------- */ // static struct task_struct * g_ts_104[IEC104_TOTAL_SOCKETS]; static pthread_t g_ts_104[IEC104_TOTAL_SOCKETS] = {0x00}; static SOCKET g_104_client[IEC104_TOTAL_SOCKETS]; static unsigned char g_net_104_buf_frame[IEC104_TOTAL_SOCKETS][NET_RECV_MAX]; // 以太网接收缓冲区 static unsigned char g_net_104_buf[IEC104_TOTAL_SOCKETS][NET_104_BUF_SIZE]; // 以太网接收缓冲区 static struct rt_fifo g_net_104_fifo[IEC104_TOTAL_SOCKETS]; static volatile int g_net_reinit[CFG_ETH_MAX_LOGIC]={NET_SOCKET_INIT};//网络重新初始化标志.0表示不用重新初始化,1表示需要重新初始化 // static struct semaphore net_r_lock[CFG_ETH_MAX_LOGIC]; //接收锁 // static struct semaphore net_s_lock[CFG_ETH_MAX_LOGIC]; //发送锁 static pthread_mutex_t net_r_lock[CFG_ETH_MAX_LOGIC]; //接收锁 static pthread_mutex_t net_s_lock[CFG_ETH_MAX_LOGIC]; //发送锁 static int g_net_104_close_flag[CFG_ETH_MAX_LOGIC]; static int g_net_104_exit_flag[CFG_ETH_MAX_LOGIC]; // static struct completion g_exit_completion_net_104[CFG_ETH_MAX_LOGIC]; extern u32 g_ip_net_maintain; // 104维护工具调试客户端地址 /*------------------------------ 函数声明 ------------------------------------- */ int _net_104_thread(IEC104_DEF *pt104); //设置socket关闭标志并等待socket关闭完成 void net_104_set_sock_closing(int no) { //if(no >= g_net_num) return; g_net_reinit[no] = NET_SOCKET_CLOSING; //置关闭标志 if(!tRunPara.b104Client) { int count=0; //等待关闭完成 while(g_net_reinit[no] != NET_SOCKET_CLOSED) { msleep(20); if(++count>150) { rt_printf("104 client socket close fail!!!\r\n"); return; } } } } //设置socket重新创建标志 void net_104_set_sock_initflag(int no) { // if(no >= g_net_num) return; if(g_net_reinit[no] == NET_SOCKET_CLOSED) { g_net_reinit[no] = NET_SOCKET_REINIT; } } /************************************************************************** 函数名称:Net_Init 函数版本:1.00 作者: 创建日期:2009.1.8 函数功能说明:以太网初始化 输入参数: 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ void Net_Init(void) { unsigned char mac[6]; unsigned char ip[4]; unsigned char mask[4]; unsigned char gw[4]; unsigned char routenet[4]; unsigned char routenetmask[4]; unsigned char routegate[4]; //char * acteth; int i,j; //char tmp[16]; int ret = 0; int gateflag = 0; if(tRunPara.w104K == 0) { if(pRunSet->bTT_SoeResumeComm || pRunSet->bTT_SoeResumePower) { rt_printf("定值参数错误:SOE通讯续传、SOE上电续传时参数104 K值不能为0.\r\n"); rt_err_set(ERR_CODE_INIT_SOFTWARE,0); } } #if 0 int debug_port; acteth = env_get_info(ENV_ETHACT); if(acteth != NULL) { if(strcmp(acteth,"eth0") == 0) { debug_port = 0; } else if(strcmp(acteth,"eth1") == 0) { debug_port = 1; } else if(strcmp(acteth,"eth2") == 0) { debug_port = 2; } else { debug_port = 2; } } else { debug_port = 2; } #endif //for(i=0;i>24)&0xff); mac[3] = (BYTE)((tRunPara.tNetPara[i].wIp>>16)&0xff); mac[4] = (BYTE)((tRunPara.tNetPara[i].wIp>>8)&0xff); mac[5] = (BYTE)((tRunPara.tNetPara[i].wIp>>0)&0xff); // 设置IP memset(ip,0,sizeof(ip)); ip[3] = (BYTE)((tRunPara.tNetPara[i].wIp>>0)&0xff);//低 ip[2] = (BYTE)((tRunPara.tNetPara[i].wIp>>8)&0xff); ip[1] = (BYTE)((tRunPara.tNetPara[i].wIp>>16)&0xff); ip[0] = (BYTE)((tRunPara.tNetPara[i].wIp>>24)&0xff);//高 192 // 设置MASK memset(mask,0,sizeof(mask)); mask[3] = (BYTE)((tRunPara.tNetPara[i].wMask>>0)&0xff); mask[2] = (BYTE)((tRunPara.tNetPara[i].wMask>>8)&0xff); mask[1] = (BYTE)((tRunPara.tNetPara[i].wMask>>16)&0xff); mask[0] = (BYTE)((tRunPara.tNetPara[i].wMask>>24)&0xff); //检查是否需要关闭虚拟网卡 if((int)tRunPara.tNetPara[i].old_index > 0) { rt_if_down2(tRunPara.tNetPara[i].old_ethid, tRunPara.tNetPara[i].old_index); } //获取配置相同的ethid有多少个 ret=get_ethcnt(i, tRunPara.tNetPara[i].ethid); //没有的话,使用物理的 if(ret==0) { // 设置网络参数 net_if_set((int)tRunPara.tNetPara[i].ethid,mac,ip, mask); tRunPara.tNetPara[i].index = 0; //rt_printf_time("ret = %d, ethID = %d\r\n",ret,tRunPara.tNetPara[i].ethid); } else //有的话使用虚拟的 { #if defined(NET3_IP5) || defined(NET4_IP5) net_if_set2((int)tRunPara.tNetPara[i].ethid, ret+1, 0, ip, mask); tRunPara.tNetPara[i].index = ret+1; #else net_if_set2((int)tRunPara.tNetPara[i].ethid, i+1, 0, ip, mask); tRunPara.tNetPara[i].index = i+1; #endif //rt_printf_time("Xnet = %d,ip=%x, ethID = %d, ret = %d\r\n",tRunPara.tNetPara[i].index,ip[3],tRunPara.tNetPara[i].ethid,ret); } tRunPara.tNetPara[i].old_ethid= tRunPara.tNetPara[i].ethid; tRunPara.tNetPara[i].old_index= tRunPara.tNetPara[i].index; #if 0 // 如果是调试串口,不初始化 if(i == debug_port) //if(i==(CFG_ETH_MAX_LOGIC-1)) { //设置IP环境变量 if(tRunPara.tNetPara[i].changed & 0x01) { sprintf(tmp, "%lu.%lu.%lu.%lu",( tRunPara.tNetPara[i].wIp>>24)&0xff, (tRunPara.tNetPara[i].wIp>>16)&0xff, (tRunPara.tNetPara[i].wIp>>8)&0xff, (tRunPara.tNetPara[i].wIp&0xff)); env_setenv("ipaddr", tmp); } //设置Gate环境变量 if(tRunPara.tNetPara[i].changed & 0x02) { sprintf(tmp, "%lu.%lu.%lu.%lu", ( tRunPara.tNetPara[i].wGate>>24)&0xff, (tRunPara.tNetPara[i].wGate>>16)&0xff, (tRunPara.tNetPara[i].wGate>>8)&0xff, (tRunPara.tNetPara[i].wGate&0xff)); if(tRunPara.tNetPara[i].wGate) env_setenv("gatewayip", tmp); } //设置Mask环境变量 if(tRunPara.tNetPara[i].changed & 0x04) { sprintf(tmp, "%lu.%lu.%lu.%lu", ( tRunPara.tNetPara[i].wMask>>24)&0xff, (tRunPara.tNetPara[i].wMask>>16)&0xff, (tRunPara.tNetPara[i].wMask>>8)&0xff, (tRunPara.tNetPara[i].wMask&0xff)); env_setenv("netmask", tmp); } //设置ethact环境变量 if(tRunPara.tNetPara[i].changed & 0x40) { if(tRunPara.tNetPara[i].ethid >= 2) { sprintf(tmp, "%s", "eth2"); } else { sprintf(tmp, "eth%d", (int)tRunPara.tNetPara[i].ethid); } env_setenv("ethact", tmp); } //continue; } #endif //网段 routenet[0] = (BYTE)((tRunPara.tNetPara[i].wRouteNet>>24)&0xff); routenet[1] = (BYTE)((tRunPara.tNetPara[i].wRouteNet>>16)&0xff); routenet[2] = (BYTE)((tRunPara.tNetPara[i].wRouteNet>>8)&0xff); routenet[3] = (BYTE)((tRunPara.tNetPara[i].wRouteNet>>0)&0xff); //网段掩码 routenetmask[0] = (BYTE)((tRunPara.tNetPara[i].wRouteNetMask>>24)&0xff); routenetmask[1] = (BYTE)((tRunPara.tNetPara[i].wRouteNetMask>>16)&0xff); routenetmask[2] = (BYTE)((tRunPara.tNetPara[i].wRouteNetMask>>8)&0xff); routenetmask[3] = (BYTE)((tRunPara.tNetPara[i].wRouteNetMask>>0)&0xff); //网关 routegate[0] = (BYTE)((tRunPara.tNetPara[i].wRouteGate>>24)&0xff); routegate[1] = (BYTE)((tRunPara.tNetPara[i].wRouteGate>>16)&0xff); routegate[2] = (BYTE)((tRunPara.tNetPara[i].wRouteGate>>8)&0xff); routegate[3] = (BYTE)((tRunPara.tNetPara[i].wRouteGate>>0)&0xff); net_route_set(routenet, routenetmask, routegate); //g_net_reinit[i] = 1;//置重新初始化标志 net_104_set_sock_initflag(i); net_debug_set_sock_initflag(); net_maintain_set_sock_initflag(); #if 0 // 得到并打印新的网络参数 // net_if_get(i,mac,ip,mask); rt_printf("new mac: %02x:%02x:%02x:%02x:%02x:%02x\r\n",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); rt_printf("new ip:%d.%d.%d.%d\r\n",ip[0],ip[1],ip[2],ip[3]); rt_printf("new mask:%d.%d.%d.%d\r\n",mask[0],mask[1],mask[2],mask[3]); rt_printf("new gw:%d.%d.%d.%d\r\n",gw[0],gw[1],gw[2],gw[3]); #endif } ret = 0; //查找是否关闭物理网卡 for(i=0;i>24)&0xff); gw[1] = (BYTE)((tRunPara.tNetPara[0].wGate>>16)&0xff); gw[2] = (BYTE)((tRunPara.tNetPara[0].wGate>>8)&0xff); gw[3] = (BYTE)((tRunPara.tNetPara[0].wGate>>0)&0xff); // 如果地址是默认的0.0.0.0,说明用户不需要网关, // 不重设网关,使uboot环境变量中的网关有效,方便调试。 if(gw[0]==0 && gw[1]==0 && gw[2]==0 && gw[3]==0) { return; } if(gateflag) { net_gateway_set(gw); } //noted by sunxi: 20230105 eth0需要重新设置mac才能成功。 for(i=0;i<1;i++) { mac[0] = 'H'; mac[1] = 'G'; mac[2] = (BYTE)((tRunPara.tNetPara[i].wIp>>24)&0xff); mac[3] = (BYTE)((tRunPara.tNetPara[i].wIp>>16)&0xff); mac[4] = (BYTE)((tRunPara.tNetPara[i].wIp>>8)&0xff); mac[5] = (BYTE)((tRunPara.tNetPara[i].wIp>>0)&0xff); rt_if_down(i); net_if_set((int)tRunPara.tNetPara[i].ethid,mac,NULL, NULL); rt_if_up(i); } return; } /*------------------------------ 外部函数 ------------------------------------- 外部函数供其它实体文件引用,必须仔细检查传入参数的合法性. */ int net_104_init(void) { int net; char buf[32]; // struct sched_param sp; // pid_t pid; int ret; pthread_attr_t attr; IEC104_Init(); for(net=0; netbTT_ESAM) { iec_GetMsgInfo(buf,len,&ti,&cot,&se,false); len = sec_encrpt_iec(ti,cot,se,buf,len,&out); if(len > 0) { ret=net_104_send_ext(net,out, len); } } else { ret=net_104_send_ext(net,buf, len); } return ret; } static int net_104_recv(int net, unsigned char *buf,int len) { int ret = 0; // down(&net_r_lock[net]); pthread_mutex_lock(&net_r_lock[net]); if(g_104_client[net] != RT_SOCKET_ERR) { ret = rt_recv (g_104_client[net], (void *)buf, NET_RECV_MAX, 0); } // up(&net_r_lock[net]); pthread_mutex_unlock(&net_r_lock[net]); return ret; } void net_104_close(int net) { // down(&net_r_lock[net]); // down(&net_s_lock[net]); pthread_mutex_lock(&net_r_lock[net]); pthread_mutex_lock(&net_s_lock[net]); if(g_104_client[net] != RT_SOCKET_ERR) { rt_socket_close (g_104_client[net]); g_104_client[net] = RT_SOCKET_ERR; } g_net_104_close_flag[net] = 0; // up(&net_s_lock[net]); // up(&net_r_lock[net]); pthread_mutex_unlock(&net_s_lock[net]); pthread_mutex_unlock(&net_r_lock[net]); } void net_104_close_flag(int net) { // down(&net_r_lock[net]); // down(&net_s_lock[net]); pthread_mutex_lock(&net_r_lock[net]); pthread_mutex_lock(&net_s_lock[net]); if(g_104_client[net] != RT_SOCKET_ERR) { g_net_104_close_flag[net] = 1; } // up(&net_s_lock[net]); // up(&net_r_lock[net]); pthread_mutex_unlock(&net_s_lock[net]); pthread_mutex_unlock(&net_r_lock[net]); } int net_104_is_connect(int net) { if((g_104_client[net] == RT_SOCKET_ERR) || g_net_104_close_flag[net]) { return 0; } return 1; } /*------------------------------ 内部函数 ------------------------------------- 内部函数以下划线‘_’开头,不需要检查参数的合法性. */ extern int _net_104Client_thread(int net); int _net_104_thread(IEC104_DEF *pt104) { struct sockaddr_in server_addr; struct sockaddr_in client_addr; struct timeval timeout; struct linger ling; SOCKET sk_server,sk_tmp ; int net; int sin_size; int len,ret; unsigned char *ip; unsigned int ip_server; int recv_timeout,recv_timeout_set; int link_timeout=0; unsigned char buf; unsigned char tmpbuf[32]; u16 w104Port; /* rt_printf("tRunPara.b104Client = %d \r\n",tRunPara.b104Client); tRunPara.b104Client = 0; if(tRunPara.b104Client) // 104采用客户端 { //net = pt104->cNetIndex; net = 1; _net_104Client_thread(net); //return 0; } */ #if 1 NET_BEGIN: net = pt104->cNetIndex; // 服务器端开始建立socket描述符 sk_server = rt_socket(AF_INET, SOCK_STREAM, 0); if (sk_server == RT_SOCKET_ERR) { RT_PRINTF_POSITION(); g_ts_104[net] = 0; return -1; } // 服务器端填充sockaddr结构 memset (&server_addr, 0, sizeof (struct sockaddr_in)); server_addr.sin_family = AF_INET; //rt_if_ip_get(net,(unsigned char *)&ip_server); //ret=get_ethcnt(net, tRunPara.tNetPara[net].ethid); //IP在物理网卡上 if( tRunPara.tNetPara[net].index == 0) { rt_if_ip_get(tRunPara.tNetPara[net].ethid, (unsigned char *)&ip_server); w104Port = tRunPara.w104Port; // 网络1、2使用可变端口,保证级联时不冲突。 } else //IP在虚拟网卡上 { //从虚拟网卡获取IP地址 rt_if_ip_get2(tRunPara.tNetPara[net].ethid, tRunPara.tNetPara[net].index, (unsigned char *)&ip_server); w104Port = NET_104_PORT; // 调试网络使用固定的2404端口,保证维护工具可以连接。 } server_addr.sin_addr.s_addr = htonl (ip_server); server_addr.sin_port = htons (w104Port); //printf("-------Server ip:%s\n",inet_ntoa(server_addr.sin_addr)); // 捆绑sockfd描述符 ret = rt_bind (sk_server, (struct sockaddr *)(&server_addr), sizeof (struct sockaddr)); if (ret < 0) { // RT_PRINTF_POSITION(); rt_socket_close (sk_server); g_ts_104[net] = 0; return -2; } // 监听sockfd描述符 ret = rt_listen (sk_server, 1); if (ret < 0) { // RT_PRINTF_POSITION(); rt_socket_close (sk_server); g_ts_104[net] = 0; return -3; } // 设置接收超时 timeout.tv_sec = 0; timeout.tv_usec = 100000; rt_setsockopt(sk_server,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout ,sizeof(struct timeval)); // 设置关闭延时 ling.l_onoff = 1; ling.l_linger = 0; rt_setsockopt(sk_server,SOL_SOCKET,SO_LINGER,(char *)&ling ,sizeof(struct linger)); ip = (unsigned char *)&ip_server; //rt_printf("应用104线程已启动(IP:%d.%d.%d.%d,PORT:%d)。\r\n",ip[0],ip[1],ip[2],ip[3],NET_104_PORT); #ifdef CPU_FUXI sprintf(tmpbuf,"net_104_%d",net); prctl(PR_SET_NAME, tmpbuf); #endif while(1) { // 服务器阻塞,直到客户程序建立连接 sin_size = sizeof (struct sockaddr_in); sk_tmp = rt_accept (sk_server, (struct sockaddr*)(&client_addr), &sin_size); // 检查是否退出 if(g_net_104_exit_flag[net]) { // 关闭客户端 net_104_close(net); // 关闭服务器 rt_socket_close (sk_server); // complete_and_exit(&g_exit_completion_net_104[net], 0); return 0; } //是否重新初始化 if(g_net_reinit[net] == NET_SOCKET_CLOSING) { // 关闭客户端 net_104_close(net); // 关闭服务器 rt_socket_close (sk_server); //置已经关闭标志 g_net_reinit[net] = NET_SOCKET_CLOSED; //等待IP修改完成 while(g_net_reinit[net] != NET_SOCKET_REINIT) { msleep(100); } //置为初始化 g_net_reinit[net] = NET_SOCKET_INIT; goto NET_BEGIN; } //注意,用指针类型的socket接口,失败是返回NULL //而整数类型的socket接口,失败返回的是负数 //#ifdef RT_SOCKET_FD #if 1 if (sk_tmp < 0) { msleep(100); // 超时. continue; } #else if (sk_tmp == RT_SOCKET_ACCEPT_RST) { msleep(100); // 超时 continue; } #endif g_104_client[net] = sk_tmp; ip = (unsigned char *)&client_addr.sin_addr.s_addr; rt_printf("104 client[net=%d,s=%d] connect(ip=%d.%d.%d.%d)!\r\n", net ,sk_tmp,ip[0],ip[1],ip[2],ip[3]); #if 0 // 检查是否主站IP if(tRunPara.ip104Master1 || tRunPara.ip104Master2 || tRunPara.ip104Master3 || tRunPara.ip104Master4) { u32 network0,network1; network0 = client_addr.sin_addr.s_addr & tRunPara.tNetPara[CFG_ETH_MAX_LOGIC-1].wMask; network1 = tRunPara.tNetPara[CFG_ETH_MAX_LOGIC-1].wIp & tRunPara.tNetPara[CFG_ETH_MAX_LOGIC-1].wMask; if( (client_addr.sin_addr.s_addr != tRunPara.ip104Master1) && (client_addr.sin_addr.s_addr != tRunPara.ip104Master2) && (client_addr.sin_addr.s_addr != tRunPara.ip104Master3) && (client_addr.sin_addr.s_addr != tRunPara.ip104Master4) && (g_ip_net_maintain == 0) // 如果维护工具已连接,任意104主站可以接入。 &&(network0 != network1)) // 运行维护网络的104连接进来 { net_104_close(net); rt_printf("主站IP不在允许范围内!\r\n"); ip = (unsigned char *)&tRunPara.ip104Master1; rt_printf("允许IP1:%d.%d.%d.%d)!\r\n",ip[0],ip[1],ip[2],ip[3]); ip = (unsigned char *)&tRunPara.ip104Master2; rt_printf("允许IP2:%d.%d.%d.%d)!\r\n",ip[0],ip[1],ip[2],ip[3]); ip = (unsigned char *)&tRunPara.ip104Master3; rt_printf("允许IP3:%d.%d.%d.%d)!\r\n",ip[0],ip[1],ip[2],ip[3]); ip = (unsigned char *)&tRunPara.ip104Master4; rt_printf("允许IP4:%d.%d.%d.%d)!\r\n",ip[0],ip[1],ip[2],ip[3]); ip = (unsigned char *)&g_ip_net_maintain; rt_printf("允许IP5:%d.%d.%d.%d)!\r\n",ip[0],ip[1],ip[2],ip[3]); msleep(500); continue; } } #endif // 客户端已连接,处理客户端命令 // 初始化接收FIFO rt_fifo_init(&g_net_104_fifo[net],g_net_104_buf[net],NET_104_BUF_SIZE); IEC104_Recv_Reset(&g_t104[net]); memcpy(g_t104[net].ip,(unsigned char *)&client_addr.sin_addr.s_addr,4); g_t104[net].LinkCountNet++; //接收帧超时初始化,超时设置为测试超时加确认超时后的2倍。 recv_timeout=0; recv_timeout_set = (tRunPara.w104TestTime + tRunPara.w104AckTime)*2*10; // 循环处理接收数据 while(1) { len = net_104_recv(net, g_net_104_buf_frame[net], NET_RECV_MAX); // 检查是否退出 if(g_net_104_exit_flag[net]) { // 关闭客户端 net_104_close(net); // 关闭服务器 rt_socket_close (sk_server); // complete_and_exit(&g_exit_completion_net_104[net], 0); return 0; } //是否重新初始化 if(g_net_reinit[net] == NET_SOCKET_CLOSING) { // 关闭客户端 net_104_close(net); // 关闭服务器 rt_socket_close (sk_server); //置已经关闭标志 g_net_reinit[net] = NET_SOCKET_CLOSED; //等待IP修改完成 while(g_net_reinit[net] != NET_SOCKET_REINIT) { msleep(100); } //置为初始化 g_net_reinit[net] = NET_SOCKET_INIT; rt_printf("net_104_thread:链路重新初始化!\r\n"); goto NET_BEGIN; } if(g_net_104_close_flag[net]) { break; } if((len == -EAGAIN)||(len==-1)) { if( rt_get_net_linkstatus(rt_get_netcard_id_from_socket(g_104_client[net] ))==1) { link_timeout++; if(link_timeout > RT_LINK_TIMEOUT*10) //100ms超时 { rt_printf("net_104_thread: link down\r\n"); link_timeout = 0; break; } } else { link_timeout=0; } // 接收帧超时 recv_timeout++; if(recv_timeout>recv_timeout_set) { rt_printf ("net_104_thread:接收帧超时(value=%d,set=%d)\r\n",recv_timeout,recv_timeout_set); break; } } else if((len == 0)||(len<-1)) { //调用recv时,如果返回0,则表示对端关闭;返回负数,表示出错 rt_printf ("net_104_thread:rt_recv(ret=%d)\r\n",len); // TODO:以下休眠语句必须屏蔽,否则net_104_close会延时调用, // 导致主循环可以在此断开的socket上继续发送数据,引起系统异常。 // msleep(100); break; } else { int space; space = rt_fifo_space(&g_net_104_fifo[net]); if(space > len) { rt_fifo_put(&g_net_104_fifo[net],g_net_104_buf_frame[net],len); if(tRunPara.tNetPara[net].ethid300) // 超时30s,端口未收到数据,关闭端口 { rt_printf_time("104客户端30S超时关闭(net=%d)\r\n",net); break; } } else if (len <= 0) { //调用recv时,如果返回0,则表示对端关闭;返回负数,表示出错 rt_printf_time ("104客户端关闭(net=%d,ret=%d)\r\n",net,len); msleep(100); break; } else { rt_fifo_put(&g_net_104_fifo[net],g_net_104_buf_frame[net],len); recv_timeout=0; //超时计数器清零 if(tRunPara.tNetPara[net].ethid /*------------------------------- 宏定义 -------------------------------------- */ #define NET_104_BUF_SIZE 16384 /*------------------------------ 类型结构 ------------------------------------- */ /*------------------------------ 全局变量 ------------------------------------- */ static struct task_struct * g_ts_104[IEC104_TOTAL_SOCKETS]; static SOCKET g_104_client[IEC104_TOTAL_SOCKETS]; static unsigned char g_net_104_buf_frame[IEC104_TOTAL_SOCKETS][NET_RECV_MAX]; // 以太网接收缓冲区 static unsigned char g_net_104_buf[IEC104_TOTAL_SOCKETS][NET_104_BUF_SIZE]; // 以太网接收缓冲区 static struct rt_fifo g_net_104_fifo[IEC104_TOTAL_SOCKETS]; static volatile int g_net_reinit[CFG_ETH_MAX_LOGIC]={NET_SOCKET_INIT};//网络重新初始化标志.0表示不用重新初始化,1表示需要重新初始化 static struct semaphore net_r_lock[CFG_ETH_MAX_LOGIC]; //接收锁 static struct semaphore net_s_lock[CFG_ETH_MAX_LOGIC]; //发送锁 static int g_net_104_close_flag[CFG_ETH_MAX_LOGIC]; static int g_net_104_exit_flag[CFG_ETH_MAX_LOGIC]; static struct completion g_exit_completion_net_104[CFG_ETH_MAX_LOGIC]; extern u32 g_ip_net_maintain; // 104维护工具调试客户端地址 /*------------------------------ 函数声明 ------------------------------------- */ int _net_104_thread(IEC104_DEF *pt104); //设置socket关闭标志并等待socket关闭完成 void net_104_set_sock_closing(int no) { //if(no >= g_net_num) return; g_net_reinit[no] = NET_SOCKET_CLOSING; //置关闭标志 if(!tRunPara.b104Client) { int count=0; //等待关闭完成 while(g_net_reinit[no] != NET_SOCKET_CLOSED) { msleep(20); if(++count>150) { rt_printf("104 client socket close fail!!!\r\n"); return; } } } } //设置socket重新创建标志 void net_104_set_sock_initflag(int no) { // if(no >= g_net_num) return; if(g_net_reinit[no] == NET_SOCKET_CLOSED) { g_net_reinit[no] = NET_SOCKET_REINIT; } } /************************************************************************** 函数名称:Net_Init 函数版本:1.00 作者: 创建日期:2009.1.8 函数功能说明:以太网初始化 输入参数: 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ void Net_Init(void) { unsigned char mac[6]; unsigned char ip[4]; unsigned char mask[4]; unsigned char gw[4]; unsigned char routenet[4]; unsigned char routenetmask[4]; unsigned char routegate[4]; // char * acteth; int i,j; char tmp[16]; int ret = 0; int gateflag = 0; if(tRunPara.w104K == 0) { if(pRunSet->bTT_SoeResumeComm || pRunSet->bTT_SoeResumePower) { rt_printf("定值参数错误:SOE通讯续传、SOE上电续传时参数104 K值不能为0.\r\n"); rt_err_set(ERR_CODE_INIT_SOFTWARE,0); } } #if 0 int debug_port; acteth = env_get_info(ENV_ETHACT); if(acteth != NULL) { if(strcmp(acteth,"FEC0") == 0) { debug_port = 0; } else if(strcmp(acteth,"FEC1") == 0) { debug_port = 1; } else if(strcmp(acteth,"DM9000") == 0) { debug_port = 2; } else { debug_port = 0; } } else { debug_port = 0; } #endif // if(acteth != NULL && strcmp(acteth,"DM9000") == 0) // { // debug_port = 2; // } // else // { // debug_port = 0; // } //for(i=0;i>24)&0xff); mac[3] = (BYTE)((tRunPara.tNetPara[i].wIp>>16)&0xff); mac[4] = (BYTE)((tRunPara.tNetPara[i].wIp>>8)&0xff); mac[5] = (BYTE)((tRunPara.tNetPara[i].wIp>>0)&0xff); } // 设置IP ip[0] = (BYTE)((tRunPara.tNetPara[i].wIp>>24)&0xff); ip[1] = (BYTE)((tRunPara.tNetPara[i].wIp>>16)&0xff); ip[2] = (BYTE)((tRunPara.tNetPara[i].wIp>>8)&0xff); ip[3] = (BYTE)((tRunPara.tNetPara[i].wIp>>0)&0xff); // 设置MASK mask[0] = (BYTE)((tRunPara.tNetPara[i].wMask>>24)&0xff); mask[1] = (BYTE)((tRunPara.tNetPara[i].wMask>>16)&0xff); mask[2] = (BYTE)((tRunPara.tNetPara[i].wMask>>8)&0xff); mask[3] = (BYTE)((tRunPara.tNetPara[i].wMask>>0)&0xff); #if defined(NET3_IP5) || defined(NET4_IP5) if(tRunPara.tNetPara[i].old_ethid != tRunPara.tNetPara[i].ethid) //网络号已改变且为物理网卡时 { BYTE count; if((int)tRunPara.tNetPara[i].old_index == 0) { rt_if_down(tRunPara.tNetPara[i].old_ethid); //此时卸载当前网卡所有P 比如eth0 eth0:2 eth0:3 //rt_printf("down eth%d\r\n",tRunPara.tNetPara[i].old_ethid); } else if((int)tRunPara.tNetPara[i].old_index > 0) { rt_if_down2(tRunPara.tNetPara[i].old_ethid, tRunPara.tNetPara[i].old_index); // rt_printf("down eth%d:%d\r\n",tRunPara.tNetPara[i].old_ethid,tRunPara.tNetPara[i].old_index); } for(count=i+1; count 0) //若关联的为虚拟网卡则默认不需要进行投退 { rt_if_down2(tRunPara.tNetPara[count].old_ethid, tRunPara.tNetPara[count].old_index); //rt_printf("down eth%d:%d\r\n",tRunPara.tNetPara[count].old_ethid,tRunPara.tNetPara[count].old_index); tRunPara.tNetPara[count].old_index = 0; } else { rt_if_down(tRunPara.tNetPara[count].old_ethid); //此时卸载当前网卡所有P 比如eth0 eth0:2 eth0:3 //rt_printf("down eth%d\r\n",tRunPara.tNetPara[count].old_ethid); } } } else if((int)tRunPara.tNetPara[i].old_index > 0) //网络号未改变时的 虚拟网卡 { rt_if_down2(tRunPara.tNetPara[i].old_ethid, tRunPara.tNetPara[i].old_index); //rt_printf("down eth%d:%d\r\n",tRunPara.tNetPara[i].old_ethid,tRunPara.tNetPara[i].old_index); } #else //检查是否需要关闭虚拟网卡 if((int)tRunPara.tNetPara[i].old_index > 0) { rt_if_down2(tRunPara.tNetPara[i].old_ethid, tRunPara.tNetPara[i].old_index); } #endif //获取配置相同的ethid有多少个 ret=get_ethcnt(i, tRunPara.tNetPara[i].ethid); //没有的话,使用物理的 if(ret==0) { // 设置网络参数 net_if_set((int)tRunPara.tNetPara[i].ethid,mac,ip, mask); tRunPara.tNetPara[i].index = 0; //rt_printf_time("ret = %d, ethID = %d\r\n",ret,tRunPara.tNetPara[i].ethid); } else //有的话使用虚拟的 { #if defined(NET3_IP5) || defined(NET4_IP5) net_if_set2((int)tRunPara.tNetPara[i].ethid, ret, 0, ip, mask); tRunPara.tNetPara[i].index = ret; #else net_if_set2((int)tRunPara.tNetPara[i].ethid, i+1, 0, ip, mask); tRunPara.tNetPara[i].index = i+1; #endif //rt_printf_time("Xnet = %d,ip=%x, ethID = %d, ret = %d\r\n",tRunPara.tNetPara[i].index,ip[3],tRunPara.tNetPara[i].ethid,ret); } tRunPara.tNetPara[i].old_ethid= tRunPara.tNetPara[i].ethid; tRunPara.tNetPara[i].old_index= tRunPara.tNetPara[i].index; // 如果是调试串口,不初始化 //if(i == debug_port) if(i==(CFG_ETH_MAX_LOGIC-1)) { //设置IP环境变量 if(tRunPara.tNetPara[i].changed & 0x01) { sprintf(tmp, "%d.%d.%d.%d",( tRunPara.tNetPara[i].wIp>>24)&0xff, (tRunPara.tNetPara[i].wIp>>16)&0xff, (tRunPara.tNetPara[i].wIp>>8)&0xff, (tRunPara.tNetPara[i].wIp&0xff)); env_setenv("ipaddr", tmp); } //设置Gate环境变量 if(tRunPara.tNetPara[i].changed & 0x02) { sprintf(tmp, "%d.%d.%d.%d", ( tRunPara.tNetPara[i].wGate>>24)&0xff, (tRunPara.tNetPara[i].wGate>>16)&0xff, (tRunPara.tNetPara[i].wGate>>8)&0xff, (tRunPara.tNetPara[i].wGate&0xff)); if(tRunPara.tNetPara[i].wGate) env_setenv("gatewayip", tmp); } //设置Mask环境变量 if(tRunPara.tNetPara[i].changed & 0x04) { sprintf(tmp, "%d.%d.%d.%d", ( tRunPara.tNetPara[i].wMask>>24)&0xff, (tRunPara.tNetPara[i].wMask>>16)&0xff, (tRunPara.tNetPara[i].wMask>>8)&0xff, (tRunPara.tNetPara[i].wMask&0xff)); env_setenv("netmask", tmp); } //设置ethact环境变量 if(tRunPara.tNetPara[i].changed & 0x40) { if(tRunPara.tNetPara[i].ethid >= 2) { sprintf(tmp, "%s", "DM9000"); } else { sprintf(tmp, "FEC%d", (int)tRunPara.tNetPara[i].ethid); } env_setenv("ethact", tmp); } //continue; } //网段 routenet[0] = (BYTE)((tRunPara.tNetPara[i].wRouteNet>>24)&0xff); routenet[1] = (BYTE)((tRunPara.tNetPara[i].wRouteNet>>16)&0xff); routenet[2] = (BYTE)((tRunPara.tNetPara[i].wRouteNet>>8)&0xff); routenet[3] = (BYTE)((tRunPara.tNetPara[i].wRouteNet>>0)&0xff); //网段掩码 routenetmask[0] = (BYTE)((tRunPara.tNetPara[i].wRouteNetMask>>24)&0xff); routenetmask[1] = (BYTE)((tRunPara.tNetPara[i].wRouteNetMask>>16)&0xff); routenetmask[2] = (BYTE)((tRunPara.tNetPara[i].wRouteNetMask>>8)&0xff); routenetmask[3] = (BYTE)((tRunPara.tNetPara[i].wRouteNetMask>>0)&0xff); //网关 routegate[0] = (BYTE)((tRunPara.tNetPara[i].wRouteGate>>24)&0xff); routegate[1] = (BYTE)((tRunPara.tNetPara[i].wRouteGate>>16)&0xff); routegate[2] = (BYTE)((tRunPara.tNetPara[i].wRouteGate>>8)&0xff); routegate[3] = (BYTE)((tRunPara.tNetPara[i].wRouteGate>>0)&0xff); net_route_set(routenet, routenetmask, routegate); //g_net_reinit[i] = 1;//置重新初始化标志 net_104_set_sock_initflag(i); net_debug_set_sock_initflag(); net_maintain_set_sock_initflag(); #if 0 // 得到并打印新的网络参数 // net_if_get(i,mac,ip,mask); rt_printf("new mac: %02x:%02x:%02x:%02x:%02x:%02x\r\n",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); rt_printf("new ip:%d.%d.%d.%d\r\n",ip[0],ip[1],ip[2],ip[3]); rt_printf("new mask:%d.%d.%d.%d\r\n",mask[0],mask[1],mask[2],mask[3]); rt_printf("new gw:%d.%d.%d.%d\r\n",gw[0],gw[1],gw[2],gw[3]); #endif } ret = 0; //查找是否关闭物理网卡 for(i=0;i>24)&0xff); gw[1] = (BYTE)((tRunPara.tNetPara[0].wGate>>16)&0xff); gw[2] = (BYTE)((tRunPara.tNetPara[0].wGate>>8)&0xff); gw[3] = (BYTE)((tRunPara.tNetPara[0].wGate>>0)&0xff); // 如果地址是默认的0.0.0.0,说明用户不需要网关, // 不重设网关,使uboot环境变量中的网关有效,方便调试。 if(gw[0]==0 && gw[1]==0 && gw[2]==0 && gw[3]==0) { return; } if(gateflag) { net_gateway_set(gw); } return; } /*------------------------------ 外部函数 ------------------------------------- 外部函数供其它实体文件引用,必须仔细检查传入参数的合法性. */ int net_104_init(void) { int net; char buf[32]; struct sched_param sp; pid_t pid; IEC104_Init(); for(net=0; netbTT_ESAM) { iec_GetMsgInfo(buf,len,&ti,&cot,&se,false); len = sec_encrpt_iec(ti,cot,se,buf,len,&out); if(len > 0) { ret=net_104_send_ext(net,out, len); } } else { ret=net_104_send_ext(net,buf, len); } return ret; } static int net_104_recv(int net, unsigned char *buf,int len) { int ret = 0; down(&net_r_lock[net]); if(g_104_client[net] != RT_SOCKET_ERR) { ret = rt_recv (g_104_client[net], (void *)buf, NET_RECV_MAX, MSG_DONTWAIT); } up(&net_r_lock[net]); return ret; } void net_104_close(int net) { down(&net_r_lock[net]); down(&net_s_lock[net]); if(g_104_client[net] != RT_SOCKET_ERR) { rt_socket_close (g_104_client[net]); g_104_client[net] = RT_SOCKET_ERR; } g_net_104_close_flag[net] = 0; up(&net_s_lock[net]); up(&net_r_lock[net]); } void net_104_close_flag(int net) { down(&net_r_lock[net]); down(&net_s_lock[net]); if(g_104_client[net] != RT_SOCKET_ERR) { g_net_104_close_flag[net] = 1; } up(&net_s_lock[net]); up(&net_r_lock[net]); } int net_104_is_connect(int net) { if((g_104_client[net] == RT_SOCKET_ERR) || g_net_104_close_flag[net]) { return 0; } return 1; } /*------------------------------ 内部函数 ------------------------------------- 内部函数以下划线‘_’开头,不需要检查参数的合法性. */ extern int _net_104Client_thread(int net); int _net_104_thread(IEC104_DEF *pt104) { struct sockaddr_in server_addr; struct sockaddr_in client_addr; struct linger ling; SOCKET sk_server,sk_tmp ; int net; int sin_size; int len,ret; unsigned char *ip; unsigned int ip_server; unsigned int recv_timeout,recv_timeout_set; unsigned int link_timeout=dTCounter; unsigned char buf; u16 w104Port; if(tRunPara.b104Client) // 104采用客户端 { net = pt104->cNetIndex; _net_104Client_thread(net); return 0; } NET_BEGIN: net = pt104->cNetIndex; // 服务器端开始建立socket描述符 //sk_server = rt_socket(AF_INET, SOCK_STREAM, 0); sk_server = rt_socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0);//noted by sunxi: 20221212 设为非阻塞型 if (sk_server == RT_SOCKET_ERR) { RT_PRINTF_POSITION(); g_ts_104[net] = NULL; return -1; } // 服务器端填充sockaddr结构 memset (&server_addr, 0, sizeof (struct sockaddr_in)); server_addr.sin_family = AF_INET; //rt_if_ip_get(net,(unsigned char *)&ip_server); //ret=get_ethcnt(net, tRunPara.tNetPara[net].ethid); //IP在物理网卡上 if( tRunPara.tNetPara[net].index == 0) { rt_if_ip_get(tRunPara.tNetPara[net].ethid, (unsigned char *)&ip_server); w104Port = tRunPara.w104Port; // 网络1、2使用可变端口,保证级联时不冲突。 } else //IP在虚拟网卡上 { //从虚拟网卡获取IP地址 rt_printf("%s 获取虚拟网卡IP地址:ethid=%d,index=%d\r\n",__func__,tRunPara.tNetPara[net].ethid,tRunPara.tNetPara[net].index); rt_if_ip_get2(tRunPara.tNetPara[net].ethid, tRunPara.tNetPara[net].index, (unsigned char *)&ip_server); w104Port = NET_104_PORT; // 调试网络使用固定的2404端口,保证维护工具可以连接。 } server_addr.sin_addr.s_addr = htonl (ip_server); server_addr.sin_port = htons (w104Port); // 捆绑sockfd描述符 ret = rt_bind (sk_server, (struct sockaddr *)(&server_addr), sizeof (struct sockaddr)); if (ret < 0) { RT_PRINTF_POSITION(); rt_socket_close (sk_server); g_ts_104[net] = NULL; return -2; } // 监听sockfd描述符 ret = rt_listen (sk_server, 1); if (ret < 0) { RT_PRINTF_POSITION(); rt_socket_close (sk_server); g_ts_104[net] = NULL; return -3; } // 设置关闭延时 ling.l_onoff = 1; ling.l_linger = 0; rt_setsockopt(sk_server,SOL_SOCKET,SO_LINGER,(char *)&ling ,sizeof(struct linger)); ip = (unsigned char *)&ip_server; //rt_printf("应用104线程已启动(IP:%d.%d.%d.%d,PORT:%d)。\r\n",ip[0],ip[1],ip[2],ip[3],NET_104_PORT); while(1) { msleep(100);//noted by sunxi: 20221212 设为非阻塞型 // 服务器阻塞,直到客户程序建立连接 sin_size = sizeof (struct sockaddr_in); sk_tmp = rt_accept (sk_server, (struct sockaddr*)(&client_addr), &sin_size); // 检查是否退出 if(g_net_104_exit_flag[net]) { // 关闭客户端 net_104_close(net); // 关闭服务器 rt_socket_close (sk_server); complete_and_exit(&g_exit_completion_net_104[net], 0); return 0; } //是否重新初始化 if(g_net_reinit[net] == NET_SOCKET_CLOSING) { // 关闭客户端 net_104_close(net); // 关闭服务器 rt_socket_close (sk_server); //置已经关闭标志 g_net_reinit[net] = NET_SOCKET_CLOSED; //等待IP修改完成 while(g_net_reinit[net] != NET_SOCKET_REINIT) { msleep(100); } //置为初始化 g_net_reinit[net] = NET_SOCKET_INIT; goto NET_BEGIN; } //注意,用指针类型的socket接口,失败是返回NULL //而整数类型的socket接口,失败返回的是负数 #ifdef RT_SOCKET_FD if (sk_tmp < 0) { //msleep(100);//noted by sunxi: 20221212 设为非阻塞型 // 超时 continue; } #else if (sk_tmp == RT_SOCKET_ACCEPT_RST) { //msleep(100);//noted by sunxi: 20221212 设为非阻塞型 // 超时 continue; } #endif g_104_client[net] = sk_tmp; ip = (unsigned char *)&client_addr.sin_addr.s_addr; rt_printf("104 client[net=%d,s=%d] connect(ip=%d.%d.%d.%d)!\r\n", net ,sk_tmp,ip[0],ip[1],ip[2],ip[3]); // 检查是否主站IP if(tRunPara.ip104Master1 || tRunPara.ip104Master2 || tRunPara.ip104Master3 || tRunPara.ip104Master4) { u32 network0,network1; network0 = client_addr.sin_addr.s_addr & tRunPara.tNetPara[CFG_ETH_MAX_LOGIC-1].wMask; network1 = tRunPara.tNetPara[CFG_ETH_MAX_LOGIC-1].wIp & tRunPara.tNetPara[CFG_ETH_MAX_LOGIC-1].wMask; if( (client_addr.sin_addr.s_addr != tRunPara.ip104Master1) && (client_addr.sin_addr.s_addr != tRunPara.ip104Master2) && (client_addr.sin_addr.s_addr != tRunPara.ip104Master3) && (client_addr.sin_addr.s_addr != tRunPara.ip104Master4) && (g_ip_net_maintain == 0) // 如果维护工具已连接,任意104主站可以接入。 &&(network0 != network1)) // 运行维护网络的104连接进来 { net_104_close(net); rt_printf("主站IP不在允许范围内!\r\n"); ip = (unsigned char *)&tRunPara.ip104Master1; rt_printf("允许IP1:%d.%d.%d.%d)!\r\n",ip[0],ip[1],ip[2],ip[3]); ip = (unsigned char *)&tRunPara.ip104Master2; rt_printf("允许IP2:%d.%d.%d.%d)!\r\n",ip[0],ip[1],ip[2],ip[3]); ip = (unsigned char *)&tRunPara.ip104Master3; rt_printf("允许IP3:%d.%d.%d.%d)!\r\n",ip[0],ip[1],ip[2],ip[3]); ip = (unsigned char *)&tRunPara.ip104Master4; rt_printf("允许IP4:%d.%d.%d.%d)!\r\n",ip[0],ip[1],ip[2],ip[3]); ip = (unsigned char *)&g_ip_net_maintain; rt_printf("允许IP5:%d.%d.%d.%d)!\r\n",ip[0],ip[1],ip[2],ip[3]); msleep(500); continue; } } // 客户端已连接,处理客户端命令 // 初始化接收FIFO rt_fifo_init(&g_net_104_fifo[net],g_net_104_buf[net],NET_104_BUF_SIZE); IEC104_Recv_Reset(&g_t104[net]); memcpy(g_t104[net].ip,(unsigned char *)&client_addr.sin_addr.s_addr,4); g_t104[net].LinkCountNet++; //接收帧超时初始化,超时设置为测试超时加确认超时后的2倍。 recv_timeout=dTCounter; recv_timeout_set = (tRunPara.w104TestTime + tRunPara.w104AckTime)*2*T_1s; // 循环处理接收数据 while(1) { msleep(2);//noted by sunxi: 20221212 设为非阻塞型 len = net_104_recv(net, g_net_104_buf_frame[net], NET_RECV_MAX); // 检查是否退出 if(g_net_104_exit_flag[net]) { // 关闭客户端 net_104_close(net); // 关闭服务器 rt_socket_close (sk_server); complete_and_exit(&g_exit_completion_net_104[net], 0); return 0; } //是否重新初始化 if(g_net_reinit[net] == NET_SOCKET_CLOSING) { // 关闭客户端 net_104_close(net); // 关闭服务器 rt_socket_close (sk_server); //置已经关闭标志 g_net_reinit[net] = NET_SOCKET_CLOSED; //等待IP修改完成 while(g_net_reinit[net] != NET_SOCKET_REINIT) { msleep(100); } //置为初始化 g_net_reinit[net] = NET_SOCKET_INIT; rt_printf("net_104_thread:链路重新初始化!\r\n"); goto NET_BEGIN; } if(g_net_104_close_flag[net]) { break; } if(len == -EAGAIN) { if( rt_get_net_linkstatus(rt_get_netcard_id_from_socket(g_104_client[net] ))==1) { if((dTCounter - link_timeout)>RT_LINK_TIMEOUT*T_1s) //500ms超时 { rt_printf("net_104_thread: link down\r\n"); link_timeout = dTCounter; break; } } else { link_timeout=dTCounter; } // 接收帧超时 if((dTCounter - recv_timeout)>recv_timeout_set) { rt_printf ("net_104_thread:接收帧超时(value=%d,set=%d)\r\n",recv_timeout,recv_timeout_set); break; } } else if (len <= 0) { //调用recv时,如果返回0,则表示对端关闭;返回负数,表示出错 rt_printf ("net_104_thread:rt_recv(ret=%d)\r\n",len); // TODO:以下休眠语句必须屏蔽,否则net_104_close会延时调用, // 导致主循环可以在此断开的socket上继续发送数据,引起系统异常。 // msleep(100); break; } else { int space; space = rt_fifo_space(&g_net_104_fifo[net]); if(space > len) { rt_fifo_put(&g_net_104_fifo[net],g_net_104_buf_frame[net],len); if(tRunPara.tNetPara[net].ethid30*T_1s) // 超时30s,端口未收到数据,关闭端口 { rt_printf_time("104客户端30S超时关闭(net=%d)\r\n",net); break; } } else if (len <= 0) { //调用recv时,如果返回0,则表示对端关闭;返回负数,表示出错 rt_printf_time ("104客户端关闭(net=%d,ret=%d)\r\n",net,len); msleep(100); break; } else { rt_fifo_put(&g_net_104_fifo[net],g_net_104_buf_frame[net],len); recv_timeout=dTCounter; //超时计数器清零 if(tRunPara.tNetPara[net].ethid