Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ACM MVP #2689

Merged
merged 24 commits into from Nov 30, 2023
Merged

ACM MVP #2689

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4a8a9ac
WIP basic network interface tester
scott-brust Aug 24, 2023
08f5af7
Allow Particle.connect() to bind to specific network interface
scott-brust Aug 30, 2023
5cd2c4f
Do not turn on modem to query cell info, cache and use those values
scott-brust Sep 29, 2023
000881f
Add connection manager
scott-brust Oct 2, 2023
b753b35
Implement reachability test message. Prioritize networks on tester ro…
scott-brust Oct 12, 2023
5db7109
Report signal correctly in diagnostics for active cloud connection
scott-brust Oct 24, 2023
266f700
Connection tester: variable size packets, use cloud address
scott-brust Oct 25, 2023
4c0837e
use system cache for cellular device info
scott-brust Nov 3, 2023
195fe1c
Use connection tester instead of ntp for internet test, fix some builds
scott-brust Nov 6, 2023
03a0a35
report both wifi and cell signal diagnostics
scott-brust Nov 7, 2023
fb6f340
fix todos. update `blank` app with manual testing commands
scott-brust Nov 9, 2023
35d6957
report cellular and wifi signal if the cloud connection uses ethernet
scott-brust Nov 9, 2023
e408066
Fix some gcc platform build errors
scott-brust Nov 15, 2023
be34aed
[msom] fixes default pinmap for wiznet with msom eval board
technobly Nov 16, 2023
23027d2
[test] update ethernet test app
technobly Nov 16, 2023
2cf35d3
[msom] add wiznet pin config
technobly Nov 16, 2023
98eb0a3
Log verbose interface states
scott-brust Nov 16, 2023
9722c36
Use device service for connection testing
scott-brust Nov 18, 2023
a4d7e8b
Add ACM define, move test app, cleanup
scott-brust Nov 21, 2023
08b5d6f
Review feedback
scott-brust Nov 27, 2023
f8e013d
feedback: process isr queue, send packet on timeout, refactor tester
scott-brust Nov 28, 2023
d6a7836
Fix diagnostics, remove test code
scott-brust Nov 29, 2023
a02cea3
fixup
scott-brust Nov 29, 2023
59c48f3
dont fail connection test if an individual packet send/receive fails
scott-brust Nov 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions hal/inc/hal_platform.h
Expand Up @@ -618,4 +618,8 @@
#define HAL_PLATFORM_PROTOBUF (1)
#endif // HAL_PLATFORM_PROTOBUF

#ifndef HAL_PLATFORM_AUTOMATIC_CONNECTION_MANAGEMENT
#define HAL_PLATFORM_AUTOMATIC_CONNECTION_MANAGEMENT (0)
#endif // HAL_PLATFORM_AUTOMATIC_CONNECTION_MANAGEMENT

#endif /* HAL_PLATFORM_H */
49 changes: 47 additions & 2 deletions hal/network/ncp/cellular/cellular_hal.cpp
Expand Up @@ -23,6 +23,7 @@
#include "ifapi.h"

#include "system_network.h" // FIXME: For network_interface_index
#include "system_cache.h"

#include "str_util.h"
#include "endian_util.h"
Expand All @@ -37,9 +38,23 @@

#include <limits>

#define CELLULAR_DEVICE_VERSION_V1 (1)
const uint16_t CELLULAR_DEVICE_VERSION = CELLULAR_DEVICE_VERSION_V1;

struct __attribute__((packed)) CellularDeviceCached
{
uint16_t size;
uint16_t version;
char iccid[21];
char imei[16];
int dev;
char radiofw[25];
};

