From 020fae874b6ec17f2a1cfc14d63007a3399335cf Mon Sep 17 00:00:00 2001 From: Hannes Diethelm Date: Sat, 23 May 2026 12:04:04 +0200 Subject: [PATCH 1/5] Xemomai EVL: Abort on evl_attach_self() failed If this fails, everything beaks and there are a thon of error messages. So better abort here --- src/rtapi/uspace_xenomai_evl.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rtapi/uspace_xenomai_evl.cc b/src/rtapi/uspace_xenomai_evl.cc index 6f1988337b0..f4965deb19a 100644 --- a/src/rtapi/uspace_xenomai_evl.cc +++ b/src/rtapi/uspace_xenomai_evl.cc @@ -124,6 +124,7 @@ struct EvlApp : RtapiApp { int tfd = evl_attach_self("linuxcnc-thread:%d", gettid()); if (tfd < 0) { rtapi_print("evl_attach_self() failed ret %i errno %i\n", tfd, errno); + return nullptr; } } From d1731461836563cda7e9279bbb40d85091ab61d8 Mon Sep 17 00:00:00 2001 From: Hannes Diethelm Date: Fri, 29 May 2026 23:26:26 +0200 Subject: [PATCH 2/5] Xenomai EVL network for Hostmot2 Split into two network implementations, one for posix and one for evl. There are now two modules: hm2_eth and hm2_eth_evl. --- src/Makefile | 13 +- src/hal/drivers/mesa-hostmot2/hm2_eth.c | 197 ++--------- src/hal/drivers/mesa-hostmot2/hm2_eth.h | 12 + src/hal/drivers/mesa-hostmot2/hm2_eth_net.c | 173 ++++++++++ src/hal/drivers/mesa-hostmot2/hm2_eth_net.h | 40 +++ .../drivers/mesa-hostmot2/hm2_eth_net_evl.c | 313 ++++++++++++++++++ 6 files changed, 577 insertions(+), 171 deletions(-) create mode 100644 src/hal/drivers/mesa-hostmot2/hm2_eth_net.c create mode 100644 src/hal/drivers/mesa-hostmot2/hm2_eth_net.h create mode 100644 src/hal/drivers/mesa-hostmot2/hm2_eth_net_evl.c diff --git a/src/Makefile b/src/Makefile index 69952ddeac9..6ddb0f28e70 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1004,6 +1004,9 @@ obj-$(CONFIG_HOSTMOT2) += hostmot2.o hm2_test.o hm2_pci.o hm2_7i43.o hm2_7i90.o obj-$(CONFIG_HOSTMOT2) += hm2_modbus.o ifeq ($(BUILD_SYS),uspace) obj-$(CONFIG_HOSTMOT2) += hm2_eth.o +ifeq ($(CONFIG_USPACE_XENOMAI_EVL),y) +obj-$(CONFIG_HOSTMOT2) += hm2_eth_evl.o +endif obj-$(CONFIG_HOSTMOT2) += hm2_spi.o hm2_rpspi.o hm2_spix.o endif hostmot2-objs := \ @@ -1049,6 +1052,11 @@ hm2_pci-objs := \ $(MATHSTUB) hm2_eth-objs := \ hal/drivers/mesa-hostmot2/hm2_eth.o \ + hal/drivers/mesa-hostmot2/hm2_eth_net.o \ + $(MATHSTUB) +hm2_eth_evl-objs := \ + hal/drivers/mesa-hostmot2/hm2_eth.o \ + hal/drivers/mesa-hostmot2/hm2_eth_net_evl.o \ $(MATHSTUB) hm2_spi-objs := \ hal/drivers/mesa-hostmot2/hm2_spi.o \ @@ -1286,7 +1294,7 @@ modules: $(patsubst %.o,../rtlib/%.so,$(obj-m)) $(Q)objdump -w -j .rtapi_export -t objects/$*.tmp \ | awk 'BEGIN{print "{ global :"} /rtapi_exported_/{printf("%s;\n", substr($$6,16))} END{print "local : * ; };"}' \ > objects/$*.ver - $(Q)$(CC) -shared -Bsymbolic -Wl,--version-script,objects/$*.ver -o $@ $^ -lm $(LDFLAGS) + $(Q)$(CC) -shared -Bsymbolic -Wl,--version-script,objects/$*.ver -o $@ $^ -lm $(XENOMAI_EVL_LDFLAGS) $(LDFLAGS) $(Q)chmod -x $@ RTFLAGS += -fno-strict-aliasing -fwrapv @@ -1297,7 +1305,7 @@ $(sort $(filter-out objects/rtemc/tp/cruckig/%,$(RTOBJS))) : objects/rt%.o : %.c @rm -f $@ @mkdir -p $(dir $@) $(Q)$(CC) -c $(OPT) $(DEBUG) $(EXTRA_DEBUG) -DRTAPI \ - $(EXTRA_CFLAGS) \ + $(EXTRA_CFLAGS) $(XENOMAI_EVL_CFLAGS) \ -MP -MD -MF "${@:.o=.d}" -MT "$@" \ $< -o $@ @@ -1397,6 +1405,7 @@ endif ../rtlib/hal_parport$(MODULE_EXT): $(addprefix objects/rt,$(hal_parport-objs)) ../rtlib/hal_ppmc$(MODULE_EXT): $(addprefix objects/rt,$(hal_ppmc-objs)) ../rtlib/hm2_eth$(MODULE_EXT): $(addprefix objects/rt,$(hm2_eth-objs)) +../rtlib/hm2_eth_evl$(MODULE_EXT): $(addprefix objects/rt,$(hm2_eth_evl-objs)) ../rtlib/hm2_spi$(MODULE_EXT): $(addprefix objects/rt,$(hm2_spi-objs)) ../rtlib/hm2_rpspi$(MODULE_EXT): $(addprefix objects/rt,$(hm2_rpspi-objs)) ../rtlib/hm2_spix$(MODULE_EXT): $(addprefix objects/rt,$(hm2_spix-objs)) diff --git a/src/hal/drivers/mesa-hostmot2/hm2_eth.c b/src/hal/drivers/mesa-hostmot2/hm2_eth.c index 5d89756a97a..94bd4732457 100644 --- a/src/hal/drivers/mesa-hostmot2/hm2_eth.c +++ b/src/hal/drivers/mesa-hostmot2/hm2_eth.c @@ -46,6 +46,7 @@ #include "hostmot2-lowlevel.h" #include "hostmot2.h" #include "hm2_eth.h" +#include "hm2_eth_net.h" struct kvlist { struct rtapi_list_head list; @@ -452,17 +453,11 @@ static const char *hm2_8cSS_pin_names[] = { }; -#define UDP_PORT 27181 -#define SEND_TIMEOUT_US 10 -#define RECV_TIMEOUT_US 10 #define READ_PCK_DELAY_NS 10000 -static hm2_eth_t boards[MAX_ETH_BOARDS]; +static hm2_eth_t boards[MAX_ETH_BOARDS]={}; -/// ethernet io functions - -static int eth_socket_send(int sockfd, const void *buffer, int len, int flags); -static int eth_socket_recv(int sockfd, void *buffer, int len, int flags); +/// firewall functions // hm2_eth installs firewall rules to isolate the dedicated Mesa // interface from non-realtime traffic. rtapi_app raises CAP_NET_ADMIN @@ -584,7 +579,7 @@ static fw_state_t fw_state = FW_UNRESOLVED; // Resolve and bring up the firewall backend on first call, caching the // result. Returns true when a backend is ready, false when isolation // is unavailable or disabled. -static bool use_firewall() { +bool use_firewall() { if(fw_state != FW_UNRESOLVED) return fw_state == FW_READY; @@ -631,7 +626,7 @@ static bool use_firewall() { // Drop all rules from our chain/table but keep the chain in place, so a // fresh set can be installed on (re-)init. -static void clear_firewall() { +void clear_firewall() { if(!use_firewall()) return; switch(fw_backend) { case FW_IPTABLES: @@ -672,7 +667,7 @@ static char* inet_ntoa_buf(struct in_addr in, char *buf, size_t n) { return buf; } -static char* fetch_ifname(int sockfd, char *buf, size_t n) { +char* fetch_ifname(int sockfd, char *buf, size_t n) { struct sockaddr_in srcaddr; struct ifaddrs *ifa, *it; @@ -699,7 +694,7 @@ static char* fetch_ifname(int sockfd, char *buf, size_t n) { return NULL; } -static int install_firewall_board(int sockfd) { +int install_firewall_board(int sockfd) { struct sockaddr_in srcaddr, dstaddr; char srchost[16], dsthost[16]; // enough for 255.255.255.255\0 char dport_s[8], sport_s[8]; @@ -744,7 +739,7 @@ static int install_firewall_board(int sockfd) { return 0; } -static int install_firewall_perinterface(const char *ifbuf) { +int install_firewall_perinterface(const char *ifbuf) { // Without these rules, 'ping' spews a lot of "Packet filtered" // messages. With them, ping prints 'ping: sendmsg: Operation not // permitted' once per second. @@ -788,16 +783,16 @@ static int install_firewall_perinterface(const char *ifbuf) { return 0; } -static int fetch_hwaddr(const char *board_ip, int sockfd, unsigned char buf[6]) { +int fetch_hwaddr(hm2_eth_t *board, unsigned char buf[6]) { lbp16_cmd_addr packet; unsigned char response[6]; LBP16_INIT_PACKET4(packet, 0x4983, 0x0002); - int res = eth_socket_send(sockfd, &packet, sizeof(packet), 0); + int res = eth_socket_send(board, &packet, sizeof(packet), 0); if(res < 0) return -errno; int i=0; do { - res = eth_socket_recv(sockfd, &response, sizeof(response), 0); + res = eth_socket_recv(board, &response, sizeof(response), 0); } while(++i < 10 && res < 0 && errno == EAGAIN); if(res < 0) return -errno; @@ -805,144 +800,11 @@ static int fetch_hwaddr(const char *board_ip, int sockfd, unsigned char buf[6]) for(i=0; i<6; i++) buf[i] = response[5-i]; LL_PRINT("%s: INFO: Hardware address (MAC): %02x:%02x:%02x:%02x:%02x:%02x\n", - board_ip, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - - return 0; -} - -int ioctl_siocsarp(void *arg) { - hm2_eth_t *board = (hm2_eth_t *)arg; - return ioctl(board->sockfd, SIOCSARP, &board->req); -} - -int ioctl_siocdarp(void *arg) { - hm2_eth_t *board = (hm2_eth_t *)arg; - return ioctl(board->sockfd, SIOCDARP, &board->req); -} - -static int init_board(hm2_eth_t *board, const char *board_ip) { - int ret; - - board->sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); - if (board->sockfd < 0) { - LL_PRINT("ERROR: can't open socket: %s\n", strerror(errno)); - return -errno; - } - board->server_addr.sin_family = AF_INET; - board->server_addr.sin_port = htons(LBP16_UDP_PORT); - board->server_addr.sin_addr.s_addr = inet_addr(board_ip); - - board->local_addr.sin_family = AF_INET; - board->local_addr.sin_addr.s_addr = INADDR_ANY; - - ret = connect(board->sockfd, (struct sockaddr *) &board->server_addr, sizeof(struct sockaddr_in)); - if (ret < 0) { - LL_PRINT("ERROR: can't connect: %s\n", strerror(errno)); - return -errno; - } - - if(!use_firewall()) { - LL_PRINT(\ -"WARNING: Unable to restrict other access to the hm2-eth device.\n" -"This means that other software using the same network interface can violate\n" -"realtime guarantees. See hm2_eth(9) for more information.\n"); - } - - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = RECV_TIMEOUT_US; - - ret = setsockopt(board->sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); - if (ret < 0) { - LL_PRINT("ERROR: can't set receive timeout socket option: %s\n", strerror(errno)); - return -errno; - } - - timeout.tv_usec = SEND_TIMEOUT_US; - ret = setsockopt(board->sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)); - if (ret < 0) { - LL_PRINT("ERROR: can't set send timeout socket option: %s\n", strerror(errno)); - return -errno; - } - - memset(&board->req, 0, sizeof(board->req)); - struct sockaddr_in *sin; - - sin = (struct sockaddr_in *) &board->req.arp_pa; - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = inet_addr(board_ip); - - board->req.arp_ha.sa_family = AF_LOCAL; - board->req.arp_flags = ATF_PERM | ATF_COM; - ret = fetch_hwaddr( board_ip, board->sockfd, (void*)&board->req.arp_ha.sa_data ); - if(ret < 0) { - LL_PRINT("ERROR: Could not retrieve hardware address (MAC) of %s: %s\n", board_ip, strerror(-ret)); - return ret; - } - - // Pinning the ARP entry needs CAP_NET_ADMIN; rootless without setcap - // fails with EPERM. Best-effort, not fatal: fall back to dynamic ARP - // so the board still loads. Clear ATF_PERM so the SIOCDARP teardown - // in close_board() does not try to remove an entry we never set. - ret = ioctl_siocsarp(board); - if(ret < 0) { - LL_PRINT("WARNING: ioctl SIOCSARP failed: %s; continuing with " - "dynamic ARP. Install file capabilities (sudo make " - "setcap) or run setuid to pin the board's ARP entry and " - "avoid occasional transmit latency.\n", strerror(errno)); - board->req.arp_flags &= ~ATF_PERM; - } - - // install_firewall_board() is a no-op when no firewall backend is - // available (rootless install without CAP_NET_ADMIN, or - // firewall=none), so it is safe to call unconditionally. - ret = install_firewall_board(board->sockfd); - if(ret < 0) return ret; - - board->write_packet_ptr = board->write_packet; - board->read_packet_ptr = board->read_packet; + board->ip, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); return 0; } -static int close_board(hm2_eth_t *board) { - int ret; - board->llio.reset(&board->llio); - - clear_firewall(); - - if(board->req.arp_flags & ATF_PERM) { - ret = ioctl_siocdarp(board); - if(ret < 0) perror("ioctl SIOCDARP"); - } - ret = shutdown(board->sockfd, SHUT_RDWR); - if (ret == -1) - LL_PRINT("ERROR: can't shutdown socket: %s\n", strerror(errno)); - - ret = close(board->sockfd); - if (ret == -1) - LL_PRINT("ERROR: can't close socket: %s\n", strerror(errno)); - - return ret < 0 ? -errno : 0; -} - -static int eth_socket_send(int sockfd, const void *buffer, int len, int flags) { - return send(sockfd, buffer, len, flags); -} - -static int eth_socket_recv(int sockfd, void *buffer, int len, int flags) { - return recv(sockfd, buffer, len, flags); -} - -static int eth_socket_recv_loop(int sockfd, void *buffer, int len, int flags, long timeout) { - long long end = rtapi_get_time() + timeout; - int result; - do { - result = eth_socket_recv(sockfd, buffer, len, flags); - } while(result < 0 && rtapi_get_time() < end); - return result; -} - /// hm2_eth io functions static int hm2_eth_read(hm2_lowlevel_io_t *this, rtapi_u32 addr, void *buffer, int size) { @@ -968,7 +830,7 @@ static int hm2_eth_read(hm2_lowlevel_io_t *this, rtapi_u32 addr, void *buffer, i LBP16_INIT_PACKET4(read_packet, CMD_READ_HOSTMOT2_ADDR32_INCR(size/4), addr & 0xFFFF); - send = eth_socket_send(board->sockfd, (void*) &read_packet, sizeof(read_packet), 0); + send = eth_socket_send(board, (void*) &read_packet, sizeof(read_packet), 0); if(send < 0) LL_PRINT("ERROR: sending packet: %s\n", strerror(errno)); LL_PRINT_IF(debug, "read(%d) : PACKET SENT [CMD:%02X%02X | ADDR: %02X%02X | SIZE: %d]\n", board->read_cnt, read_packet.cmd_hi, read_packet.cmd_lo, @@ -976,7 +838,7 @@ static int hm2_eth_read(hm2_lowlevel_io_t *this, rtapi_u32 addr, void *buffer, i t1 = rtapi_get_time(); do { errno = 0; - recv = eth_socket_recv(board->sockfd, (void*) &tmp_buffer, size, 0); + recv = eth_socket_recv(board, (void*) &tmp_buffer, size, 0); if(recv < 0) rtapi_delay(READ_PCK_DELAY_NS); t2 = rtapi_get_time(); i++; @@ -1021,7 +883,7 @@ static int hm2_eth_send_queued_reads(hm2_lowlevel_io_t *this) { board->queue_reads_count++; board->queue_buff_size += 8; - send = eth_socket_send(board->sockfd, (void*) &board->read_packet, board->read_packet_ptr - board->read_packet, 0); + send = eth_socket_send(board, (void*) &board->read_packet, board->read_packet_ptr - board->read_packet, 0); if(send < 0) { LL_PRINT("ERROR: sending packet: %s\n", strerror(errno)); return 0; @@ -1085,7 +947,7 @@ static int hm2_eth_receive_queued_reads(hm2_lowlevel_io_t *this) { do { do_recv_packet: errno = 0; - recv = eth_socket_recv(board->sockfd, (void*) &tmp_buffer, board->queue_buff_size, MSG_DONTWAIT); + recv = eth_socket_recv(board, (void*) &tmp_buffer, board->queue_buff_size, MSG_DONTWAIT); if(recv < 0) rtapi_delay(READ_PCK_DELAY_NS); t2 = rtapi_get_time(); i++; @@ -1130,7 +992,7 @@ static int hm2_eth_reset(hm2_lowlevel_io_t *this) { // Make the watchdog timer bite in 1ns from now lbp16_cmd_addr_data32 bite_packet; LBP16_INIT_PACKET8(bite_packet, CMD_WRITE_HOSTMOT2_ADDR32_INCR(1), HM2_ADDR_WATCHDOG, 0x0001); - int ret = eth_socket_send(board->sockfd, (void*) &bite_packet, sizeof(bite_packet), 0); + int ret = eth_socket_send(board, (void*) &bite_packet, sizeof(bite_packet), 0); if(ret < 0) perror("eth_socket_send(bite_packet)"); return ret < 0 ? -errno : 0; } @@ -1170,7 +1032,7 @@ static int hm2_eth_write(hm2_lowlevel_io_t *this, rtapi_u32 addr, const void *bu memcpy(packet.tmp_buffer, buffer, size); LBP16_INIT_PACKET4(packet.wr_packet, CMD_WRITE_HOSTMOT2_ADDR32_INCR(size/4), addr & 0xFFFF); - send = eth_socket_send(board->sockfd, (void*) &packet, sizeof(lbp16_cmd_addr) + size, 0); + send = eth_socket_send(board, (void*) &packet, sizeof(lbp16_cmd_addr) + size, 0); if(send < 0) LL_PRINT("ERROR: sending packet: %s\n", strerror(errno)); LL_PRINT_IF(debug, "write(%d): PACKET SENT [CMD:%02X%02X | ADDR: %02X%02X | SIZE: %d]\n", board->write_cnt, packet.wr_packet.cmd_hi, packet.wr_packet.cmd_lo, @@ -1194,7 +1056,7 @@ static int hm2_eth_send_queued_writes(hm2_lowlevel_io_t *this) { board->write_packet_size += (sizeof(*packet) + 4); t0 = rtapi_get_time(); - send = eth_socket_send(board->sockfd, (void*) &board->write_packet, board->write_packet_size, 0); + send = eth_socket_send(board, (void*) &board->write_packet, board->write_packet_size, 0); if(send < 0) { LL_PRINT("ERROR: sending packet: %s\n", strerror(errno)); return 0; @@ -1246,12 +1108,12 @@ static int hm2_eth_probe(hm2_eth_t *board) { char llio_name[16] = {}; LBP16_INIT_PACKET4(read_packet, CMD_READ_BOARD_INFO_ADDR16_INCR(16/2), 0); - send = eth_socket_send(board->sockfd, (void*) &read_packet, sizeof(read_packet), 0); + send = eth_socket_send(board, (void*) &read_packet, sizeof(read_packet), 0); if(send < 0) { LL_PRINT("ERROR: sending packet: %s\n", strerror(errno)); return -errno; } - recv = eth_socket_recv_loop(board->sockfd, (void*) &board_name, 16, 0, + recv = eth_socket_recv_loop(board, (void*) &board_name, 16, 0, 200 * 1000 * 1000); if(recv < 0) { LL_PRINT("ERROR: receiving packet: %s\n", strerror(errno)); @@ -1736,7 +1598,7 @@ int rtapi_app_main(void) { LL_PRINT("loading Mesa AnyIO HostMot2 ethernet driver version " HM2_ETH_VERSION "\n"); - ret = hal_init(HM2_LLIO_NAME); + ret = hal_init(HM2_LLIO_MODULE_NAME); if (ret < 0) return ret; comp_id = ret; @@ -1768,21 +1630,18 @@ int rtapi_app_main(void) { } for(i = 0; i, + * Jeff Epler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "hostmot2-lowlevel.h" +#include "hm2_eth_net.h" + +char* HM2_LLIO_MODULE_NAME="hm2_eth"; + +/// ethernet io functions + +int init_board(hm2_eth_t *board, const char *board_ip) { + int ret; + LL_PRINT("%s: INFO: init board (POSIX)\n", board_ip); + board->sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (board->sockfd < 0) { + LL_PRINT("ERROR: can't open socket: %s\n", strerror(errno)); + return -errno; + } + + board->server_addr.sin_family = AF_INET; + board->server_addr.sin_port = htons(LBP16_UDP_PORT); + board->server_addr.sin_addr.s_addr = inet_addr(board_ip); + + board->local_addr.sin_family = AF_INET; + board->local_addr.sin_addr.s_addr = INADDR_ANY; + + ret = connect(board->sockfd, (struct sockaddr *) &board->server_addr, sizeof(struct sockaddr_in)); + if (ret < 0) { + LL_PRINT("ERROR: can't connect: %s\n", strerror(errno)); + return -errno; + } + + strncpy(board->ip, board_ip, sizeof(board->ip)-1); + char *ifptr = fetch_ifname(board->sockfd, board->ifname, sizeof(board->ifname)); + if(!ifptr) { + LL_PRINT("failed to retrieve interface name for board\n"); + return 0; + } + + if(!use_firewall()) { + LL_PRINT(\ +"WARNING: Unable to restrict other access to the hm2-eth device.\n" +"This means that other software using the same network interface can violate\n" +"realtime guarantees. See hm2_eth(9) for more information.\n"); + } + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = RECV_TIMEOUT_US; + ret = setsockopt(board->sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); + if (ret < 0) { + LL_PRINT("ERROR: can't set receive timeout socket option: %s\n", strerror(errno)); + return -errno; + } + + timeout.tv_sec = 0; + timeout.tv_usec = SEND_TIMEOUT_US; + ret = setsockopt(board->sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)); + if (ret < 0) { + LL_PRINT("ERROR: can't set send timeout socket option: %s\n", strerror(errno)); + return -errno; + } + + memset(&board->req, 0, sizeof(board->req)); + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *) &board->req.arp_pa; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = inet_addr(board_ip); + + board->req.arp_ha.sa_family = AF_LOCAL; + board->req.arp_flags = ATF_PERM | ATF_COM; + ret = fetch_hwaddr( board, (void*)&board->req.arp_ha.sa_data ); + if(ret < 0) { + LL_PRINT("ERROR: Could not retrieve hardware address (MAC) of %s: %s\n", board_ip, strerror(-ret)); + return ret; + } + + // Pinning the ARP entry needs CAP_NET_ADMIN; rootless without setcap + // fails with EPERM. Best-effort, not fatal: fall back to dynamic ARP + // so the board still loads. Clear ATF_PERM so the SIOCDARP teardown + // in close_board() does not try to remove an entry we never set. + ret = ioctl(board->sockfd, SIOCSARP, &board->req); + if(ret < 0) { + LL_PRINT("WARNING: ioctl SIOCSARP failed: %s; continuing with " + "dynamic ARP. Install file capabilities (sudo make " + "setcap) or run setuid to pin the board's ARP entry and " + "avoid occasional transmit latency.\n", strerror(errno)); + board->req.arp_flags &= ~ATF_PERM; + } + + // install_firewall_board() is a no-op when no firewall backend is + // available (rootless install without CAP_NET_ADMIN, or + // firewall=none), so it is safe to call unconditionally. + ret = install_firewall_board(board->sockfd); + if(ret < 0) return ret; + + board->write_packet_ptr = board->write_packet; + board->read_packet_ptr = board->read_packet; + + return 0; +} + +int init_board_realtime(hm2_eth_t *board){ + (void)board; + return 0; //Nothing todo +} + +int close_board(hm2_eth_t *board) { + int ret; + board->llio.reset(&board->llio); + + clear_firewall(); + + if(board->req.arp_flags & ATF_PERM) { + ret = ioctl(board->sockfd, SIOCDARP, &board->req); + if(ret < 0) perror("ioctl SIOCDARP"); + } + ret = shutdown(board->sockfd, SHUT_RDWR); + if (ret == -1) + LL_PRINT("ERROR: can't shutdown socket: %s\n", strerror(errno)); + + ret = close(board->sockfd); + if (ret == -1) + LL_PRINT("ERROR: can't close socket: %s\n", strerror(errno)); + + return ret < 0 ? -errno : 0; +} + +int eth_socket_send(hm2_eth_t *board, const void *buffer, int len, int flags) { + return send(board->sockfd, buffer, len, flags); +} + +int eth_socket_recv(hm2_eth_t *board, void *buffer, int len, int flags) { + return recv(board->sockfd, buffer, len, flags); +} + +int eth_socket_recv_loop(hm2_eth_t *board, void *buffer, int len, int flags, long timeout) { + long long end = rtapi_get_time() + timeout; + int result; + do { + result = eth_socket_recv(board, buffer, len, flags); + } while(result < 0 && rtapi_get_time() < end); + return result; +} diff --git a/src/hal/drivers/mesa-hostmot2/hm2_eth_net.h b/src/hal/drivers/mesa-hostmot2/hm2_eth_net.h new file mode 100644 index 00000000000..ddfcd68fc4c --- /dev/null +++ b/src/hal/drivers/mesa-hostmot2/hm2_eth_net.h @@ -0,0 +1,40 @@ +/* This is a component of LinuxCNC + * Copyright 2013,2014 Michael Geszkiewicz , + * Jeff Epler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __INCLUDE_HM2_ETH_NET_H +#define __INCLUDE_HM2_ETH_NET_H + +#include "hm2_eth.h" + +//We use the linker to define the module name +//It must match the .so file +extern char* HM2_LLIO_MODULE_NAME; + +int init_board(hm2_eth_t *board, const char *board_ip); +int init_board_realtime(hm2_eth_t *board); +int close_board(hm2_eth_t *board); +int eth_socket_send(hm2_eth_t *board, const void *buffer, int len, int flags); +int eth_socket_recv(hm2_eth_t *board, void *buffer, int len, int flags); +int eth_socket_recv_loop(hm2_eth_t *board, void *buffer, int len, int flags, long timeout); + +#define UDP_PORT 27181 +#define SEND_TIMEOUT_US 10 +#define RECV_TIMEOUT_US 10 + +#endif diff --git a/src/hal/drivers/mesa-hostmot2/hm2_eth_net_evl.c b/src/hal/drivers/mesa-hostmot2/hm2_eth_net_evl.c new file mode 100644 index 00000000000..f6c3dddcb3c --- /dev/null +++ b/src/hal/drivers/mesa-hostmot2/hm2_eth_net_evl.c @@ -0,0 +1,313 @@ +/* This is a component of LinuxCNC + * Copyright 2013,2014 Michael Geszkiewicz , + * Jeff Epler + * + * EVL Port: + * Copyright 2024 Hannes Diethelm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "hostmot2-lowlevel.h" +#include "hm2_eth_net.h" + +char* HM2_LLIO_MODULE_NAME="hm2_eth_evl"; + +/// ethernet io functions + +int oob_enable_port(hm2_eth_t *board); +int oob_disable_port(hm2_eth_t *board); + +int init_board(hm2_eth_t *board, const char *board_ip) { + int ret; + LL_PRINT("%s: INFO: init board (Xenomai EVL)\n", board_ip); + board->is_evl_oob_active = false; + board->sockfd = socket(PF_INET, SOCK_DGRAM | SOCK_OOB, IPPROTO_IP); + if (board->sockfd < 0) { + LL_PRINT("ERROR: can't open socket: %s\n", strerror(errno)); + return -errno; + } + + board->server_addr.sin_family = AF_INET; + board->server_addr.sin_port = htons(LBP16_UDP_PORT); + board->server_addr.sin_addr.s_addr = inet_addr(board_ip); + + board->local_addr.sin_family = AF_INET; + board->local_addr.sin_addr.s_addr = INADDR_ANY; + + ret = connect(board->sockfd, (struct sockaddr *) &board->server_addr, sizeof(struct sockaddr_in)); + if (ret < 0) { + LL_PRINT("ERROR: can't connect: %s\n", strerror(errno)); + return -errno; + } + + strncpy(board->ip, board_ip, sizeof(board->ip)-1); + char *ifptr = fetch_ifname(board->sockfd, board->ifname, sizeof(board->ifname)); + if(!ifptr) { + LL_PRINT("failed to retrieve interface name for board\n"); + return 0; + } + + if (setsockopt(board->sockfd, SOL_SOCKET, SO_BINDTODEVICE, board->ifname, strlen(board->ifname))){ + LL_PRINT("ERROR: can't SO_BINDTODEVICE socket: %s\n", strerror(errno)); + } + + if(!use_firewall()) { + LL_PRINT(\ +"WARNING: Unable to restrict other access to the hm2-eth device.\n" +"This means that other software using the same network interface can violate\n" +"realtime guarantees. See hm2_eth(9) for more information.\n"); + } + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = RECV_TIMEOUT_US; + ret = setsockopt(board->sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); + if (ret < 0) { + LL_PRINT("ERROR: can't set receive timeout socket option: %s\n", strerror(errno)); + return -errno; + } + + timeout.tv_sec = 0; + timeout.tv_usec = SEND_TIMEOUT_US; + ret = setsockopt(board->sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)); + if (ret < 0) { + LL_PRINT("ERROR: can't set send timeout socket option: %s\n", strerror(errno)); + return -errno; + } + + memset(&board->req, 0, sizeof(board->req)); + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *) &board->req.arp_pa; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = inet_addr(board_ip); + + board->req.arp_ha.sa_family = AF_LOCAL; + board->req.arp_flags = ATF_PERM | ATF_COM; + ret = fetch_hwaddr( board, (void*)&board->req.arp_ha.sa_data ); + if(ret < 0) { + LL_PRINT("ERROR: Could not retrieve hardware address (MAC) of %s: %s\n", board_ip, strerror(-ret)); + return ret; + } + + // install_firewall_board() is a no-op when no firewall backend is + // available (rootless install without CAP_NET_ADMIN, or + // firewall=none), so it is safe to call unconditionally. + ret = install_firewall_board(board->sockfd); + if(ret < 0) return ret; + + board->write_packet_ptr = board->write_packet; + board->read_packet_ptr = board->read_packet; + + return 0; +} + +int init_board_realtime(hm2_eth_t *board){ + return oob_enable_port(board); +} + +int oob_enable_port(hm2_eth_t *board){ + //long long t1, t2; + int devfd, ret; + + LL_PRINT("%s: INFO: enable OOB for board on if %s\n", board->ip, board->ifname); + + //t1 = rtapi_get_time(); + size_t poolsz = 0, bufsz = 0; /* Use defaults. */ + + //Need root fsuid for this + uid_t previous = setfsuid(0); + + devfd = evl_net_open_dev(board->ifname); + if (devfd < 0){ + LL_PRINT("ERROR: cannot open out-of-band port %s\n", board->ifname); + return -1; + } + + ret = evl_net_enable_port(devfd, poolsz, bufsz); + if (ret){ + LL_PRINT("ERROR: cannot enable out-of-band port on %s\n", board->ifname); + return -1; + } + + close(devfd); /* We don't need the fildes of the oob port. */ + + ret = evl_net_solicit(board->sockfd, (struct sockaddr*)&board->server_addr, EVL_NEIGH_PERMANENT); + if (ret){ + LL_PRINT("solicit did not respond\n"); + return -1; + } + + setfsuid(previous); + + /*t2 = rtapi_get_time(); + LL_PRINT("Enable dur = %lli\n", t2-t1);*/ + + board->is_evl_oob_active=true; + + return 0; +} + +int disable_oob_port(hm2_eth_t *board) +{ + (void)board; + int ret, devfd; + + LL_PRINT("%s: INFO: disable OOB for board on if %s\n", board->ip, board->ifname); + + //Need root fsuid for this + uid_t previous = setfsuid(0); + + devfd = evl_net_open_dev(board->ifname); + if (devfd < 0){ + LL_PRINT("ERROR: cannot open out-of-band port %s\n", board->ifname); + return -1; + } + + ret = evl_net_disable_port(devfd); + if (ret < 0){ + LL_PRINT("ERROR: cannot disable out-of-band port on %s\n", board->ifname); + return -1; + } + + close(devfd); + + setfsuid(previous); + + board->is_evl_oob_active=false; + + return 0; +} + +int close_board(hm2_eth_t *board) { + int ret; + disable_oob_port(board); + + board->llio.reset(&board->llio); + + clear_firewall(); + + ret = close(board->sockfd); + if (ret == -1) + LL_PRINT("ERROR: can't close socket: %s\n", strerror(errno)); + + return ret < 0 ? -errno : 0; +} +/* +static void print_addr(char* desc, struct sockaddr_in *addr){ + char ip_str[INET_ADDRSTRLEN+1]; + inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str)); + LL_PRINT("--------%s--------\n", desc); + LL_PRINT("IP-Address: %s\n", ip_str); + LL_PRINT("Port: %d\n", ntohs(addr->sin_port)); + LL_PRINT("Family: %d\n", ntohs(addr->sin_family)); +} +*/ + +int eth_socket_send(hm2_eth_t *board, const void *buffer, int len, int flags){ + ssize_t ret = 0; + if(board->is_evl_oob_active){ + struct iovec iov; + struct oob_msghdr msghdr; + struct timespec ts_timeout; + + evl_read_clock(EVL_CLOCK_MONOTONIC, &ts_timeout); + ts_timeout.tv_nsec += 1000*1000; + while(ts_timeout.tv_nsec >= 1000000000){ + ts_timeout.tv_nsec -= 1000000000; + ts_timeout.tv_sec ++; + } + + /* OUTPUT */ + iov.iov_base = (void *)buffer; + iov.iov_len = len; + msghdr.msg_iov = &iov; + msghdr.msg_iovlen = 1; + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + msghdr.msg_name = &board->server_addr; + msghdr.msg_namelen = sizeof(board->server_addr); + /*msghdr.msg_name = NULL; + msghdr.msg_namelen = 0;*/ + msghdr.msg_flags = 0; + ret = oob_sendmsg(board->sockfd, &msghdr, &ts_timeout, flags); + if(ret == -1){ + LL_PRINT("ERROR: oob_sendmsg %m %i %li\n", errno, ret); + } + }else{ + ret = send(board->sockfd, buffer, len, flags); + } + return ret; +} + +int eth_socket_recv(hm2_eth_t *board, void *buffer, int len, int flags){ + ssize_t ret = 0; + if(board->is_evl_oob_active){ + struct oob_msghdr msghdr; + struct iovec iov; + struct timespec ts_timeout; + + evl_read_clock(EVL_CLOCK_MONOTONIC, &ts_timeout); + ts_timeout.tv_nsec += 1000*1000; + while(ts_timeout.tv_nsec >= 1000000000){ + ts_timeout.tv_nsec -= 1000000000; + ts_timeout.tv_sec ++; + } + + /* INPUT */ + iov.iov_base = buffer; + iov.iov_len = len; + msghdr.msg_iov = &iov; + msghdr.msg_iovlen = 1; + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + msghdr.msg_name = &board->local_addr; + msghdr.msg_namelen = sizeof(board->local_addr); + /*msghdr.msg_name = NULL; + msghdr.msg_namelen = 0;*/ + msghdr.msg_flags = 0; + ret = oob_recvmsg(board->sockfd, &msghdr, &ts_timeout, flags); + }else{ + ret = recv(board->sockfd, buffer, len, flags); + } + return ret; +} + +int eth_socket_recv_loop(hm2_eth_t *board, void *buffer, int len, int flags, long timeout) { + long long end = rtapi_get_time() + timeout; + int result; + do { + result = eth_socket_recv(board, buffer, len, flags); + } while(result < 0 && rtapi_get_time() < end); + return result; +} From 56d4a1532b6547138ad6e2173617d8e085f7fce9 Mon Sep 17 00:00:00 2001 From: Hannes Diethelm Date: Mon, 29 Jun 2026 23:52:26 +0200 Subject: [PATCH 3/5] Xenomai EVL network for Hostmot2: Single component Change back to one component that uses a "board_rtnet" argument to switch between the types --- src/Makefile | 15 ++-- src/configure.ac | 2 +- src/hal/drivers/mesa-hostmot2/hm2_eth.c | 69 ++++++++++++++++++- src/hal/drivers/mesa-hostmot2/hm2_eth.h | 14 +++- .../drivers/mesa-hostmot2/hm2_eth_net_evl.c | 23 ++----- .../{hm2_eth_net.h => hm2_eth_net_evl.h} | 22 +++--- .../{hm2_eth_net.c => hm2_eth_net_posix.c} | 23 ++----- .../drivers/mesa-hostmot2/hm2_eth_net_posix.h | 35 ++++++++++ 8 files changed, 141 insertions(+), 62 deletions(-) rename src/hal/drivers/mesa-hostmot2/{hm2_eth_net.h => hm2_eth_net_evl.h} (64%) rename src/hal/drivers/mesa-hostmot2/{hm2_eth_net.c => hm2_eth_net_posix.c} (89%) create mode 100644 src/hal/drivers/mesa-hostmot2/hm2_eth_net_posix.h diff --git a/src/Makefile b/src/Makefile index 6ddb0f28e70..f43362f943b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1004,9 +1004,6 @@ obj-$(CONFIG_HOSTMOT2) += hostmot2.o hm2_test.o hm2_pci.o hm2_7i43.o hm2_7i90.o obj-$(CONFIG_HOSTMOT2) += hm2_modbus.o ifeq ($(BUILD_SYS),uspace) obj-$(CONFIG_HOSTMOT2) += hm2_eth.o -ifeq ($(CONFIG_USPACE_XENOMAI_EVL),y) -obj-$(CONFIG_HOSTMOT2) += hm2_eth_evl.o -endif obj-$(CONFIG_HOSTMOT2) += hm2_spi.o hm2_rpspi.o hm2_spix.o endif hostmot2-objs := \ @@ -1052,12 +1049,11 @@ hm2_pci-objs := \ $(MATHSTUB) hm2_eth-objs := \ hal/drivers/mesa-hostmot2/hm2_eth.o \ - hal/drivers/mesa-hostmot2/hm2_eth_net.o \ - $(MATHSTUB) -hm2_eth_evl-objs := \ - hal/drivers/mesa-hostmot2/hm2_eth.o \ - hal/drivers/mesa-hostmot2/hm2_eth_net_evl.o \ - $(MATHSTUB) + hal/drivers/mesa-hostmot2/hm2_eth_net_posix.o +ifeq ($(CONFIG_USPACE_XENOMAI_EVL),y) +hm2_eth-objs += hal/drivers/mesa-hostmot2/hm2_eth_net_evl.o +endif +hm2_eth-objs += $(MATHSTUB) hm2_spi-objs := \ hal/drivers/mesa-hostmot2/hm2_spi.o \ hal/drivers/mesa-hostmot2/llio_info.o \ @@ -1405,7 +1401,6 @@ endif ../rtlib/hal_parport$(MODULE_EXT): $(addprefix objects/rt,$(hal_parport-objs)) ../rtlib/hal_ppmc$(MODULE_EXT): $(addprefix objects/rt,$(hal_ppmc-objs)) ../rtlib/hm2_eth$(MODULE_EXT): $(addprefix objects/rt,$(hm2_eth-objs)) -../rtlib/hm2_eth_evl$(MODULE_EXT): $(addprefix objects/rt,$(hm2_eth_evl-objs)) ../rtlib/hm2_spi$(MODULE_EXT): $(addprefix objects/rt,$(hm2_spi-objs)) ../rtlib/hm2_rpspi$(MODULE_EXT): $(addprefix objects/rt,$(hm2_rpspi-objs)) ../rtlib/hm2_spix$(MODULE_EXT): $(addprefix objects/rt,$(hm2_spix-objs)) diff --git a/src/configure.ac b/src/configure.ac index 15054b93100..5caef02058d 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -264,7 +264,7 @@ if test "$RTS" = uspace || test -z "$RTS" -a "$RTAI_CONFIG" = "none" ; then CONFIG_USPACE_XENOMAI_EVL=y XENOMAI_EVL_CFLAGS= XENOMAI_EVL_LDFLAGS=-levl - AC_DEFINE([USPACE_XENOMAI_EVL], [], [Define if uspace realtime should optionally support Xenomai-evl]) + AC_DEFINE([USPACE_XENOMAI_EVL], [], [Define if uspace realtime should optionally support Xenomai EVL]) fi AC_SUBST([CONFIG_USPACE_XENOMAI_EVL]) diff --git a/src/hal/drivers/mesa-hostmot2/hm2_eth.c b/src/hal/drivers/mesa-hostmot2/hm2_eth.c index 94bd4732457..e9291a8817b 100644 --- a/src/hal/drivers/mesa-hostmot2/hm2_eth.c +++ b/src/hal/drivers/mesa-hostmot2/hm2_eth.c @@ -43,10 +43,15 @@ #include +#include "config.h" //For USPACE_XENOMAI_EVL + #include "hostmot2-lowlevel.h" #include "hostmot2.h" #include "hm2_eth.h" -#include "hm2_eth_net.h" +#include "hm2_eth_net_posix.h" +#ifdef USPACE_XENOMAI_EVL +#include "hm2_eth_net_evl.h" +#endif struct kvlist { struct rtapi_list_head list; @@ -90,6 +95,9 @@ MODULE_SUPPORTED_DEVICE("Mesa-AnythingIO-7i80"); static char *board_ip[MAX_ETH_BOARDS]; RTAPI_MP_ARRAY_STRING(board_ip, MAX_ETH_BOARDS, "ip address of ethernet board(s)"); +static char *board_rtnet[MAX_ETH_BOARDS]; +RTAPI_MP_ARRAY_STRING(board_rtnet, MAX_ETH_BOARDS, "realtime network type of ethernet board(s)"); + static char *config[MAX_ETH_BOARDS]; RTAPI_MP_ARRAY_STRING(config, MAX_ETH_BOARDS, "config string for the AnyIO boards (see hostmot2(9) manpage)"); @@ -457,6 +465,9 @@ static const char *hm2_8cSS_pin_names[] = { static hm2_eth_t boards[MAX_ETH_BOARDS]={}; +static int eth_socket_send(hm2_eth_t *board, const void *buffer, int len, int flags); +static int eth_socket_recv(hm2_eth_t *board, void *buffer, int len, int flags); + /// firewall functions // hm2_eth installs firewall rules to isolate the dedicated Mesa @@ -805,6 +816,58 @@ int fetch_hwaddr(hm2_eth_t *board, unsigned char buf[6]) { return 0; } +/// ethernet io functions mapping +static int init_board(hm2_eth_t *board, const char *board_ip, const char *board_rtnet){ + //Default (NULL) is posix + if(board_rtnet == NULL || strcmp(board_rtnet, "posix") == 0){ + board->init_board = &hm2_posix_init_board; + board->init_board_realtime = &hm2_posix_init_board_realtime; + board->close_board = &hm2_posix_close_board; + board->eth_socket_send = &hm2_posix_eth_socket_send; + board->eth_socket_recv = &hm2_posix_eth_socket_recv; + } else if (strcmp(board_rtnet, "evl") == 0) { +#ifdef USPACE_XENOMAI_EVL + board->init_board = &hm2_evl_init_board; + board->init_board_realtime = &hm2_evl_init_board_realtime; + board->close_board = &hm2_evl_close_board; + board->eth_socket_send = &hm2_evl_eth_socket_send; + board->eth_socket_recv = &hm2_evl_eth_socket_recv; +#else + LL_PRINT("ERROR: board_rtnet = %s not available, LinuxCNC was built without Xenomai EVL support\n", board_rtnet) +#endif + } else { + LL_PRINT("ERROR: board_rtnet = %s undefined\n", board_rtnet) + return -1; + } + + return board->init_board(board, board_ip); +} + +static inline int init_board_realtime(hm2_eth_t *board){ + return board->init_board_realtime(board); +} + +static inline int close_board(hm2_eth_t *board){ + return board->close_board(board); +} + +static inline int eth_socket_send(hm2_eth_t *board, const void *buffer, int len, int flags){ + return board->eth_socket_send(board, buffer, len, flags); +} + +static inline int eth_socket_recv(hm2_eth_t *board, void *buffer, int len, int flags){ + return board->eth_socket_recv(board, buffer, len, flags); +} + +int eth_socket_recv_loop(hm2_eth_t *board, void *buffer, int len, int flags, long timeout) { + long long end = rtapi_get_time() + timeout; + int result; + do { + result = eth_socket_recv(board, buffer, len, flags); + } while(result < 0 && rtapi_get_time() < end); + return result; +} + /// hm2_eth io functions static int hm2_eth_read(hm2_lowlevel_io_t *this, rtapi_u32 addr, void *buffer, int size) { @@ -1598,7 +1661,7 @@ int rtapi_app_main(void) { LL_PRINT("loading Mesa AnyIO HostMot2 ethernet driver version " HM2_ETH_VERSION "\n"); - ret = hal_init(HM2_LLIO_MODULE_NAME); + ret = hal_init(HM2_LLIO_NAME); if (ret < 0) return ret; comp_id = ret; @@ -1606,7 +1669,7 @@ int rtapi_app_main(void) { clear_firewall(); for(i = 0, ret = 0; ret == 0 && i #include "hostmot2-lowlevel.h" -#include "hm2_eth_net.h" - -char* HM2_LLIO_MODULE_NAME="hm2_eth_evl"; +#include "hm2_eth_net_evl.h" /// ethernet io functions int oob_enable_port(hm2_eth_t *board); int oob_disable_port(hm2_eth_t *board); -int init_board(hm2_eth_t *board, const char *board_ip) { +int hm2_evl_init_board(hm2_eth_t *board, const char *board_ip) { int ret; LL_PRINT("%s: INFO: init board (Xenomai EVL)\n", board_ip); board->is_evl_oob_active = false; @@ -132,7 +130,7 @@ int init_board(hm2_eth_t *board, const char *board_ip) { return 0; } -int init_board_realtime(hm2_eth_t *board){ +int hm2_evl_init_board_realtime(hm2_eth_t *board){ return oob_enable_port(board); } @@ -209,7 +207,7 @@ int disable_oob_port(hm2_eth_t *board) return 0; } -int close_board(hm2_eth_t *board) { +int hm2_evl_close_board(hm2_eth_t *board) { int ret; disable_oob_port(board); @@ -234,7 +232,7 @@ static void print_addr(char* desc, struct sockaddr_in *addr){ } */ -int eth_socket_send(hm2_eth_t *board, const void *buffer, int len, int flags){ +int hm2_evl_eth_socket_send(hm2_eth_t *board, const void *buffer, int len, int flags){ ssize_t ret = 0; if(board->is_evl_oob_active){ struct iovec iov; @@ -270,7 +268,7 @@ int eth_socket_send(hm2_eth_t *board, const void *buffer, int len, int flags){ return ret; } -int eth_socket_recv(hm2_eth_t *board, void *buffer, int len, int flags){ +int hm2_evl_eth_socket_recv(hm2_eth_t *board, void *buffer, int len, int flags){ ssize_t ret = 0; if(board->is_evl_oob_active){ struct oob_msghdr msghdr; @@ -302,12 +300,3 @@ int eth_socket_recv(hm2_eth_t *board, void *buffer, int len, int flags){ } return ret; } - -int eth_socket_recv_loop(hm2_eth_t *board, void *buffer, int len, int flags, long timeout) { - long long end = rtapi_get_time() + timeout; - int result; - do { - result = eth_socket_recv(board, buffer, len, flags); - } while(result < 0 && rtapi_get_time() < end); - return result; -} diff --git a/src/hal/drivers/mesa-hostmot2/hm2_eth_net.h b/src/hal/drivers/mesa-hostmot2/hm2_eth_net_evl.h similarity index 64% rename from src/hal/drivers/mesa-hostmot2/hm2_eth_net.h rename to src/hal/drivers/mesa-hostmot2/hm2_eth_net_evl.h index ddfcd68fc4c..33b8113d2f0 100644 --- a/src/hal/drivers/mesa-hostmot2/hm2_eth_net.h +++ b/src/hal/drivers/mesa-hostmot2/hm2_eth_net_evl.h @@ -2,6 +2,9 @@ * Copyright 2013,2014 Michael Geszkiewicz , * Jeff Epler * + * EVL Port: + * Copyright 2024 Hannes Diethelm + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -17,21 +20,16 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef __INCLUDE_HM2_ETH_NET_H -#define __INCLUDE_HM2_ETH_NET_H +#ifndef __INCLUDE_HM2_ETH_NET_EVL_H +#define __INCLUDE_HM2_ETH_NET_EVL_H #include "hm2_eth.h" -//We use the linker to define the module name -//It must match the .so file -extern char* HM2_LLIO_MODULE_NAME; - -int init_board(hm2_eth_t *board, const char *board_ip); -int init_board_realtime(hm2_eth_t *board); -int close_board(hm2_eth_t *board); -int eth_socket_send(hm2_eth_t *board, const void *buffer, int len, int flags); -int eth_socket_recv(hm2_eth_t *board, void *buffer, int len, int flags); -int eth_socket_recv_loop(hm2_eth_t *board, void *buffer, int len, int flags, long timeout); +int hm2_evl_init_board(hm2_eth_t *board, const char *board_ip); +int hm2_evl_init_board_realtime(hm2_eth_t *board); +int hm2_evl_close_board(hm2_eth_t *board); +int hm2_evl_eth_socket_send(hm2_eth_t *board, const void *buffer, int len, int flags); +int hm2_evl_eth_socket_recv(hm2_eth_t *board, void *buffer, int len, int flags); #define UDP_PORT 27181 #define SEND_TIMEOUT_US 10 diff --git a/src/hal/drivers/mesa-hostmot2/hm2_eth_net.c b/src/hal/drivers/mesa-hostmot2/hm2_eth_net_posix.c similarity index 89% rename from src/hal/drivers/mesa-hostmot2/hm2_eth_net.c rename to src/hal/drivers/mesa-hostmot2/hm2_eth_net_posix.c index ad0a7fbcc7f..6ce8919e876 100644 --- a/src/hal/drivers/mesa-hostmot2/hm2_eth_net.c +++ b/src/hal/drivers/mesa-hostmot2/hm2_eth_net_posix.c @@ -30,13 +30,11 @@ #include #include "hostmot2-lowlevel.h" -#include "hm2_eth_net.h" - -char* HM2_LLIO_MODULE_NAME="hm2_eth"; +#include "hm2_eth_net_posix.h" /// ethernet io functions -int init_board(hm2_eth_t *board, const char *board_ip) { +int hm2_posix_init_board(hm2_eth_t *board, const char *board_ip) { int ret; LL_PRINT("%s: INFO: init board (POSIX)\n", board_ip); board->sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); @@ -129,12 +127,12 @@ int init_board(hm2_eth_t *board, const char *board_ip) { return 0; } -int init_board_realtime(hm2_eth_t *board){ +int hm2_posix_init_board_realtime(hm2_eth_t *board){ (void)board; return 0; //Nothing todo } -int close_board(hm2_eth_t *board) { +int hm2_posix_close_board(hm2_eth_t *board) { int ret; board->llio.reset(&board->llio); @@ -155,19 +153,10 @@ int close_board(hm2_eth_t *board) { return ret < 0 ? -errno : 0; } -int eth_socket_send(hm2_eth_t *board, const void *buffer, int len, int flags) { +int hm2_posix_eth_socket_send(hm2_eth_t *board, const void *buffer, int len, int flags) { return send(board->sockfd, buffer, len, flags); } -int eth_socket_recv(hm2_eth_t *board, void *buffer, int len, int flags) { +int hm2_posix_eth_socket_recv(hm2_eth_t *board, void *buffer, int len, int flags) { return recv(board->sockfd, buffer, len, flags); } - -int eth_socket_recv_loop(hm2_eth_t *board, void *buffer, int len, int flags, long timeout) { - long long end = rtapi_get_time() + timeout; - int result; - do { - result = eth_socket_recv(board, buffer, len, flags); - } while(result < 0 && rtapi_get_time() < end); - return result; -} diff --git a/src/hal/drivers/mesa-hostmot2/hm2_eth_net_posix.h b/src/hal/drivers/mesa-hostmot2/hm2_eth_net_posix.h new file mode 100644 index 00000000000..0aa04cfac2a --- /dev/null +++ b/src/hal/drivers/mesa-hostmot2/hm2_eth_net_posix.h @@ -0,0 +1,35 @@ +/* This is a component of LinuxCNC + * Copyright 2013,2014 Michael Geszkiewicz , + * Jeff Epler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __INCLUDE_HM2_ETH_NET_POSIX_H +#define __INCLUDE_HM2_ETH_NET_POSIX_H + +#include "hm2_eth.h" + +int hm2_posix_init_board(hm2_eth_t *board, const char *board_ip); +int hm2_posix_init_board_realtime(hm2_eth_t *board); +int hm2_posix_close_board(hm2_eth_t *board); +int hm2_posix_eth_socket_send(hm2_eth_t *board, const void *buffer, int len, int flags); +int hm2_posix_eth_socket_recv(hm2_eth_t *board, void *buffer, int len, int flags); + +#define UDP_PORT 27181 +#define SEND_TIMEOUT_US 10 +#define RECV_TIMEOUT_US 10 + +#endif From 510076f10eb6bab5741da42e39e7cabd2425cf8d Mon Sep 17 00:00:00 2001 From: Hannes Diethelm Date: Tue, 30 Jun 2026 12:04:57 +0200 Subject: [PATCH 4/5] hal_lib: Add missing EXPORT_SYMBOL(hal_get_realtime_type) --- src/hal/hal_lib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hal/hal_lib.c b/src/hal/hal_lib.c index 162f9451565..1d1c7639364 100644 --- a/src/hal/hal_lib.c +++ b/src/hal/hal_lib.c @@ -4692,6 +4692,7 @@ EXPORT_SYMBOL(hal_set_unready); EXPORT_SYMBOL(hal_exit); EXPORT_SYMBOL(hal_malloc); EXPORT_SYMBOL(hal_comp_name); +EXPORT_SYMBOL(hal_get_realtime_type); EXPORT_SYMBOL(hal_pin_bit_new); EXPORT_SYMBOL(hal_pin_float_new); From 07a66839e91d8a748a4335ebc1f3ba187e57f5be Mon Sep 17 00:00:00 2001 From: Hannes Diethelm Date: Tue, 30 Jun 2026 12:07:37 +0200 Subject: [PATCH 5/5] Xenomai EVL network for Hostmot2: Check for EVL running --- src/hal/drivers/mesa-hostmot2/hm2_eth.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/hal/drivers/mesa-hostmot2/hm2_eth.c b/src/hal/drivers/mesa-hostmot2/hm2_eth.c index e9291a8817b..4c363b5fca6 100644 --- a/src/hal/drivers/mesa-hostmot2/hm2_eth.c +++ b/src/hal/drivers/mesa-hostmot2/hm2_eth.c @@ -819,7 +819,7 @@ int fetch_hwaddr(hm2_eth_t *board, unsigned char buf[6]) { /// ethernet io functions mapping static int init_board(hm2_eth_t *board, const char *board_ip, const char *board_rtnet){ //Default (NULL) is posix - if(board_rtnet == NULL || strcmp(board_rtnet, "posix") == 0){ + if (board_rtnet == NULL || strcmp(board_rtnet, "posix") == 0) { board->init_board = &hm2_posix_init_board; board->init_board_realtime = &hm2_posix_init_board_realtime; board->close_board = &hm2_posix_close_board; @@ -827,6 +827,9 @@ static int init_board(hm2_eth_t *board, const char *board_ip, const char *board_ board->eth_socket_recv = &hm2_posix_eth_socket_recv; } else if (strcmp(board_rtnet, "evl") == 0) { #ifdef USPACE_XENOMAI_EVL + if (hal_get_realtime_type() != REALTIME_TYPE_XENOMAI_EVL) { + LL_PRINT("ERROR: board_rtnet = %s not available, LinuxCNC not running with Xenomai4 EVL realtime\n", board_rtnet) + } board->init_board = &hm2_evl_init_board; board->init_board_realtime = &hm2_evl_init_board_realtime; board->close_board = &hm2_evl_close_board;