ext: lib: mgmt: mcumgr management infrastructure.

The goal of mcumgr is to define a common management infrastructure with
pluggable transport and encoding components.  In addition, mcumgr
provides definitions and handlers for some core commands: e.g., image
management, file system management, and OS managment.

Origin: mcumgr
License: Apache 2.0
URL: https://github.com/apache/mynewt-mcumgr
commit: 59210e372c
Purpose: Introduction of mcumgr
Maintained-by: External

Signed-off-by: Christopher Collins <ccollins@apache.org>
This commit is contained in:
Christopher Collins 2018-01-17 17:52:40 -08:00 committed by Carles Cufí
commit deadab4d09
66 changed files with 6433 additions and 0 deletions

15
CMakeLists.txt Normal file
View File

@ -0,0 +1,15 @@
zephyr_interface_library_named(MCUMGR)
zephyr_library()
add_subdirectory(cborattr)
add_subdirectory(cmd)
add_subdirectory(mgmt)
add_subdirectory(smp)
add_subdirectory(util)
zephyr_library_link_libraries(MCUMGR)
target_link_libraries(MCUMGR INTERFACE
zephyr_interface
TINYCBOR
)

38
Kconfig Normal file
View File

@ -0,0 +1,38 @@
# 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.
menuconfig MCUMGR
bool
prompt "mcumgr Support"
select TINYCBOR
default n
help
This option enables the mcumgr management library.
if MCUMGR
source "ext/lib/mgmt/mcumgr/mgmt/port/zephyr/Kconfig"
source "ext/lib/mgmt/mcumgr/cmd/Kconfig"
config APP_LINK_WITH_MCUMGR
bool "Link 'app' with MCUMGR"
default y
help
Add MCUMGR header files to the 'app' include path. It may be
disabled if the include paths for MCUMGR are causing aliasing
issues for 'app'.
endif

31
README-mynewt.md Normal file
View File

@ -0,0 +1,31 @@
## Building and using mcumgr with Apache Mynewt
NOTE: The *mcumgr* library consists of functionality that is already present in
the `apache-mynewt-core` repo. There is currently no need to use the external
*mcumgr* library with Mynewt, as the functionality is already built in to the
OS. To use this library with a Mynewt application, you will need to remove the
duplicate functionality from your copy of the `apache-mynewt-core` repo.
### Configuration
To use *mcumgr*, your Mynewt app needs to be configured to use:
1. An mcumgr transfer encoding
2. An mcumgr transport
3. (optional) Command handlers.
This is done by adding the necessary dependencies to your app or target. The following list of dependencies adds support for the SMP transfer encoding, the Bluetooth and shell transports, and all the built-in command handlers:
```
- '@apache-mynewt-core/mgmt/newtmgr/transport/ble'
- '@apache-mynewt-core/mgmt/newtmgr/transport/nmgr_shell'
- '@mynewt-mcumgr/cmd/fs_mgmt'
- '@mynewt-mcumgr/cmd/img_mgmt'
- '@mynewt-mcumgr/cmd/os_mgmt'
- '@mynewt-mcumgr/smp'
```
For an example of an app that uses mcumgr, see the `smp_svr` sample app in `samples/smp_svr/mynewt`.
### Building
With the necessary dependencies in place, your project can be built using the usual `newt build <target-name>` or `newt run <target-name>`

15
README-zephyr.md Normal file
View File

@ -0,0 +1,15 @@
## Using mcumgr with Zephyr
### Configuration
The `samples/smp_svr/zephyr/prj.conf` file provides a good starting point for
configuring an application to use *mcumgr*. The major configuration settings
are described below:
| Setting | Description | Default |
| ------------- | ------------- | ------- |
| `CONFIG_MCUMGR` | Enable the mcumgr management library. | n |
| `CONFIG_MCUMGR_CMD_FS_MGMT` | Enable mcumgr handlers for file management | n |
| `CONFIG_MCUMGR_CMD_IMG_MGMT` | Enable mcumgr handlers for image management | n |
| `CONFIG_MCUMGR_CMD_LOG_MGMT` | Enable mcumgr handlers for log management | n |
| `CONFIG_MCUMGR_CMD_OS_MGMT` | Enable mcumgr handlers for OS management | n |

141
README.md Normal file
View File