namespace {

using namespace particle;
using namespace services;

const size_t MAX_RESP_SIZE = 1024;

Expand Down Expand Up @@ -188,9 +203,25 @@ int cellular_device_info(CellularDevice* info, void* reserved) {
const auto client = mgr->ncpClient();
CHECK_TRUE(client, SYSTEM_ERROR_UNKNOWN);

CellularDeviceCached cacheRead = {};
int r = SystemCache::instance().get(SystemCacheKey::CELLULAR_DEVICE_INFO, (uint8_t*)&cacheRead, sizeof(cacheRead));

const NcpClientLock lock(client);
// Ensure the modem is powered on
CHECK(client->on());

// If modem is off, return the cached values if present
if (client->ncpPowerState() != NcpPowerState::ON) {
if (r != sizeof(CellularDeviceCached) || cacheRead.version != CELLULAR_DEVICE_VERSION) {
SystemCache::instance().del(SystemCacheKey::CELLULAR_DEVICE_INFO);
technobly marked this conversation as resolved.
Show resolved Hide resolved
return SYSTEM_ERROR_BAD_DATA;
}

strlcpy(info->iccid, cacheRead.iccid, sizeof(info->iccid));
strlcpy(info->imei, cacheRead.imei, sizeof(info->imei));
info->dev = cacheRead.dev;
strlcpy(info->radiofw, cacheRead.radiofw, sizeof(info->radiofw));
return 0;
}

CHECK(client->getIccid(info->iccid, sizeof(info->iccid)));
CHECK(client->getImei(info->imei, sizeof(info->imei)));
if (info->size >= offsetof(CellularDevice, dev) + sizeof(CellularDevice::dev)) {
Expand All @@ -199,6 +230,20 @@ int cellular_device_info(CellularDevice* info, void* reserved) {
if (info->size >= offsetof(CellularDevice, radiofw) + sizeof(CellularDevice::radiofw)) {
CHECK(client->getFirmwareVersionString(info->radiofw, sizeof(info->radiofw)));
}

// Update the cached values if they dont match the last queried data.
CellularDeviceCached cacheWrite = {};
cacheWrite.size = sizeof(CellularDeviceCached);
cacheWrite.version = CELLULAR_DEVICE_VERSION;
strlcpy(cacheWrite.iccid, info->iccid, sizeof(cacheWrite.iccid));
strlcpy(cacheWrite.imei, info->imei, sizeof(cacheWrite.imei));
cacheWrite.dev = info->dev;
strlcpy(cacheWrite.radiofw, info->radiofw, sizeof(cacheWrite.radiofw));

if (memcmp(&cacheRead, &cacheWrite, sizeof(CellularDeviceCached))) {
SystemCache::instance().set(SystemCacheKey::CELLULAR_DEVICE_INFO, (uint8_t*)&cacheWrite, sizeof(cacheWrite));
}

return 0;
}

Expand Down
8 changes: 5 additions & 3 deletions hal/src/msom/hal_platform_config.h
Expand Up @@ -39,7 +39,9 @@
#define PRODUCT_SERIES "Mseries"

#if HAL_PLATFORM_ETHERNET
#define HAL_PLATFORM_ETHERNET_WIZNETIF_CS_PIN_DEFAULT (D5)
#define HAL_PLATFORM_ETHERNET_WIZNETIF_RESET_PIN_DEFAULT (D3)
#define HAL_PLATFORM_ETHERNET_WIZNETIF_INT_PIN_DEFAULT (D4)
#define HAL_PLATFORM_ETHERNET_WIZNETIF_CS_PIN_DEFAULT (D8)
#define HAL_PLATFORM_ETHERNET_WIZNETIF_RESET_PIN_DEFAULT (A7)
#define HAL_PLATFORM_ETHERNET_WIZNETIF_INT_PIN_DEFAULT (D22)
#endif // HAL_PLATFORM_ETHERNET

#define HAL_PLATFORM_AUTOMATIC_CONNECTION_MANAGEMENT (1)
7 changes: 6 additions & 1 deletion hal/src/msom/network/network.cpp
Expand Up @@ -18,6 +18,7 @@
#define NO_STATIC_ASSERT
#include "ifapi.h"
#include "wiznet/wiznetif.h"
#include "wiznet/wiznetif_config.h"
#include <mutex>
#include <memory>
#include "random.h"
Expand Down Expand Up @@ -158,7 +159,11 @@ int if_init_platform(void*) {
CHECK(hal_get_mac_address(HAL_DEVICE_MAC_ETHERNET, mac, HAL_DEVICE_MAC_ADDR_SIZE, nullptr));

if (HAL_Feature_Get(FEATURE_ETHERNET_DETECTION)) {
en2 = new WizNetif(HAL_SPI_INTERFACE1, D8, D28, D22, mac);
WizNetifConfigData wizNetifConfigData;
wizNetifConfigData.size = sizeof(WizNetifConfigData);
wizNetifConfigData.version = WIZNETIF_CONFIG_DATA_VERSION;
WizNetifConfig::instance()->getConfigData(&wizNetifConfigData);
en2 = new WizNetif(HAL_SPI_INTERFACE1, wizNetifConfigData.cs_pin, wizNetifConfigData.reset_pin, wizNetifConfigData.int_pin, mac);
}

uint8_t dummy;
Expand Down
12 changes: 12 additions & 0 deletions services/inc/diagnostics.h
Expand Up @@ -44,6 +44,11 @@
#define DIAG_NAME_NETWORK_SIGNAL_QUALITY "net:sigqual"
#define DIAG_NAME_NETWORK_SIGNAL_QUALITY_VALUE "net:sigqualv"
#define DIAG_NAME_NETWORK_ACCESS_TECNHOLOGY "net:at"
#define DIAG_NAME_ALT_NETWORK_SIGNAL_STRENGTH "net:alt:sigstr"
#define DIAG_NAME_ALT_NETWORK_SIGNAL_STRENGTH_VALUE "net:alt:sigstrv"
#define DIAG_NAME_ALT_NETWORK_SIGNAL_QUALITY "net:alt:sigqual"
#define DIAG_NAME_ALT_NETWORK_SIGNAL_QUALITY_VALUE "net:alt:sigqualv"
#define DIAG_NAME_ALT_NETWORK_ACCESS_TECNHOLOGY "net:alt:at"
#define DIAG_NAME_NETWORK_CELLULAR_CELL_GLOBAL_IDENTITY_MOBILE_COUNTRY_CODE "net:cell:cgi:mcc"
#define DIAG_NAME_NETWORK_CELLULAR_CELL_GLOBAL_IDENTITY_MOBILE_NETWORK_CODE "net:cell:cgi:mnc"
#define DIAG_NAME_NETWORK_CELLULAR_CELL_GLOBAL_IDENTITY_LOCATION_AREA_CODE "net:cell:cgi:lac"
Expand All @@ -53,6 +58,7 @@
#define DIAG_NAME_CLOUD_DISCONNECTS "cloud:dconn"
#define DIAG_NAME_CLOUD_CONNECTION_ATTEMPTS "cloud:connatt"
#define DIAG_NAME_CLOUD_DISCONNECTION_REASON "cloud:dconnrsn"
#define DIAG_NAME_CLOUD_CONNECTION_INTERFACE "cloud:connif"
#define DIAG_NAME_CLOUD_RETRANSMITTED_MESSAGES "coap:retransmit"
#define DIAG_NAME_CLOUD_UNACKNOWLEDGED_MESSAGES "coap:unack"
#define DIAG_NAME_CLOUD_TRANSMITTED_MESSAGES "coap:transmit"
Expand Down Expand Up @@ -100,13 +106,19 @@ typedef enum diag_id {
DIAG_ID_CLOUD_DISCONNECTS = 14, // cloud:dconn
DIAG_ID_CLOUD_CONNECTION_ATTEMPTS = 29, // cloud:connatt
DIAG_ID_CLOUD_DISCONNECTION_REASON = 30, // cloud:dconnrsn
DIAG_ID_CLOUD_CONNECTION_INTERFACE = 44, // cloud:connif
DIAG_ID_CLOUD_RETRANSMITTED_MESSAGES = 21, // coap:retransmit
DIAG_ID_CLOUD_UNACKNOWLEDGED_MESSAGES = 22, // coap:unack
DIAG_ID_CLOUD_TRANSMITTED_MESSAGES = 23, // coap:transmit
DIAG_ID_CLOUD_RATE_LIMITED_EVENTS = 20, // pub:throttle
DIAG_ID_SYSTEM_TOTAL_RAM = 25, // sys:tram
DIAG_ID_SYSTEM_USED_RAM = 26, // sys:uram
DIAG_ID_CLOUD_COAP_ROUND_TRIP = 31, // coap:roundtrip
DIAG_ID_ALT_NETWORK_SIGNAL_STRENGTH_VALUE = 45, // net:alt:sigstrv
DIAG_ID_ALT_NETWORK_SIGNAL_STRENGTH = 46, // net:alt:sigstr
DIAG_ID_ALT_NETWORK_SIGNAL_QUALITY = 47, // net:alt:sigqual
DIAG_ID_ALT_NETWORK_SIGNAL_QUALITY_VALUE = 48, // net:alt:sigqualv
DIAG_ID_ALT_NETWORK_ACCESS_TECNHOLOGY = 49, // net:alt:at
DIAG_ID_USER = 32768 // Base value for application-specific source IDs
} diag_id;

Expand Down
1 change: 1 addition & 0 deletions services/inc/system_cache.h
Expand Up @@ -27,6 +27,7 @@ enum class SystemCacheKey : uint16_t {
ADC_CALIBRATION_OFFSET = 0x0002,
WIZNET_CONFIG_DATA = 0x0003,
CELLULAR_NCP_OPERATION_MODE = 0x0004,
CELLULAR_DEVICE_INFO = 0x0005,
ASSET_MANAGER_CONSUMER_STATE = 0x0010,
};

Expand Down
4 changes: 3 additions & 1 deletion system/inc/system_cloud.h
Expand Up @@ -350,7 +350,9 @@ typedef enum spark_connection_property {
SPARK_CLOUD_DISCONNECT_OPTIONS = 2, ///< Default disconnection options (set).
SPARK_CLOUD_MAX_EVENT_DATA_SIZE = 3, ///< Maximum size of event data (get).
SPARK_CLOUD_MAX_VARIABLE_VALUE_SIZE = 4, ///< Maximum size of a variable value (get).
SPARK_CLOUD_MAX_FUNCTION_ARGUMENT_SIZE = 5 ///< Maximum size of a function call argument (get).
SPARK_CLOUD_MAX_FUNCTION_ARGUMENT_SIZE = 5, ///< Maximum size of a function call argument (get).
SPARK_CLOUD_BIND_NETWORK_INTERFACE = 6, ///< The cloud connection should only use a specified network interface
SPARK_CLOUD_GET_NETWORK_INTERFACE = 7 ///< Which interface is being used for the current cloud connection
} spark_connection_property;

int spark_set_connection_property(unsigned property, unsigned value, const void* data, void* reserved);
Expand Down
2 changes: 2 additions & 0 deletions system/inc/system_dynalib_net.h
Expand Up @@ -53,6 +53,8 @@ DYNALIB_FN(17, system_net, network_is_off, bool(network_handle_t, void*))
DYNALIB_FN(18, system_net, network_set_configuration, int(network_handle_t, const network_configuration_t*, void*))
DYNALIB_FN(19, system_net, network_get_configuration, int(network_handle_t, network_configuration_t**, size_t*, const char*, size_t, void*))
DYNALIB_FN(20, system_net, network_free_configuration, int(network_configuration_t*, size_t, void*))
DYNALIB_FN(21, system_net, network_preferred, network_handle_t(network_handle_t, bool, void*))
DYNALIB_FN(22, system_net, network_is_preferred, bool(network_handle_t, void*))

DYNALIB_END(system_net)

Expand Down
2 changes: 2 additions & 0 deletions system/inc/system_network.h
Expand Up @@ -63,6 +63,8 @@ void network_off(network_handle_t network, uint32_t flags, uint32_t param1, void
bool network_is_on(network_handle_t network, void* reserved);
bool network_is_off(network_handle_t network, void* reserved);
int network_connect_cancel(network_handle_t network, uint32_t flags, uint32_t param1, void* reserved);
network_handle_t network_preferred(network_handle_t network, bool preferred, void* reserved);
bool network_is_preferred(network_handle_t network, void* reserved);

#define NETWORK_LISTEN_EXIT (1<<0)
/**
Expand Down
25 changes: 25 additions & 0 deletions system/src/system_cloud.cpp
Expand Up @@ -34,11 +34,13 @@
#include "spark_wiring_cloud.h"
#include "system_cloud.h"
#include "system_cloud_internal.h"
#include "system_cloud_connection.h"
#include "system_publish_vitals.h"
#include "system_task.h"
#include "system_threading.h"
#include "system_update.h"
#include "system_cloud_internal.h"
#include "system_connection_manager.h"
#include "string_convert.h"
#include "spark_protocol_functions.h"
#include "events.h"
Expand Down Expand Up @@ -300,6 +302,11 @@ int spark_set_connection_property(unsigned property, unsigned value, const void*
const auto r = spark_protocol_set_connection_property(sp, property, value, d, reserved);
return spark_protocol_to_system_error(r);
}
case SPARK_CLOUD_BIND_NETWORK_INTERFACE: {
CloudConnectionSettings::instance()->setBoundInterface((network_interface_t)value);
return 0;
}

default:
return SYSTEM_ERROR_INVALID_ARGUMENT;
}
Expand All @@ -324,6 +331,24 @@ int spark_get_connection_property(unsigned property, void* data, size_t* size, v
return SYSTEM_ERROR_INVALID_STATE;
}
return getConnectionProperty(protocol::Connection::MAX_FUNCTION_ARGUMENT_SIZE, data, size);
case SPARK_CLOUD_BIND_NETWORK_INTERFACE: {
if (*size >= sizeof(network_interface_t)) {
*((network_interface_t*)data) = CloudConnectionSettings::instance()->getBoundInterface();
return 0;
}
return SYSTEM_ERROR_INVALID_ARGUMENT;
}
case SPARK_CLOUD_GET_NETWORK_INTERFACE: {
scott-brust marked this conversation as resolved.
Show resolved Hide resolved
if (*size >= sizeof(network_interface_t)) {
#if HAL_PLATFORM_AUTOMATIC_CONNECTION_MANAGEMENT
*((network_interface_t*)data) = ConnectionManager::instance()->getCloudConnectionNetwork();
#else
*((network_interface_t*)data) = NETWORK_INTERFACE_ALL;
#endif
return 0;
}
return SYSTEM_ERROR_INVALID_ARGUMENT;
}
default:
return SYSTEM_ERROR_INVALID_ARGUMENT;
}
Expand Down
5 changes: 5 additions & 0 deletions system/src/system_cloud_connection.cpp
Expand Up @@ -62,6 +62,11 @@ void spark_cloud_udp_port_set(uint16_t port)
cloud_udp_port = port;
}

uint16_t spark_cloud_udp_port_get()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a more authoritative source for the cloud connection port to use?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cloud_udp_port is it, so I think you are good. Do you want to update port = cloud_udp_port; to port = spark_cloud_udp_port_get() as well? If we are not planning on using spark_cloud_udp_port_set(), we could remove it.

{
return cloud_udp_port;
}

#if HAL_PLATFORM_CLOUD_UDP

namespace {
Expand Down
1 change: 1 addition & 0 deletions system/src/system_cloud_connection.h
Expand Up @@ -53,6 +53,7 @@ sock_handle_t system_cloud_get_socket_handle();
* and system upgrades.
*/
void spark_cloud_udp_port_set(uint16_t port);
uint16_t spark_cloud_udp_port_get();
int spark_cloud_socket_connect(void);
int spark_cloud_socket_disconnect(bool graceful=true);
uint8_t spark_cloud_socket_closed();
Expand Down
22 changes: 19 additions & 3 deletions system/src/system_cloud_connection_posix.cpp
Expand Up @@ -14,14 +14,15 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/

#include "hal_platform.h"

#if HAL_USE_SOCKET_HAL_POSIX
#include "system_cloud_connection.h"
#include "system_cloud_internal.h"
#include "system_connection_manager.h"
#include "system_error.h"
#include "inet_hal.h"
#include "ifapi.h"
#include "netdb_hal.h"
#include "system_string_interpolate.h"
#include "spark_wiring_ticks.h"
Expand Down Expand Up @@ -200,6 +201,17 @@ int system_cloud_connect(int protocol, const ServerAddress* address, sockaddr* s
}
}

network_interface_t cloudInterface = particle::system::ConnectionManager::instance()->selectCloudConnectionNetwork();

if (cloudInterface != NETWORK_INTERFACE_ALL) {
// Bind to specific netif
struct ifreq ifr = {};
if_index_to_name(cloudInterface, ifr.ifr_name);

auto sockOptRet = sock_setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
LOG(TRACE, "%d Bound cloud socket to lwip interface %s", sockOptRet, ifr.ifr_name);
}

/* FIXME: timeout for TCP */
/* NOTE: we do this for UDP sockets as well in order to automagically filter
* on source address and port */
Expand Down Expand Up @@ -298,6 +310,7 @@ int system_cloud_disconnect(int flags)
int system_cloud_send(const uint8_t* buf, size_t buflen, int flags)
{
(void)flags;
LOG_DEBUG(TRACE, "TX s_state.socket %d buflen %d", s_state.socket, buflen);
int r = sock_send(s_state.socket, buf, buflen, 0);
if (r < 0) {
if (errno == ENOMEM) {
Expand Down Expand Up @@ -328,13 +341,16 @@ int system_cloud_recv(uint8_t* buf, size_t buflen, int flags)
}
}

if (recvd) {
LOG_DEBUG(TRACE, "RX s_state.socket %d buflen %d recvd %d", s_state.socket, buflen, recvd);
}

return recvd;
}

int system_internet_test(void* reserved)
{
particle::SimpleNtpClient ntp;
return ntp.ntpDate(nullptr);
return particle::system::ConnectionManager::instance()->testConnections();
}

int system_multicast_announce_presence(void* reserved)
Expand Down
5 changes: 5 additions & 0 deletions system/src/system_cloud_internal.cpp
Expand Up @@ -522,11 +522,16 @@ int Spark_Save(const void* buffer, size_t length, uint8_t type, void* reserved)

int Spark_Restore(void* buffer, size_t max_length, uint8_t type, void* reserved)
{
#if PLATFORM_ID == PLATFORM_MSOM
// FIXME: Force new session handshake for now
return 0;
scott-brust marked this conversation as resolved.
Show resolved Hide resolved
#else
size_t length = 0;
int error = HAL_System_Backup_Restore(0, buffer, max_length, &length, nullptr);
if (error)
length = 0;
return length;
#endif
}

void update_persisted_state(std::function<void(SessionPersistOpaque&)> fn)
Expand Down
12 changes: 11 additions & 1 deletion system/src/system_cloud_internal.h
Expand Up @@ -142,7 +142,8 @@ class CloudConnectionSettings {
CloudConnectionSettings() :
defaultDisconnectTimeout_(DEFAULT_DISCONNECT_TIMEOUT),
defaultDisconnectGracefully_(DEFAULT_DISCONNECT_GRACEFULLY),
defaultDisconnectClearSession_(DEFAULT_DISCONNECT_CLEAR_SESSION) {
defaultDisconnectClearSession_(DEFAULT_DISCONNECT_CLEAR_SESSION),
boundInterface_(NETWORK_INTERFACE_ALL) {
}

void setDefaultDisconnectOptions(const CloudDisconnectOptions& options) {
Expand Down Expand Up @@ -194,6 +195,14 @@ class CloudConnectionSettings {

static CloudConnectionSettings* instance();

void setBoundInterface(network_interface_t network) {
boundInterface_ = network;
}

network_interface_t getBoundInterface() {
return boundInterface_;
}

private:
SimpleAtomicFlagMutex mutex_;
// Default disconnection options can only be set in the context of the system thread.
Expand All @@ -203,6 +212,7 @@ class CloudConnectionSettings {
bool defaultDisconnectClearSession_;
// Pending disconnection options are set atomically and guarded by a spinlock
CloudDisconnectOptions pendingDisconnectOptions_;
network_interface_t boundInterface_;
};

// Use this function instead of Particle.publish() in the system code
Expand Down