blob: 763bd2b914350387b8fa3669aef6cd0ceaa4cb0e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <assert.h>
#include <string.h>
#include "os/mynewt.h"
#include "mem/mem.h"
#include "mgmt/mgmt.h"
#include "os_mgmt/os_mgmt.h"
#include "mynewt_smp/smp.h"
#include "tinycbor/cbor.h"
#include "tinycbor/cbor_mbuf_writer.h"
#include "tinycbor/cbor_mbuf_reader.h"
/* Shared queue that SMP uses for work items. */
struct os_eventq *g_smp_evq;
static mgmt_alloc_rsp_fn smp_alloc_rsp;
static mgmt_trim_front_fn smp_trim_front;
static mgmt_reset_buf_fn smp_reset_buf;
static mgmt_write_at_fn smp_write_at;
static mgmt_init_reader_fn smp_init_reader;
static mgmt_init_writer_fn smp_init_writer;
static mgmt_free_buf_fn smp_free_buf;
const struct mgmt_streamer_cfg g_smp_cbor_cfg = {
.alloc_rsp = smp_alloc_rsp,
.trim_front = smp_trim_front,
.reset_buf = smp_reset_buf,
.write_at = smp_write_at,
.init_reader = smp_init_reader,
.init_writer = smp_init_writer,
.free_buf = smp_free_buf,
};
void
mgmt_evq_set(struct os_eventq *evq)
{
g_smp_evq = evq;
}
struct os_eventq *
mgmt_evq_get(void)
{
return g_smp_evq;
}
static void *
smp_alloc_rsp(const void *req, void *arg)
{
struct os_mbuf *m;
struct os_mbuf *rsp;
if (!req) {
return NULL;
}
m = (struct os_mbuf *)req;
rsp = os_msys_get_pkthdr(0, OS_MBUF_USRHDR_LEN(m));
if (!rsp) {
return NULL;
}
memcpy(OS_MBUF_USRHDR(rsp), OS_MBUF_USRHDR(m), OS_MBUF_USRHDR_LEN(m));
return rsp;
}
static void
smp_trim_front(void *m, size_t len, void *arg)
{
os_mbuf_adj(m, len);
}
static void
smp_reset_buf(void *m, void *arg)
{
if (!m) {
return;
}
/* We need to trim from the back because the head
* contains useful information which we do not want
* to get rid of
*/
os_mbuf_adj(m, -1 * OS_MBUF_PKTLEN((struct os_mbuf *)m));
}
static int
smp_write_at(struct cbor_encoder_writer *writer, size_t offset,
const void *data, size_t len, void *arg)
{
struct cbor_mbuf_writer *cmw;
struct os_mbuf *m;
int rc;
if (!writer) {
return MGMT_ERR_EINVAL;
}
cmw = (struct cbor_mbuf_writer *)writer;
m = cmw->m;
if (offset > OS_MBUF_PKTLEN(m)) {
return MGMT_ERR_EINVAL;
}
rc = os_mbuf_copyinto(m, offset, data, len);
if (rc) {
return MGMT_ERR_ENOMEM;
}
writer->bytes_written = OS_MBUF_PKTLEN(m);
return 0;
}
static void
smp_free_buf(void *m, void *arg)
{
if (!m) {
return;
}
os_mbuf_free_chain(m);
}
static int
smp_init_reader(struct cbor_decoder_reader *reader, void *m,
void *arg)
{
struct cbor_mbuf_reader *cmr;
if (!reader) {
return MGMT_ERR_EINVAL;
}
cmr = (struct cbor_mbuf_reader *)reader;
cbor_mbuf_reader_init(cmr, m, 0);
return 0;
}
static int
smp_init_writer(struct cbor_encoder_writer *writer, void *m,
void *arg)
{
struct cbor_mbuf_writer *cmw;
if (!writer) {
return MGMT_ERR_EINVAL;
}
cmw = (struct cbor_mbuf_writer *)writer;
cbor_mbuf_writer_init(cmw, m);
return 0;
}
/**
* Allocates an mbuf to costain an outgoing response fragment.
*/
static struct os_mbuf *
smp_rsp_frag_alloc(uint16_t frag_size, void *arg)
{
struct os_mbuf *src_rsp;
struct os_mbuf *frag;
/* We need to duplicate the user header from the source response, as that
* is where transport-specific information is stored.
*/
src_rsp = arg;
frag = os_msys_get_pkthdr(frag_size, OS_MBUF_USRHDR_LEN(src_rsp));
if (frag != NULL) {
/* Copy the user header from the response into the fragmest mbuf. */
memcpy(OS_MBUF_USRHDR(frag), OS_MBUF_USRHDR(src_rsp),
OS_MBUF_USRHDR_LEN(src_rsp));
}
return frag;
}
int
smp_tx_rsp(struct smp_streamer *ns, void *rsp, void *arg)
{
struct smp_transport *st;
struct os_mbuf *frag;
struct os_mbuf *m;
uint16_t mtu;
int rc;
st = arg;
m = rsp;
mtu = st->st_get_mtu(rsp);
if (mtu == 0U) {
/* The transport cannot support a transmission right now. */
return MGMT_ERR_EUNKNOWN;
}
while (m != NULL) {
frag = mem_split_frag(&m, mtu, smp_rsp_frag_alloc, rsp);
if (frag == NULL) {
return MGMT_ERR_ENOMEM;
}
rc = st->st_output(frag);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
}
return 0;
}
/**
* Processes a single SMP packet and sends the corresponding response(s).
*/
static int
smp_process_packet(struct smp_transport *st)
{
struct cbor_mbuf_reader reader;
struct cbor_mbuf_writer writer;
struct os_mbuf *m;
int rc;
if (!st) {
return MGMT_ERR_EINVAL;
}
st->st_streamer = (struct smp_streamer) {
.mgmt_stmr = {
.cfg = &g_smp_cbor_cfg,
.reader = &reader.r,
.writer = &writer.enc,
.cb_arg = st,
},
.tx_rsp_cb = smp_tx_rsp,
};
while (1) {
m = os_mqueue_get(&st->st_imq);
if (!m) {
break;
}
rc = smp_process_request_packet(&st->st_streamer, m);
if (rc) {
return rc;
}
}
return 0;
}
int
smp_rx_req(struct smp_transport *st, struct os_mbuf *req)
{
int rc;
rc = os_mqueue_put(&st->st_imq, os_eventq_dflt_get(), req);
if (rc) {
goto err;
}
return 0;
err:
os_mbuf_free_chain(req);
return rc;
}
static void
smp_event_data_in(struct os_event *ev)
{
smp_process_packet(ev->ev_arg);
}
int
smp_transport_init(struct smp_transport *st,
smp_transport_out_func_t output_func,
smp_transport_get_mtu_func_t get_mtu_func)
{
int rc;
st->st_output = output_func;
st->st_get_mtu = get_mtu_func;
rc = os_mqueue_init(&st->st_imq, smp_event_data_in, st);
if (rc != 0) {
goto err;
}
return 0;
err:
return rc;
}
void
smp_pkg_init(void)
{
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
mgmt_evq_set(os_eventq_dflt_get());
}