You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2018/01/18 01:36:41 UTC

[mynewt-mcumgr] 03/11: smp - The built-in transfer encoding.

This is an automated email from the ASF dual-hosted git repository.

ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-mcumgr.git

commit 0f10b533a1b82d93b0fb2ddad1c529b4e4bfea97
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Wed Jan 17 17:11:12 2018 -0800

    smp - The built-in transfer encoding.
    
    Simple management protocol.
---
 smp/CMakeLists.txt    |   7 +
 smp/include/smp/smp.h |  91 ++++++++++++
 smp/pkg.yml           |  40 ++++++
 smp/src/smp.c         | 379 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 517 insertions(+)

diff --git a/smp/CMakeLists.txt b/smp/CMakeLists.txt
new file mode 100644
index 0000000..74072ab
--- /dev/null
+++ b/smp/CMakeLists.txt
@@ -0,0 +1,7 @@
+target_include_directories(MCUMGR INTERFACE 
+    include
+)
+
+zephyr_library_sources(
+    smp/src/smp.c
+)
diff --git a/smp/include/smp/smp.h b/smp/include/smp/smp.h
new file mode 100644
index 0000000..7afa7ad
--- /dev/null
+++ b/smp/include/smp/smp.h
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file
+ * @brief SMP - Simple Management Protocol.
+ *
+ * SMP is a basic protocol that sits on top of the mgmt layer.  SMP requests
+ * and responses have the following format:
+ *
+ *     [Offset 0]: Mgmt header
+ *     [Offset 8]: CBOR map of command-specific key-value pairs.
+ *
+ * SMP request packets may contain multiple concatenated requests.  Each
+ * request must start at an offset that is a multiple of 4, so padding should
+ * be inserted between requests as necessary.  Requests are processed
+ * sequentially from the start of the packet to the end.  Each response is sent
+ * individually in its own packet.  If a request elicits an error response,
+ * processing of the packet is aborted.
+ */
+
+#ifndef H_SMP_
+#define H_SMP_
+
+#include "mgmt/mgmt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct smp_streamer;
+struct mgmt_hdr;
+
+/** @typedef smp_tx_rsp_fn
+ * @brief Transmits an SMP response packet.
+ *
+ * @param ss                    The streamer to transmit via.
+ * @param buf                   Buffer containing the response packet.
+ * @param arg                   Optional streamer argument.
+ *
+ * @return                      0 on success, MGMT_ERR_[...] code on failure.
+ */
+typedef int smp_tx_rsp_fn(struct smp_streamer *ss, void *buf, void *arg);
+
+/**
+ * @brief Decodes, encodes, and transmits SMP packets.
+ */
+struct smp_streamer {
+    struct mgmt_streamer mgmt_stmr;
+    smp_tx_rsp_fn *tx_rsp_cb;
+};
+
+/**
+ * @brief Processes a single SMP request packet and sends all corresponding
+ *        responses.
+ *
+ * Processes all SMP requests in an incoming packet.  Requests are processed
+ * sequentially from the start of the packet to the end.  Each response is sent
+ * individually in its own packet.  If a request elicits an error response,
+ * processing of the packet is aborted.  This function consumes the supplied
+ * request buffer regardless of the outcome.
+ *
+ * @param streamer              The streamer providing the required SMP
+ *                                  callbacks.
+ * @param req                   The request packet to process.
+ *
+ * @return                      0 on success, MGMT_ERR_[...] code on failure.
+ */
+int smp_process_request_packet(struct smp_streamer *streamer, void *req);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_SMP_ */
diff --git a/smp/pkg.yml b/smp/pkg.yml
new file mode 100644
index 0000000..b9fcc78
--- /dev/null
+++ b/smp/pkg.yml
@@ -0,0 +1,40 @@
+#
+# 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.
+#
+
+pkg.name: smp
+pkg.description: Server-side simple management protocol (SMP) functionality.
+pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+    - '@apache-mynewt-core/encoding/cborattr'
+    - '@apache-mynewt-core/kernel/os'
+    - '@apache-mynewt-core/util/mem'
+    - '@mynewt-mcumgr/mgmt'
+    - '@mynewt-mcumgr/smp'
+
+pkg.deps.NEWTMGR_BLE_HOST:
+    - '@apache-mynewt-core/mgmt/newtmgr/transport/ble'
+
+pkg.apis:
+    - newtmgr
+
+pkg.init:
+    smp_pkg_init: 500
diff --git a/smp/src/smp.c b/smp/src/smp.c
new file mode 100644
index 0000000..b014be5
--- /dev/null
+++ b/smp/src/smp.c
@@ -0,0 +1,379 @@
+/*
+ * 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.
+ */
+
+/** SMP - Simple Management Protocol. */
+
+#include <assert.h>
+#include <string.h>
+
+#include "mgmt/endian.h"
+#include "mgmt/mgmt.h"
+#include "smp/smp.h"
+#include "cbor.h"
+
+static int
+smp_align4(int x)
+{
+    int rem;
+
+    rem = x % 4;
+    if (rem == 0) {
+        return x;
+    } else {
+        return x - rem + 4;
+    }
+}
+
+/**
+ * Converts a request opcode to its corresponding response opcode.
+ */
+static uint8_t
+smp_rsp_op(uint8_t req_op)
+{
+    if (req_op == MGMT_OP_READ) {
+        return MGMT_OP_READ_RSP;
+    } else {
+        return MGMT_OP_WRITE_RSP;
+    }
+}
+
+static void
+smp_init_rsp_hdr(const struct mgmt_hdr *req_hdr, struct mgmt_hdr *rsp_hdr)
+{
+    *rsp_hdr = (struct mgmt_hdr) {
+        .nh_len = 0,
+        .nh_flags = 0,
+        .nh_op = smp_rsp_op(req_hdr->nh_op),
+        .nh_group = req_hdr->nh_group,
+        .nh_seq = req_hdr->nh_seq,
+        .nh_id = req_hdr->nh_id,
+    };
+}
+
+static int
+smp_read_hdr(struct smp_streamer *streamer, struct mgmt_hdr *dst_hdr)
+{
+    struct cbor_decoder_reader *reader;
+
+    reader = streamer->mgmt_stmr.reader;
+
+    if (reader->message_size < sizeof *dst_hdr) {
+        return MGMT_ERR_EINVAL;
+    }
+
+    reader->cpy(reader, (char *)dst_hdr, 0, sizeof *dst_hdr);
+    return 0;
+}
+
+static int
+smp_write_hdr(struct smp_streamer *streamer, const struct mgmt_hdr *src_hdr)
+{
+    int rc;
+
+    rc = mgmt_streamer_write_at(&streamer->mgmt_stmr, 0, src_hdr,
+                                sizeof *src_hdr);
+    return mgmt_err_from_cbor(rc);
+}
+
+static int
+smp_build_err_rsp(struct smp_streamer *streamer,
+                  const struct mgmt_hdr *req_hdr,
+                  int status)
+{
+    struct CborEncoder map;
+    struct mgmt_ctxt cbuf;
+    struct mgmt_hdr rsp_hdr;
+    int rc;
+
+    rc = mgmt_ctxt_init(&cbuf, &streamer->mgmt_stmr);
+    if (rc != 0) {
+        return rc;
+    }
+
+    smp_init_rsp_hdr(req_hdr, &rsp_hdr);
+    rc = smp_write_hdr(streamer, &rsp_hdr);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = cbor_encoder_create_map(&cbuf.encoder, &map, CborIndefiniteLength);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = mgmt_write_rsp_status(&cbuf, status);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = cbor_encoder_close_container(&cbuf.encoder, &map);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rsp_hdr.nh_len = cbor_encode_bytes_written(&cbuf.encoder) - MGMT_HDR_SIZE;
+    mgmt_hton_hdr(&rsp_hdr);
+    rc = smp_write_hdr(streamer, &rsp_hdr);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+/**
+ * Processes a single SMP request and generates a response payload (i.e.,
+ * everything after the management header).  On success, the response payload
+ * is written to the supplied cbuf but not transmitted.  On failure, no error
+ * response gets written; the caller is expected to build an error response
+ * from the return code.
+ *
+ * @param cbuf                  A cbuf containing the request and response
+ *                                  buffer.
+ * @param req_hdr               The management header belonging to the incoming
+ *                                  request (host-byte order).
+ *
+ * @return                      A MGMT_ERR_[...] error code.
+ */
+static int
+smp_handle_single_payload(struct mgmt_ctxt *cbuf,
+                          const struct mgmt_hdr *req_hdr)
+{
+    const struct mgmt_handler *handler;
+    struct CborEncoder payload_encoder;
+    int rc;
+
+    handler = mgmt_find_handler(req_hdr->nh_group, req_hdr->nh_id);
+    if (handler == NULL) {
+        return MGMT_ERR_ENOTSUP;
+    }
+
+    /* Begin response payload.  Response fields are inserted into the root
+     * map as key value pairs.
+     */
+    rc = cbor_encoder_create_map(&cbuf->encoder, &payload_encoder,
+                                 CborIndefiniteLength);
+    rc = mgmt_err_from_cbor(rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    switch (req_hdr->nh_op) {
+    case MGMT_OP_READ:
+        if (handler->mh_read != NULL) {
+            rc = handler->mh_read(cbuf);
+        } else {
+            rc = MGMT_ERR_ENOTSUP;
+        }
+        break;
+
+    case MGMT_OP_WRITE:
+        if (handler->mh_write != NULL) {
+            rc = handler->mh_write(cbuf);
+        } else {
+            rc = MGMT_ERR_ENOTSUP;
+        }
+        break;
+
+    default:
+        rc = MGMT_ERR_EINVAL;
+        break;
+    }
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* End response payload. */
+    rc = cbor_encoder_close_container(&cbuf->encoder, &payload_encoder);
+    return mgmt_err_from_cbor(rc);
+}
+
+/**
+ * Processes a single SMP request and generates a complete response (i.e.,
+ * header and payload).  On success, the response is written using the supplied
+ * streamer but not transmitted.  On failure, no error response gets written;
+ * the caller is expected to build an error response from the return code.
+ *
+ * @param streamer              The SMP streamer to use for reading the request
+ *                                  and writing the response.
+ * @param req_hdr               The management header belonging to the incoming
+ *                                  request (host-byte order).
+ *
+ * @return                      A MGMT_ERR_[...] error code.
+ */
+static int
+smp_handle_single_req(struct smp_streamer *streamer,
+                      const struct mgmt_hdr *req_hdr)
+{
+    struct mgmt_ctxt cbuf;
+    struct mgmt_hdr rsp_hdr;
+    int rc;
+
+    rc = mgmt_ctxt_init(&cbuf, &streamer->mgmt_stmr);
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* Write a dummy header to the beginning of the response buffer.  Some
+     * fields will need to be fixed up later.
+     */
+    smp_init_rsp_hdr(req_hdr, &rsp_hdr);
+    rc = smp_write_hdr(streamer, &rsp_hdr);
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* Process the request and write the response payload. */
+    rc = smp_handle_single_payload(&cbuf, req_hdr);
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* Fix up the response header with the correct length. */
+    rsp_hdr.nh_len = cbor_encode_bytes_written(&cbuf.encoder) - MGMT_HDR_SIZE;
+    mgmt_hton_hdr(&rsp_hdr);
+    rc = smp_write_hdr(streamer, &rsp_hdr);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+/**
+ * Attempts to transmit an SMP error response.  This function consumes both
+ * supplied buffers.
+ *
+ * @param streamer              The SMP streamer for building and transmitting
+ *                                  the response.
+ * @param req_hdr               The header of the request which elicited the
+ *                                  error.
+ * @param req                   The buffer holding the request.
+ * @param rsp                   The buffer holding the response, or NULL if
+ *                                  none was allocated.
+ * @param status                The status to indicate in the error response.
+ */
+static void
+smp_on_err(struct smp_streamer *streamer, const struct mgmt_hdr *req_hdr,
+           void *req, void *rsp, int status)
+{
+    int rc;
+
+    /* Prefer the response buffer for holding the error response.  If no
+     * response buffer was allocated, use the request buffer instead.
+     */
+    if (rsp == NULL) {
+        rsp = req;
+        req = NULL;
+    }
+
+    /* Clear the partial response from the buffer, if any. */
+    mgmt_streamer_reset_buf(&streamer->mgmt_stmr, rsp);
+    mgmt_streamer_init_writer(&streamer->mgmt_stmr, rsp);
+
+    /* Build and transmit the error response. */
+    rc = smp_build_err_rsp(streamer, req_hdr, status);
+    if (rc == 0) {
+        streamer->tx_rsp_cb(streamer, rsp, streamer->mgmt_stmr.cb_arg);
+        rsp = NULL;
+    }
+
+    /* Free any extra buffers. */
+    mgmt_streamer_free_buf(&streamer->mgmt_stmr, req);
+    mgmt_streamer_free_buf(&streamer->mgmt_stmr, rsp);
+}
+
+/**
+ * Processes all SMP requests in an incoming packet.  Requests are processed
+ * sequentially from the start of the packet to the end.  Each response is sent
+ * individually in its own packet.  If a request elicits an error response,
+ * processing of the packet is aborted.  This function consumes the supplied
+ * request buffer regardless of the outcome.
+ *
+ * @param streamer              The streamer to use for reading, writing, and
+ *                                  transmitting.
+ * @param req                   A buffer containing the request packet.
+ *
+ * @return                      0 on success, MGMT_ERR_[...] code on failure.
+ */
+int
+smp_process_request_packet(struct smp_streamer *streamer, void *req)
+{
+    struct mgmt_hdr req_hdr;
+    void *rsp;
+    bool valid_hdr;
+    int rc;
+
+    rsp = NULL;
+    valid_hdr = true;
+
+    while (1) {
+        rc = mgmt_streamer_init_reader(&streamer->mgmt_stmr, req);
+        if (rc != 0) {
+            valid_hdr = false;
+            break;
+        }
+
+        /* Read the management header and strip it from the request. */
+        rc = smp_read_hdr(streamer, &req_hdr);
+        if (rc != 0) {
+            valid_hdr = false;
+            break;
+        }
+        mgmt_ntoh_hdr(&req_hdr);
+        mgmt_streamer_trim_front(&streamer->mgmt_stmr, req, MGMT_HDR_SIZE);
+
+        rsp = mgmt_streamer_alloc_rsp(&streamer->mgmt_stmr, req);
+        if (rsp == NULL) {
+            rc = MGMT_ERR_ENOMEM;
+            break;
+        }
+
+        rc = mgmt_streamer_init_writer(&streamer->mgmt_stmr, rsp);
+        if (rc != 0) {
+            break;
+        }
+
+        /* Process the request payload and build the response. */
+        rc = smp_handle_single_req(streamer, &req_hdr);
+        if (rc != 0) {
+            break;
+        }
+
+        /* Send the response. */
+        rc = streamer->tx_rsp_cb(streamer, rsp, streamer->mgmt_stmr.cb_arg);
+        rsp = NULL;
+        if (rc != 0) {
+            break;
+        }
+
+        /* Trim processed request to free up space for subsequent responses. */
+        mgmt_streamer_trim_front(&streamer->mgmt_stmr, req,
+                                 smp_align4(req_hdr.nh_len));
+    }
+
+    if (rc != 0 && valid_hdr) {
+        smp_on_err(streamer, &req_hdr, req, rsp, rc);
+        return rc;
+    }
+
+    mgmt_streamer_free_buf(&streamer->mgmt_stmr, req);
+    mgmt_streamer_free_buf(&streamer->mgmt_stmr, rsp);
+    return 0;
+}

-- 
To stop receiving notification emails like this one, please contact
"commits@mynewt.apache.org" <co...@mynewt.apache.org>.