1 /*
2 * Copyright (c) 2024 Alexandre Bailon
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <errno.h>
8
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_DECLARE(coap);
11
12 #include "coap_utils.h"
13 #include <openthread/platform/radio.h>
14
15 static uint8_t coap_buf[COAP_MAX_BUF_SIZE];
16 static uint8_t coap_dev_id[COAP_DEVICE_ID_SIZE];
17
18 #ifdef CONFIG_OT_COAP_SAMPLE_SERVER
coap_default_handler(void * context,otMessage * message,const otMessageInfo * message_info)19 static void coap_default_handler(void *context, otMessage *message,
20 const otMessageInfo *message_info)
21 {
22 ARG_UNUSED(context);
23 ARG_UNUSED(message);
24 ARG_UNUSED(message_info);
25
26 LOG_INF("Received CoAP message that does not match any request "
27 "or resource");
28 }
29 #endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
30
coap_req_send(const char * addr,const char * uri,uint8_t * buf,int len,otCoapResponseHandler handler,void * ctx,otCoapCode code)31 static int coap_req_send(const char *addr, const char *uri, uint8_t *buf, int len,
32 otCoapResponseHandler handler, void *ctx, otCoapCode code)
33 {
34 otInstance *ot;
35 otMessage *msg;
36 otMessageInfo msg_info;
37 otError err;
38 int ret;
39
40 ot = openthread_get_default_instance();
41 if (!ot) {
42 LOG_ERR("Failed to get an OpenThread instance");
43 return -ENODEV;
44 }
45
46 memset(&msg_info, 0, sizeof(msg_info));
47 otIp6AddressFromString(addr, &msg_info.mPeerAddr);
48 msg_info.mPeerPort = OT_DEFAULT_COAP_PORT;
49
50 msg = otCoapNewMessage(ot, NULL);
51 if (!msg) {
52 LOG_ERR("Failed to allocate a new CoAP message");
53 return -ENOMEM;
54 }
55
56 otCoapMessageInit(msg, OT_COAP_TYPE_CONFIRMABLE, code);
57
58 err = otCoapMessageAppendUriPathOptions(msg, uri);
59 if (err != OT_ERROR_NONE) {
60 LOG_ERR("Failed to append uri-path: %s", otThreadErrorToString(err));
61 ret = -EBADMSG;
62 goto err;
63 }
64
65 err = otCoapMessageSetPayloadMarker(msg);
66 if (err != OT_ERROR_NONE) {
67 LOG_ERR("Failed to set payload marker: %s", otThreadErrorToString(err));
68 ret = -EBADMSG;
69 goto err;
70 }
71
72 err = otMessageAppend(msg, buf, len);
73 if (err != OT_ERROR_NONE) {
74 LOG_ERR("Failed to set append payload to response: %s", otThreadErrorToString(err));
75 ret = -EBADMSG;
76 goto err;
77 }
78
79 err = otCoapSendRequest(ot, msg, &msg_info, handler, ctx);
80 if (err != OT_ERROR_NONE) {
81 LOG_ERR("Failed to send the request: %s", otThreadErrorToString(err));
82 ret = -EIO; /* Find a better error code */
83 goto err;
84 }
85
86 return 0;
87
88 err:
89 otMessageFree(msg);
90 return ret;
91 }
92
coap_put_req_send(const char * addr,const char * uri,uint8_t * buf,int len,otCoapResponseHandler handler,void * ctx)93 int coap_put_req_send(const char *addr, const char *uri, uint8_t *buf, int len,
94 otCoapResponseHandler handler, void *ctx)
95 {
96 return coap_req_send(addr, uri, buf, len, handler, ctx, OT_COAP_CODE_PUT);
97 }
98
coap_get_req_send(const char * addr,const char * uri,uint8_t * buf,int len,otCoapResponseHandler handler,void * ctx)99 int coap_get_req_send(const char *addr, const char *uri, uint8_t *buf, int len,
100 otCoapResponseHandler handler, void *ctx)
101 {
102 return coap_req_send(addr, uri, buf, len, handler, ctx, OT_COAP_CODE_GET);
103 }
104
coap_resp_send(otMessage * req,const otMessageInfo * req_info,uint8_t * buf,int len)105 int coap_resp_send(otMessage *req, const otMessageInfo *req_info, uint8_t *buf, int len)
106 {
107 otInstance *ot;
108 otMessage *resp;
109 otCoapCode resp_code;
110 otCoapType resp_type;
111 otError err;
112 int ret;
113
114 ot = openthread_get_default_instance();
115 if (!ot) {
116 LOG_ERR("Failed to get an OpenThread instance");
117 return -ENODEV;
118 }
119
120 resp = otCoapNewMessage(ot, NULL);
121 if (!resp) {
122 LOG_ERR("Failed to allocate a new CoAP message");
123 return -ENOMEM;
124 }
125
126 switch (otCoapMessageGetType(req)) {
127 case OT_COAP_TYPE_CONFIRMABLE:
128 resp_type = OT_COAP_TYPE_ACKNOWLEDGMENT;
129 break;
130 case OT_COAP_TYPE_NON_CONFIRMABLE:
131 resp_type = OT_COAP_TYPE_NON_CONFIRMABLE;
132 break;
133 default:
134 LOG_ERR("Invalid message type");
135 ret = -EINVAL;
136 goto err;
137 }
138
139 switch (otCoapMessageGetCode(req)) {
140 case OT_COAP_CODE_GET:
141 resp_code = OT_COAP_CODE_CONTENT;
142 break;
143 case OT_COAP_CODE_PUT:
144 resp_code = OT_COAP_CODE_CHANGED;
145 break;
146 default:
147 LOG_ERR("Invalid message code");
148 ret = -EINVAL;
149 goto err;
150 }
151
152 err = otCoapMessageInitResponse(resp, req, resp_type, resp_code);
153 if (err != OT_ERROR_NONE) {
154 LOG_ERR("Failed to initialize the response: %s", otThreadErrorToString(err));
155 ret = -EBADMSG;
156 goto err;
157 }
158
159 err = otCoapMessageSetPayloadMarker(resp);
160 if (err != OT_ERROR_NONE) {
161 LOG_ERR("Failed to set payload marker: %s", otThreadErrorToString(err));
162 ret = -EBADMSG;
163 goto err;
164 }
165
166 err = otMessageAppend(resp, buf, len);
167 if (err != OT_ERROR_NONE) {
168 LOG_ERR("Failed to set append payload to response: %s", otThreadErrorToString(err));
169 ret = -EBADMSG;
170 goto err;
171 }
172
173 err = otCoapSendResponse(ot, resp, req_info);
174 if (err != OT_ERROR_NONE) {
175 LOG_ERR("Failed to send the response: %s", otThreadErrorToString(err));
176 ret = -EIO;
177 goto err;
178 }
179
180 return 0;
181
182 err:
183 otMessageFree(resp);
184 return ret;
185 }
186
coap_req_handler(void * ctx,otMessage * msg,const otMessageInfo * msg_info,coap_req_handler_put put_fn,coap_req_handler_get get_fn)187 int coap_req_handler(void *ctx, otMessage *msg, const otMessageInfo *msg_info,
188 coap_req_handler_put put_fn, coap_req_handler_get get_fn)
189 {
190 otCoapCode msg_code = otCoapMessageGetCode(msg);
191 otCoapType msg_type = otCoapMessageGetType(msg);
192 int ret;
193
194 if (msg_type != OT_COAP_TYPE_CONFIRMABLE && msg_type != OT_COAP_TYPE_NON_CONFIRMABLE) {
195 return -EINVAL;
196 }
197
198 if (msg_code == OT_COAP_CODE_PUT && put_fn) {
199 int len = otMessageGetLength(msg) - otMessageGetOffset(msg);
200
201 otMessageRead(msg, otMessageGetOffset(msg), coap_buf, len);
202 ret = put_fn(ctx, coap_buf, len);
203 if (ret) {
204 return ret;
205 }
206
207 if (msg_type == OT_COAP_TYPE_CONFIRMABLE) {
208 ret = get_fn(ctx, msg, msg_info);
209 }
210
211 return ret;
212 }
213
214 if (msg_code == OT_COAP_CODE_GET) {
215 return get_fn(ctx, msg, msg_info);
216 }
217
218 return -EINVAL;
219 }
220
coap_device_id(void)221 const char *coap_device_id(void)
222 {
223 otInstance *ot = openthread_get_default_instance();
224 otExtAddress eui64;
225 int i;
226
227 if (coap_dev_id[0] != '\0') {
228 return coap_dev_id;
229 }
230
231 otPlatRadioGetIeeeEui64(ot, eui64.m8);
232 for (i = 0; i < 8; i++) {
233 if (i * 2 >= COAP_DEVICE_ID_SIZE) {
234 i = COAP_DEVICE_ID_SIZE - 1;
235 break;
236 }
237 sprintf(coap_dev_id + i * 2, "%02x", eui64.m8[i]);
238 }
239 coap_dev_id[i * 2] = '\0';
240
241 return coap_dev_id;
242 }
243
coap_get_data(otMessage * msg,void * buf,int * len)244 int coap_get_data(otMessage *msg, void *buf, int *len)
245 {
246 int coap_len = otMessageGetLength(msg) - otMessageGetOffset(msg);
247
248 if (coap_len > *len) {
249 return -ENOMEM;
250 }
251
252 *len = coap_len;
253 otMessageRead(msg, otMessageGetOffset(msg), buf, coap_len);
254
255 return 0;
256 }
257
coap_init(void)258 int coap_init(void)
259 {
260 otError err;
261 otInstance *ot;
262
263 #ifdef CONFIG_OT_COAP_SAMPLE_SERVER
264 LOG_INF("Initializing OpenThread CoAP server");
265 #else /* CONFIG_OT_COAP_SAMPLE_SERVER */
266 LOG_INF("Initializing OpenThread CoAP client");
267 #endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
268 ot = openthread_get_default_instance();
269 if (!ot) {
270 LOG_ERR("Failed to get an OpenThread instance");
271 return -ENODEV;
272 }
273
274 #ifdef CONFIG_OT_COAP_SAMPLE_SERVER
275 otCoapSetDefaultHandler(ot, coap_default_handler, NULL);
276 #endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
277
278 err = otCoapStart(ot, OT_DEFAULT_COAP_PORT);
279 if (err != OT_ERROR_NONE) {
280 LOG_ERR("Cannot start CoAP: %s", otThreadErrorToString(err));
281 return -EBADMSG;
282 }
283
284 return 0;
285 }
286