diff --git a/userspace/wrsw_hal/hal_port_fsm.c b/userspace/wrsw_hal/hal_port_fsm.c index 0dc12e84a62c78eec63c6cc6d22fc0f31168336b..8d62d16f529d7c5316fa2c9cd7c5102d0e3ccb49 100644 --- a/userspace/wrsw_hal/hal_port_fsm.c +++ b/userspace/wrsw_hal/hal_port_fsm.c @@ -12,8 +12,10 @@ #include <linux/if_ether.h> #include <linux/if_arp.h> #include <linux/if.h> +#include <linux/rtnetlink.h> #include <stdlib.h> #include <rt_ipc.h> +#include <errno.h> #include <libwr/hal_shmem.h> #include <libwr/switch_hw.h> @@ -66,6 +68,7 @@ static int port_fsm_state_link_up(fsm_t *fsm, int eventMsk, int isNewState); static void init_port(struct hal_port_state * ps); static void reset_port(struct hal_port_state * ps); static int get_port_link_state(struct hal_port_state * ps,int *linkUp); +static int link_status_process_netlink(struct hal_port_state * ports); static fsm_state_table_entry_t port_fsm_states[] = { @@ -363,9 +366,11 @@ static int port_fsm_build_events(fsm_t *fsm) { void hal_port_state_fsm_init_all( struct hal_port_state * ports, halGlobalLPDC_t *globalLpdc) { int portIndex; + uint32_t bmcr; + struct hal_port_state* ps; for (portIndex = 0; portIndex < HAL_MAX_PORTS; portIndex++) { - struct hal_port_state* ps = &ports[portIndex]; + ps = &ports[portIndex]; if ( ps->in_use) { @@ -383,6 +388,28 @@ void hal_port_state_fsm_init_all( struct hal_port_state * ports, halGlobalLPDC_t hal_port_tx_setup_init_all(ports, globalLpdc); hal_port_rx_setup_init_all(ports); hal_port_pll_setup_init_all(ports); + + /* Read initial link state (up or down). + * NOTE: Check with RTM_NEWLINK can only notify about changes, does not + * read the current state. */ + for (portIndex = 0; portIndex < HAL_MAX_PORTS; portIndex++) { + ps = &ports[portIndex]; + + /* Skip not used ports */ + if (!ps->in_use) + continue; + + pcs_readl (ps, MII_BMCR, &bmcr); + ps->evt_powerDown = (bmcr & BMCR_PDOWN); + + if (get_port_link_state(ps, &ps->evt_linkUp) < 0) { + /* IOTCL error : We put -1 in the link state. + * It will be considered as invalid */ + ps->evt_linkUp = -1; + } + + fsm_generic_run(&ps->fsm); + } } /* Call FSM for on all ports */ @@ -391,6 +418,10 @@ void hal_port_state_fsm_run_all( struct hal_port_state * ports) { hal_port_poll_rts_state(); // Update rts state on all ports + /* Check if any link changed its status (up/down). + * If changed, run fsm for it. */ + link_status_process_netlink(ports); + /* Call state machine for all ports */ for (portIndex = 0; portIndex < HAL_MAX_PORTS; portIndex++) { struct hal_port_state* ps = &ports[portIndex]; @@ -401,11 +432,6 @@ void hal_port_state_fsm_run_all( struct hal_port_state * ports) { ps->evt_powerDown = (bmcr & BMCR_PDOWN); - if ( get_port_link_state(ps, &ps->evt_linkUp) < 0 ) { - // IOTCL error : We put -1 in the link state. It will be considered as invalid - ps->evt_linkUp=-1; - } - fsm_generic_run(&ps->fsm); } } @@ -495,3 +521,112 @@ static int get_port_link_state(struct hal_port_state * ps,int *linkUp) return 0; } +static void link_status_process_netlink_msg(struct hal_port_state * ports, + struct nlmsghdr *h) +{ + int nlmsg_len; + int portIndex; + int new_state; + struct hal_port_state* ps; + struct ifinfomsg *ifi; + struct rtattr *attr; + + ifi = NLMSG_DATA(h); + nlmsg_len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); + + /* Check all attributes of the NEWLINK message */ + for (attr = IFLA_RTA(ifi); + RTA_OK(attr, nlmsg_len); + attr = RTA_NEXT(attr, nlmsg_len)) { + switch(attr->rta_type) { + case IFLA_IFNAME: + /* Match interface of incoming message to wri port + * (via interface name) */ + for (portIndex = 0; + portIndex < HAL_MAX_PORTS; + portIndex++) { + ps = &ports[portIndex]; + + /* Skip not used ports */ + if (!ps->in_use) + continue; + + if (!strncmp((char *) RTA_DATA(attr), + ps->name, + sizeof(ps->name))) { + new_state = !!(ifi->ifi_flags & IFF_RUNNING); + + if (ps->evt_linkUp == new_state) + continue; + + pr_info("%s: Link state change detected" + ": was %s, is %s\n", + ps->name, + ps->evt_linkUp ? "up": "down", + new_state ? "up": "down"); + + ps->evt_linkUp = new_state; + + /* Run fsm for this port to be sure that + * link down/up event is reflected in + * fsm state. */ + fsm_generic_run(&ps->fsm); + } + } + break; + + default: + break; + } + } +} + +static int link_status_process_netlink(struct hal_port_state * ports) +{ + int ret; + static char buf[8192]; + struct iovec iov = {buf, sizeof(buf)}; + struct sockaddr_nl peer; + struct msghdr m = { &peer, sizeof(peer), &iov, 1}; + struct nlmsghdr *h; + + /* Pocess all available messages */ + while (1) { + /* NOTE: Check with RTM_NEWLINK can only notify about changes, + * does not read the current state. */ + ret = recvmsg (halPorts.hal_link_state_fd, &m, MSG_DONTWAIT); + if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + /* No message available */ + return 1; + } + + if (ret < 0) { + /* Other error */ + pr_error("%s: recvmsg(netlink): %s\n", __func__, + strerror(errno)); + return -1; + } + + for (h = (struct nlmsghdr *)buf; + NLMSG_OK(h, ret); + h = NLMSG_NEXT (h, ret)) { + + if (h->nlmsg_type == NLMSG_DONE) + break; + + if (h->nlmsg_type == NLMSG_ERROR) { + pr_error("%s: netlink message error\n", __func__); + continue; + } + if (h->nlmsg_type == RTM_NEWLINK) { + /* Link change detected, process it further */ + link_status_process_netlink_msg(ports, h); + continue; + } + pr_error("%s: unexpected message %i\n", __func__, + h->nlmsg_type); + } + } + + return 0; +} diff --git a/userspace/wrsw_hal/hal_ports.c b/userspace/wrsw_hal/hal_ports.c index 1b90173e816d4c9e42b675a074a82e088ce6ec60..2ff34bfdee67151c59ecb7b34e0d5e9b84084834 100644 --- a/userspace/wrsw_hal/hal_ports.c +++ b/userspace/wrsw_hal/hal_ports.c @@ -14,6 +14,7 @@ #include <linux/if_ether.h> #include <linux/if_arp.h> #include <linux/if.h> +#include <linux/rtnetlink.h> /* LOTs of hardware includes */ #include <rt_ipc.h> @@ -102,6 +103,7 @@ static timer_parameter_t _timerParameters[] = { /* prototypes */ static int hal_port_check_lpdc_support(struct hal_port_state * ps); +static void link_status_prepare_fd(int *fd); /* checks if the port is supported by the FPGA firmware */ static int hal_port_check_presence(const char *if_name, unsigned char *mac) @@ -233,6 +235,9 @@ int hal_port_shmem_init(char *logfilename) pr_error("Can't create socket: %s\n", strerror(errno)); return -1; } + + link_status_prepare_fd(&halPorts.hal_link_state_fd); + /* Allocate the ports in shared memory, so wr_mon etc can see them Use lock since some (like rtud) wait for hal to be available */ hal_shmem_hdr = wrs_shm_get(wrs_shm_hal, "wrsw_hal", @@ -802,3 +807,26 @@ void hal_port_update_info(char *iface_name, int mode, int synchronized){ } } +/* This prepares polling using netlink, so we get notification on change */ +static void link_status_prepare_fd(int *fd) +{ + struct sockaddr_nl addr = {}; + + *fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (*fd < 0) { + pr_error("%s: socket(netlink): %s\n", __func__, strerror(errno)); + *fd = -1; + return; + } + + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid (); + addr.nl_groups = RTMGRP_LINK; + + if (bind (*fd, (struct sockaddr *)&addr, sizeof (addr)) < 0) { + pr_error("%s: bind(netlink): %s\n", __func__, strerror(errno)); + *fd = -1; + return; + } + return; +} diff --git a/userspace/wrsw_hal/hal_ports.h b/userspace/wrsw_hal/hal_ports.h index 658542f69947f9e4bcc3f28ff323bb61db307a2e..e188f64245628c0d6c42364e3694560ac0e3796e 100644 --- a/userspace/wrsw_hal/hal_ports.h +++ b/userspace/wrsw_hal/hal_ports.h @@ -17,6 +17,7 @@ typedef struct { struct hal_port_state *ports; int numberOfPorts; int hal_port_fd; /* An fd of always opened raw sockets for ioctl()-ing Ethernet devices */ + int hal_link_state_fd; /* RT subsystem PLL state, polled regularly via mini-ipc */ struct rts_pll_state rts_state;