blob: 44e39d3bdc56a2a78bf3f765b7fd714fd858dc31 [file] [log] [blame]
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/net/buf.h>
#include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
#include <zephyr/mgmt/mcumgr/smp/smp.h>
#include <zephyr/mgmt/mcumgr/transport/smp.h>
#include <mgmt/mcumgr/transport/smp_internal.h>
void smp_reassembly_init(struct smp_transport *smpt)
{
smpt->__reassembly.current = NULL;
smpt->__reassembly.expected = 0;
}
int smp_reassembly_expected(const struct smp_transport *smpt)
{
if (smpt->__reassembly.current == NULL) {
return -EINVAL;
}
return smpt->__reassembly.expected;
}
int smp_reassembly_collect(struct smp_transport *smpt, const void *buf, uint16_t len)
{
if (smpt->__reassembly.current == NULL) {
/*
* Collecting the first fragment: need to allocate buffer for it and prepare
* the reassembly context.
*/
if (len >= sizeof(struct smp_hdr)) {
uint16_t expected = sys_be16_to_cpu(((struct smp_hdr *)buf)->nh_len);
/*
* The length field in the header does not count the header size,
* but the reassembly does so the size needs to be added to the number of
* expected bytes.
*/
expected += sizeof(struct smp_hdr);
/* Joining net_bufs not supported yet */
if (len > CONFIG_MCUMGR_BUF_SIZE || expected > CONFIG_MCUMGR_BUF_SIZE) {
return -ENOSR;
}
if (len > expected) {
return -EOVERFLOW;
}
smpt->__reassembly.current = smp_packet_alloc();
if (smpt->__reassembly.current != NULL) {
smpt->__reassembly.expected = expected;
} else {
return -ENOMEM;
}
} else {
/* Not enough data to even collect header */
return -ENODATA;
}
}
/* len is expected to be > 0 */
if (smpt->__reassembly.expected >= len) {
net_buf_add_mem(smpt->__reassembly.current, buf, len);
smpt->__reassembly.expected -= len;
} else {
/*
* A fragment is longer than the expected size and will not fit into the buffer.
*/
return -EOVERFLOW;
}
return smpt->__reassembly.expected;
}
int smp_reassembly_complete(struct smp_transport *smpt, bool force)
{
if (smpt->__reassembly.current == NULL) {
return -EINVAL;
}
if (smpt->__reassembly.expected == 0 || force) {
int expected = smpt->__reassembly.expected;
smp_rx_req(smpt, smpt->__reassembly.current);
smpt->__reassembly.expected = 0;
smpt->__reassembly.current = NULL;
return expected;
}
return -ENODATA;
}
int smp_reassembly_drop(struct smp_transport *smpt)
{
if (smpt->__reassembly.current == NULL) {
return -EINVAL;
}
smp_packet_free(smpt->__reassembly.current);
smpt->__reassembly.expected = 0;
smpt->__reassembly.current = NULL;
return 0;
}
void *smp_reassembly_get_ud(const struct smp_transport *smpt)
{
if (smpt->__reassembly.current != NULL) {
return net_buf_user_data(smpt->__reassembly.current);
}
return NULL;
}