/* Copyright (c) 2024 Arduino SA This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ANetworkConfigurator_Config.h" #if NETWORK_CONFIGURATOR_COMPATIBLE #include "BoardConfigurationProtocol.h" #include "Arduino_DebugUtils.h" #include "CBORAdapter.h" #include "cbor/CBOR.h" #define PACKET_VALIDITY_MS 30000 /****************************************************************************** * PUBLIC MEMBER FUNCTIONS ******************************************************************************/ bool BoardConfigurationProtocol::getMsg(ProvisioningInputMessage &msg) { if (_inputMessagesList.size() == 0) { return false; } InputPacketBuffer *buf = &_inputMessagesList.front(); ProvisioningMessageDown cborMsg; bool decodeRes = CBORAdapter::getMsgFromCBOR(buf->get_ptr(), buf->len(), &cborMsg); _inputMessagesList.pop_front(); if (!decodeRes) { DEBUG_DEBUG("BoardConfigurationProtocol::%s Invalid message", __FUNCTION__); sendStatus(StatusMessage::INVALID_PARAMS); return false; } if (cborMsg.c.id == ProvisioningMessageId::TimestampProvisioningMessageId) { msg.type = MessageInputType::TIMESTAMP; msg.m.timestamp = cborMsg.provisioningTimestamp.timestamp; } else if (cborMsg.c.id == ProvisioningMessageId::CommandsProvisioningMessageId) { msg.type = MessageInputType::COMMANDS; msg.m.cmd = (RemoteCommands)cborMsg.provisioningCommands.cmd; } else { msg.type = MessageInputType::NETWORK_SETTINGS; msg.m.netSetting.type = NetworkAdapter::NONE; memcpy(&msg.m.netSetting, &cborMsg.provisioningNetworkConfig.networkSetting, sizeof(models::NetworkSetting)); } return true; } bool BoardConfigurationProtocol::sendMsg(ProvisioningOutputMessage &msg) { bool res = false; switch (msg.type) { case MessageOutputType::STATUS: res = sendStatus(msg.m.status); break; case MessageOutputType::NETWORK_OPTIONS: res = sendNetworkOptions(msg.m.netOptions); break; case MessageOutputType::UHWID: res = sendUhwid(msg.m.uhwid); break; case MessageOutputType::JWT: res = sendJwt(msg.m.jwt, strlen(msg.m.jwt)); break; case MessageOutputType::BLE_MAC_ADDRESS: res = sendBleMacAddress(msg.m.BLEMacAddress, BLE_MAC_ADDRESS_SIZE); break; case MessageOutputType::WIFI_FW_VERSION: res = sendVersion(msg.m.wifiFwVersion, msg.type); break; case MessageOutputType::PROV_SKETCH_VERSION: res = sendVersion(msg.m.provSketchVersion, msg.type); break; case MessageOutputType::NETCONFIG_LIB_VERSION: res = sendVersion(msg.m.netConfigLibVersion, msg.type); break; default: break; } return res; } bool BoardConfigurationProtocol::msgAvailable() { return _inputMessagesList.size() > 0; } /****************************************************************************** * PROTECTED MEMBER FUNCTIONS ******************************************************************************/ BoardConfigurationProtocol::TransmissionResult BoardConfigurationProtocol::sendAndReceive() { TransmissionResult transmissionRes = TransmissionResult::NOT_COMPLETED; if (!isPeerConnected()) { return TransmissionResult::PEER_NOT_AVAILABLE; } if (_outputMessagesList.size() > 0) { checkOutputPacketValidity(); transmitStream(); } if (!received()) { return transmissionRes; } int receivedDataLen = available(); for (int i = 0; i < receivedDataLen; i++) { PacketManager::ReceivingState res; uint8_t val = read(); res = PacketManager::PacketReceiver::getInstance().handleReceivedByte(_packet, val); if (res == PacketManager::ReceivingState::ERROR) { DEBUG_DEBUG("BoardConfigurationProtocol::%s Malformed packet", __FUNCTION__); sendNak(); clearInputBuffer(); transmissionRes = TransmissionResult::INVALID_DATA; break; } else if (res == PacketManager::ReceivingState::RECEIVED) { switch (_packet.Type) { case PacketManager::MessageType::DATA: { #if BCP_DEBUG_PACKET == 1 printPacket("payload", _packet.Payload.get_ptr(), _packet.Payload.len()); #endif _inputMessagesList.push_back(_packet.Payload); //Consider all sent data as received _outputMessagesList.clear(); transmissionRes = TransmissionResult::DATA_RECEIVED; } break; case PacketManager::MessageType::TRANSMISSION_CONTROL: { if (_packet.Payload.len() == 1 && _packet.Payload[0] == (uint8_t)PacketManager::TransmissionControlMessage::NACK) { for (std::list<OutputPacketBuffer>::iterator packet = _outputMessagesList.begin(); packet != _outputMessagesList.end(); ++packet) { packet->startProgress(); } } else if (_packet.Payload.len() == 1 && _packet.Payload[0] == (uint8_t)PacketManager::TransmissionControlMessage::DISCONNECT) { handleDisconnectRequest(); } } break; default: break; } PacketManager::PacketReceiver::getInstance().clear(_packet); } } return transmissionRes; } bool BoardConfigurationProtocol::sendNak() { uint8_t data = 0x03; return sendData(PacketManager::MessageType::TRANSMISSION_CONTROL, &data, sizeof(data)); } bool BoardConfigurationProtocol::sendData(PacketManager::MessageType type, const uint8_t *data, size_t len) { OutputPacketBuffer outputMsg; outputMsg.setValidityTs(millis() + PACKET_VALIDITY_MS); if (!PacketManager::createPacket(outputMsg, type, data, len)) { return false; } #if BCP_DEBUG_PACKET == 1 printPacket("output message", outputMsg.get_ptr(), outputMsg.len()); #endif _outputMessagesList.push_back(outputMsg); TransmissionResult res = TransmissionResult::NOT_COMPLETED; do { res = transmitStream(); if (res == TransmissionResult::PEER_NOT_AVAILABLE) { break; } } while (res == TransmissionResult::NOT_COMPLETED); return true; } void BoardConfigurationProtocol::clear() { PacketManager::PacketReceiver::getInstance().clear(_packet); _outputMessagesList.clear(); _inputMessagesList.clear(); } void BoardConfigurationProtocol::checkOutputPacketValidity() { if (_outputMessagesList.size() == 0) { return; } _outputMessagesList.remove_if([](OutputPacketBuffer &packet) { if (packet.getValidityTs() != 0 && packet.getValidityTs() < millis()) { return true; } return false; }); } /****************************************************************************** * PRIVATE MEMBER FUNCTIONS ******************************************************************************/ bool BoardConfigurationProtocol::sendStatus(StatusMessage msg) { bool res = false; size_t len = CBOR_DATA_STATUS_LEN; uint8_t data[len]; res = CBORAdapter::statusToCBOR(msg, data, &len); if (!res) { return res; } res = sendData(PacketManager::MessageType::DATA, data, len); if (!res) { DEBUG_WARNING("BoardConfigurationProtocol::%s failed to send status: %d ", __FUNCTION__, (int)msg); } return res; } size_t BoardConfigurationProtocol::calculateTotalOptionsLength(const NetworkOptions *netOptions) { size_t length = CBOR_DATA_HEADER_LEN; if (netOptions->type == NetworkOptionsClass::WIFI) { for (uint8_t i = 0; i < netOptions->option.wifi.numDiscoveredWiFiNetworks; i++) { length += 4; //for RSSI and text identifier length += netOptions->option.wifi.discoveredWifiNetworks[i].SSIDsize; } } return length; } bool BoardConfigurationProtocol::sendNetworkOptions(const NetworkOptions *netOptions) { bool res = false; size_t len = calculateTotalOptionsLength(netOptions); uint8_t data[len]; if (!CBORAdapter::networkOptionsToCBOR(netOptions, data, &len)) { return res; } res = sendData(PacketManager::MessageType::DATA, data, len); if (!res) { DEBUG_WARNING("BoardConfigurationProtocol::%s failed to send network options", __FUNCTION__); } return res; } bool BoardConfigurationProtocol::sendUhwid(const byte *uhwid) { bool res = false; size_t cborDataLen = CBOR_DATA_UHWID_LEN; uint8_t data[cborDataLen]; res = CBORAdapter::uhwidToCBOR(uhwid, data, &cborDataLen); if (!res) { return res; } res = sendData(PacketManager::MessageType::DATA, data, cborDataLen); if (!res) { DEBUG_WARNING("BoardConfigurationProtocol::%s failed to send uhwid", __FUNCTION__); return res; } return res; } bool BoardConfigurationProtocol::sendJwt(const char *jwt, size_t len) { bool res = false; if (len > MAX_JWT_SIZE) { return res; } size_t cborDataLen = CBOR_DATA_JWT_LEN; uint8_t data[cborDataLen]; res = CBORAdapter::jwtToCBOR(jwt, data, &cborDataLen); if (!res) { return res; } res = sendData(PacketManager::MessageType::DATA, data, cborDataLen); if (!res) { DEBUG_WARNING("BoardConfigurationProtocol::%s failed to send JWT", __FUNCTION__); return res; } return res; } bool BoardConfigurationProtocol::sendBleMacAddress(const uint8_t *mac, size_t len) { bool res = false; if (len != BLE_MAC_ADDRESS_SIZE) { return res; } size_t cborDataLen = CBOR_DATA_BLE_MAC_LEN; uint8_t data[cborDataLen]; res = CBORAdapter::BLEMacAddressToCBOR(mac, data, &cborDataLen); if (!res) { return res; } res = sendData(PacketManager::MessageType::DATA, data, cborDataLen); if (!res) { DEBUG_WARNING("BoardConfigurationProtocol::%s failed to send BLE MAC address", __FUNCTION__); return res; } return res; } bool BoardConfigurationProtocol::sendVersion(const char *version, MessageOutputType type) { bool res = false; size_t cborDataLen = CBOR_MIN_WIFI_FW_VERSION_LEN + strlen(version); uint8_t data[cborDataLen]; switch (type) { case MessageOutputType::WIFI_FW_VERSION: res = CBORAdapter::wifiFWVersionToCBOR (version, data, &cborDataLen); break; case MessageOutputType::PROV_SKETCH_VERSION: res = CBORAdapter::provSketchVersionToCBOR (version, data, &cborDataLen); break; case MessageOutputType::NETCONFIG_LIB_VERSION: res = CBORAdapter::netConfigLibVersionToCBOR(version, data, &cborDataLen); break; default: return false; } if (!res) { return res; } res = sendData(PacketManager::MessageType::DATA, data, cborDataLen); if (!res) { DEBUG_WARNING("BoardConfigurationProtocol::%s failed to send version of type %d", __FUNCTION__, (int)type); } return res; } BoardConfigurationProtocol::TransmissionResult BoardConfigurationProtocol::transmitStream() { if (!isPeerConnected()) { return TransmissionResult::PEER_NOT_AVAILABLE; } if (_outputMessagesList.size() == 0) { return TransmissionResult::COMPLETED; } TransmissionResult res = TransmissionResult::COMPLETED; for (std::list<OutputPacketBuffer>::iterator packet = _outputMessagesList.begin(); packet != _outputMessagesList.end(); ++packet) { if (packet->hasBytesToSend()) { res = TransmissionResult::NOT_COMPLETED; packet->incrementBytesSent(write(packet->get_ptrAt(packet->bytesSent()), packet->bytesToSend())); #if BCP_DEBUG_PACKET == 1 DEBUG_DEBUG("BoardConfigurationProtocol::%s transferred: %d of %d", __FUNCTION__, packet->bytesSent(), packet->len()); #endif break; } } return res; } void BoardConfigurationProtocol::printPacket(const char *label, const uint8_t *data, size_t len) { if (Debug.getDebugLevel() == DBG_VERBOSE) { DEBUG_VERBOSE("Print %s data:", label); Debug.newlineOff(); for (size_t i = 0; i < len; i++) { DEBUG_VERBOSE("%02x ", data[i]); if ((i + 1) % 10 == 0) { DEBUG_VERBOSE("\n"); } } DEBUG_VERBOSE("\n"); } Debug.newlineOn(); } #endif // NETWORK_CONFIGURATOR_COMPATIBLE