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