diff --git a/src/Makefile b/src/Makefile index 4b221e6546b..77efe8c3914 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1002,6 +1002,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 := \ @@ -1047,6 +1050,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 \ @@ -1284,7 +1292,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 @@ -1295,7 +1303,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 $@ @@ -1395,6 +1403,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..2bd9c96248c --- /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 = 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=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=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){ + 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){ + 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; +} 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; } }