1 /*
2  * Copyright (c) 2024 Alexandre Bailon
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/gpio.h>
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_DECLARE(coap);
10 
11 #include "coap_utils.h"
12 #include "led.h"
13 
14 #ifdef CONFIG_OT_COAP_SAMPLE_SERVER
15 struct led_rsc_data {
16 	const struct gpio_dt_spec gpio;
17 	int state;
18 };
19 
20 struct led_rsc_ctx {
21 	struct led_rsc_data *led;
22 	int count;
23 };
24 #endif
25 
26 static const struct json_obj_descr json_led_state_descr[] = {
27 	JSON_OBJ_DESCR_PRIM(struct json_led_state, led_id, JSON_TOK_NUMBER),
28 	JSON_OBJ_DESCR_PRIM(struct json_led_state, state, JSON_TOK_NUMBER),
29 };
30 
31 static const struct json_obj_descr json_led_get_descr[] = {
32 	JSON_OBJ_DESCR_PRIM(struct json_led_get, device_id, JSON_TOK_STRING),
33 	JSON_OBJ_DESCR_OBJ_ARRAY(struct json_led_get, leds, JSON_MAX_LED, count,
34 				 json_led_state_descr, ARRAY_SIZE(json_led_state_descr)),
35 };
36 
37 K_SEM_DEFINE(led_get_sem, 0, 1);
38 
39 #ifdef CONFIG_OT_COAP_SAMPLE_SERVER
led_init(otCoapResource * rsc)40 static int led_init(otCoapResource *rsc)
41 {
42 	struct led_rsc_ctx *led_ctx = rsc->mContext;
43 	int ret;
44 
45 	LOG_INF("Initializing the LED");
46 	for (int i = 0; i < led_ctx->count; i++) {
47 		struct led_rsc_data *led = &led_ctx->led[i];
48 
49 		if (!gpio_is_ready_dt(&led->gpio)) {
50 			return -ENODEV;
51 		}
52 
53 		ret = gpio_pin_configure_dt(&led->gpio, GPIO_OUTPUT);
54 		if (ret) {
55 			LOG_ERR("Failed to configure the GPIO");
56 			return ret;
57 		}
58 	}
59 
60 	return 0;
61 }
62 
led_handler_put(void * ctx,uint8_t * buf,int size)63 static int led_handler_put(void *ctx, uint8_t *buf, int size)
64 {
65 	struct json_led_state led_data;
66 	struct led_rsc_ctx *led_ctx = ctx;
67 	struct led_rsc_data *led;
68 	int ret = -EINVAL;
69 
70 	json_obj_parse(buf, size, json_led_state_descr, ARRAY_SIZE(json_led_state_descr),
71 		       &led_data);
72 
73 	if (led_data.led_id >= led_ctx->count) {
74 		LOG_ERR("Invalid led id: %x", led_data.led_id);
75 		return -EINVAL;
76 	}
77 	led = &led_ctx->led[led_data.led_id];
78 
79 	switch (led_data.state) {
80 	case LED_MSG_STATE_ON:
81 		ret = gpio_pin_set_dt(&led->gpio, 1);
82 		led->state = 1;
83 		break;
84 	case LED_MSG_STATE_OFF:
85 		ret = gpio_pin_set_dt(&led->gpio, 0);
86 		led->state = 0;
87 		break;
88 	case LED_MSG_STATE_TOGGLE:
89 		led->state = 1 - led->state;
90 		ret = gpio_pin_set_dt(&led->gpio, led->state);
91 		break;
92 	default:
93 		LOG_ERR("Set an unsupported LED state: %x", led_data.state);
94 	}
95 
96 	return ret;
97 }
98 
led_handler_get(void * ctx,otMessage * msg,const otMessageInfo * msg_info)99 static int led_handler_get(void *ctx, otMessage *msg, const otMessageInfo *msg_info)
100 {
101 	uint8_t buf[COAP_MAX_BUF_SIZE];
102 	struct led_rsc_ctx *led_ctx = ctx;
103 
104 	struct json_led_get led_data = {
105 		.device_id = coap_device_id(),
106 	};
107 
108 	for (int i = 0; i < led_ctx->count; i++) {
109 		led_data.leds[i].led_id = i;
110 		led_data.leds[i].state = led_ctx->led[i].state;
111 	}
112 	led_data.count = led_ctx->count;
113 
114 	json_obj_encode_buf(json_led_get_descr, ARRAY_SIZE(json_led_get_descr), &led_data, buf,
115 			    COAP_MAX_BUF_SIZE);
116 
117 	return coap_resp_send(msg, msg_info, buf, strlen(buf) + 1);
118 }
119 
led_handler(void * ctx,otMessage * msg,const otMessageInfo * msg_info)120 static void led_handler(void *ctx, otMessage *msg, const otMessageInfo *msg_info)
121 {
122 	coap_req_handler(ctx, msg, msg_info, led_handler_put, led_handler_get);
123 }
124 
125 #define DEFINE_LED_CTX(node_id)                                                                    \
126 	{                                                                                          \
127 		.gpio = GPIO_DT_SPEC_GET(node_id, gpios),                                          \
128 		.state = 0,                                                                        \
129 	},
130 
131 #define DEFINE_LEDS_CTX(inst, compat, ...) DT_FOREACH_CHILD(DT_INST(inst, compat), DEFINE_LED_CTX)
132 
133 static struct led_rsc_data led_rsc_data[] = {
134 	DT_COMPAT_FOREACH_STATUS_OKAY_VARGS(gpio_leds, DEFINE_LEDS_CTX)};
135 
136 static struct led_rsc_ctx led_rsc_ctx = {
137 	.led = led_rsc_data,
138 	.count = ARRAY_SIZE(led_rsc_data),
139 };
140 
141 static otCoapResource led_rsc = {
142 	.mUriPath = LED_URI,
143 	.mHandler = led_handler,
144 	.mContext = &led_rsc_ctx,
145 	.mNext = NULL,
146 };
147 
coap_led_reg_rsc(void)148 void coap_led_reg_rsc(void)
149 {
150 	otInstance *ot = openthread_get_default_instance();
151 
152 	LOG_INF("Registering LED rsc");
153 	led_init(&led_rsc);
154 	otCoapAddResource(ot, &led_rsc);
155 }
156 #endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
157 
coap_led_send_req_cb(void * ctx,otMessage * msg,const otMessageInfo * msg_info,otError error)158 static void coap_led_send_req_cb(void *ctx, otMessage *msg, const otMessageInfo *msg_info,
159 				 otError error)
160 {
161 }
162 
coap_led_set_state(const char * addr,int led_id,int state)163 int coap_led_set_state(const char *addr, int led_id, int state)
164 {
165 	uint8_t buf[COAP_MAX_BUF_SIZE];
166 
167 	struct json_led_state led_data = {
168 		.led_id = led_id,
169 		.state = state,
170 	};
171 
172 	json_obj_encode_buf(json_led_state_descr, ARRAY_SIZE(json_led_state_descr), &led_data, buf,
173 			    COAP_MAX_BUF_SIZE);
174 
175 	return coap_put_req_send(addr, LED_URI, buf, strlen(buf) + 1, coap_led_send_req_cb, NULL);
176 }
177 
coap_led_get_state_cb(void * ctx,otMessage * msg,const otMessageInfo * msg_info,otError error)178 static void coap_led_get_state_cb(void *ctx, otMessage *msg, const otMessageInfo *msg_info,
179 				  otError error)
180 {
181 	uint8_t buf[COAP_MAX_BUF_SIZE];
182 	int len = COAP_MAX_BUF_SIZE;
183 	struct json_led_get *led = (struct json_led_get *)ctx;
184 	int ret;
185 
186 	ret = coap_get_data(msg, buf, &len);
187 	if (ret) {
188 		led->count = 0;
189 		goto exit;
190 	}
191 
192 	json_obj_parse(buf, len, json_led_get_descr, ARRAY_SIZE(json_led_get_descr), led);
193 exit:
194 	k_sem_give(&led_get_sem);
195 }
196 
coap_led_get_state(const char * addr,int led_id,int * state)197 int coap_led_get_state(const char *addr, int led_id, int *state)
198 {
199 	struct json_led_get led;
200 	int ret;
201 
202 	ret = coap_get_req_send(addr, LED_URI, NULL, 0, coap_led_get_state_cb, &led);
203 	if (ret) {
204 		return ret;
205 	}
206 
207 	ret = k_sem_take(&led_get_sem, K_FOREVER);
208 	if (ret) {
209 		return ret;
210 	}
211 
212 	if (led_id >= led.count) {
213 		return -ENODEV;
214 	}
215 
216 	*state = led.leds[led_id].state;
217 	return ret;
218 }
219