1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stddef.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <zcbor_common.h>
12 #include <zcbor_decode.h>
13 #include <zcbor_encode.h>
14 
15 #include <zephyr/kernel.h>
16 #include <zephyr/device.h>
17 #include <zephyr/sys/byteorder.h>
18 #include <zephyr/net_buf.h>
19 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
20 #include <zephyr/mgmt/mcumgr/smp/smp.h>
21 #include <zephyr/mgmt/mcumgr/smp/smp_client.h>
22 #include <zephyr/mgmt/mcumgr/transport/smp.h>
23 #include <zephyr/mgmt/mcumgr/grp/os_mgmt/os_mgmt.h>
24 #include <zephyr/mgmt/mcumgr/grp/os_mgmt/os_mgmt_client.h>
25 
26 #include <mgmt/mcumgr/util/zcbor_bulk.h>
27 #include <mgmt/mcumgr/transport/smp_internal.h>
28 
29 #include <zephyr/logging/log.h>
30 LOG_MODULE_REGISTER(mcumgr_grp_os_client, CONFIG_MCUMGR_GRP_OS_CLIENT_LOG_LEVEL);
31 
32 static struct os_mgmt_client *active_client;
33 static K_SEM_DEFINE(mcummgr_os_client_grp_sem, 0, 1);
34 static K_MUTEX_DEFINE(mcummgr_os_client_grp_mutex);
35 
os_mgmt_client_init(struct os_mgmt_client * client,struct smp_client_object * smp_client)36 void os_mgmt_client_init(struct os_mgmt_client *client, struct smp_client_object *smp_client)
37 {
38 	client->smp_client = smp_client;
39 }
40 
41 #ifdef CONFIG_MCUMGR_GRP_OS_CLIENT_RESET
42 
reset_res_fn(struct net_buf * nb,void * user_data)43 static int reset_res_fn(struct net_buf *nb, void *user_data)
44 {
45 	if (!nb) {
46 		active_client->status = MGMT_ERR_ETIMEOUT;
47 	} else {
48 		active_client->status = MGMT_ERR_EOK;
49 	}
50 	k_sem_give(user_data);
51 	return 0;
52 }
53 
os_mgmt_client_reset(struct os_mgmt_client * client)54 int os_mgmt_client_reset(struct os_mgmt_client *client)
55 {
56 	struct net_buf *nb;
57 	int rc;
58 
59 	k_mutex_lock(&mcummgr_os_client_grp_mutex, K_FOREVER);
60 	active_client = client;
61 	/* allocate buffer */
62 	nb = smp_client_buf_allocation(active_client->smp_client, MGMT_GROUP_ID_OS,
63 				       OS_MGMT_ID_RESET, MGMT_OP_WRITE, SMP_MCUMGR_VERSION_1);
64 	if (!nb) {
65 		active_client->status = MGMT_ERR_ENOMEM;
66 		goto end;
67 	}
68 	k_sem_reset(&mcummgr_os_client_grp_sem);
69 	rc = smp_client_send_cmd(active_client->smp_client, nb, reset_res_fn,
70 				 &mcummgr_os_client_grp_sem, CONFIG_SMP_CMD_DEFAULT_LIFE_TIME);
71 	if (rc) {
72 		active_client->status = rc;
73 		smp_packet_free(nb);
74 		goto end;
75 	}
76 	/* Wait for process end update event */
77 	k_sem_take(&mcummgr_os_client_grp_sem, K_FOREVER);
78 end:
79 	rc = active_client->status;
80 	active_client = NULL;
81 	k_mutex_unlock(&mcummgr_os_client_grp_mutex);
82 	return rc;
83 }
84 
85 #endif /* CONFIG_MCUMGR_GRP_OS_CLIENT_RESET */
86 
87 #ifdef CONFIG_MCUMGR_GRP_OS_CLIENT_ECHO
88 
echo_res_fn(struct net_buf * nb,void * user_data)89 static int echo_res_fn(struct net_buf *nb, void *user_data)
90 {
91 	struct zcbor_string val = {0};
92 	zcbor_state_t zsd[CONFIG_MCUMGR_SMP_CBOR_MAX_DECODING_LEVELS + 2];
93 	size_t decoded;
94 	bool ok;
95 	int rc;
96 	struct zcbor_map_decode_key_val echo_response[] = {
97 		ZCBOR_MAP_DECODE_KEY_DECODER("r", zcbor_tstr_decode, &val)
98 		};
99 
100 	if (!nb) {
101 		LOG_ERR("Echo command timeout");
102 		active_client->status = MGMT_ERR_ETIMEOUT;
103 		goto end;
104 	}
105 
106 	/* Init ZCOR decoder state */
107 	zcbor_new_decode_state(zsd, ARRAY_SIZE(zsd), nb->data, nb->len, 1, NULL, 0);
108 
109 	ok = zcbor_map_decode_bulk(zsd, echo_response, ARRAY_SIZE(echo_response), &decoded) == 0;
110 
111 	if (!ok) {
112 		active_client->status = MGMT_ERR_ECORRUPT;
113 		goto end;
114 	}
115 	active_client->status = MGMT_ERR_EOK;
116 end:
117 	rc = active_client->status;
118 	k_sem_give(user_data);
119 	return rc;
120 }
121 
os_mgmt_client_echo(struct os_mgmt_client * client,const char * echo_string,size_t max_len)122 int os_mgmt_client_echo(struct os_mgmt_client *client, const char *echo_string, size_t max_len)
123 {
124 	struct net_buf *nb;
125 	int rc;
126 	bool ok;
127 	zcbor_state_t zse[CONFIG_MCUMGR_SMP_CBOR_MAX_DECODING_LEVELS];
128 
129 	k_mutex_lock(&mcummgr_os_client_grp_mutex, K_FOREVER);
130 	active_client = client;
131 	nb = smp_client_buf_allocation(active_client->smp_client, MGMT_GROUP_ID_OS, OS_MGMT_ID_ECHO,
132 				       MGMT_OP_WRITE, SMP_MCUMGR_VERSION_1);
133 	if (!nb) {
134 		rc = active_client->status = MGMT_ERR_ENOMEM;
135 		goto end;
136 	}
137 
138 	zcbor_new_encode_state(zse, ARRAY_SIZE(zse), nb->data + nb->len, net_buf_tailroom(nb), 0);
139 
140 	ok = zcbor_map_start_encode(zse, 2) &&
141 	     zcbor_tstr_put_lit(zse, "d") &&
142 	     zcbor_tstr_put_term(zse, echo_string, max_len) &&
143 	     zcbor_map_end_encode(zse, 2);
144 
145 	if (!ok) {
146 		smp_packet_free(nb);
147 		rc = active_client->status = MGMT_ERR_ENOMEM;
148 		goto end;
149 	}
150 
151 	nb->len = zse->payload - nb->data;
152 
153 	LOG_DBG("Echo Command packet len %d", nb->len);
154 	k_sem_reset(&mcummgr_os_client_grp_sem);
155 	rc = smp_client_send_cmd(active_client->smp_client, nb, echo_res_fn,
156 				 &mcummgr_os_client_grp_sem, CONFIG_SMP_CMD_DEFAULT_LIFE_TIME);
157 	if (rc) {
158 		smp_packet_free(nb);
159 	} else {
160 		k_sem_take(&mcummgr_os_client_grp_sem, K_FOREVER);
161 		/* Take response status */
162 		rc = active_client->status;
163 	}
164 end:
165 	active_client = NULL;
166 	k_mutex_unlock(&mcummgr_os_client_grp_mutex);
167 	return rc;
168 }
169 #endif
170