blob: 11d34dd0ae247d65e46ddc878d4872d761dd23d3 [file] [log] [blame]
/*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <arpa/inet.h>
#include "platforms/nemora/portable/ncsi.h"
#include "platforms/nemora/portable/ncsi_client.h"
#include "platforms/nemora/portable/ncsi_fsm.h"
#define GO_TO_STATE(variable, state) do { *variable = state; } while (0)
#define GO_TO_NEXT_STATE(variable) do { (*variable)++; } while (0)
// TODO - This state machine needs to be rewritten, now that we have a
// better idea of the states and transitions involved.
// The NC-SI related states of the state machine are currently organized in
// request/response pairs. However when I added support for the second channel
// this resulted in more hard-coded pairs which worked okay for X
// (despite some ugliness, see ch_under_test below) but broke down for X
// since it only supports 1 channel. For now just add a little more ugliness
// by stepping by 1 or 3 when going from a pair to the next depending on whether
// the second channel is supported (1) or not (3 - skip over the second channel
// pair).
#define GO_TO_NEXT_CHANNEL(variable, ncsi_state)\
do { *variable += (ncsi_state->channel_count == 1)\
? 3 : 1; } while (0)
static void ncsi_fsm_clear_state(ncsi_state_t* ncsi_state) {
// This implicitly resets:
// l2_config_state to NCSI_STATE_L2_CONFIG_BEGIN
// l3l4_config_state to NCSI_STATE_L3L4_CONFIG_BEGIN
// test_state to NCSI_STATE_TEST_BEGIN
memset(ncsi_state, 0, sizeof(ncsi_state_t));
}
static void ncsi_fsm_fail(ncsi_state_t* ncsi_state,
network_debug_t* network_debug) {
network_debug->ncsi.fail_count++;
memcpy(&network_debug->ncsi.state_that_failed, ncsi_state,
sizeof(network_debug->ncsi.state_that_failed));
ncsi_fsm_clear_state(ncsi_state);
}
ncsi_connection_state_t ncsi_fsm_connection_state(
const ncsi_state_t* ncsi_state, const network_debug_t* network_debug) {
if (!network_debug->ncsi.enabled) {
return NCSI_CONNECTION_DISABLED;
}
if (ncsi_state->l2_config_state != NCSI_STATE_L2_CONFIG_END) {
if (network_debug->ncsi.loopback) {
return NCSI_CONNECTION_LOOPBACK;
} else {
return NCSI_CONNECTION_DOWN;
}
}
if (ncsi_state->l3l4_config_state != NCSI_STATE_L3L4_CONFIG_END) {
return NCSI_CONNECTION_UP;
}
return NCSI_CONNECTION_UP_AND_CONFIGURED;
}
ncsi_response_type_t ncsi_fsm_poll_l2_config(ncsi_state_t* ncsi_state,
network_debug_t* network_debug,
ncsi_buf_t* ncsi_buf,
mac_addr_t* mac) {
ncsi_l2_config_state_t* const state_variable = &ncsi_state->l2_config_state;
ncsi_response_type_t ncsi_response_type = NCSI_RESPONSE_NONE;
uint32_t len = 0;
switch(*state_variable) {
case NCSI_STATE_RESTART:
if (++ncsi_state->restart_delay_count >= NCSI_FSM_RESTART_DELAY_COUNT) {
network_debug->ncsi.pending_restart = false;
GO_TO_NEXT_STATE(state_variable);
ncsi_state->restart_delay_count = 0;
}
break;
case NCSI_STATE_CLEAR_0: // necessary to get mac
len = ncsi_cmd_clear_initial_state(ncsi_buf->data, CHANNEL_0_ID);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_CLEAR_0_RESPONSE:
{
bool loopback = false;
ncsi_response_type = ncsi_validate_std_response(
ncsi_buf->data, ncsi_buf->len, NCSI_CLEAR_INITIAL_STATE);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
GO_TO_NEXT_STATE(state_variable);
} else {
// If we did not receive a response but we did receive something,
// then maybe there is a physical loopback, so check that we received
// exactly what we sent
if (ncsi_buf->len >= sizeof(ncsi_simple_command_t)) {
ncsi_simple_command_t expected_loopback_data;
(void)ncsi_cmd_clear_initial_state((uint8_t*)&expected_loopback_data,
CHANNEL_0_ID);
if (0 == memcmp((uint8_t*)&expected_loopback_data,
ncsi_buf->data, sizeof(expected_loopback_data))) {
loopback = true;
}
}
ncsi_fsm_fail(ncsi_state, network_debug);
}
network_debug->ncsi.loopback = loopback;
}
break;
case NCSI_STATE_GET_VERSION:
len = ncsi_cmd_get_version(ncsi_buf->data, CHANNEL_0_ID);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_GET_VERSION_RESPONSE:
ncsi_response_type = ncsi_validate_std_response(
ncsi_buf->data, ncsi_buf->len, NCSI_GET_VERSION_ID);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
ncsi_version_id_response_t* get_version_response =
(ncsi_version_id_response_t*)ncsi_buf->data;
// TODO - Add check for this being actually X
network_debug->ncsi.mlx_legacy =
((ntohl(get_version_response->version.firmware_version) >> 24) ==
0x08);
GO_TO_NEXT_CHANNEL(state_variable, ncsi_state);
} else {
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_GET_CAPABILITIES:
len = ncsi_cmd_get_capabilities(ncsi_buf->data, CHANNEL_0_ID);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_GET_CAPABILITIES_RESPONSE:
ncsi_response_type = ncsi_validate_std_response(
ncsi_buf->data, ncsi_buf->len, NCSI_GET_CAPABILITIES);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
const ncsi_capabilities_response_t* get_capabilities_response =
(ncsi_capabilities_response_t*) ncsi_buf->data;
if (1 != get_capabilities_response->channel_count &&
2 != get_capabilities_response->channel_count) {
/* TODO: Return Error
CPRINTF("[NCSI Unsupported channel count %d]\n",
get_capabilities_response->channel_count);
*/
ncsi_fsm_fail(ncsi_state, network_debug);
} else {
ncsi_state->channel_count =
get_capabilities_response->channel_count;
GO_TO_NEXT_CHANNEL(state_variable, ncsi_state);
}
} else{
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_CLEAR_1:
len = ncsi_cmd_clear_initial_state(ncsi_buf->data, CHANNEL_1_ID);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_CLEAR_1_RESPONSE:
ncsi_response_type = ncsi_validate_std_response(
ncsi_buf->data, ncsi_buf->len, NCSI_CLEAR_INITIAL_STATE);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
GO_TO_NEXT_STATE(state_variable);
} else {
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_RESET_CHANNEL_0:
if (network_debug->ncsi.pending_stop) {
len = ncsi_cmd_reset_channel(ncsi_buf->data, CHANNEL_0_ID);
GO_TO_NEXT_STATE(state_variable);
} else {
// skip resetting channels
GO_TO_STATE(state_variable, NCSI_STATE_GET_MAC);
}
break;
case NCSI_STATE_RESET_CHANNEL_0_RESPONSE:
ncsi_response_type = ncsi_validate_std_response(
ncsi_buf->data, ncsi_buf->len, NCSI_RESET_CHANNEL);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
GO_TO_NEXT_CHANNEL(state_variable, ncsi_state);
} else {
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_RESET_CHANNEL_1:
len = ncsi_cmd_reset_channel(ncsi_buf->data, CHANNEL_1_ID);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_RESET_CHANNEL_1_RESPONSE:
ncsi_response_type = ncsi_validate_std_response(
ncsi_buf->data, ncsi_buf->len, NCSI_RESET_CHANNEL);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
GO_TO_NEXT_STATE(state_variable);
} else {
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_STOPPED:
network_debug->ncsi.pending_stop = false;
// Reset the L2 config state machine through fail(). This state machine
// will not be executed again so long as 'enabled' is false.
network_debug->ncsi.enabled = false;
ncsi_fsm_fail(ncsi_state, network_debug);
break;
// TODO: Add check for MFG ID and firmware version before trying
// any OEM commands.
case NCSI_STATE_GET_MAC:
// Only get MAC from channel 0, because that's the one that identifies the
// host machine (for both MDB and DHCP).
len = ncsi_oem_cmd_get_host_mac(ncsi_buf->data, CHANNEL_0_ID);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_GET_MAC_RESPONSE:
ncsi_response_type = ncsi_validate_oem_response(
ncsi_buf->data, ncsi_buf->len, NCSI_OEM_COMMAND_GET_HOST_MAC);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
ncsi_host_mac_response_t* get_mac_response =
(ncsi_host_mac_response_t*) ncsi_buf->data;
memcpy(mac->octet, get_mac_response->mac, sizeof(mac_addr_t));
GO_TO_NEXT_STATE(state_variable);
} else {
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_SET_MAC_FILTER_0:
len = ncsi_cmd_set_mac(ncsi_buf->data, CHANNEL_0_ID, mac);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_SET_MAC_FILTER_0_RESPONSE:
ncsi_response_type = ncsi_validate_std_response(
ncsi_buf->data, ncsi_buf->len, NCSI_SET_MAC_ADDRESS);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
GO_TO_NEXT_CHANNEL(state_variable, ncsi_state);
} else{
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_SET_MAC_FILTER_1:
len = ncsi_cmd_set_mac(ncsi_buf->data, CHANNEL_1_ID, mac);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_SET_MAC_FILTER_1_RESPONSE:
ncsi_response_type = ncsi_validate_std_response(
ncsi_buf->data, ncsi_buf->len, NCSI_SET_MAC_ADDRESS);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
GO_TO_NEXT_STATE(state_variable);
} else{
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_ENABLE_CHANNEL_0:
len = ncsi_cmd_enable_channel(ncsi_buf->data, CHANNEL_0_ID);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_ENABLE_CHANNEL_0_RESPONSE:
ncsi_response_type = ncsi_validate_std_response(
ncsi_buf->data, ncsi_buf->len, NCSI_ENABLE_CHANNEL);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
GO_TO_NEXT_CHANNEL(state_variable, ncsi_state);
} else{
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_ENABLE_CHANNEL_1:
len = ncsi_cmd_enable_channel(ncsi_buf->data, CHANNEL_1_ID);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_ENABLE_CHANNEL_1_RESPONSE:
ncsi_response_type = ncsi_validate_std_response(
ncsi_buf->data, ncsi_buf->len, NCSI_ENABLE_CHANNEL);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
GO_TO_NEXT_STATE(state_variable);
} else{
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
// TODO: Enable broadcast filter to block ARP.
case NCSI_STATE_ENABLE_TX:
// The NIC FW transmits all passthrough TX on the lowest enabled channel,
// so there is no point in enabling TX on the second channel.
// TODO: - In the future we may add a check for link status,
// in which case we may want to intelligently disable ch.0
// (if down) and enable ch.1
len = ncsi_cmd_enable_tx(ncsi_buf->data, CHANNEL_0_ID);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_ENABLE_TX_RESPONSE:
ncsi_response_type = ncsi_validate_std_response(
ncsi_buf->data, ncsi_buf->len, NCSI_ENABLE_CHANNEL_NETWORK_TX);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
GO_TO_NEXT_STATE(state_variable);
} else{
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_L2_CONFIG_END:
// Done
break;
default:
ncsi_fsm_fail(ncsi_state, network_debug);
break;
}
ncsi_buf->len = len;
return ncsi_response_type;
}
static uint32_t write_ncsi_oem_config_filter(uint8_t* buffer, uint8_t channel,
network_debug_t* network_debug,
mac_addr_t* mac,
uint32_t ipv4_addr,
uint16_t rx_port) {
uint32_t len;
(void)ipv4_addr;
if (network_debug->ncsi.oem_filter_disable) {
mac_addr_t zero_mac = {.octet = {0,}};
len = ncsi_oem_cmd_set_filter(buffer, channel, &zero_mac, 0, 0, 0);
} else {
len = ncsi_oem_cmd_set_filter(buffer, channel, mac, 0, rx_port, 1);
}
return len;
}
ncsi_response_type_t ncsi_fsm_poll_l3l4_config(ncsi_state_t* ncsi_state,
network_debug_t* network_debug,
ncsi_buf_t* ncsi_buf,
mac_addr_t* mac,
uint32_t ipv4_addr,
uint16_t rx_port) {
uint32_t len = 0;
ncsi_response_type_t ncsi_response_type = NCSI_RESPONSE_NONE;
if (ncsi_state->l3l4_config_state == NCSI_STATE_L3L4_CONFIG_BEGIN) {
ncsi_state->l3l4_channel = 0;
ncsi_state->l3l4_waiting_response = false;
ncsi_state->l3l4_config_state = NCSI_STATE_CONFIG_FILTERS;
}
/* Go through every state with every channel. */
if (ncsi_state->l3l4_waiting_response) {
ncsi_response_type = ncsi_validate_oem_response(
ncsi_buf->data, ncsi_buf->len, ncsi_state->l3l4_command);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
/* Current channel ACK'ed, go to the next one. */
ncsi_state->l3l4_channel++;
if (ncsi_state->l3l4_channel >= ncsi_state->channel_count) {
/* All channels done, reset channel number and go to the next state.
* NOTE: This assumes that state numbers are sequential.*/
ncsi_state->l3l4_config_state += 1;
ncsi_state->l3l4_channel = 0;
}
} else {
ncsi_fsm_fail(ncsi_state, network_debug);
}
ncsi_state->l3l4_waiting_response = false;
} else {
// Send appropriate command.
switch(ncsi_state->l3l4_config_state) {
case NCSI_STATE_CONFIG_FILTERS:
len =
write_ncsi_oem_config_filter(ncsi_buf->data, ncsi_state->l3l4_channel,
network_debug, mac, ipv4_addr, rx_port);
ncsi_state->l3l4_command = NCSI_OEM_COMMAND_SET_FILTER;
ncsi_state->l3l4_waiting_response = true;
break;
default:
ncsi_fsm_fail(ncsi_state, network_debug);
break;
}
}
ncsi_buf->len = len;
return ncsi_response_type;
}
/*
* Start a sub-section of the state machine that runs health checks.
* This is dependent on the NC-SI configuration being completed
* (e.g. ncsi_channel_count must be known).
*/
static bool ncsi_fsm_start_test(network_debug_t* network_debug,
uint8_t channel_count) {
if (network_debug->ncsi.test.max_tries > 0) {
network_debug->ncsi.test.runs++;
if (2 == channel_count) {
network_debug->ncsi.test.ch_under_test ^= 1;
} else {
network_debug->ncsi.test.ch_under_test = 0;
}
return true;
}
return false;
}
/*
* Allow for a limited number of retries for the NC-SI test because
* it can fail under heavy TCP/IP load (since NC-SI responses share
* the RX buffers in chip/$(CHIP)/net.c with TCP/IP incoming traffic).
*/
static bool ncsi_fsm_retry_test(network_debug_t* network_debug) {
const uint8_t max_tries = network_debug->ncsi.test.max_tries;
if (max_tries) {
uint8_t remaining_tries = max_tries - 1 - network_debug->ncsi.test.tries;
if (remaining_tries > 0) {
network_debug->ncsi.test.tries++;
return true;
}
}
network_debug->ncsi.test.tries = 0;
return false;
}
/*
* Returns true if we have executed an NC-SI Get OEM Filter command for all
* channels and the flags indicate that it is running in hostless mode.
* This means that we can DHCP/ARP if needed.
* Otherwise returns false.
*
* NOTE: We default to false, if we cannot complete the L2 config state
* machine or the test sequence.
*/
bool ncsi_fsm_is_nic_hostless(const ncsi_state_t* ncsi_state) {
uint8_t flags = ncsi_state->flowsteering[0].flags;
if (ncsi_state->channel_count > 1) {
flags &= ncsi_state->flowsteering[1].flags;
}
return flags & NCSI_OEM_FILTER_FLAGS_HOSTLESS;
}
static void ncsi_fsm_update_passthrough_stats(
const ncsi_passthrough_stats_t* increment, network_debug_t* network_debug) {
ncsi_passthrough_stats_t* accumulated =
&network_debug->ncsi.pt_stats_be[network_debug->ncsi.test.ch_under_test];
#define ACCUMULATE_PT_STATS(stat) accumulated->stat += increment->stat;
ACCUMULATE_PT_STATS(tx_packets_received_hi);
ACCUMULATE_PT_STATS(tx_packets_received_lo);
ACCUMULATE_PT_STATS(tx_packets_dropped);
ACCUMULATE_PT_STATS(tx_channel_errors);
ACCUMULATE_PT_STATS(tx_undersized_errors);
ACCUMULATE_PT_STATS(tx_oversized_errors);
ACCUMULATE_PT_STATS(rx_packets_received);
ACCUMULATE_PT_STATS(rx_packets_dropped);
ACCUMULATE_PT_STATS(rx_channel_errors);
ACCUMULATE_PT_STATS(rx_undersized_errors);
ACCUMULATE_PT_STATS(rx_oversized_errors);
#undef ACCUMULATE_PT_STATS
}
static void ncsi_fsm_update_passthrough_stats_legacy(
const ncsi_passthrough_stats_legacy_t* read,
network_debug_t* network_debug) {
// Legacy MLX response does not include tx_packets_received_hi and also MLX
// counters
// are not reset on read (i.e. we cannot accumulate them).
ncsi_passthrough_stats_t* accumulated =
&network_debug->ncsi.pt_stats_be[network_debug->ncsi.test.ch_under_test];
accumulated->tx_packets_received_hi = 0;
accumulated->tx_packets_received_lo = read->tx_packets_received;
#define COPY_PT_STATS(stat) accumulated->stat = read->stat;
COPY_PT_STATS(tx_packets_dropped);
COPY_PT_STATS(tx_channel_errors);
COPY_PT_STATS(tx_undersized_errors);
COPY_PT_STATS(tx_oversized_errors);
COPY_PT_STATS(rx_packets_received);
COPY_PT_STATS(rx_packets_dropped);
COPY_PT_STATS(rx_channel_errors);
COPY_PT_STATS(rx_undersized_errors);
COPY_PT_STATS(rx_oversized_errors);
#undef COPY_PT_STATS
}
ncsi_response_type_t ncsi_fsm_poll_test(ncsi_state_t* ncsi_state,
network_debug_t* network_debug,
ncsi_buf_t* ncsi_buf, mac_addr_t* mac,
uint32_t ipv4_addr, uint16_t rx_port) {
ncsi_test_state_t* const state_variable =
&ncsi_state->test_state;
ncsi_response_type_t ncsi_response_type = NCSI_RESPONSE_NONE;
uint32_t len = 0;
switch(*state_variable) {
case NCSI_STATE_TEST_PARAMS:
if (ncsi_fsm_start_test(network_debug, ncsi_state->channel_count)) {
GO_TO_NEXT_STATE(state_variable);
} else {
// debugging only - skip test by setting max_tries to 0
GO_TO_STATE(state_variable, NCSI_STATE_TEST_END);
}
break;
case NCSI_STATE_ECHO:
len = ncsi_oem_cmd_echo(ncsi_buf->data,
network_debug->ncsi.test.ch_under_test,
network_debug->ncsi.test.ping.tx);
network_debug->ncsi.test.ping.tx_count++;
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_ECHO_RESPONSE:
ncsi_response_type = ncsi_validate_oem_response(
ncsi_buf->data, ncsi_buf->len, NCSI_OEM_COMMAND_ECHO);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
network_debug->ncsi.test.ping.rx_count++;
ncsi_oem_echo_response_t* echo_response =
(ncsi_oem_echo_response_t*) ncsi_buf->data;
if (0 == memcmp(echo_response->pattern,
network_debug->ncsi.test.ping.tx,
sizeof(echo_response->pattern))) {
GO_TO_NEXT_STATE(state_variable);
break;
} else {
network_debug->ncsi.test.ping.bad_rx_count++;
memcpy(network_debug->ncsi.test.ping.last_bad_rx,
echo_response->pattern,
sizeof(network_debug->ncsi.test.ping.last_bad_rx));
}
}
if (ncsi_fsm_retry_test(network_debug)) {
GO_TO_STATE(state_variable, NCSI_STATE_TEST_BEGIN);
} else {
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_CHECK_FILTERS:
len = ncsi_oem_cmd_get_filter(ncsi_buf->data,
network_debug->ncsi.test.ch_under_test);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_CHECK_FILTERS_RESPONSE:
ncsi_response_type = ncsi_validate_oem_response(
ncsi_buf->data, ncsi_buf->len, NCSI_OEM_COMMAND_GET_FILTER);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
ncsi_oem_get_filter_response_t* get_filter_response =
(ncsi_oem_get_filter_response_t*) ncsi_buf->data;
// Stash away response because it contains information about NIC mode
memcpy((void*)ncsi_state->flowsteering[
network_debug->ncsi.test.ch_under_test].regid,
(void*)get_filter_response->filter.regid,
sizeof(ncsi_state->flowsteering[0].regid));
ncsi_state->flowsteering[
network_debug->ncsi.test.ch_under_test].flags =
get_filter_response->filter.flags;
// Test filter parameters only if we know that we configured the NIC,
// and if the NIC is in host-based mode (it appears to return all zeros's
// in hostless mode!).
if (NCSI_STATE_L3L4_CONFIG_END != ncsi_state->l3l4_config_state ||
ncsi_fsm_is_nic_hostless(ncsi_state)) {
GO_TO_NEXT_STATE(state_variable);
break;
}
ncsi_oem_set_filter_cmd_t expected;
(void)write_ncsi_oem_config_filter(
(uint8_t*)&expected, network_debug->ncsi.test.ch_under_test,
network_debug, mac, ipv4_addr, rx_port);
/* TODO: handle these responses in error reporting routine */
if (0 != memcmp((void*)&get_filter_response->filter.mac,
(void*)&expected.filter.mac,
sizeof(expected.filter.mac))) {
ncsi_response_type = NCSI_RESPONSE_UNEXPECTED_PARAMS;
} else if (get_filter_response->filter.ip != expected.filter.ip ||
get_filter_response->filter.port != expected.filter.port) {
ncsi_response_type = NCSI_RESPONSE_UNEXPECTED_PARAMS;
} else {
GO_TO_NEXT_STATE(state_variable);
break;
}
}
if (ncsi_fsm_retry_test(network_debug)) {
GO_TO_STATE(state_variable, NCSI_STATE_TEST_BEGIN);
} else {
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_GET_PT_STATS:
len = ncsi_cmd_get_passthrough_stats(
ncsi_buf->data, network_debug->ncsi.test.ch_under_test);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_GET_PT_STATS_RESPONSE:
if (!network_debug->ncsi.mlx_legacy) {
ncsi_response_type = ncsi_validate_std_response(
ncsi_buf->data, ncsi_buf->len, NCSI_GET_PASSTHROUGH_STATISTICS);
if (ncsi_response_type == NCSI_RESPONSE_ACK) {
const ncsi_passthrough_stats_response_t* response =
(const ncsi_passthrough_stats_response_t*)ncsi_buf->data;
ncsi_fsm_update_passthrough_stats(&response->stats, network_debug);
GO_TO_NEXT_STATE(state_variable);
break;
}
} else {
uint32_t response_size =
ncsi_get_response_size(NCSI_GET_PASSTHROUGH_STATISTICS) -
sizeof(uint32_t);
ncsi_response_type = ncsi_validate_response(
ncsi_buf->data, ncsi_buf->len, NCSI_GET_PASSTHROUGH_STATISTICS, false,
response_size);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
const ncsi_passthrough_stats_legacy_response_t* legacy_response =
(const ncsi_passthrough_stats_legacy_response_t*)ncsi_buf->data;
ncsi_fsm_update_passthrough_stats_legacy(&legacy_response->stats,
network_debug);
GO_TO_NEXT_STATE(state_variable);
break;
}
}
if (ncsi_fsm_retry_test(network_debug)) {
GO_TO_STATE(state_variable, NCSI_STATE_TEST_BEGIN);
} else {
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_GET_LINK_STATUS:
// We only care about ch.0 link status because that's the only one we use
// to transmit.
len = ncsi_cmd_get_link_status(ncsi_buf->data, 0);
GO_TO_NEXT_STATE(state_variable);
break;
case NCSI_STATE_GET_LINK_STATUS_RESPONSE:
ncsi_response_type = ncsi_validate_std_response(
ncsi_buf->data, ncsi_buf->len, NCSI_GET_LINK_STATUS);
if (NCSI_RESPONSE_ACK == ncsi_response_type) {
const ncsi_link_status_response_t* response =
(const ncsi_link_status_response_t*)ncsi_buf->data;
const uint32_t link_status = ntohl(response->link_status.link_status);
if (link_status & NCSI_LINK_STATUS_UP) {
GO_TO_NEXT_STATE(state_variable);
break;
}
// TODO: report this error.
// CPRINTF("[NCSI Link Status down 0x%08lx]\n", link_status);
}
if (ncsi_fsm_retry_test(network_debug)) {
GO_TO_STATE(state_variable, NCSI_STATE_TEST_BEGIN);
} else {
ncsi_fsm_fail(ncsi_state, network_debug);
}
break;
case NCSI_STATE_TEST_END:
network_debug->ncsi.test.tries = 0;
if (network_debug->ncsi.pending_restart) {
ncsi_fsm_fail(ncsi_state, network_debug); // (Ab)use fail to restart.
}
if (++ncsi_state->retest_delay_count >= NCSI_FSM_RETEST_DELAY_COUNT) {
GO_TO_STATE(state_variable, NCSI_STATE_TEST_BEGIN);
ncsi_state->retest_delay_count = 0;
}
break;
default:
ncsi_fsm_fail(ncsi_state, network_debug);
break;
}
ncsi_buf->len = len;
return ncsi_response_type;
}