@ -0,0 +1,141 @@
# mcumgr
This is mcumgr, version 0.0.1
mcumgr is a management library for 32-bit MCUs. The goal of mcumgr is to
define a common management infrastructure with pluggable transport and encoding
components. In addition, mcumgr provides definitions and handlers for some
core commands: image management, file system management, and OS managment.
mcumgr is operating system and hardware independent. It relies on hardware
porting layers from the operating system it runs on. Currently, mcumgr runs on
both the Apache Mynewt and Zephyr operating systems.
## Getting started
For tips on using mcumgr with your particular OS, see the appropriate file from
the list below:
* README-mynewt.md
* README-zephyr.md
## Dependencies
To use mcumgr's image management support, your device must be running version
1.1.0 or later of the [MCUboot boot
loader](https://github.com/runtimeco/mcuboot). The other mcumgr features do
not require MCUboot.
## Command line tool
The `mcumgr` command line tool is available at:
https://github.com/apache/mynewt-mcumgr-cli. The command line tool requires [Go
1.7 or later](https://golang.org/dl/). Once Go is installed and set up on your
system, you can install the mcumgr CLI tool by issuing the following `go get`
command:
```
$ go get github.com/apache/mynewt-mcumgr-cli/mcumgr
```
The `mcumgr` tool allows you to manage devices running an mcumgr server.
## Architecture
The mcumgr stack has the following layout:
```
+---------------------+---------------------+
| <command handlers> |
+---------------------+---------------------+
| mgmt |
+---------------------+---------------------+
| <transfer encoding(s)> |
+---------------------+---------------------+
| <transport(s)> |
+---------------------+---------------------+
```
Items enclosed in angled brackets represent generic components that can be plugged into mcumgr. The items in this stack diagram are defined below:
* *Command handler*: Processes incoming mcumgr requests and generates corresponding responses. A command handler is associated with a single command type, defined by a (group ID, command ID) pair.
* *mgmt*: The core of mcumgr; facilitates the passing of requests and responses between the generic command handlers and the concrete transports and transfer encodings.
* *Transfer encoding*: Defines how mcumgr requests and responses are encoded on the wire.
* *Transport*: Sends and receives mcumgr packets over a particular medium.
Each transport is configured with a single transfer encoding.
As an example, the sample application `smp_svr` uses the following components:
* Command handlers:
* Image management (`img_mgmt`)
* File system management (`fs_mgmt`)
* Log management (`log_mgmt`)
* OS management (`os_mgmt`)
* Transfer/Transports protocols:
* SMP/Bluetooth
* SMP/Shell
yielding the following stack diagram:
```
+----------+----------+----------+----------+
| img_mgmt | fs_mgmt | log_mgmt | os_mgmt |
+----------+----------+----------+----------+
| mgmt |
+---------------------+---------------------+
| SMP | SMP |
+---------------------+---------------------+
| Bluetooth | Shell |
+---------------------+---------------------+
```
## Command definition
An mcumgr request or response consists of the following two components:
* mcumgr header
* CBOR key-value map
How these two components are encoded and parsed depends on the transfer
encoding used.
The mcumgr header structure is defined in `mgmt/include/mgmt/mgmt.h` as
`struct mgmt_hdr`.
The contents of the CBOR key-value map are specified per command type.
## Supported transfer encodings
Mcumgr comes with one built-in transfer encoding: Simple Management Protocol
(SMP). SMP requests and responses have a very basic structure. For details,
see the comments at the top of `smp/include/smp/smp.h`.
## Supported transports
The mcumgr project defines two transports:
* SMP/Console
* SMP/Bluetooth
Particulars of these transports are specified in the following documents:
* SMP/Console: `transports/smp-console.md`
* SMP/Bluetooth: `transports/smp-bluetooth.md`
Implementations, being hardware- and OS-specified, are not included.
## Browsing
Information and documentation for mcumgr is stored within the source.
For more information in the source, here are some pointers:
- [cborattr](https://github.com/apache/mynewt-mcumgr/tree/master/cborattr): Used for parsing incoming mcumgr requests. Destructures mcumgr packets and populates corresponding field variables.
- [cmd](https://github.com/apache/mynewt-mcumgr/tree/master/cmd): Built-in command handlers for the core mcumgr commands.
- [ext](https://github.com/apache/mynewt-mcumgr/tree/master/ext): Third-party libraries that mcumgr depends on.
- [mgmt](https://github.com/apache/mynewt-mcumgr/tree/master/mgmt): Code implementing the `mgmt` layer of mcumgr.
- [samples](https://github.com/apache/mynewt-mcumgr/tree/master/samples): Sample applications utilizing mcumgr.
- [smp](https://github.com/apache/mynewt-mcumgr/tree/master/smp): The built-in transfer encoding: Simple management protocol.
## Joining
Developers welcome!
* Our Slack channel: https://mynewt.slack.com/messages/C7Y3K0C2J

7
cborattr/CMakeLists.txt Normal file
View File

@ -0,0 +1,7 @@
target_include_directories(MCUMGR INTERFACE
include
)
zephyr_library_sources(
cborattr/src/cborattr.c
)

View File

@ -0,0 +1,153 @@
/*
* 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.
*/
#ifndef CBORATTR_H
#define CBORATTR_H
#include <stdbool.h>
#include <stdint.h>
#include <ctype.h>
#include <stdio.h>
#include <sys/types.h>
#include "cbor.h"
#ifdef __cplusplus
extern "C" {
#endif
/* This library wraps the tinycbor decoder with a attribute based decoder
* suitable for decoding a binary version of json. Specifically, the
* contents of the cbor contains pairs of attributes. where the attribute
* is a key/value pair. keys are always text strings, but values can be
* many different things (enumerated below) */
typedef enum CborAttrType {
CborAttrIntegerType = 1,
CborAttrUnsignedIntegerType,
CborAttrByteStringType,
CborAttrTextStringType,
CborAttrBooleanType,
CborAttrFloatType,
CborAttrDoubleType,
CborAttrArrayType,
CborAttrObjectType,
CborAttrStructObjectType,
CborAttrNullType,
} CborAttrType;
struct cbor_attr_t;
struct cbor_enum_t {
char *name;
long long int value;
};
struct cbor_array_t {
CborAttrType element_type;
union {
struct {
const struct cbor_attr_t *subtype;
char *base;
size_t stride;
} objects;
struct {
char **ptrs;
char *store;
int storelen;
} strings;
struct {
long long int *store;
} integers;
struct {
long long unsigned int *store;
} uintegers;
struct {
double *store;
} reals;
struct {
bool *store;
} booleans;
} arr;
int *count;
int maxlen;
};
struct cbor_attr_t {
char *attribute;
CborAttrType type;
union {
long long int *integer;
long long unsigned int *uinteger;
double *real;
float *fval;
char *string;
bool *boolean;
struct byte_string {
uint8_t *data;
size_t *len;
} bytestring;
struct cbor_array_t array;
size_t offset;
struct cbor_attr_t *obj;
} addr;
union {
long long int integer;
double real;
bool boolean;
float fval;
} dflt;
size_t len;
bool nodefault;
};
/*
* Use the following macros to declare template initializers for
* CborAttrStructObjectType arrays. Writing the equivalents out by hand is
* error-prone.
*
* CBOR_STRUCT_OBJECT takes a structure name s, and a fieldname f in s.
*
* CBOR_STRUCT_ARRAY takes the name of a structure array, a pointer to a an
* initializer defining the subobject type, and the address of an integer to
* store the length in.
*/
#define CBORATTR_STRUCT_OBJECT(s, f) .addr.offset = offsetof(s, f)
#define CBORATTR_STRUCT_ARRAY(a, e, n) \
.addr.array.element_type = CborAttrStructObjectType, \
.addr.array.arr.objects.subtype = e, \
.addr.array.arr.objects.base = (char*)a, \
.addr.array.arr.objects.stride = sizeof(a[0]), \
.addr.array.count = n, \
.addr.array.maxlen = (int)(sizeof(a)/sizeof(a[0]))
#define CBORATTR_ATTR_UNNAMED (char *)(-1)
int cbor_read_object(struct CborValue *, const struct cbor_attr_t *);
int cbor_read_array(struct CborValue *, const struct cbor_array_t *);
int cbor_read_flat_attrs(const uint8_t *data, int len,
const struct cbor_attr_t *attrs);
#ifdef __cplusplus
}
#endif
#endif /* CBORATTR_H */

423
cborattr/src/cborattr.c Normal file
View File

@ -0,0 +1,423 @@
/*
* 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 <syscfg/syscfg.h>
#include "cborattr/cborattr.h"
#include "cbor.h"
#include "cbor_buf_reader.h"
#define CBORATTR_MAX_SIZE 512
/* this maps a CborType to a matching CborAtter Type. The mapping is not
* one-to-one because of signedness of integers
* and therefore we need a function to do this trickery */
static int
valid_attr_type(CborType ct, CborAttrType at)
{
switch (at) {
case CborAttrIntegerType:
case CborAttrUnsignedIntegerType:
if (ct == CborIntegerType) {
return 1;
}
break;
case CborAttrByteStringType:
if (ct == CborByteStringType) {
return 1;
}
break;
case CborAttrTextStringType:
if (ct == CborTextStringType) {
return 1;
}
break;
case CborAttrBooleanType:
if (ct == CborBooleanType) {
return 1;
}
#if FLOAT_SUPPORT
case CborAttrFloatType:
if (ct == CborFloatType) {
return 1;
}
break;
case CborAttrDoubleType:
if (ct == CborDoubleType) {
return 1;
}
break;
#endif
case CborAttrArrayType:
if (ct == CborArrayType) {
return 1;
}
break;
case CborAttrObjectType:
if (ct == CborMapType) {
return 1;
}
break;
case CborAttrNullType:
if (ct == CborNullType) {
return 1;
}
break;
default:
break;
}
return 0;
}
/* this function find the pointer to the memory location to
* write or read and attribute from the cbor_attr_r structure */
static char *
cbor_target_address(const struct cbor_attr_t *cursor,
const struct cbor_array_t *parent, int offset)
{
char *targetaddr = NULL;
if (parent == NULL || parent->element_type != CborAttrStructObjectType) {
/* ordinary case - use the address in the cursor structure */
switch (cursor->type) {
case CborAttrNullType:
targetaddr = NULL;
break;
case CborAttrIntegerType:
targetaddr = (char *)&cursor->addr.integer[offset];
break;
case CborAttrUnsignedIntegerType:
targetaddr = (char *)&cursor->addr.uinteger[offset];
break;
#if FLOAT_SUPPORT
case CborAttrFloatType:
targetaddr = (char *)&cursor->addr.fval[offset];
break;
case CborAttrDoubleType:
targetaddr = (char *)&cursor->addr.real[offset];
break;
#endif
case CborAttrByteStringType:
targetaddr = (char *) cursor->addr.bytestring.data;
break;
case CborAttrTextStringType:
targetaddr = cursor->addr.string;
break;
case CborAttrBooleanType:
targetaddr = (char *)&cursor->addr.boolean[offset];
break;
default:
targetaddr = NULL;
break;
}
} else {
/* tricky case - hacking a member in an array of structures */
targetaddr =
parent->arr.objects.base + (offset * parent->arr.objects.stride) +
cursor->addr.offset;
}
return targetaddr;
}
static int
cbor_internal_read_object(CborValue *root_value,
const struct cbor_attr_t *attrs,
const struct cbor_array_t *parent,
int offset)
{
const struct cbor_attr_t *cursor, *best_match;
char attrbuf[CBORATTR_MAX_SIZE + 1];
void *lptr;
CborValue cur_value;
CborError err = 0;
size_t len;
CborType type = CborInvalidType;
/* stuff fields with defaults in case they're omitted in the JSON input */
for (cursor = attrs; cursor->attribute != NULL; cursor++) {
if (!cursor->nodefault) {
lptr = cbor_target_address(cursor, parent, offset);
if (lptr != NULL) {
switch (cursor->type) {
case CborAttrIntegerType:
memcpy(lptr, &cursor->dflt.integer, sizeof(long long int));
break;
case CborAttrUnsignedIntegerType:
memcpy(lptr, &cursor->dflt.integer,
sizeof(long long unsigned int));
break;
case CborAttrBooleanType:
memcpy(lptr, &cursor->dflt.boolean, sizeof(bool));
break;
#if FLOAT_SUPPORT
case CborAttrFloatType:
memcpy(lptr, &cursor->dflt.fval, sizeof(float));
break;
case CborAttrDoubleType:
memcpy(lptr, &cursor->dflt.real, sizeof(double));
break;
#endif
default:
break;
}
}
}
}
if (cbor_value_is_map(root_value)) {
err |= cbor_value_enter_container(root_value, &cur_value);
} else {
err |= CborErrorIllegalType;
return err;
}
/* contains key value pairs */
while (cbor_value_is_valid(&cur_value) && !err) {
/* get the attribute */
if (cbor_value_is_text_string(&cur_value)) {
if (cbor_value_calculate_string_length(&cur_value, &len) == 0) {
if (len > CBORATTR_MAX_SIZE) {
err |= CborErrorDataTooLarge;
break;
}
err |= cbor_value_copy_text_string(&cur_value, attrbuf, &len,
NULL);
}
/* at least get the type of the next value so we can match the
* attribute name and type for a perfect match */
err |= cbor_value_advance(&cur_value);
if (cbor_value_is_valid(&cur_value)) {
type = cbor_value_get_type(&cur_value);
} else {
err |= CborErrorIllegalType;
break;
}
} else {
attrbuf[0] = '\0';
type = cbor_value_get_type(&cur_value);
}
/* find this attribute in our list */
best_match = NULL;
for (cursor = attrs; cursor->attribute != NULL; cursor++) {
if (valid_attr_type(type, cursor->type)) {
if (cursor->attribute == CBORATTR_ATTR_UNNAMED &&
attrbuf[0] == '\0') {
best_match = cursor;
} else if (strlen(cursor->attribute) == len &&
!memcmp(cursor->attribute, attrbuf, len)) {
break;
}
}
}
if (!cursor->attribute && best_match) {
cursor = best_match;
}
/* we found a match */
if (cursor->attribute != NULL) {
lptr = cbor_target_address(cursor, parent, offset);
switch (cursor->type) {
case CborAttrNullType:
/* nothing to do */
break;
case CborAttrBooleanType:
err |= cbor_value_get_boolean(&cur_value, lptr);
break;
case CborAttrIntegerType:
err |= cbor_value_get_int64(&cur_value, lptr);
break;
case CborAttrUnsignedIntegerType:
err |= cbor_value_get_uint64(&cur_value, lptr);
break;
#if FLOAT_SUPPORT
case CborAttrFloatType:
err |= cbor_value_get_float(&cur_value, lptr);
break;
case CborAttrDoubleType:
err |= cbor_value_get_double(&cur_value, lptr);
break;
#endif
case CborAttrByteStringType: {
size_t len = cursor->len;
err |= cbor_value_copy_byte_string(&cur_value, lptr,
&len, NULL);
*cursor->addr.bytestring.len = len;
break;
}
case CborAttrTextStringType: {
size_t len = cursor->len;
err |= cbor_value_copy_text_string(&cur_value, lptr,
&len, NULL);
break;
}
case CborAttrArrayType:
err |= cbor_read_array(&cur_value, &cursor->addr.array);
continue;
case CborAttrObjectType:
err |= cbor_internal_read_object(&cur_value, cursor->addr.obj,
NULL, 0);
continue;
default:
err |= CborErrorIllegalType;
}
}
cbor_value_advance(&cur_value);
}
if (!err) {
/* that should be it for this container */
err |= cbor_value_leave_container(root_value, &cur_value);
}
return err;
}
int
cbor_read_array(struct CborValue *value, const struct cbor_array_t *arr)
{
CborError err = 0;
struct CborValue elem;
int off, arrcount;
size_t len;
void *lptr;
char *tp;
err = cbor_value_enter_container(value, &elem);
if (err) {
return err;
}
arrcount = 0;
tp = arr->arr.strings.store;
for (off = 0; off < arr->maxlen; off++) {
switch (arr->element_type) {
case CborAttrBooleanType:
lptr = &arr->arr.booleans.store[off];
err |= cbor_value_get_boolean(&elem, lptr);
break;
case CborAttrIntegerType:
lptr = &arr->arr.integers.store[off];
err |= cbor_value_get_int64(&elem, lptr);
break;
case CborAttrUnsignedIntegerType:
lptr = &arr->arr.uintegers.store[off];
err |= cbor_value_get_uint64(&elem, lptr);
break;
#if FLOAT_SUPPORT
case CborAttrFloatType:
case CborAttrDoubleType:
lptr = &arr->arr.reals.store[off];
err |= cbor_value_get_double(&elem, lptr);
break;
#endif
case CborAttrTextStringType:
len = arr->arr.strings.storelen - (tp - arr->arr.strings.store);
err |= cbor_value_copy_text_string(&elem, tp, &len, NULL);
arr->arr.strings.ptrs[off] = tp;
tp += len + 1;
break;
case CborAttrStructObjectType:
err |= cbor_internal_read_object(&elem, arr->arr.objects.subtype,
arr, off);
break;
default:
err |= CborErrorIllegalType;
break;
}
arrcount++;
if (arr->element_type != CborAttrStructObjectType) {
err |= cbor_value_advance(&elem);
}
if (!cbor_value_is_valid(&elem)) {
break;
}
}
if (arr->count) {
*arr->count = arrcount;
}
while (!cbor_value_at_end(&elem)) {
err |= CborErrorDataTooLarge;
cbor_value_advance(&elem);
}
err |= cbor_value_leave_container(value, &elem);
return err;
}
int
cbor_read_object(struct CborValue *value, const struct cbor_attr_t *attrs)
{
int st;
st = cbor_internal_read_object(value, attrs, NULL, 0);
return st;
}
/*
* Read in cbor key/values from flat buffer pointed by data, and fill them
* into attrs.
*
* @param data Pointer to beginning of cbor encoded data
* @param len Number of bytes in the buffer
* @param attrs Array of cbor objects to look for.
*
* @return 0 on success; non-zero on failure.
*/
int
cbor_read_flat_attrs(const uint8_t *data, int len,
const struct cbor_attr_t *attrs)
{
struct cbor_buf_reader reader;
struct CborParser parser;
struct CborValue value;
CborError err;
cbor_buf_reader_init(&reader, data, len);
err = cbor_parser_cust_reader_init(&reader.r, 0, &parser, &value);
if (err != CborNoError) {
return -1;
}
return cbor_read_object(&value, attrs);
}
#if 0
/*
* Read in cbor key/values from os_mbuf pointed by m, and fill them
* into attrs.
*
* @param m Pointer to os_mbuf containing cbor encoded data
* @param off Offset into mbuf where cbor data begins
* @param len Number of bytes to decode
* @param attrs Array of cbor objects to look for.
*
* @return 0 on success; non-zero on failure.
*/
int
cbor_read_mbuf_attrs(struct os_mbuf *m, uint16_t off, uint16_t len,
const struct cbor_attr_t *attrs)
{
struct cbor_mbuf_reader cmr;
struct CborParser parser;
struct CborValue value;
CborError err;
cbor_mbuf_reader_init(&cmr, m, off);
err = cbor_parser_init(&cmr.r, 0, &parser, &value);
if (err != CborNoError) {
return -1;
}
return cbor_read_object(&value, attrs);
}
#endif

5
cmd/CMakeLists.txt Normal file
View File

@ -0,0 +1,5 @@
add_subdirectory_ifdef(CONFIG_MCUMGR_CMD_FS_MGMT fs_mgmt)
add_subdirectory_ifdef(CONFIG_MCUMGR_CMD_IMG_MGMT img_mgmt)
add_subdirectory_ifdef(CONFIG_MCUMGR_CMD_LOG_MGMT log_mgmt)
add_subdirectory_ifdef(CONFIG_MCUMGR_CMD_OS_MGMT os_mgmt)
add_subdirectory_ifdef(CONFIG_MCUMGR_CMD_STAT_MGMT stat_mgmt)

26
cmd/Kconfig Normal file
View File

@ -0,0 +1,26 @@
# 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.
menu "Command handlers"
source "ext/lib/mgmt/mcumgr/cmd/fs_mgmt/Kconfig"
source "ext/lib/mgmt/mcumgr/cmd/img_mgmt/Kconfig"
source "ext/lib/mgmt/mcumgr/cmd/log_mgmt/Kconfig"
source "ext/lib/mgmt/mcumgr/cmd/os_mgmt/Kconfig"
source "ext/lib/mgmt/mcumgr/cmd/stat_mgmt/Kconfig"
endmenu

View File

@ -0,0 +1,9 @@
target_include_directories(MCUMGR INTERFACE
include
)
zephyr_library_sources(
cmd/fs_mgmt/port/zephyr/src/zephyr_fs_mgmt.c
cmd/fs_mgmt/src/fs_mgmt.c
cmd/fs_mgmt/src/stubs.c
)

52
cmd/fs_mgmt/Kconfig Normal file
View File

@ -0,0 +1,52 @@
# 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.
menuconfig MCUMGR_CMD_FS_MGMT
bool
prompt "Enable mcumgr handlers for file management"
depends on FILE_SYSTEM
default n
help
Enables mcumgr handlers for file management
if MCUMGR_CMD_FS_MGMT
config FS_MGMT_UL_CHUNK_SIZE
int
prompt "Maximum chunk size for file uploads"
default 512
help
Limits the maximum chunk size for file uploads, in bytes. A buffer of
this size gets allocated on the stack during handling of a file upload command.
config FS_MGMT_DL_CHUNK_SIZE
int
prompt "Maximum chunk size for file downloads"
default 512
help
Limits the maximum chunk size for file downloads, in bytes. A buffer of
this size gets allocated on the stack during handling of a file download
command.
config FS_MGMT_PATH_SIZE
int
prompt "Maximum file path length"
default 64
help
Limits the maximum path length for file operations, in bytes. A buffer
of this size gets allocated on the stack during handling of file upload
and download commands.
endif

View File

@ -0,0 +1,41 @@
/*
* 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.
*/
#ifndef H_FS_MGMT_
#define H_FS_MGMT_
#ifdef __cplusplus
extern "C" {
#endif
/**
* Command IDs for file system management group.
*/
#define FS_MGMT_ID_FILE 0
/**
* @brief Registers the file system management command handler group.
*/
void fs_mgmt_register_group(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,77 @@
/*
* 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 Declares implementation-specific functions required by file system
* management. The default stubs can be overridden with functions that
* are compatible with the host OS.
*/
#ifndef H_FS_MGMT_IMPL_
#define H_FS_MGMT_IMPL_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Retrieves the length of the file at the specified path.
*
* @param path The path of the file to query.
* @param out_len On success, the file length gets written here.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int fs_mgmt_impl_filelen(const char *path, size_t *out_len);
/**
* @brief Reads the specified chunk of file data.
*
* @param path The path of the file to read from.
* @param offset The byte offset to read from.
* @param len The number of bytes to read.
* @param out_data On success, the file data gets written here.
* @param out_len On success, the number of bytes actually read
* gets written here.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int fs_mgmt_impl_read(const char *path, size_t offset, size_t len,
void *out_data, size_t *out_len);
/**
* @brief Writes the specified chunk of file data. A write to offset 0 must
* truncate the file; other offsets must append.
*
* @param path The path of the file to write to.
* @param offset The byte offset to write to.
* @param data The data to write to the file.
* @param len The number of bytes to write.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int fs_mgmt_impl_write(const char *path, size_t offset, const void *data,
size_t len);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,151 @@
/*
* 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 <fs.h>
#include <mgmt/mgmt.h>
#include <fs_mgmt/fs_mgmt_impl.h>
int
fs_mgmt_impl_filelen(const char *path, size_t *out_len)
{
struct fs_dirent dirent;
int rc;
rc = fs_stat(path, &dirent);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
if (dirent.type != FS_DIR_ENTRY_FILE) {
return MGMT_ERR_EUNKNOWN;
}
*out_len = dirent.size;
return 0;
}
int
fs_mgmt_impl_read(const char *path, size_t offset, size_t len,
void *out_data, size_t *out_len)
{
fs_file_t file;
ssize_t bytes_read;
int rc;
rc = fs_open(&file, path);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
rc = fs_seek(&file, offset, FS_SEEK_SET);
if (rc != 0) {
goto done;
}
bytes_read = fs_read(&file, out_data, len);
if (bytes_read < 0) {
goto done;
}
*out_len = bytes_read;
done:
fs_close(&file);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
} else {
return 0;
}
}
static int
zephyr_fs_mgmt_truncate(const char *path)
{
size_t len;
int rc;
/* Attempt to get the length of the file at the specified path. This is a
* quick way to determine if there is already a file there.
*/
rc = fs_mgmt_impl_filelen(path, &len);
if (rc == 0) {
/* There is already a file with the specified path. Unlink it to
* simulate a truncate operation.
*
* XXX: This isn't perfect - if the file is currently open, the unlink
* operation won't actually delete the file. Consequently, the file
* will get partially overwritten rather than truncated. The NFFS port
* doesn't support the truncate operation, so this is an imperfect
* workaround.
*/
rc = fs_unlink(path);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
}
return 0;
}
int
fs_mgmt_impl_write(const char *path, size_t offset, const void *data,
size_t len)
{
fs_file_t file;
int rc;
/* Truncate the file before writing the first chunk. This is done to
* properly handle an overwrite of an existing file.
*
*/
if (offset == 0) {
rc = zephyr_fs_mgmt_truncate(path);
if (rc != 0) {
return rc;
}
}
rc = fs_open(&file, path);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
rc = fs_seek(&file, offset, FS_SEEK_SET);
if (rc != 0) {
goto done;
}
rc = fs_write(&file, data, len);
if (rc < 0) {
goto done;
}
rc = 0;
done:
fs_close(&file);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
} else {
return 0;
}
}

250
cmd/fs_mgmt/src/fs_mgmt.c Normal file
View File

@ -0,0 +1,250 @@
/*
* 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 <limits.h>
#include <string.h>
#include "cborattr/cborattr.h"
#include "mgmt/mgmt.h"
#include "fs_mgmt/fs_mgmt.h"
#include "fs_mgmt/fs_mgmt_impl.h"
#include "fs_mgmt_config.h"
static mgmt_handler_fn fs_mgmt_file_download;
static mgmt_handler_fn fs_mgmt_file_upload;
static struct {
/** Whether an upload is currently in progress. */
bool uploading;
/** Expected offset of next upload request. */
size_t off;
/** Total length of file currently being uploaded. */
size_t len;
} fs_mgmt_ctxt;
static const struct mgmt_handler fs_mgmt_handlers[] = {
[FS_MGMT_ID_FILE] = {
.mh_read = fs_mgmt_file_download,
.mh_write = fs_mgmt_file_upload,
},
};
#define FS_MGMT_HANDLER_CNT \
(sizeof fs_mgmt_handlers / sizeof fs_mgmt_handlers[0])
static struct mgmt_group fs_mgmt_group = {
.mg_handlers = fs_mgmt_handlers,
.mg_handlers_count = FS_MGMT_HANDLER_CNT,
.mg_group_id = MGMT_GROUP_ID_FS,
};
/**
* Command handler: fs file (read)
*/
static int
fs_mgmt_file_download(struct mgmt_ctxt *ctxt)
{
uint8_t file_data[FS_MGMT_DL_CHUNK_SIZE];
char path[FS_MGMT_PATH_SIZE + 1];
unsigned long long off;
CborError err;
size_t bytes_read;
size_t file_len;
int rc;
const struct cbor_attr_t dload_attr[] = {
{
.attribute = "off",
.type = CborAttrUnsignedIntegerType,
.addr.uinteger = &off,
},
{
.attribute = "name",
.type = CborAttrTextStringType,
.addr.string = path,
.len = sizeof path,
},
{ 0 },
};
off = ULLONG_MAX;
rc = cbor_read_object(&ctxt->it, dload_attr);
if (rc != 0 || off == ULLONG_MAX) {
return MGMT_ERR_EINVAL;
}
/* Only the response to the first download request contains the total file
* length.
*/
if (off == 0) {
rc = fs_mgmt_impl_filelen(path, &file_len);
if (rc != 0) {
return rc;
}
}
/* Read the requested chunk from the file. */
rc = fs_mgmt_impl_read(path, off, FS_MGMT_DL_CHUNK_SIZE,
file_data, &bytes_read);
if (rc != 0) {
return rc;
}
/* Encode the response. */
err = 0;
err |= cbor_encode_text_stringz(&ctxt->encoder, "off");
err |= cbor_encode_uint(&ctxt->encoder, off);
err |= cbor_encode_text_stringz(&ctxt->encoder, "data");
err |= cbor_encode_byte_string(&ctxt->encoder, file_data, bytes_read);
err |= cbor_encode_text_stringz(&ctxt->encoder, "rc");
err |= cbor_encode_int(&ctxt->encoder, MGMT_ERR_EOK);
if (off == 0) {
err |= cbor_encode_text_stringz(&ctxt->encoder, "len");
err |= cbor_encode_uint(&ctxt->encoder, file_len);
}
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Encodes a file upload response.
*/
static int
fs_mgmt_file_upload_rsp(struct mgmt_ctxt *ctxt, int rc, unsigned long long off)
{
CborError err;
err = 0;
err |= cbor_encode_text_stringz(&ctxt->encoder, "rc");
err |= cbor_encode_int(&ctxt->encoder, rc);
err |= cbor_encode_text_stringz(&ctxt->encoder, "off");
err |= cbor_encode_uint(&ctxt->encoder, off);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Command handler: fs file (write)
*/
static int
fs_mgmt_file_upload(struct mgmt_ctxt *ctxt)
{
uint8_t file_data[FS_MGMT_UL_CHUNK_SIZE];
char file_name[FS_MGMT_PATH_SIZE + 1];
unsigned long long len;
unsigned long long off;
size_t data_len;
size_t new_off;
int rc;
const struct cbor_attr_t uload_attr[5] = {
[0] = {
.attribute = "off",
.type = CborAttrUnsignedIntegerType,
.addr.uinteger = &off,
.nodefault = true
},
[1] = {
.attribute = "data",
.type = CborAttrByteStringType,
.addr.bytestring.data = file_data,
.addr.bytestring.len = &data_len,
.len = sizeof(file_data)
},
[2] = {
.attribute = "len",
.type = CborAttrUnsignedIntegerType,
.addr.uinteger = &len,
.nodefault = true
},
[3] = {
.attribute = "name",
.type = CborAttrTextStringType,
.addr.string = file_name,
.len = sizeof(file_name)
},
[4] = { 0 },
};
len = ULLONG_MAX;
off = ULLONG_MAX;
rc = cbor_read_object(&ctxt->it, uload_attr);
if (rc != 0 || off == ULLONG_MAX || file_name[0] == '\0') {
return MGMT_ERR_EINVAL;
}
if (off == 0) {
/* Total file length is a required field in the first chunk request. */
if (len == ULLONG_MAX) {
return MGMT_ERR_EINVAL;
}
fs_mgmt_ctxt.uploading = true;
fs_mgmt_ctxt.off = 0;
fs_mgmt_ctxt.len = len;
} else {
if (!fs_mgmt_ctxt.uploading) {
return MGMT_ERR_EINVAL;
}
if (off != fs_mgmt_ctxt.off) {
/* Invalid offset. Drop the data and send the expected offset. */
return fs_mgmt_file_upload_rsp(ctxt, MGMT_ERR_EINVAL,
fs_mgmt_ctxt.off);
}
}
new_off = fs_mgmt_ctxt.off + data_len;
if (new_off > fs_mgmt_ctxt.len) {
/* Data exceeds image length. */
return MGMT_ERR_EINVAL;
}
if (data_len > 0) {
/* Write the data chunk to the file. */
rc = fs_mgmt_impl_write(file_name, off, file_data, data_len);
if (rc != 0) {
return rc;
}
fs_mgmt_ctxt.off = new_off;
}
if (fs_mgmt_ctxt.off == fs_mgmt_ctxt.len) {
/* Upload complete. */
fs_mgmt_ctxt.uploading = false;
}
/* Send the response. */
return fs_mgmt_file_upload_rsp(ctxt, 0, fs_mgmt_ctxt.off);
}
void
fs_mgmt_register_group(void)
{
mgmt_register_group(&fs_mgmt_group);
}

View File

@ -0,0 +1,45 @@
/*
* 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.
*/
#ifndef H_FS_MGMT_CONFIG_
#define H_FS_MGMT_CONFIG_
#if defined MYNEWT
#include "syscfg/syscfg.h"
#define FS_MGMT_DL_CHUNK_SIZE MYNEWT_VAL(FS_MGMT_DL_CHUNK_SIZE)
#define FS_MGMT_PATH_SIZE MYNEWT_VAL(FS_MGMT_PATH_SIZE)
#define FS_MGMT_UL_CHUNK_SIZE MYNEWT_VAL(FS_MGMT_UL_CHUNK_SIZE)
#elif defined __ZEPHYR__
#define FS_MGMT_DL_CHUNK_SIZE CONFIG_FS_MGMT_DL_CHUNK_SIZE
#define FS_MGMT_PATH_SIZE CONFIG_FS_MGMT_PATH_SIZE
#define FS_MGMT_UL_CHUNK_SIZE CONFIG_FS_MGMT_UL_CHUNK_SIZE
#else
/* No direct support for this OS. The application needs to define the above
* settings itself.
*/
#endif
#endif

46
cmd/fs_mgmt/src/stubs.c Normal file
View File

@ -0,0 +1,46 @@
/*
* 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.
*/
/**
* These stubs get linked in when there is no equivalent OS-specific
* implementation.
*/
#include "mgmt/mgmt.h"
#include "fs_mgmt/fs_mgmt_impl.h"
int __attribute__((weak))
fs_mgmt_impl_filelen(const char *path, size_t *out_len)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
fs_mgmt_impl_read(const char *path, size_t offset, size_t len,
void *out_data, size_t *out_len)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
fs_mgmt_impl_write(const char *path, size_t offset, const void *data,
size_t len)
{
return MGMT_ERR_ENOTSUP;
}

View File

@ -0,0 +1,11 @@
target_include_directories(MCUMGR INTERFACE
include
)
zephyr_library_sources(
cmd/img_mgmt/port/zephyr/src/zephyr_img_mgmt.c
cmd/img_mgmt/src/img_mgmt.c
cmd/img_mgmt/src/img_mgmt_state.c
cmd/img_mgmt/src/img_mgmt_util.c
cmd/img_mgmt/src/stubs.c
)

39
cmd/img_mgmt/Kconfig Normal file
View File

@ -0,0 +1,39 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE image
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this image
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this image 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.
menuconfig MCUMGR_CMD_IMG_MGMT
bool
prompt "Enable mcumgr handlers for image management"
select FLASH
select MPU_ALLOW_FLASH_WRITE if CPU_HAS_MPU
select IMG_MANAGER
select MCUBOOT_IMG_MANAGER
default n
help
Enables mcumgr handlers for image management
if MCUMGR_CMD_IMG_MGMT
config IMG_MGMT_UL_CHUNK_SIZE
int
prompt "Maximum chunk size for image uploads"
default 512
help
Limits the maximum chunk size for image uploads, in bytes. A buffer of
this size gets allocated on the stack during handling of a image upload
command.
endif

View File

@ -0,0 +1,82 @@
/*
* 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.
*/
#ifndef H_IMAGE_
#define H_IMAGE_
#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
#endif
#define IMAGE_MAGIC 0x96f3b83d
#define IMAGE_TLV_INFO_MAGIC 0x6907
#define IMAGE_HEADER_SIZE 32
/** Image header flags. */
#define IMAGE_F_NON_BOOTABLE 0x00000010 /* Split image app. */
/** Image trailer TLV types. */
#define IMAGE_TLV_SHA256 0x10 /* SHA256 of image hdr and body */
/** Image TLV-specific definitions. */
#define IMAGE_HASH_LEN 32
struct image_version {
uint8_t iv_major;
uint8_t iv_minor;
uint16_t iv_revision;
uint32_t iv_build_num;
};
/** Image header. All fields are in little endian byte order. */
struct image_header {
uint32_t ih_magic;
uint32_t ih_load_addr;
uint16_t ih_hdr_size; /* Size of image header (bytes). */
uint16_t _pad2;
uint32_t ih_img_size; /* Does not include header. */
uint32_t ih_flags; /* IMAGE_F_[...]. */
struct image_version ih_ver;
uint32_t _pad3;
};
/** Image TLV header. All fields in little endian. */
struct image_tlv_info {
uint16_t it_magic;
uint16_t it_tlv_tot; /* size of TLV area (including tlv_info header) */
};
/** Image trailer TLV format. All fields in little endian. */
struct image_tlv {
uint8_t it_type; /* IMAGE_TLV_[...]. */
uint8_t _pad;
uint16_t it_len; /* Data length (not including TLV header). */
};
_Static_assert(sizeof(struct image_header) == IMAGE_HEADER_SIZE,
"struct image_header not required size");
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,49 @@
/*
* 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.
*/
#ifndef H_IMG_MGMT_
#define H_IMG_MGMT_
#include <inttypes.h>
struct image_version;
#ifdef __cplusplus
extern "C" {
#endif
/**
* Command IDs for image management group.
*/
#define IMG_MGMT_ID_STATE 0
#define IMG_MGMT_ID_UPLOAD 1
#define IMG_MGMT_ID_FILE 2
#define IMG_MGMT_ID_CORELIST 3
#define IMG_MGMT_ID_CORELOAD 4
#define IMG_MGMT_ID_ERASE 5
/**
* @brief Registers the image management command handler group.
*/
void img_mgmt_register_group(void);
#ifdef __cplusplus
}
#endif
#endif /* H_IMG_MGMT_ */

View File

@ -0,0 +1,107 @@
/*
* 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 Declares implementation-specific functions required by image
* management. The default stubs can be overridden with functions that
* are compatible with the host OS.
*/
#ifndef H_IMG_MGMT_IMPL_
#define H_IMG_MGMT_IMPL_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Ensures the spare slot (slot 1) is fully erased.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int img_mgmt_impl_erase_slot(void);
/**
* @brief Marks the image in the specified slot as pending. On the next reboot,
* the system will perform a boot of the specified image.
*
* @param slot The slot to mark as pending. In the typical
* use case, this is 1.
* @param permanent Whether the image should be used permanently or
* only tested once:
* 0=run image once, then confirm or
* revert.
* 1=run image forever.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int img_mgmt_impl_write_pending(int slot, bool permanent);
/**
* @brief Marks the image in slot 0 as confirmed. The system will continue
* booting into the image in slot 0 until told to boot from a different slot.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int img_mgmt_impl_write_confirmed(void);
/**
* @brief Reads the specified chunk of data from an image slot.
*
* @param slot The index of the slot to read from.
* @param offset The offset within the slot to read from.
* @param dst On success, the read data gets written here.
* @param num_bytes The number of byets to read.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int img_mgmt_impl_read(int slot, unsigned int offset, void *dst,
unsigned int num_bytes);
/**
* @brief Writes the specified chunk of image data to slot 1.
*
* @param offset The offset within slot 1 to write to.
* @param data The image data to write.
* @param num_bytes The number of bytes to read.
* @param last Whether this chunk is the end of the image:
* false=additional image chunks are
* forthcoming.
* true=last image chunk; flush unwritten data
* to disk.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int img_mgmt_impl_write_image_data(unsigned int offset, const void *data,
unsigned int num_bytes, bool last);
/**
* @brief Indicates the type of swap operation that will occur on the next
* reboot, if any.
*
* @return An IMG_MGMT_SWAP_TYPE_[...] code.
*/
int img_mgmt_impl_swap_type(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,228 @@
/*
* 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 <flash.h>
#include <zephyr.h>
#include <soc.h>
#include <init.h>
#include <dfu/mcuboot.h>
#include <dfu/flash_img.h>
#include <mgmt/mgmt.h>
#include <img_mgmt/img_mgmt_impl.h>
#include <img_mgmt/img_mgmt.h>
#include "../../../src/img_mgmt_priv.h"
static struct device *zephyr_img_mgmt_flash_dev;
static struct flash_img_context zephyr_img_mgmt_flash_ctxt;
/**
* Determines if the specified area of flash is completely unwritten.
*/
static int
zephyr_img_mgmt_flash_check_empty(off_t offset, size_t size, bool *out_empty)
{
uint32_t data[16];
off_t addr;
off_t end;
int bytes_to_read;
int rc;
int i;
assert(size % 4 == 0);
end = offset + size;
for (addr = offset; addr < end; addr += sizeof data) {
if (end - addr < sizeof data) {
bytes_to_read = end - addr;
} else {
bytes_to_read = sizeof data;
}
rc = flash_read(zephyr_img_mgmt_flash_dev, addr, data, bytes_to_read);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
for (i = 0; i < bytes_to_read / 4; i++) {
if (data[i] != 0xffffffff) {
*out_empty = false;
return 0;
}
}
}
*out_empty = true;
return 0;
}
/**
* Converts an offset within an image slot to an absolute address.
*/
static off_t
zephyr_img_mgmt_abs_offset(int slot, off_t sub_offset)
{
off_t slot_start;
switch (slot) {
case 0:
slot_start = FLASH_AREA_IMAGE_0_OFFSET;
break;
case 1:
slot_start = FLASH_AREA_IMAGE_1_OFFSET;
break;
default:
assert(0);
slot_start = FLASH_AREA_IMAGE_1_OFFSET;
break;
}
return slot_start + sub_offset;
}
int
img_mgmt_impl_erase_slot(void)
{
bool empty;
int rc;
rc = zephyr_img_mgmt_flash_check_empty(FLASH_AREA_IMAGE_1_OFFSET,
FLASH_AREA_IMAGE_1_SIZE,
&empty);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
if (!empty) {
rc = boot_erase_img_bank(FLASH_AREA_IMAGE_1_OFFSET);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
}
return 0;
}
int
img_mgmt_impl_write_pending(int slot, bool permanent)
{
int rc;
if (slot != 1) {
return MGMT_ERR_EINVAL;
}
rc = boot_request_upgrade(permanent);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
return 0;
}
int
img_mgmt_impl_write_confirmed(void)
{
int rc;
rc = boot_write_img_confirmed();
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
return 0;
}
int
img_mgmt_impl_read(int slot, unsigned int offset, void *dst,
unsigned int num_bytes)
{
off_t abs_offset;
int rc;
abs_offset = zephyr_img_mgmt_abs_offset(slot, offset);
rc = flash_read(zephyr_img_mgmt_flash_dev, abs_offset, dst, num_bytes);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
return 0;
}
int
img_mgmt_impl_write_image_data(unsigned int offset, const void *data,
unsigned int num_bytes, bool last)
{
int rc;
if (offset == 0) {
flash_img_init(&zephyr_img_mgmt_flash_ctxt, zephyr_img_mgmt_flash_dev);
}
/* Cast away const. */
rc = flash_img_buffered_write(&zephyr_img_mgmt_flash_ctxt, (void *)data,
num_bytes, false);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
if (last) {
rc = flash_img_buffered_write(&zephyr_img_mgmt_flash_ctxt,
NULL, 0, true);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
}
return 0;
}
int
img_mgmt_impl_swap_type(void)
{
switch (boot_swap_type()) {
case BOOT_SWAP_TYPE_NONE:
return IMG_MGMT_SWAP_TYPE_NONE;
case BOOT_SWAP_TYPE_TEST:
return IMG_MGMT_SWAP_TYPE_TEST;
case BOOT_SWAP_TYPE_PERM:
return IMG_MGMT_SWAP_TYPE_PERM;
case BOOT_SWAP_TYPE_REVERT:
return IMG_MGMT_SWAP_TYPE_REVERT;
default:
assert(0);
return IMG_MGMT_SWAP_TYPE_NONE;
}
}
static int
zephyr_img_mgmt_init(struct device *dev)
{
ARG_UNUSED(dev);
zephyr_img_mgmt_flash_dev = device_get_binding(FLASH_DEV_NAME);
if (zephyr_img_mgmt_flash_dev == NULL) {
return -ENODEV;
}
return 0;
}
SYS_INIT(zephyr_img_mgmt_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);

400
cmd/img_mgmt/src/img_mgmt.c Normal file
View File

@ -0,0 +1,400 @@
/*
* 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 <limits.h>
#include <assert.h>
#include <string.h>
#include "cborattr/cborattr.h"
#include "mgmt/mgmt.h"
#include "img_mgmt/image.h"
#include "img_mgmt/img_mgmt.h"
#include "img_mgmt/img_mgmt_impl.h"
#include "img_mgmt_priv.h"
#include "img_mgmt_config.h"
static mgmt_handler_fn img_mgmt_upload;
static mgmt_handler_fn img_mgmt_erase;
static const struct mgmt_handler img_mgmt_handlers[] = {
[IMG_MGMT_ID_STATE] = {
.mh_read = img_mgmt_state_read,
.mh_write = img_mgmt_state_write,
},
[IMG_MGMT_ID_UPLOAD] = {
.mh_read = NULL,
.mh_write = img_mgmt_upload
},
[IMG_MGMT_ID_ERASE] = {
.mh_read = NULL,
.mh_write = img_mgmt_erase
},
};
#define IMG_MGMT_HANDLER_CNT \
sizeof(img_mgmt_handlers) / sizeof(img_mgmt_handlers[0])
static struct mgmt_group img_mgmt_group = {
.mg_handlers = (struct mgmt_handler *)img_mgmt_handlers,
.mg_handlers_count = IMG_MGMT_HANDLER_CNT,
.mg_group_id = MGMT_GROUP_ID_IMAGE,
};
static struct {
/* Whether an upload is currently in progress. */
bool uploading;
/** Expected offset of next upload request. */
size_t off;
/** Total length of image currently being uploaded. */
size_t len;
} img_mgmt_ctxt;
/**
* Finds the TLVs in the specified image slot, if any.
*/
static int
img_mgmt_find_tlvs(const struct image_header *hdr,
int slot, size_t *start_off, size_t *end_off)
{
struct image_tlv_info tlv_info;
int rc;
rc = img_mgmt_impl_read(slot, *start_off, &tlv_info, sizeof tlv_info);
if (rc != 0) {
/* Read error. */
return MGMT_ERR_EUNKNOWN;
}
if (tlv_info.it_magic != IMAGE_TLV_INFO_MAGIC) {
/* No TLVs. */
return MGMT_ERR_ENOENT;
}
*start_off += sizeof tlv_info;
*end_off = *start_off + tlv_info.it_tlv_tot;
return 0;
}
/*
* Reads the version and build hash from the specified image slot.
*/
int
img_mgmt_read_info(int image_slot, struct image_version *ver, uint8_t *hash,
uint32_t *flags)
{
struct image_header hdr;
struct image_tlv tlv;
size_t data_off;
size_t data_end;
bool hash_found;
int rc;
rc = img_mgmt_impl_read(image_slot, 0, &hdr, sizeof hdr);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
if (ver != NULL) {
memset(ver, 0xff, sizeof(*ver));
}
if (hdr.ih_magic == IMAGE_MAGIC) {
if (ver != NULL) {
memcpy(ver, &hdr.ih_ver, sizeof(*ver));
}
} else if (hdr.ih_magic == 0xffffffff) {
return MGMT_ERR_ENOENT;
} else {
return MGMT_ERR_EUNKNOWN;
}
if (flags != NULL) {
*flags = hdr.ih_flags;
}
/* Read the image's TLVs. All images are required to have a hash TLV. If
* the hash is missing, the image is considered invalid.
*/
data_off = hdr.ih_hdr_size + hdr.ih_img_size;
rc = img_mgmt_find_tlvs(&hdr, image_slot, &data_off, &data_end);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
hash_found = false;
while (data_off + sizeof tlv <= data_end) {
rc = img_mgmt_impl_read(image_slot, data_off, &tlv, sizeof tlv);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
if (tlv.it_type == 0xff && tlv.it_len == 0xffff) {
return MGMT_ERR_EUNKNOWN;
}
if (tlv.it_type != IMAGE_TLV_SHA256 || tlv.it_len != IMAGE_HASH_LEN) {
/* Non-hash TLV. Skip it. */
data_off += sizeof tlv + tlv.it_len;
continue;
}
if (hash_found) {
/* More than one hash. */
return MGMT_ERR_EUNKNOWN;
}
hash_found = true;
data_off += sizeof tlv;
if (hash != NULL) {
if (data_off + IMAGE_HASH_LEN > data_end) {
return MGMT_ERR_EUNKNOWN;
}
rc = img_mgmt_impl_read(image_slot, data_off, hash,
IMAGE_HASH_LEN);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
}
}
if (!hash_found) {
return MGMT_ERR_EUNKNOWN;
}
return 0;
}
/*
* Finds image given version number. Returns the slot number image is in,
* or -1 if not found.
*/
int
img_mgmt_find_by_ver(struct image_version *find, uint8_t *hash)
{
int i;
struct image_version ver;
for (i = 0; i < 2; i++) {
if (img_mgmt_read_info(i, &ver, hash, NULL) != 0) {
continue;
}
if (!memcmp(find, &ver, sizeof(ver))) {
return i;
}
}
return -1;
}
/*
* Finds image given hash of the image. Returns the slot number image is in,
* or -1 if not found.
*/
int
img_mgmt_find_by_hash(uint8_t *find, struct image_version *ver)
{
int i;
uint8_t hash[IMAGE_HASH_LEN];
for (i = 0; i < 2; i++) {
if (img_mgmt_read_info(i, ver, hash, NULL) != 0) {
continue;
}
if (!memcmp(hash, find, IMAGE_HASH_LEN)) {
return i;
}
}
return -1;
}
/**
* Command handler: image erase
*/
static int
img_mgmt_erase(struct mgmt_ctxt *ctxt)
{
CborError err;
int rc;
rc = img_mgmt_impl_erase_slot();
err = 0;
err |= cbor_encode_text_stringz(&ctxt->encoder, "rc");
err |= cbor_encode_int(&ctxt->encoder, rc);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Encodes an image upload response.
*/
static int
img_mgmt_encode_upload_rsp(struct mgmt_ctxt *ctxt, int status)
{
CborError err;
err = 0;
err |= cbor_encode_text_stringz(&ctxt->encoder, "rc");
err |= cbor_encode_int(&ctxt->encoder, status);
err |= cbor_encode_text_stringz(&ctxt->encoder, "off");
err |= cbor_encode_int(&ctxt->encoder, img_mgmt_ctxt.off);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Processes an upload request specifying an offset of 0 (i.e., the first image
* chunk). The caller is responsible for encoding the response.
*/
static int
img_mgmt_upload_first_chunk(struct mgmt_ctxt *ctxt, const uint8_t *req_data,
size_t len)
{
struct image_header hdr;
int rc;
if (len < sizeof hdr) {
return MGMT_ERR_EINVAL;
}
memcpy(&hdr, req_data, sizeof hdr);
if (hdr.ih_magic != IMAGE_MAGIC) {
return MGMT_ERR_EINVAL;
}
if (img_mgmt_slot_in_use(1)) {
/* No free slot. */
return MGMT_ERR_ENOMEM;
}
rc = img_mgmt_impl_erase_slot();
if (rc != 0) {
return rc;
}
img_mgmt_ctxt.uploading = true;
img_mgmt_ctxt.off = 0;
img_mgmt_ctxt.len = 0;
return 0;
}
/**
* Command handler: image upload
*/
static int
img_mgmt_upload(struct mgmt_ctxt *ctxt)
{
uint8_t img_mgmt_data[IMG_MGMT_UL_CHUNK_SIZE];
unsigned long long len;
unsigned long long off;
size_t data_len;
size_t new_off;
bool last;
int rc;
const struct cbor_attr_t off_attr[4] = {
[0] = {
.attribute = "data",
.type = CborAttrByteStringType,
.addr.bytestring.data = img_mgmt_data,
.addr.bytestring.len = &data_len,
.len = sizeof(img_mgmt_data)
},
[1] = {
.attribute = "len",
.type = CborAttrUnsignedIntegerType,
.addr.uinteger = &len,
.nodefault = true
},
[2] = {
.attribute = "off",
.type = CborAttrUnsignedIntegerType,
.addr.uinteger = &off,
.nodefault = true
},
[3] = { 0 },
};
len = ULLONG_MAX;
off = ULLONG_MAX;
data_len = 0;
rc = cbor_read_object(&ctxt->it, off_attr);
if (rc || off == ULLONG_MAX) {
return MGMT_ERR_EINVAL;
}
if (off == 0) {
/* Total image length is a required field in the first request. */
if (len == ULLONG_MAX) {
return MGMT_ERR_EINVAL;
}
rc = img_mgmt_upload_first_chunk(ctxt, img_mgmt_data, data_len);
if (rc != 0) {
return rc;
}
img_mgmt_ctxt.len = len;
} else {
if (!img_mgmt_ctxt.uploading) {
return MGMT_ERR_EINVAL;
}
if (off != img_mgmt_ctxt.off) {
/* Invalid offset. Drop the data and send the expected offset. */
return img_mgmt_encode_upload_rsp(ctxt, 0);
}
}
new_off = img_mgmt_ctxt.off + data_len;
if (new_off > img_mgmt_ctxt.len) {
/* Data exceeds image length. */
return MGMT_ERR_EINVAL;
}
last = new_off == img_mgmt_ctxt.len;
if (data_len > 0) {
rc = img_mgmt_impl_write_image_data(off, img_mgmt_data, data_len,
last);
if (rc != 0) {
return rc;
}
}
img_mgmt_ctxt.off = new_off;
if (last) {
/* Upload complete. */
img_mgmt_ctxt.uploading = false;
}
return img_mgmt_encode_upload_rsp(ctxt, 0);
}
void
img_mgmt_register_group(void)
{
mgmt_register_group(&img_mgmt_group);
}

View File

@ -0,0 +1,41 @@
/*
* 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.
*/
#ifndef H_IMG_MGMT_CONFIG_
#define H_IMG_MGMT_CONFIG_
#if defined MYNEWT
#include "syscfg/syscfg.h"
#define IMG_MGMT_UL_CHUNK_SIZE MYNEWT_VAL(IMG_MGMT_UL_CHUNK_SIZE)
#elif defined __ZEPHYR__
#define IMG_MGMT_UL_CHUNK_SIZE CONFIG_IMG_MGMT_UL_CHUNK_SIZE
#else
/* No direct support for this OS. The application needs to define the above
* settings itself.
*/
#endif
#endif

View File

@ -0,0 +1,96 @@
/*
* 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.
*/
#ifndef H_IMG_PRIV_
#define H_IMG_PRIV_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define IMG_MGMT_SWAP_TYPE_NONE 0
#define IMG_MGMT_SWAP_TYPE_TEST 1
#define IMG_MGMT_SWAP_TYPE_PERM 2
#define IMG_MGMT_SWAP_TYPE_REVERT 3
/*
* Response to list:
* {
* "images":[ <version1>, <version2>]
* }
*
*
* Request to boot to version:
* {
* "test":<version>
* }
*
*
* Response to boot read:
* {
* "test":<version>,
* "main":<version>,
* "active":<version>
* }
*
*
* Request to image upload:
* {
* "off":<offset>,
* "len":<img_size> inspected when off = 0
* "data":<base64encoded binary>
* }
*
*
* Response to upload:
* {
* "off":<offset>
* }
*
*
* Request to image upload:
* {
* "off":<offset>
* "name":<filename> inspected when off = 0
* "len":<file_size> inspected when off = 0
* "data":<base64encoded binary>
* }
*/
struct mgmt_ctxt;
int img_mgmt_core_erase(struct mgmt_ctxt *);
int img_mgmt_core_list(struct mgmt_ctxt *);
int img_mgmt_core_load(struct mgmt_ctxt *);
int img_mgmt_find_by_hash(uint8_t *find, struct image_version *ver);
int img_mgmt_find_by_ver(struct image_version *find, uint8_t *hash);
int img_mgmt_read_info(int image_slot, struct image_version *ver,
uint8_t *hash, uint32_t *flags);
int img_mgmt_slot_in_use(int slot);
int img_mgmt_state_read(struct mgmt_ctxt *ctxt);
int img_mgmt_state_write(struct mgmt_ctxt *njb);
int img_mgmt_ver_str(const struct image_version *ver, char *dst);
#ifdef __cplusplus
}
#endif
#endif /* __IMG_PRIV_H */

View File

@ -0,0 +1,315 @@
/*
* 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 "cbor.h"
#include "cborattr/cborattr.h"
#include "mgmt/mgmt.h"
#include "img_mgmt/img_mgmt.h"
#include "img_mgmt/image.h"
#include "img_mgmt_priv.h"
#include "img_mgmt/img_mgmt_impl.h"
#define IMG_MGMT_STATE_F_PENDING 0x01
#define IMG_MGMT_STATE_F_CONFIRMED 0x02
#define IMG_MGMT_STATE_F_ACTIVE 0x04
#define IMG_MGMT_STATE_F_PERMANENT 0x08
#define IMG_MGMT_VER_MAX_STR_LEN 25 /* 255.255.65535.4294967295\0 */
/**
* Collects information about the specified image slot.
*/
static uint8_t
img_mgmt_state_flags(int query_slot)
{
uint8_t flags;
int swap_type;
assert(query_slot == 0 || query_slot == 1);
flags = 0;
/* Determine if this is is pending or confirmed (only applicable for
* unified images and loaders.
*/
swap_type = img_mgmt_impl_swap_type();
switch (swap_type) {
case IMG_MGMT_SWAP_TYPE_NONE:
if (query_slot == 0) {
flags |= IMG_MGMT_STATE_F_CONFIRMED;
flags |= IMG_MGMT_STATE_F_ACTIVE;
}
break;
case IMG_MGMT_SWAP_TYPE_TEST:
if (query_slot == 0) {
flags |= IMG_MGMT_STATE_F_CONFIRMED;
} else if (query_slot == 1) {
flags |= IMG_MGMT_STATE_F_PENDING;
}
break;
case IMG_MGMT_SWAP_TYPE_PERM:
if (query_slot == 0) {
flags |= IMG_MGMT_STATE_F_CONFIRMED;
} else if (query_slot == 1) {
flags |= IMG_MGMT_STATE_F_PENDING | IMG_MGMT_STATE_F_PERMANENT;
}
break;
case IMG_MGMT_SWAP_TYPE_REVERT:
if (query_slot == 0) {
flags |= IMG_MGMT_STATE_F_ACTIVE;
} else if (query_slot == 1) {
flags |= IMG_MGMT_STATE_F_CONFIRMED;
}
break;
}
/* Slot 0 is always active. */
/* XXX: The slot 0 assumption only holds when running from flash. */
if (query_slot == 0) {
flags |= IMG_MGMT_STATE_F_ACTIVE;
}
return flags;
}
/**
* Indicates whether any image slot is pending (i.e., whether a test swap will
* happen on the next reboot.
*/
static int
img_mgmt_state_any_pending(void)
{
return img_mgmt_state_flags(0) & IMG_MGMT_STATE_F_PENDING ||
img_mgmt_state_flags(1) & IMG_MGMT_STATE_F_PENDING;
}
/**
* Indicates whether the specified slot has any flags. If no flags are set,
* the slot can be freely erased.
*/
int
img_mgmt_slot_in_use(int slot)
{
uint8_t state_flags;
state_flags = img_mgmt_state_flags(slot);
return state_flags & IMG_MGMT_STATE_F_ACTIVE ||
state_flags & IMG_MGMT_STATE_F_CONFIRMED ||
state_flags & IMG_MGMT_STATE_F_PENDING;
}
/**
* Sets the pending flag for the specified image slot. That is, the system
* will swap to the specified image on the next reboot. If the permanent
* argument is specified, the system doesn't require a confirm after the swap
* occurs.
*/
int
img_mgmt_state_set_pending(int slot, int permanent)
{
uint8_t state_flags;
int rc;
state_flags = img_mgmt_state_flags(slot);
/* Unconfirmed slots are always runable. A confirmed slot can only be
* run if it is a loader in a split image setup.
*/
if (state_flags & IMG_MGMT_STATE_F_CONFIRMED && slot != 0) {
return MGMT_ERR_EBADSTATE;
}
rc = img_mgmt_impl_write_pending(slot, permanent);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
return 0;
}
/**
* Confirms the current image state. Prevents a fallback from occurring on the
* next reboot if the active image is currently being tested.
*/
int
img_mgmt_state_confirm(void)
{
int rc;
/* Confirm disallowed if a test is pending. */
if (img_mgmt_state_any_pending()) {
return MGMT_ERR_EBADSTATE;
}
rc = img_mgmt_impl_write_confirmed();
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
return 0;
}
/**
* Command handler: image state read
*/
int
img_mgmt_state_read(struct mgmt_ctxt *ctxt)
{
char vers_str[IMG_MGMT_VER_MAX_STR_LEN];
uint8_t hash[IMAGE_HASH_LEN]; /* SHA256 hash */
struct image_version ver;
CborEncoder images;
CborEncoder image;
CborError err;
uint32_t flags;
uint8_t state_flags;
int rc;
int i;
err = 0;
err |= cbor_encode_text_stringz(&ctxt->encoder, "images");
err |= cbor_encoder_create_array(&ctxt->encoder, &images,
CborIndefiniteLength);
for (i = 0; i < 2; i++) {
rc = img_mgmt_read_info(i, &ver, hash, &flags);
if (rc != 0) {
continue;
}
state_flags = img_mgmt_state_flags(i);
err |= cbor_encoder_create_map(&images, &image,
CborIndefiniteLength);
err |= cbor_encode_text_stringz(&image, "slot");
err |= cbor_encode_int(&image, i);
err |= cbor_encode_text_stringz(&image, "version");
img_mgmt_ver_str(&ver, vers_str);
err |= cbor_encode_text_stringz(&image, vers_str);
err |= cbor_encode_text_stringz(&image, "hash");
err |= cbor_encode_byte_string(&image, hash, IMAGE_HASH_LEN);
err |= cbor_encode_text_stringz(&image, "bootable");
err |= cbor_encode_boolean(&image, !(flags & IMAGE_F_NON_BOOTABLE));
err |= cbor_encode_text_stringz(&image, "pending");
err |= cbor_encode_boolean(&image,
state_flags & IMG_MGMT_STATE_F_PENDING);
err |= cbor_encode_text_stringz(&image, "confirmed");
err |= cbor_encode_boolean(&image,
state_flags & IMG_MGMT_STATE_F_CONFIRMED);
err |= cbor_encode_text_stringz(&image, "active");
err |= cbor_encode_boolean(&image,
state_flags & IMG_MGMT_STATE_F_ACTIVE);
err |= cbor_encode_text_stringz(&image, "permanent");
err |= cbor_encode_boolean(&image,
state_flags & IMG_MGMT_STATE_F_PERMANENT);
err |= cbor_encoder_close_container(&images, &image);
}
err |= cbor_encoder_close_container(&ctxt->encoder, &images);
err |= cbor_encode_text_stringz(&ctxt->encoder, "splitStatus");
err |= cbor_encode_int(&ctxt->encoder, 0);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Command handler: image state write
*/
int
img_mgmt_state_write(struct mgmt_ctxt *ctxt)
{
uint8_t hash[IMAGE_HASH_LEN];
size_t hash_len;
bool confirm;
int slot;
int rc;
const struct cbor_attr_t write_attr[] = {
[0] = {
.attribute = "hash",
.type = CborAttrByteStringType,
.addr.bytestring.data = hash,
.addr.bytestring.len = &hash_len,
.len = sizeof(hash),
},
[1] = {
.attribute = "confirm",
.type = CborAttrBooleanType,
.addr.boolean = &confirm,
.dflt.boolean = false,
},
[2] = { 0 },
};
hash_len = 0;
rc = cbor_read_object(&ctxt->it, write_attr);
if (rc != 0) {
return MGMT_ERR_EINVAL;
}
/* Determine which slot is being operated on. */
if (hash_len == 0) {
if (confirm) {
slot = 0;
} else {
/* A 'test' without a hash is invalid. */
return MGMT_ERR_EINVAL;
}
} else {
slot = img_mgmt_find_by_hash(hash, NULL);
if (slot < 0) {
return MGMT_ERR_EINVAL;
}
}
if (slot == 0 && confirm) {
/* Confirm current setup. */
rc = img_mgmt_state_confirm();
} else {
rc = img_mgmt_state_set_pending(slot, confirm);
}
if (rc != 0) {
return rc;
}
/* Send the current image state in the response. */
rc = img_mgmt_state_read(ctxt);
if (rc != 0) {
return rc;
}
return 0;
}

View File

@ -0,0 +1,50 @@
/*
* 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 <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util/mcumgr_util.h"
#include "img_mgmt/image.h"
#include "img_mgmt/img_mgmt.h"
int
img_mgmt_ver_str(const struct image_version *ver, char *dst)
{
int off;
off = 0;
off += ull_to_s(ver->iv_major, INT_MAX, dst + off);
dst[off++] = '.';
off += ull_to_s(ver->iv_minor, INT_MAX, dst + off);
dst[off++] = '.';
off += ull_to_s(ver->iv_revision, INT_MAX, dst + off);
if (ver->iv_build_num != 0) {
dst[off++] = '.';
off += ull_to_s(ver->iv_revision, INT_MAX, dst + off);
}
return 0;
}

64
cmd/img_mgmt/src/stubs.c Normal file
View File

@ -0,0 +1,64 @@
/*
* 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.
*/
/**
* These stubs get linked in when there is no equivalent OS-specific
* implementation.
*/
#include "mgmt/mgmt.h"
#include "img_mgmt/img_mgmt_impl.h"
int __attribute__((weak))
img_mgmt_impl_erase_slot(void)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
img_mgmt_impl_write_pending(int slot, bool permanent)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
img_mgmt_impl_write_confirmed(void)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
img_mgmt_impl_read(int slot, unsigned int offset, void *dst,
unsigned int num_bytes)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
img_mgmt_impl_write_image_data(unsigned int offset, const void *data,
unsigned int num_bytes, bool last)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
img_mgmt_impl_swap_type(void)
{
return MGMT_ERR_ENOTSUP;
}

View File

@ -0,0 +1,9 @@
target_include_directories(MCUMGR INTERFACE
include
)
zephyr_library_sources(
cmd/log_mgmt/port/zephyr/src/zephyr_log_mgmt.c
cmd/log_mgmt/src/log_mgmt.c
cmd/log_mgmt/src/stubs.c
)

54
cmd/log_mgmt/Kconfig Normal file
View File

@ -0,0 +1,54 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE log
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this log
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this log 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.
menuconfig MCUMGR_CMD_LOG_MGMT
bool
prompt "Enable mcumgr handlers for log management"
depends on MDLOG
default n
help
Enables mcumgr handlers for log management
if MCUMGR_CMD_LOG_MGMT
config LOG_MGMT_CHUNK_SIZE
int
prompt "Maximum chunk size for log downloads"
default 512
help
Limits the maximum chunk size for log downloads, in bytes. A buffer of
this size gets allocated on the stack during handling of the log show command.
config LOG_MGMT_NAME_LEN
int
prompt "Maximum log name length"
default 64
help
Limits the maximum length of log names, in bytes. If a log's name length
exceeds this number, it gets truncated in management responses. A buffer
of this size gets allocated on the stack during handling of all log
management commands.
config LOG_MGMT_BODY_LEN
int
prompt "Maximum log body length"
default 128
help
Limits the maximum length of log entry bodies, in bytes. If a log
entry's body length exceeds this number, it gets truncated in management
responses. A buffer of this size gets allocated on the stack during
handling of the log show command.
endif

View File

@ -0,0 +1,83 @@
/*
* 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.
*/
#ifndef H_LOG_MGMT_
#define H_LOG_MGMT_
#ifdef __cplusplus
extern "C" {
#endif
/**
* Command IDs for log management group.
*/
#define LOG_MGMT_ID_SHOW 0
#define LOG_MGMT_ID_CLEAR 1
#define LOG_MGMT_ID_APPEND 2
#define LOG_MGMT_ID_MODULE_LIST 3
#define LOG_MGMT_ID_LEVEL_LIST 4
#define LOG_MGMT_ID_LOGS_LIST 5
/** @brief Log output is streamed without retention (e.g., console). */
#define LOG_MGMT_TYPE_STREAM 0
/** @brief Log entries are stored in RAM. */
#define LOG_MGMT_TYPE_MEMORY 1
/** @brief Log entries are persisted across reboots. */
#define LOG_MGMT_TYPE_STORAGE 2
/** @brief Generic descriptor for an OS-specific log. */
struct log_mgmt_log {
const char *name;
int type;
};
/** @brief Generic descriptor for an OS-specific log entry. */
struct log_mgmt_entry {
int64_t ts;
uint32_t index;
const void *data;
size_t len;
uint8_t module;
uint8_t level;
};
/** @brief Indicates which log entries to operate on. */
struct log_mgmt_filter {
/* If min_timestamp == -1: Only access last log entry;
* Elif min_timestamp == 0: Don't filter by timestamp;
* Else: Only access entries whose ts >= min_timestamp.
*/
int64_t min_timestamp;
/* Only access entries whose index >= min_index. */
uint32_t min_index;
};
/**
* @brief Registers the log management command handler group.
*/
void log_mgmt_register_group(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,120 @@
/*
* 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 Declares implementation-specific functions required by log
* management. The default stubs can be overridden with functions that
* are compatible with the host OS.
*/
#ifndef H_LOG_MGMT_IMPL_
#define H_LOG_MGMT_IMPL_
#ifdef __cplusplus
extern "C" {
#endif
struct log_mgmt_filter;
struct log_mgmt_entry;
struct log_mgmt_log;
typedef int log_mgmt_foreach_entry_fn(const struct log_mgmt_entry *entry,
void *arg);
/**
* @brief Retrieves the log at the specified index.
*
* @param idx The index of the log to retrieve.
* @param out_name On success, the requested log gets written
* here.
*
* @return 0 on success;
* MGMT_ERR_ENOENT if no log with the specified
* index exists;
* Other MGMT_ERR_[...] code on failure.
*/
int log_mgmt_impl_get_log(int idx, struct log_mgmt_log *out_log);
/**
* @brief Retrieves the name of log module at the specified index.
*
* @param idx The index of the log module to retrieve.
* @param out_name On success, the requested module's name gets
* written here.
*
* @return 0 on success;
* MGMT_ERR_ENOENT if no log module with the
* specified index exists;
* Other MGMT_ERR_[...] code on failure.
*/
int log_mgmt_impl_get_module(int idx, const char **out_module_name);
/**
* @brief Retrieves the name of log level at the specified index.
*
* @param idx The index of the log level to retrieve.
* @param out_name On success, the requested level's name gets
* written here.
*
* @return 0 on success;
* MGMT_ERR_ENOENT if no log level with the
* specified index exists;
* Other MGMT_ERR_[...] code on failure.
*/
int log_mgmt_impl_get_level(int idx, const char **out_level_name);
/**
* @brief Retrieves the index that the next log entry will use.
*
* @param out_idx On success, the next index gets written here.
*
* @return 0 on success; MGMT_ERR_[...] code on failure.
*/
int log_mgmt_impl_get_next_idx(uint32_t *out_idx);
/**
* @brief Applies a function to every matching entry in the specified log.
*
* @param log_name The name of the log to operate on.
* @param filter Specifies which log entries to operate on.
* @param cb The callback to apply to each log entry.
* @param arg An optional argument to pass to the callback.
*
* @return 0 on success; MGMT_ERR_[...] code on failure.
*/
int log_mgmt_impl_foreach_entry(const char *log_name,
const struct log_mgmt_filter *filter,
log_mgmt_foreach_entry_fn *cb,
void *arg);
/**
* @brief Clear the log with the specified name.
*
* @param log_name The name of the log to clear.
*
* @return 0 on success; MGMT_ERR_[...] code on failure.
*/
int log_mgmt_impl_clear(const char *log_name);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,189 @@
/*
* 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 <misc/util.h>
#include <logging/mdlog.h>
#include <mgmt/mgmt.h>
#include <log_mgmt/log_mgmt.h>
#include <log_mgmt/log_mgmt_impl.h>
#include "../../../src/log_mgmt_config.h"
struct zephyr_log_mgmt_walk_arg {
log_mgmt_foreach_entry_fn *cb;
uint8_t body[LOG_MGMT_BODY_LEN];
void *arg;
};
int
log_mgmt_impl_get_log(int idx, struct log_mgmt_log *out_log)
{
struct mdlog *mdlog;
int i;
mdlog = NULL;
for (i = 0; i <= idx; i++) {
mdlog = mdlog_get_next(mdlog);
if (mdlog == NULL) {
return MGMT_ERR_ENOENT;
}
}
out_log->name = mdlog->l_name;
out_log->type = mdlog->l_handler->type;
return 0;
}
int
log_mgmt_impl_get_module(int idx, const char **out_module_name)
{
const char *name;
name = mdlog_module_name(idx);
if (name == NULL) {
return MGMT_ERR_ENOENT;
} else {
*out_module_name = name;
return 0;
}
}
int
log_mgmt_impl_get_level(int idx, const char **out_level_name)
{
const char *name;
name = mdlog_level_name(idx);
if (name == NULL) {
return MGMT_ERR_ENOENT;
} else {
*out_level_name = name;
return 0;
}
}
int
log_mgmt_impl_get_next_idx(uint32_t *out_idx)
{
*out_idx = mdlog_get_next_index();
return 0;
}
static int
zephyr_log_mgmt_walk_cb(struct mdlog *log, struct mdlog_offset *log_offset,
const void *desciptor, uint16_t len)
{
struct zephyr_log_mgmt_walk_arg *zephyr_log_mgmt_walk_arg;
struct log_mgmt_entry entry;
struct mdlog_entry_hdr ueh;
int read_len;
int rc;
zephyr_log_mgmt_walk_arg = log_offset->lo_arg;
rc = mdlog_read(log, desciptor, &ueh, 0, sizeof ueh);
if (rc != sizeof ueh) {
return MGMT_ERR_EUNKNOWN;
}
/* If specified timestamp is nonzero, it is the primary criterion, and the
* specified index is the secondary criterion. If specified timetsamp is
* zero, specified index is the only criterion.
*
* If specified timestamp == 0: encode entries whose index >=
* specified index.
* Else: encode entries whose timestamp >= specified timestamp and whose
* index >= specified index
*/
if (log_offset->lo_ts == 0) {
if (log_offset->lo_index > ueh.ue_index) {
return 0;
}
} else if (ueh.ue_ts < log_offset->lo_ts ||
(ueh.ue_ts == log_offset->lo_ts &&
ueh.ue_index < log_offset->lo_index)) {
return 0;
}
read_len = min(len - sizeof ueh, LOG_MGMT_BODY_LEN - sizeof ueh);
rc = mdlog_read(log, desciptor, zephyr_log_mgmt_walk_arg->body, sizeof ueh,
read_len);
if (rc < 0) {
return MGMT_ERR_EUNKNOWN;
}
entry.ts = ueh.ue_ts;
entry.index = ueh.ue_index;
entry.module = ueh.ue_module;
entry.level = ueh.ue_level;
entry.len = rc;
entry.data = zephyr_log_mgmt_walk_arg->body;
return zephyr_log_mgmt_walk_arg->cb(&entry, zephyr_log_mgmt_walk_arg->arg);
}
int
log_mgmt_impl_foreach_entry(const char *log_name,
const struct log_mgmt_filter *filter,
log_mgmt_foreach_entry_fn *cb, void *arg)
{
struct zephyr_log_mgmt_walk_arg walk_arg;
struct mdlog_offset offset;
struct mdlog *mdlog;
walk_arg = (struct zephyr_log_mgmt_walk_arg) {
.cb = cb,
.arg = arg,
};
mdlog = mdlog_find(log_name);
if (mdlog == NULL) {
return MGMT_ERR_ENOENT;
}
if (strcmp(mdlog->l_name, log_name) == 0) {
offset.lo_arg = &walk_arg;
offset.lo_ts = filter->min_timestamp;
offset.lo_index = filter->min_index;
offset.lo_data_len = 0;
return mdlog_walk(mdlog, zephyr_log_mgmt_walk_cb, &offset);
}
return MGMT_ERR_ENOENT;
}
int
log_mgmt_impl_clear(const char *log_name)
{
struct mdlog *mdlog;
int rc;
mdlog = mdlog_find(log_name);
if (mdlog == NULL) {
return MGMT_ERR_ENOENT;
}
rc = mdlog_flush(mdlog);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
return 0;
}

494
cmd/log_mgmt/src/log_mgmt.c Normal file
View File

@ -0,0 +1,494 @@
/*
* 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 <string.h>
#include <stdio.h>
#include "mgmt/mgmt.h"
#include "cborattr/cborattr.h"
#include "cbor_cnt_writer.h"
#include "log_mgmt/log_mgmt.h"
#include "log_mgmt/log_mgmt_impl.h"
#include "log_mgmt_config.h"
/** Context used during walks. */
struct log_walk_ctxt {
/* The number of bytes encoded to the response so far. */
size_t rsp_len;
/* The encoder to use to write the current log entry. */
struct CborEncoder *enc;
};
static mgmt_handler_fn log_mgmt_show;
static mgmt_handler_fn log_mgmt_clear;
static mgmt_handler_fn log_mgmt_module_list;
static mgmt_handler_fn log_mgmt_level_list;
static mgmt_handler_fn log_mgmt_logs_list;
static struct mgmt_handler log_mgmt_handlers[] = {
[LOG_MGMT_ID_SHOW] = { log_mgmt_show, NULL },
[LOG_MGMT_ID_CLEAR] = { NULL, log_mgmt_clear },
[LOG_MGMT_ID_MODULE_LIST] = { log_mgmt_module_list, NULL },
[LOG_MGMT_ID_LEVEL_LIST] = { log_mgmt_level_list, NULL },
[LOG_MGMT_ID_LOGS_LIST] = { log_mgmt_logs_list, NULL },
};
#define LOG_MGMT_HANDLER_CNT \
sizeof log_mgmt_handlers / sizeof log_mgmt_handlers[0]
static struct mgmt_group log_mgmt_group = {
.mg_handlers = log_mgmt_handlers,
.mg_handlers_count = LOG_MGMT_HANDLER_CNT,
.mg_group_id = MGMT_GROUP_ID_LOG,
};
static int
log_mgmt_encode_entry(CborEncoder *enc, const struct log_mgmt_entry *entry,
size_t *out_len)
{
CborEncoder entry_enc;
CborError err;
err = 0;
err |= cbor_encoder_create_map(enc, &entry_enc, 5);
err |= cbor_encode_text_stringz(&entry_enc, "msg");
err |= cbor_encode_byte_string(&entry_enc, entry->data, entry->len);
err |= cbor_encode_text_stringz(&entry_enc, "ts");
err |= cbor_encode_int(&entry_enc, entry->ts);
err |= cbor_encode_text_stringz(&entry_enc, "level");
err |= cbor_encode_uint(&entry_enc, entry->level);
err |= cbor_encode_text_stringz(&entry_enc, "index");
err |= cbor_encode_uint(&entry_enc, entry->index);
err |= cbor_encode_text_stringz(&entry_enc, "module");
err |= cbor_encode_uint(&entry_enc, entry->module);
err |= cbor_encoder_close_container(enc, &entry_enc);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
if (out_len != NULL) {
*out_len = cbor_encode_bytes_written(enc);
}
return 0;
}
static int
log_mgmt_cb_encode(const struct log_mgmt_entry *entry, void *arg)
{
struct CborCntWriter cnt_writer;
struct log_walk_ctxt *ctxt;
CborEncoder cnt_encoder;
size_t entry_len;
int rc;
ctxt = arg;
/*** First, determine if this entry would fit. */
cbor_cnt_writer_init(&cnt_writer);
cbor_encoder_cust_writer_init(&cnt_encoder, &cnt_writer.enc, 0);
rc = log_mgmt_encode_entry(&cnt_encoder, entry, &entry_len);
if (rc != 0) {
return rc;
}
/* `+ 1` to account for the CBOR array terminator. */
if (ctxt->rsp_len + entry_len + 1 > LOG_MGMT_CHUNK_SIZE) {
return MGMT_ERR_EMSGSIZE;
}
ctxt->rsp_len += entry_len;
/*** The entry fits. Now encode it. */
rc = log_mgmt_encode_entry(ctxt->enc, entry, NULL);
if (rc != 0) {
return rc;
}
return 0;
}
static int
log_encode_entries(const struct log_mgmt_log *log, CborEncoder *enc,
int64_t timestamp, uint32_t index)
{
struct log_mgmt_filter filter;
struct log_walk_ctxt ctxt;
CborEncoder entries;
CborError err;
int rc;
err = 0;
err |= cbor_encode_text_stringz(enc, "entries");
err |= cbor_encoder_create_array(enc, &entries, CborIndefiniteLength);
filter = (struct log_mgmt_filter) {
.min_timestamp = timestamp,
.min_index = index,
};
ctxt = (struct log_walk_ctxt) {
.enc = &entries,
.rsp_len = cbor_encode_bytes_written(enc),
};
rc = log_mgmt_impl_foreach_entry(log->name, &filter,
log_mgmt_cb_encode, &ctxt);
if (rc != 0 && rc != MGMT_ERR_EMSGSIZE) {
return rc;
}
err |= cbor_encoder_close_container(enc, &entries);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
static int
log_encode(const struct log_mgmt_log *log, CborEncoder *ctxt,
int64_t timestamp, uint32_t index)
{
CborEncoder logs;
CborError err;
int rc;
err = 0;
err |= cbor_encoder_create_map(ctxt, &logs, CborIndefiniteLength);
err |= cbor_encode_text_stringz(&logs, "name");
err |= cbor_encode_text_stringz(&logs, log->name);
err |= cbor_encode_text_stringz(&logs, "type");
err |= cbor_encode_uint(&logs, log->type);
rc = log_encode_entries(log, &logs, timestamp, index);
if (rc != 0) {
return rc;
}
err |= cbor_encoder_close_container(ctxt, &logs);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Command handler: log show
*/
static int
log_mgmt_show(struct mgmt_ctxt *ctxt)
{
char name[LOG_MGMT_NAME_LEN];
struct log_mgmt_log log;
CborEncoder logs;
CborError err;
uint64_t index;
int64_t timestamp;
uint32_t next_idx;
int name_len;
int log_idx;
int rc;
const struct cbor_attr_t attr[] = {
{
.attribute = "log_name",
.type = CborAttrTextStringType,
.addr.string = name,
.len = sizeof(name),
},
{
.attribute = "ts",
.type = CborAttrIntegerType,
.addr.integer = &timestamp,
},
{
.attribute = "index",
.type = CborAttrUnsignedIntegerType,
.addr.uinteger = &index,
},
{
.attribute = NULL,
},
};
name[0] = '\0';
rc = cbor_read_object(&ctxt->it, attr);
if (rc != 0) {
return MGMT_ERR_EINVAL;
}
name_len = strlen(name);
/* Determine the index that the next log entry would use. */
rc = log_mgmt_impl_get_next_idx(&next_idx);
if (rc != 0) {
return MGMT_ERR_EUNKNOWN;
}
err = 0;
err |= cbor_encode_text_stringz(&ctxt->encoder, "next_index");
err |= cbor_encode_uint(&ctxt->encoder, next_idx);
err |= cbor_encode_text_stringz(&ctxt->encoder, "logs");
err |= cbor_encoder_create_array(&ctxt->encoder, &logs,
CborIndefiniteLength);
/* Iterate list of logs, encoding each that matches the client request. */
for (log_idx = 0; ; log_idx++) {
rc = log_mgmt_impl_get_log(log_idx, &log);
if (rc == MGMT_ERR_ENOENT) {
/* Log list fully iterated. */
if (name_len != 0) {
/* Client specified log name, but the log wasn't found. */
return MGMT_ERR_ENOENT;
} else {
break;
}
} else if (rc != 0) {
return rc;
}
/* Stream logs cannot be read. */
if (log.type != LOG_MGMT_TYPE_STREAM) {
if (name_len == 0 || strcmp(name, log.name) == 0) {
rc = log_encode(&log, &logs, timestamp, index);
if (rc != 0) {
return rc;
}
/* If the client specified this log, he isn't interested in the
* remaining ones.
*/
if (name_len != 0) {
break;
}
}
}
}
err |= cbor_encoder_close_container(&ctxt->encoder, &logs);
err |= cbor_encode_text_stringz(&ctxt->encoder, "rc");
err |= cbor_encode_int(&ctxt->encoder, rc);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Command handler: log module_list
*/
static int
log_mgmt_module_list(struct mgmt_ctxt *ctxt)
{
const char *module_name;
CborEncoder modules;
CborError err;
int module;
int rc;
err = 0;
err |= cbor_encode_text_stringz(&ctxt->encoder, "rc");
err |= cbor_encode_int(&ctxt->encoder, MGMT_ERR_EOK);
err |= cbor_encode_text_stringz(&ctxt->encoder, "module_map");
err |= cbor_encoder_create_map(&ctxt->encoder, &modules,
CborIndefiniteLength);
for (module = 0; ; module++) {
rc = log_mgmt_impl_get_module(module, &module_name);
if (rc == MGMT_ERR_ENOENT) {
break;
}
if (rc != 0) {
return rc;
}
if (module_name != NULL) {
err |= cbor_encode_text_stringz(&modules, module_name);
err |= cbor_encode_uint(&modules, module);
}
}
err |= cbor_encoder_close_container(&ctxt->encoder, &modules);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Command handler: log list
*/
static int
log_mgmt_logs_list(struct mgmt_ctxt *ctxt)
{
struct log_mgmt_log log;
CborEncoder log_list;
CborError err;
int log_idx;
int rc;
err = 0;
err |= cbor_encode_text_stringz(&ctxt->encoder, "rc");
err |= cbor_encode_int(&ctxt->encoder, MGMT_ERR_EOK);
err |= cbor_encode_text_stringz(&ctxt->encoder, "log_list");
err |= cbor_encoder_create_array(&ctxt->encoder, &log_list,
CborIndefiniteLength);
for (log_idx = 0; ; log_idx++) {
rc = log_mgmt_impl_get_log(log_idx, &log);
if (rc == MGMT_ERR_ENOENT) {
break;
}
if (rc != 0) {
return rc;
}
if (log.type != LOG_MGMT_TYPE_STREAM) {
err |= cbor_encode_text_stringz(&log_list, log.name);
}
}
err |= cbor_encoder_close_container(&ctxt->encoder, &log_list);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Command handler: log level_list
*/
static int
log_mgmt_level_list(struct mgmt_ctxt *ctxt)
{
const char *level_name;
CborEncoder level_map;
CborError err;
int level;
int rc;
err = 0;
err |= cbor_encode_text_stringz(&ctxt->encoder, "rc");
err |= cbor_encode_int(&ctxt->encoder, MGMT_ERR_EOK);
err |= cbor_encode_text_stringz(&ctxt->encoder, "level_map");
err |= cbor_encoder_create_map(&ctxt->encoder, &level_map,
CborIndefiniteLength);
for (level = 0; ; level++) {
rc = log_mgmt_impl_get_level(level, &level_name);
if (rc == MGMT_ERR_ENOENT) {
break;
}
if (rc != 0) {
return rc;
}
if (level_name != NULL) {
err |= cbor_encode_text_stringz(&level_map, level_name);
err |= cbor_encode_uint(&level_map, level);
}
}
err |= cbor_encoder_close_container(&ctxt->encoder, &level_map);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Command handler: log clear
*/
static int
log_mgmt_clear(struct mgmt_ctxt *ctxt)
{
struct log_mgmt_log log;
char name[LOG_MGMT_NAME_LEN] = {0};
int name_len;
int log_idx;
int rc;
const struct cbor_attr_t attr[] = {
{
.attribute = "log_name",
.type = CborAttrTextStringType,
.addr.string = name,
.len = sizeof(name)
},
{
.attribute = NULL
},
};
name[0] = '\0';
rc = cbor_read_object(&ctxt->it, attr);
if (rc != 0) {
return MGMT_ERR_EINVAL;
}
name_len = strlen(name);
for (log_idx = 0; ; log_idx++) {
rc = log_mgmt_impl_get_log(log_idx, &log);
if (rc == MGMT_ERR_ENOENT) {
return 0;
}
if (rc != 0) {
return rc;
}
if (log.type != LOG_MGMT_TYPE_STREAM) {
if (name_len == 0 || strcmp(log.name, name) == 0) {
rc = log_mgmt_impl_clear(log.name);
if (rc != 0) {
return rc;
}
if (name_len != 0) {
return 0;
}
}
}
}
if (name_len != 0) {
return MGMT_ERR_ENOENT;
}
return 0;
}
void
log_mgmt_register_group(void)
{
mgmt_register_group(&log_mgmt_group);
}

View File

@ -0,0 +1,45 @@
/*
* 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.
*/
#ifndef H_LOG_MGMT_CONFIG_
#define H_LOG_MGMT_CONFIG_
#if defined MYNEWT
#include "syscfg/syscfg.h"
#define LOG_MGMT_CHUNK_SIZE MYNEWT_VAL(LOG_MGMT_CHUNK_SIZE)
#define LOG_MGMT_NAME_LEN MYNEWT_VAL(LOG_MGMT_NAME_LEN)
#define LOG_MGMT_BODY_LEN MYNEWT_VAL(LOG_MGMT_BODY_LEN)
#elif defined __ZEPHYR__
#define LOG_MGMT_CHUNK_SIZE CONFIG_LOG_MGMT_CHUNK_SIZE
#define LOG_MGMT_NAME_LEN CONFIG_LOG_MGMT_NAME_LEN
#define LOG_MGMT_BODY_LEN CONFIG_LOG_MGMT_BODY_LEN
#else
/* No direct support for this OS. The application needs to define the above
* settings itself.
*/
#endif
#endif

65
cmd/log_mgmt/src/stubs.c Normal file
View File

@ -0,0 +1,65 @@
/*
* 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.
*/
/**
* These stubs get linked in when there is no equivalent OS-specific
* implementation.
*/
#include "mgmt/mgmt.h"
#include "log_mgmt/log_mgmt_impl.h"
int __attribute__((weak))
log_mgmt_impl_get_log(int idx, struct log_mgmt_log *out_log)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
log_mgmt_impl_get_module(int idx, const char **out_module_name)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
log_mgmt_impl_get_level(int idx, const char **out_level_name)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
log_mgmt_impl_get_next_idx(uint32_t *out_idx)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
log_mgmt_impl_foreach_entry(const char *log_name,
const struct log_mgmt_filter *filter,
log_mgmt_foreach_entry_fn *cb,
void *arg)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
log_mgmt_impl_clear(const char *log_name)
{
return MGMT_ERR_ENOTSUP;
}

View File

@ -0,0 +1,9 @@
target_include_directories(MCUMGR INTERFACE
include
)
zephyr_library_sources(
cmd/os_mgmt/port/zephyr/src/zephyr_os_mgmt.c
cmd/os_mgmt/src/os_mgmt.c
cmd/os_mgmt/src/stubs.c
)

35
cmd/os_mgmt/Kconfig Normal file
View File

@ -0,0 +1,35 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE image
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this image
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this image 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.
menuconfig MCUMGR_CMD_OS_MGMT
bool
prompt "Enable mcumgr handlers for OS management"
select REBOOT
default n
help
Enables mcumgr handlers for OS management
if MCUMGR_CMD_OS_MGMT
config OS_MGMT_RESET_MS
int
prompt "Delay before executing reset command (ms)"
default 250
help
When a reset command is received, the system waits this many milliseconds
before performing the reset. This delay allows time for the mcumgr
response to be delivered.
endif

View File

@ -0,0 +1,62 @@
/*
* 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.
*/
#ifndef H_OS_MGMT_
#define H_OS_MGMT_
#ifdef __cplusplus
extern "C" {
#endif
/**
* Command IDs for OS management group.
*/
#define OS_MGMT_ID_ECHO 0
#define OS_MGMT_ID_CONS_ECHO_CTRL 1
#define OS_MGMT_ID_TASKSTAT 2
#define OS_MGMT_ID_MPSTAT 3
#define OS_MGMT_ID_DATETIME_STR 4
#define OS_MGMT_ID_RESET 5
#define OS_MGMT_TASK_NAME_LEN 32
struct os_mgmt_task_info {
uint8_t oti_prio;
uint8_t oti_taskid;
uint8_t oti_state;
uint16_t oti_stkusage;
uint16_t oti_stksize;
uint32_t oti_cswcnt;
uint32_t oti_runtime;
uint32_t oti_last_checkin;
uint32_t oti_next_checkin;
char oti_name[OS_MGMT_TASK_NAME_LEN];
};
/**
* @brief Registers the OS management command handler group.
*/
void os_mgmt_register_group(void);
#ifdef __cplusplus
}
#endif
#endif /* H_OS_MGMT_ */

View File

@ -0,0 +1,62 @@
/*
* 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 Declares implementation-specific functions required by OS management.
* The default stubs can be overridden with functions that are
* compatible with the host OS.
*/
#ifndef H_OS_MGMT_IMPL_
#define H_OS_MGMT_IMPL_
#ifdef __cplusplus
extern "C" {
#endif
struct os_mgmt_task_info;
/**
* @brief Retrieves information about the specified task.
*
* @param idx The index of the task to query.
* @param out_info On success, the requested information gets
* written here.
*
* @return 0 on success;
* MGMT_ERR_ENOENT if no such task exists;
* Other MGMT_ERR_[...] code on failure.
*/
int os_mgmt_impl_task_info(int idx, struct os_mgmt_task_info *out_info);
/**
* @brief Schedules a near-immediate system reset. There must be a slight
* delay before the reset occurs to allow time for the mgmt response to be
* delivered.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int os_mgmt_impl_reset(unsigned int delay_ms);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,96 @@
/*
* 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 <zephyr.h>
#include <misc/reboot.h>
#include <debug/object_tracing.h>
#include <kernel_structs.h>
#include <mgmt/mgmt.h>
#include <util/mcumgr_util.h>
#include <os_mgmt/os_mgmt.h>
#include <os_mgmt/os_mgmt_impl.h>
static void zephyr_os_mgmt_reset_cb(struct k_timer *timer);
static void zephyr_os_mgmt_reset_work_handler(struct k_work *work);
static K_TIMER_DEFINE(zephyr_os_mgmt_reset_timer,
zephyr_os_mgmt_reset_cb, NULL);
K_WORK_DEFINE(zephyr_os_mgmt_reset_work, zephyr_os_mgmt_reset_work_handler);
#ifdef CONFIG_THREAD_MONITOR
static const struct k_thread *
zephyr_os_mgmt_task_at(int idx)
{
const struct k_thread *thread;
int i;
thread = SYS_THREAD_MONITOR_HEAD;
for (i = 0; i < idx; i++) {
if (thread == NULL) {
break;
}
thread = SYS_THREAD_MONITOR_NEXT(thread);
}
return thread;
}
int
os_mgmt_impl_task_info(int idx, struct os_mgmt_task_info *out_info)
{
const struct k_thread *thread;
thread = zephyr_os_mgmt_task_at(idx);
if (thread == NULL) {
return MGMT_ERR_ENOENT;
}
*out_info = (struct os_mgmt_task_info){ 0 };
ll_to_s(thread->base.prio, sizeof out_info->oti_name, out_info->oti_name);
out_info->oti_prio = thread->base.prio;
out_info->oti_taskid = idx;
out_info->oti_state = thread->base.thread_state;
#ifdef THREAD_STACK_INFO
out_info->oti_stksize = thread->stack_info.size / 4;
#endif
return 0;
}
#endif /* CONFIG_THREAD_MONITOR */
static void
zephyr_os_mgmt_reset_work_handler(struct k_work *work)
{
sys_reboot(SYS_REBOOT_WARM);
}
static void
zephyr_os_mgmt_reset_cb(struct k_timer *timer)
{
/* Reboot the system from the system workqueue thread. */
k_work_submit(&zephyr_os_mgmt_reset_work);
}
int
os_mgmt_impl_reset(unsigned int delay_ms)
{
k_timer_start(&zephyr_os_mgmt_reset_timer, K_MSEC(delay_ms), 0);
return 0;
}

190
cmd/os_mgmt/src/os_mgmt.c Normal file
View File

@ -0,0 +1,190 @@
/*
* 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 "cbor.h"
#include "cborattr/cborattr.h"
#include "mgmt/mgmt.h"
#include "os_mgmt/os_mgmt.h"
#include "os_mgmt/os_mgmt_impl.h"
#include "os_mgmt_config.h"
static mgmt_handler_fn os_mgmt_echo;
static mgmt_handler_fn os_mgmt_reset;
static mgmt_handler_fn os_mgmt_taskstat_read;
static const struct mgmt_handler os_mgmt_group_handlers[] = {
[OS_MGMT_ID_ECHO] = {
os_mgmt_echo, os_mgmt_echo
},
[OS_MGMT_ID_TASKSTAT] = {
os_mgmt_taskstat_read, NULL
},
[OS_MGMT_ID_RESET] = {
NULL, os_mgmt_reset
},
};
#define OS_MGMT_GROUP_SZ \
(sizeof os_mgmt_group_handlers / sizeof os_mgmt_group_handlers[0])
static struct mgmt_group os_mgmt_group = {
.mg_handlers = os_mgmt_group_handlers,
.mg_handlers_count = OS_MGMT_GROUP_SZ,
.mg_group_id = MGMT_GROUP_ID_OS,
};
/**
* Command handler: os echo
*/
static int
os_mgmt_echo(struct mgmt_ctxt *ctxt)
{
char echo_buf[128];
CborError err;
const struct cbor_attr_t attrs[2] = {
[0] = {
.attribute = "d",
.type = CborAttrTextStringType,
.addr.string = echo_buf,
.nodefault = 1,
.len = sizeof echo_buf,
},
[1] = {
.attribute = NULL
}
};
echo_buf[0] = '\0';
err = cbor_read_object(&ctxt->it, attrs);
if (err != 0) {
return MGMT_ERR_EINVAL;
}
err |= cbor_encode_text_stringz(&ctxt->encoder, "r");
err |= cbor_encode_text_string(&ctxt->encoder, echo_buf, strlen(echo_buf));
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Encodes a single taskstat entry.
*/
static int
os_mgmt_taskstat_encode_one(struct CborEncoder *encoder,
const struct os_mgmt_task_info *task_info)
{
CborEncoder task_map;
CborError err;
err = 0;
err |= cbor_encode_text_stringz(encoder, task_info->oti_name);
err |= cbor_encoder_create_map(encoder, &task_map, CborIndefiniteLength);
err |= cbor_encode_text_stringz(&task_map, "prio");
err |= cbor_encode_uint(&task_map, task_info->oti_prio);
err |= cbor_encode_text_stringz(&task_map, "tid");
err |= cbor_encode_uint(&task_map, task_info->oti_taskid);
err |= cbor_encode_text_stringz(&task_map, "state");
err |= cbor_encode_uint(&task_map, task_info->oti_state);
err |= cbor_encode_text_stringz(&task_map, "stkuse");
err |= cbor_encode_uint(&task_map, task_info->oti_stkusage);
err |= cbor_encode_text_stringz(&task_map, "stksiz");
err |= cbor_encode_uint(&task_map, task_info->oti_stksize);
err |= cbor_encode_text_stringz(&task_map, "cswcnt");
err |= cbor_encode_uint(&task_map, task_info->oti_cswcnt);
err |= cbor_encode_text_stringz(&task_map, "runtime");
err |= cbor_encode_uint(&task_map, task_info->oti_runtime);
err |= cbor_encode_text_stringz(&task_map, "last_checkin");
err |= cbor_encode_uint(&task_map, task_info->oti_last_checkin);
err |= cbor_encode_text_stringz(&task_map, "next_checkin");
err |= cbor_encode_uint(&task_map, task_info->oti_next_checkin);
err |= cbor_encoder_close_container(encoder, &task_map);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Command handler: os taskstat
*/
static int
os_mgmt_taskstat_read(struct mgmt_ctxt *ctxt)
{
struct os_mgmt_task_info task_info;
struct CborEncoder tasks_map;
CborError err;
int task_idx;
int rc;
err = 0;
err |= cbor_encode_text_stringz(&ctxt->encoder, "tasks");
err |= cbor_encoder_create_map(&ctxt->encoder, &tasks_map,
CborIndefiniteLength);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
/* Iterate the list of tasks, encoding each. */
for (task_idx = 0; ; task_idx++) {
rc = os_mgmt_impl_task_info(task_idx, &task_info);
if (rc == MGMT_ERR_ENOENT) {
/* No more tasks to encode. */
break;
} else if (rc != 0) {
return rc;
}
rc = os_mgmt_taskstat_encode_one(&tasks_map, &task_info);
if (rc != 0) {
return rc;
}
}
err = cbor_encoder_close_container(&ctxt->encoder, &tasks_map);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Command handler: os reset
*/
static int
os_mgmt_reset(struct mgmt_ctxt *ctxt)
{
return os_mgmt_impl_reset(OS_MGMT_RESET_MS);
}
void
os_mgmt_register_group(void)
{
mgmt_register_group(&os_mgmt_group);
}

View File

@ -0,0 +1,41 @@
/*
* 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.
*/
#ifndef H_OS_MGMT_CONFIG_
#define H_OS_MGMT_CONFIG_
#if defined MYNEWT
#include "syscfg/syscfg.h"
#define OS_MGMT_RESET_MS MYNEWT_VAL(OS_MGMT_RESET_MS)
#elif defined __ZEPHYR__
#define OS_MGMT_RESET_MS CONFIG_OS_MGMT_RESET_MS
#else
/* No direct support for this OS. The application needs to define the above
* settings itself.
*/
#endif
#endif

38
cmd/os_mgmt/src/stubs.c Normal file
View File

@ -0,0 +1,38 @@
/*
* 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.
*/
/**
* These stubs get linked in when there is no equivalent OS-specific
* implementation.
*/
#include "mgmt/mgmt.h"
#include "os_mgmt/os_mgmt_impl.h"
int __attribute__((weak))
os_mgmt_impl_task_info(int idx, struct os_mgmt_task_info *out_info)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
os_mgmt_impl_reset(unsigned int delay_ms)
{
return MGMT_ERR_ENOTSUP;
}

View File

@ -0,0 +1,9 @@
target_include_directories(MCUMGR INTERFACE
include
)
zephyr_library_sources(
cmd/stat_mgmt/port/zephyr/src/zephyr_stat_mgmt.c
cmd/stat_mgmt/src/stat_mgmt.c
cmd/stat_mgmt/src/stubs.c
)

36
cmd/stat_mgmt/Kconfig Normal file
View File

@ -0,0 +1,36 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE log
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this log
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this log 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.
menuconfig MCUMGR_CMD_STAT_MGMT
bool
prompt "Enable mcumgr handlers for statistics management"
depends on STATS
default n
help
Enables mcumgr handlers for statistics management.
if MCUMGR_CMD_STAT_MGMT
config STAT_MGMT_MAX_NAME_LEN
int
prompt "Maximum stat group name length"
default 32
help
Limits the maximum stat group name length in mcumgr requests, in bytes.
A buffer of this size gets allocated on the stack during handling of all
stat read commands. If a stat group's name exceeds this limit, it will
be impossible to retrieve its values with a stat show command.
endif

View File

@ -0,0 +1,50 @@
/*
* 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.
*/
#ifndef H_STAT_MGMT_
#define H_STAT_MGMT_
#ifdef __cplusplus
extern "C" {
#endif
/**
* Command IDs for statistics management group.
*/
#define STAT_MGMT_ID_SHOW 0
#define STAT_MGMT_ID_LIST 1
/**
* @brief Represents a single value in a statistics group.
*/
struct stat_mgmt_entry {
const char *name;
uint64_t value;
};
/**
* @brief Registers the statistics management command handler group.
*/
void stat_mgmt_register_group(void);
#ifdef __cplusplus
}
#endif
#endif /* H_STAT_MGMT_ */

View File

@ -0,0 +1,70 @@
/*
* 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 Declares implementation-specific functions required by statistics
* management. The default stubs can be overridden with functions that
* are compatible with the host OS.
*/
#ifndef H_STAT_MGMT_IMPL_
#define H_STAT_MGMT_IMPL_
#ifdef __cplusplus
extern "C" {
#endif
struct stat_mgmt_entry;
typedef int stat_mgmt_foreach_entry_fn(struct stat_mgmt_entry *entry,
void *arg);
/**
* @brief Retrieves the name of the stat group at the specified index.
*
* @param idx The index of the stat group to retrieve.
* @param out_name On success, the name of the requested stat
* group gets written here.
*
* @return 0 on success;
* MGMT_ERR_ENOENT if no group with the specified
* index exists;
* Other MGMT_ERR_[...] code on failure.
*/
int stat_mgmt_impl_get_group(int idx, const char **out_name);
/**
* @brief Applies a function to every entry in the specified stat group.
*
* @param group_name The name of the stat group to operate on.
* @param cb The callback to apply to each stat entry.
* @param arg An optional argument to pass to the callback.
*
* @return 0 on success; MGMT_ERR_[...] code on failure.
*/
int stat_mgmt_impl_foreach_entry(const char *group_name,
stat_mgmt_foreach_entry_fn *cb,
void *arg);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,97 @@
/*
* 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 <misc/util.h>
#include <stats.h>
#include <mgmt/mgmt.h>
#include <stat_mgmt/stat_mgmt.h>
#include <stat_mgmt/stat_mgmt_impl.h>
struct zephyr_stat_mgmt_walk_arg {
stat_mgmt_foreach_entry_fn *cb;
void *arg;
};
int
stat_mgmt_impl_get_group(int idx, const char **out_name)
{
const struct stats_hdr *cur;
int i;
cur = NULL;
for (i = 0; i <= idx; i++) {
cur = stats_group_get_next(cur);
if (cur == NULL) {
return MGMT_ERR_ENOENT;
}
}
*out_name = cur->s_name;
return 0;
}
static int
zephyr_stat_mgmt_walk_cb(struct stats_hdr *hdr, void *arg,
const char *name, uint16_t off)
{
struct zephyr_stat_mgmt_walk_arg *walk_arg;
struct stat_mgmt_entry entry;
void *stat_val;
walk_arg = arg;
stat_val = (uint8_t *)hdr + off;
switch (hdr->s_size) {
case sizeof (uint16_t):
entry.value = *(uint16_t *) stat_val;
break;
case sizeof (uint32_t):
entry.value = *(uint32_t *) stat_val;
break;
case sizeof (uint64_t):
entry.value = *(uint64_t *) stat_val;
break;
default:
return MGMT_ERR_EUNKNOWN;
}
entry.name = name;
return walk_arg->cb(&entry, walk_arg->arg);
}
int
stat_mgmt_impl_foreach_entry(const char *group_name,
stat_mgmt_foreach_entry_fn *cb,
void *arg)
{
struct zephyr_stat_mgmt_walk_arg walk_arg;
struct stats_hdr *hdr;
hdr = stats_group_find(group_name);
if (hdr == NULL) {
return MGMT_ERR_ENOENT;
}
walk_arg = (struct zephyr_stat_mgmt_walk_arg) {
.cb = cb,
.arg = arg,
};
return stats_walk(hdr, zephyr_stat_mgmt_walk_cb, &walk_arg);
}

View File

@ -0,0 +1,160 @@
/*
* 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 <string.h>
#include <stdio.h>
#include "mgmt/mgmt.h"
#include "cborattr/cborattr.h"
#include "stat_mgmt/stat_mgmt.h"
#include "stat_mgmt/stat_mgmt_impl.h"
#include "stat_mgmt_config.h"
static mgmt_handler_fn stat_mgmt_show;
static mgmt_handler_fn stat_mgmt_list;
static struct mgmt_handler stat_mgmt_handlers[] = {
[STAT_MGMT_ID_SHOW] = { stat_mgmt_show, NULL },
[STAT_MGMT_ID_LIST] = { stat_mgmt_list, NULL },
};
#define STAT_MGMT_HANDLER_CNT \
sizeof stat_mgmt_handlers / sizeof stat_mgmt_handlers[0]
static struct mgmt_group stat_mgmt_group = {
.mg_handlers = stat_mgmt_handlers,
.mg_handlers_count = STAT_MGMT_HANDLER_CNT,
.mg_group_id = MGMT_GROUP_ID_STAT,
};
static int
stat_mgmt_cb_encode(struct stat_mgmt_entry *entry, void *arg)
{
CborEncoder *enc;
CborError err;
enc = arg;
err = 0;
err |= cbor_encode_text_stringz(enc, entry->name);
err |= cbor_encode_uint(enc, entry->value);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Command handler: stat show
*/
static int
stat_mgmt_show(struct mgmt_ctxt *ctxt)
{
char stat_name[CONFIG_STAT_MGMT_MAX_NAME_LEN];
CborEncoder map_enc;
CborError err;
int rc;
struct cbor_attr_t attrs[] = {
{
.attribute = "name",
.type = CborAttrTextStringType,
.addr.string = stat_name,
.len = sizeof(stat_name)
},
{ NULL },
};
err = cbor_read_object(&ctxt->it, attrs);
if (err != 0) {
return MGMT_ERR_EINVAL;
}
err |= cbor_encode_text_stringz(&ctxt->encoder, "rc");
err |= cbor_encode_int(&ctxt->encoder, MGMT_ERR_EOK);
err |= cbor_encode_text_stringz(&ctxt->encoder, "name");
err |= cbor_encode_text_stringz(&ctxt->encoder, stat_name);
err |= cbor_encode_text_stringz(&ctxt->encoder, "fields");
err |= cbor_encoder_create_map(&ctxt->encoder, &map_enc,
CborIndefiniteLength);
rc = stat_mgmt_impl_foreach_entry(stat_name, stat_mgmt_cb_encode,
&map_enc);
if (rc != 0) {
return rc;
}
err |= cbor_encoder_close_container(&ctxt->encoder, &map_enc);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
/**
* Command handler: stat list
*/
static int
stat_mgmt_list(struct mgmt_ctxt *ctxt)
{
const char *group_name;
CborEncoder arr_enc;
CborError err;
int rc;
int i;
err = 0;
err |= cbor_encode_text_stringz(&ctxt->encoder, "stat_list");
err |= cbor_encoder_create_array(&ctxt->encoder, &arr_enc,
CborIndefiniteLength);
/* Iterate the list of stat groups, encoding each group's name in the CBOR
* array.
*/
for (i = 0; ; i++) {
rc = stat_mgmt_impl_get_group(i, &group_name);
if (rc == MGMT_ERR_ENOENT) {
/* No more stat groups. */
break;
} else if (rc != 0) {
/* Error. */
return rc;
}
err |= cbor_encode_text_stringz(&ctxt->encoder, group_name);
}
err |= cbor_encoder_close_container(&ctxt->encoder, &arr_enc);
if (err != 0) {
return MGMT_ERR_ENOMEM;
}
return 0;
}
void
stat_mgmt_register_group(void)
{
mgmt_register_group(&stat_mgmt_group);
}

View File

@ -0,0 +1,41 @@
/*
* 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.
*/
#ifndef H_STAT_MGMT_CONFIG_
#define H_STAT_MGMT_CONFIG_
#if defined MYNEWT
#include "syscfg/syscfg.h"
#define STAT_MGMT_MAX_NAME_LEN MYNEWT_VAL(STAT_MGMT_MAX_NAME_LEN)
#elif defined __ZEPHYR__
#define STAT_MGMT_MAX_NAME_LEN CONFIG_STAT_MGMT_MAX_NAME_LEN
#else
/* No direct support for this OS. The application needs to define the above
* settings itself.
*/
#endif
#endif

40
cmd/stat_mgmt/src/stubs.c Normal file
View File

@ -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.
*/
/**
* These stubs get linked in when there is no equivalent OS-specific
* implementation.
*/
#include "mgmt/mgmt.h"
#include "stat_mgmt/stat_mgmt_impl.h"
int __attribute__((weak))
stat_mgmt_impl_get_group(int idx, const char **out_name)
{
return MGMT_ERR_ENOTSUP;
}
int __attribute__((weak))
stat_mgmt_impl_foreach_entry(const char *stat_name,
stat_mgmt_foreach_entry_fn *cb,
void *arg)
{
return MGMT_ERR_ENOTSUP;
}

8
mgmt/CMakeLists.txt Normal file
View File

@ -0,0 +1,8 @@
target_include_directories(MCUMGR INTERFACE
include
port/zephyr/include
)
zephyr_library_sources(
mgmt/src/mgmt.c
)

View File

@ -0,0 +1,58 @@
/*
* 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.
*/
#ifndef H_MGMT_ENDIAN_
#define H_MGMT_ENDIAN_
#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
#endif
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#ifndef ntohs
#define ntohs(x) (x)
#endif
#ifndef htons
#define htons(x) (x)
#endif
#else
/* Little endian. */
#ifndef ntohs
#define ntohs(x) ((uint16_t) \
((((x) & 0xff00) >> 8) | \
(((x) & 0x00ff) << 8)))
#endif
#ifndef htons
#define htons(x) (ntohs(x))
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif

388
mgmt/include/mgmt/mgmt.h Normal file
View File

@ -0,0 +1,388 @@
/*
* 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.
*/
#ifndef H_MGMT_MGMT_
#define H_MGMT_MGMT_
#include <inttypes.h>
#include "cbor.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Opcodes; encoded in first byte of header. */
#define MGMT_OP_READ 0
#define MGMT_OP_READ_RSP 1
#define MGMT_OP_WRITE 2
#define MGMT_OP_WRITE_RSP 3
/**
* The first 64 groups are reserved for system level mcumgr commands.
* Per-user commands are then defined after group 64.
*/
#define MGMT_GROUP_ID_OS 0
#define MGMT_GROUP_ID_IMAGE 1
#define MGMT_GROUP_ID_STAT 2
#define MGMT_GROUP_ID_CONFIG 3
#define MGMT_GROUP_ID_LOG 4
#define MGMT_GROUP_ID_CRASH 5
#define MGMT_GROUP_ID_SPLIT 6
#define MGMT_GROUP_ID_RUN 7
#define MGMT_GROUP_ID_FS 8
#define MGMT_GROUP_ID_PERUSER 64
/**
* mcumgr error codes.
*/
#define MGMT_ERR_EOK 0
#define MGMT_ERR_EUNKNOWN 1
#define MGMT_ERR_ENOMEM 2
#define MGMT_ERR_EINVAL 3
#define MGMT_ERR_ETIMEOUT 4
#define MGMT_ERR_ENOENT 5
#define MGMT_ERR_EBADSTATE 6 /* Current state disallows command. */
#define MGMT_ERR_EMSGSIZE 7 /* Response too large. */
#define MGMT_ERR_ENOTSUP 8 /* Command not supported. */
#define MGMT_ERR_EPERUSER 256
#define MGMT_HDR_SIZE 8
struct mgmt_hdr {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
uint8_t nh_op:3; /* MGMT_OP_[...] */
uint8_t _res1:5;
#endif
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
uint8_t _res1:5;
uint8_t nh_op:3; /* MGMT_OP_[...] */
#endif
uint8_t nh_flags; /* Reserved for future flags */
uint16_t nh_len; /* Length of the payload */
uint16_t nh_group; /* MGMT_GROUP_ID_[...] */
uint8_t nh_seq; /* Sequence number */
uint8_t nh_id; /* Message ID within group */
};
/** @typedef mgmt_alloc_rsp_fn
* @brief Allocates a buffer suitable for holding a response.
*
* If a source buf is provided, its user data is copied into the new buffer.
*
* @param src_buf An optional source buffer to copy user data
* from.
* @param arg Optional streamer argument.
*
* @return Newly-allocated buffer on success
* NULL on failure.
*/
typedef void *mgmt_alloc_rsp_fn(const void *src_buf, void *arg);
/** @typedef mgmt_trim_front_fn
* @brief Trims data from the front of a buffer.
*
* If the amount to trim exceeds the size of the buffer, the buffer is
* truncated to a length of 0.
*
* @param buf The buffer to trim.
* @param len The number of bytes to remove.
* @param arg Optional streamer argument.
*/
typedef void mgmt_trim_front_fn(void *buf, size_t len, void *arg);
/** @typedef mgmt_reset_buf_fn
* @brief Resets a buffer to a length of 0.
*
* The buffer's user data remains, but its payload is cleared.
*
* @param buf The buffer to reset.
* @param arg Optional streamer argument.
*/
typedef void mgmt_reset_buf_fn(void *buf, void *arg);
/** @typedef mgmt_write_at_fn
* @brief Writes data to a CBOR encoder.
*
* Any existing data at the specified offset is overwritten by the new data.
* Any new data that extends past the buffer's current length is appended.
*
* @param writer The encoder to write to.
* @param offset The byte offset to write to,
* @param data The data to write.
* @param len The number of bytes to write.
* @param arg Optional streamer argument.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
typedef int mgmt_write_at_fn(struct cbor_encoder_writer *writer, size_t offset,
const void *data, size_t len, void *arg);
/** @typedef mgmt_init_reader_fn
* @brief Initializes a CBOR reader with the specified buffer.
*
* @param reader The reader to initialize.
* @param buf The buffer to configure the reader with.
* @param arg Optional streamer argument.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
typedef int mgmt_init_reader_fn(struct cbor_decoder_reader *reader, void *buf,
void *arg);
/** @typedef mgmt_init_writer_fn
* @brief Initializes a CBOR writer with the specified buffer.
*
* @param writer The writer to initialize.
* @param buf The buffer to configure the writer with.
* @param arg Optional streamer argument.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
typedef int mgmt_init_writer_fn(struct cbor_encoder_writer *writer, void *buf,
void *arg);
/** @typedef mgmt_init_writer_fn
* @brief Frees the specified buffer.
*
* @param buf The buffer to free.
* @param arg Optional streamer argument.
*/
typedef void mgmt_free_buf_fn(void *buf, void *arg);
/**
* @brief Configuration for constructing a mgmt_streamer object.
*/
struct mgmt_streamer_cfg {
mgmt_alloc_rsp_fn *alloc_rsp;
mgmt_trim_front_fn *trim_front;
mgmt_reset_buf_fn *reset_buf;
mgmt_write_at_fn *write_at;
mgmt_init_reader_fn *init_reader;
mgmt_init_writer_fn *init_writer;
mgmt_free_buf_fn *free_buf;
};
/**
* @brief Decodes requests and encodes responses for any mcumgr protocol.
*/
struct mgmt_streamer {
const struct mgmt_streamer_cfg *cfg;
void *cb_arg;
struct cbor_decoder_reader *reader;
struct cbor_encoder_writer *writer;
};
/**
* @brief Context required by command handlers for parsing requests and writing
* responses.
*/
struct mgmt_ctxt {
struct CborEncoder encoder;
struct CborParser parser;
struct CborValue it;
};
/** @typedef mgmt_handler_fn
* @brief Processes a request and writes the corresponding response.
*
* A separate handler is required for each supported op-ID pair.
*
* @param ctxt The mcumgr context to use.
*
* @return 0 if a response was successfully encoded,
* MGMT_ERR_[...] code on failure.
*/
typedef int mgmt_handler_fn(struct mgmt_ctxt *ctxt);
/**
* @brief Read handler and write handler for a single command ID.
*/
struct mgmt_handler {
mgmt_handler_fn *mh_read;
mgmt_handler_fn *mh_write;
};
/**
* @brief A collection of handlers for an entire command group.
*/
struct mgmt_group {
/** Points to the next group in the list. */
struct mgmt_group *mg_next;
/** Array of handlers; one entry per command ID. */
const struct mgmt_handler *mg_handlers;
uint16_t mg_handlers_count;
/* The numeric ID of this group. */
uint16_t mg_group_id;
};
/**
* @brief Uses the specified streamer to allocates a response buffer.
*
* If a source buf is provided, its user data is copied into the new buffer.
*
* @param streamer The streamer providing the callback.
* @param src_buf An optional source buffer to copy user data
* from.
*
* @return Newly-allocated buffer on success
* NULL on failure.
*/
void *mgmt_streamer_alloc_rsp(struct mgmt_streamer *streamer,
const void *src_buf);
/**
* @brief Uses the specified streamer to trim data from the front of a buffer.
*
* If the amount to trim exceeds the size of the buffer, the buffer is
* truncated to a length of 0.
*
* @param streamer The streamer providing the callback.
* @param buf The buffer to trim.
* @param len The number of bytes to remove.
*/
void mgmt_streamer_trim_front(struct mgmt_streamer *streamer, void *buf,
size_t len);
/**
* @brief Uses the specified streamer to reset a buffer to a length of 0.
*
* The buffer's user data remains, but its payload is cleared.
*
* @param streamer The streamer providing the callback.
* @param buf The buffer to reset.
*/
void mgmt_streamer_reset_buf(struct mgmt_streamer *streamer, void *buf);
/**
* @brief Uses the specified streamer to write data to a CBOR encoder.
*
* Any existing data at the specified offset is overwritten by the new data.
* Any new data that extends past the buffer's current length is appended.
*
* @param streamer The streamer providing the callback.
* @param writer The encoder to write to.
* @param offset The byte offset to write to,
* @param data The data to write.
* @param len The number of bytes to write.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int mgmt_streamer_write_at(struct mgmt_streamer *streamer, size_t offset,
const void *data, int len);
/**
* @brief Uses the specified streamer to initialize a CBOR reader.
*
* @param streamer The streamer providing the callback.
* @param reader The reader to initialize.
* @param buf The buffer to configure the reader with.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int mgmt_streamer_init_reader(struct mgmt_streamer *streamer, void *buf);
/**
* @brief Uses the specified streamer to initializes a CBOR writer.
*
* @param streamer The streamer providing the callback.
* @param writer The writer to initialize.
* @param buf The buffer to configure the writer with.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int mgmt_streamer_init_writer(struct mgmt_streamer *streamer, void *buf);
/**
* @brief Uses the specified streamer to free a buffer.
*
* @param streamer The streamer providing the callback.
* @param buf The buffer to free.
*/
void mgmt_streamer_free_buf(struct mgmt_streamer *streamer, void *buf);
/**
* @brief Registers a full command group.
*
* @param group The group to register.
*/
void mgmt_register_group(struct mgmt_group *group);
/**
* @brief Finds a registered command handler.
*
* @param group_id The group of the command to find.
* @param command_id The ID of the command to find.
*
* @return The requested command handler on success;
* NULL on failure.
*/
const struct mgmt_handler *mgmt_find_handler(uint16_t group_id,
uint16_t command_id);
/**
* @brief Encodes a response status into the specified management context.
*
* @param ctxt The management context to encode into.
* @param status The response status to write.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int mgmt_write_rsp_status(struct mgmt_ctxt *ctxt, int status);
/**
* @brief Initializes a management context object with the specified streamer.
*
* @param ctxt The context object to initialize.
* @param streamer The streamer that will be used with the
* context.
*
* @return 0 on success, MGMT_ERR_[...] code on failure.
*/
int mgmt_ctxt_init(struct mgmt_ctxt *ctxt, struct mgmt_streamer *streamer);
/**
* @brief Converts a CBOR status code to a MGMT_ERR_[...] code.
*
* @param cbor_status The CBOR status code to convert.
*
* @return The corresponding MGMT_ERR_[,,,] code.
*/
int mgmt_err_from_cbor(int cbor_status);
/**
* @brief Byte-swaps an mcumgr header from network to host byte order.
*
* @param hdr The mcumgr header to byte-swap.
*/
void mgmt_ntoh_hdr(struct mgmt_hdr *hdr);
/**
* @brief Byte-swaps an mcumgr header from host to network byte order.
*
* @param hdr The mcumgr header to byte-swap.
*/
void mgmt_hton_hdr(struct mgmt_hdr *hdr);
#ifdef __cplusplus
}
#endif
#endif /* MGMT_MGMT_H_ */

170
mgmt/src/mgmt.c Normal file
View File

@ -0,0 +1,170 @@
/*
* 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 <string.h>
#include "cbor.h"
#include "mgmt/endian.h"
#include "mgmt/mgmt.h"
static struct mgmt_group *mgmt_group_list;
static struct mgmt_group *mgmt_group_list_end;
void *
mgmt_streamer_alloc_rsp(struct mgmt_streamer *streamer, const void *req)
{
return streamer->cfg->alloc_rsp(req, streamer->cb_arg);
}
void
mgmt_streamer_trim_front(struct mgmt_streamer *streamer, void *buf, size_t len)
{
streamer->cfg->trim_front(buf, len, streamer->cb_arg);
}
void
mgmt_streamer_reset_buf(struct mgmt_streamer *streamer, void *buf)
{
streamer->cfg->reset_buf(buf, streamer->cb_arg);
}
int
mgmt_streamer_write_at(struct mgmt_streamer *streamer, size_t offset,
const void *data, int len)
{
return streamer->cfg->write_at(streamer->writer, offset, data, len,
streamer->cb_arg);
}
int
mgmt_streamer_init_reader(struct mgmt_streamer *streamer, void *buf)
{
return streamer->cfg->init_reader(streamer->reader, buf, streamer->cb_arg);
}
int
mgmt_streamer_init_writer(struct mgmt_streamer *streamer, void *buf)
{
return streamer->cfg->init_writer(streamer->writer, buf, streamer->cb_arg);
}
void
mgmt_streamer_free_buf(struct mgmt_streamer *streamer, void *buf)
{
streamer->cfg->free_buf(buf, streamer->cb_arg);
}
void
mgmt_register_group(struct mgmt_group *group)
{
if (mgmt_group_list_end == NULL) {
mgmt_group_list = group;
} else {
mgmt_group_list_end->mg_next = group;
}
mgmt_group_list_end = group;
}
static struct mgmt_group *
mgmt_find_group(uint16_t group_id)
{
struct mgmt_group *group;
for (group = mgmt_group_list; group != NULL; group = group->mg_next) {
if (group->mg_group_id == group_id) {
return group;
}
}
return NULL;
}
const struct mgmt_handler *
mgmt_find_handler(uint16_t group_id, uint16_t command_id)
{
const struct mgmt_group *group;
group = mgmt_find_group(group_id);
if (group == NULL) {
return NULL;
}
if (command_id >= group->mg_handlers_count) {
return NULL;
}
return &group->mg_handlers[command_id];
}
int
mgmt_write_rsp_status(struct mgmt_ctxt *ctxt, int errcode)
{
int rc;
rc = cbor_encode_text_stringz(&ctxt->encoder, "rc");
if (rc != 0) {
return rc;
}
rc = cbor_encode_int(&ctxt->encoder, errcode);
if (rc != 0) {
return rc;
}
return 0;
}
int
mgmt_err_from_cbor(int cbor_status)
{
switch (cbor_status) {
case CborNoError: return MGMT_ERR_EOK;
case CborErrorOutOfMemory: return MGMT_ERR_ENOMEM;
default: return MGMT_ERR_EUNKNOWN;
}
}
int
mgmt_ctxt_init(struct mgmt_ctxt *ctxt, struct mgmt_streamer *streamer)
{
int rc;
rc = cbor_parser_cust_reader_init(streamer->reader, 0, &ctxt->parser,
&ctxt->it);
if (rc != CborNoError) {
return mgmt_err_from_cbor(rc);
}
cbor_encoder_cust_writer_init(&ctxt->encoder, streamer->writer, 0);
return 0;
}
void
mgmt_ntoh_hdr(struct mgmt_hdr *hdr)
{
hdr->nh_len = ntohs(hdr->nh_len);
hdr->nh_group = ntohs(hdr->nh_group);
}
void
mgmt_hton_hdr(struct mgmt_hdr *hdr)
{
hdr->nh_len = htons(hdr->nh_len);
hdr->nh_group = htons(hdr->nh_group);
}

7
smp/CMakeLists.txt Normal file
View File

@ -0,0 +1,7 @@
target_include_directories(MCUMGR INTERFACE
include
)
zephyr_library_sources(
smp/src/smp.c
)

91
smp/include/smp/smp.h Normal file
View File

@ -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_ */

379
smp/src/smp.c Normal file
View File

@ -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;
}

View File

@ -0,0 +1,50 @@
# SMP over Bluetooth
This document specifies how the mcumgr Simple Management Procotol (SMP) is
transmitted over Bluetooth.
## Overview
All SMP communication utilizes a single GATT characteristic. An SMP request is
sent in the form of either 1) a GATT Write Command, or 2) a GATT Write Without
Response command. An SMP response is sent in the form of a GATT Notification
specifying the same characteristic that was written.
If an SMP request or response is too large to fit in a single GATT command, the
sender fragments it across several commands. No additional framing is
introduced when a request or response is fragmented; the payload is simply
split among several commands. Since Bluetooth guarantees ordered delivery of
packets, the SMP header in the first fragment contains sufficient information
for reassembly.
## Services
### SMP service
UUID: `8D53DC1D-1DB7-4CD3-868B-8A527460AA84`
### Characteristics
#### SMP Characteristic
| Field | Value |
| ----- | ----------------------------------------------------------------- |
| Name | SMP |
| Description | Used for both SMP requests and responses. |
| Read | Excluded |
| Write | Mandatory |
| WriteWithoutResponse | Mandatory |
| SignedWrite | Excluded |
| Notify | Mandatory |
| Indicate | Excluded |
| WritableAuxiliaries | Excluded |
| Broadcast | Excluded |
| ExtendedProperties | |
As indicated, SMP requests can be sent in the form of either a Write or a Write
Without Response. The Write Without Response form is generally preferred, as
an application-layer response is always sent in the form of a Notification.
The regular Write form is accepted in case the client requires a GATT response
to initiate pairing.
Security for this characteristic is optional.

56
transport/smp-console.md Normal file
View File

@ -0,0 +1,56 @@
# SMP over console
This document specifies how the mcumgr Simple Management Procotol (SMP) is
transmitted over text consoles.
## Overview
Mcumgr packets sent over serial are fragmented into frames of 128 bytes or
fewer.
The initial frame in a packet has the following format:
```
offset 0: 0x06 0x09
=== Begin base64 encoding ===
offset 2: <16-bit packet-length>
offset ?: <body>
offset ?: <crc16> (if final frame)
=== End base64 encoding ===
offset ?: 0x0a (newline)
```
All subsequent frames have the following format:
```
offset 0: 0x04 0x14
=== Begin base64 encoding ===
offset 2: <body>
offset ?: <crc16> (if final frame)
=== End base64 encoding ===
offset ?: 0x0a (newline)
```
All integers are represented in big-endian. The packet fields are described
below:
| Field | Description |
| ----- | ----------- |
| 0x06 0x09 | Byte pair indicating the start of a packet. |
| 0x04 0x14 | Byte pair indicating the start of a continuation frame. |
| Packet length | The combined total length of the *unencoded* body. |
| Body | The actual SMP data (i.e., 8-byte header and CBOR key-value map). |
| CRC16 | A CRC16 of the *unencoded* body of the entire packet. This field is only present in the final frame of a packet. |
| Newline | A 0x0a byte; terminates a frame. |
The packet is fully received when <packet-length> bytes of body has been
received.
## CRC details
The CRC16 should be calculated with the following parameters:
| Field | Value |
| ------------- | ------------- |
| Polynomial | 0x1021 |
| Initial Value | 0 |

8
util/CMakeLists.txt Normal file
View File

@ -0,0 +1,8 @@
target_include_directories(MCUMGR INTERFACE
include
)
zephyr_library_sources(
util/src/mcumgr_util.c
)

View File

@ -0,0 +1,55 @@
/*
* 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.
*/
#ifndef H_MCUMGR_UTIL_
#define H_MCUMGR_UTIL_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Converts an unsigned long long to a null-terminated string.
*
* @param val The source number to convert.
* @param dst_max_len The size, in bytes, of the destination buffer.
* @param dst The destination buffer.
*
* @return The length of the resulting string on success;
* -1 if the buffer is too small.
*/
int ull_to_s(unsigned long long val, int dst_max_len, char *dst);
/**
* @brief Converts a long long to a null-terminated string.
*
* @param val The source number to convert.
* @param dst_max_len The size, in bytes, of the destination buffer.
* @param dst The destination buffer.
*
* @return The length of the resulting string on success;
* -1 if the buffer is too small.
*/
int ll_to_s(long long val, int dst_max_len, char *dst);
#ifdef __cplusplus
}
#endif
#endif

61
util/src/mcumgr_util.c Normal file
View File

@ -0,0 +1,61 @@
#include <stdbool.h>
#include "util/mcumgr_util.h"
int
ull_to_s(unsigned long long val, int dst_max_len, char *dst)
{
unsigned long copy;
int digit;
int off;
int len;
/* First, calculate the length of the resulting string. */
copy = val;
for (len = 0; copy != 0; len++) {
copy /= 10;
}
/* A value of 0 still requires one character ("0"). */
if (len == 0) {
len = 1;
}
/* Ensure the buffer can accommodate the string and terminator. */
if (len >= dst_max_len - 1) {
return -1;
}
/* Encode the string from right to left. */
off = len;
dst[off--] = '\0';
do {
digit = val % 10;
dst[off--] = '0' + digit;
val /= 10;
} while (val > 0);
return len;
}
int
ll_to_s(long long val, int dst_max_len, char *dst)
{
unsigned long long ull;
if (val < 0) {
if (dst_max_len < 1) {
return -1;
}
dst[0] = '-';
dst_max_len--;
dst++;
ull = -val;
} else {
ull = val;
}
return ull_to_s(ull, dst_max_len, dst);
}