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 "button.h"
13 
14 struct btn_rsc_data {
15 	const struct gpio_dt_spec gpio;
16 };
17 
18 struct btn_rsc_ctx {
19 	struct btn_rsc_data *btn;
20 	int count;
21 };
22 
23 static const struct json_obj_descr json_btn_state_descr[] = {
24 	JSON_OBJ_DESCR_PRIM(struct json_btn_state, btn_id, JSON_TOK_NUMBER),
25 	JSON_OBJ_DESCR_PRIM(struct json_btn_state, state, JSON_TOK_NUMBER),
26 };
27 
28 static const struct json_obj_descr json_btn_get_descr[] = {
29 	JSON_OBJ_DESCR_PRIM(struct json_btn_get, device_id, JSON_TOK_STRING),
30 	JSON_OBJ_DESCR_OBJ_ARRAY(struct json_btn_get, btns, JSON_MAX_BTN, count,
31 				 json_btn_state_descr, ARRAY_SIZE(json_btn_state_descr)),
32 };
33 
34 static K_SEM_DEFINE(btn_get_sem, 0, 1);
35 
36 #ifdef CONFIG_OT_COAP_SAMPLE_SERVER
btn_handler_get(void * ctx,otMessage * msg,const otMessageInfo * msg_info)37 static int btn_handler_get(void *ctx, otMessage *msg, const otMessageInfo *msg_info)
38 {
39 	uint8_t buf[COAP_MAX_BUF_SIZE];
40 	struct btn_rsc_ctx *btn_ctx = ctx;
41 
42 	struct json_btn_get btn_data = {
43 		.device_id = coap_device_id(),
44 	};
45 
46 	for (int i = 0; i < btn_ctx->count; i++) {
47 		btn_data.btns[i].btn_id = i;
48 		btn_data.btns[i].state = gpio_pin_get_dt(&btn_ctx->btn[i].gpio);
49 	}
50 	btn_data.count = btn_ctx->count;
51 
52 	json_obj_encode_buf(json_btn_get_descr, ARRAY_SIZE(json_btn_get_descr), &btn_data, buf,
53 			    COAP_MAX_BUF_SIZE);
54 
55 	return coap_resp_send(msg, msg_info, buf, strlen(buf) + 1);
56 }
57 
btn_handler(void * ctx,otMessage * msg,const otMessageInfo * msg_info)58 static void btn_handler(void *ctx, otMessage *msg, const otMessageInfo *msg_info)
59 {
60 	coap_req_handler(ctx, msg, msg_info, NULL, btn_handler_get);
61 }
62 
63 #define DEFINE_BTN_CTX(node_id)                                                                    \
64 	{                                                                                          \
65 		.gpio = GPIO_DT_SPEC_GET(node_id, gpios),                                          \
66 	},
67 
68 #define DEFINE_BTNS_CTX(inst, compat, ...) DT_FOREACH_CHILD(DT_INST(inst, compat), DEFINE_BTN_CTX)
69 
70 static struct btn_rsc_data btn_rsc_data[] = {
71 	DT_COMPAT_FOREACH_STATUS_OKAY_VARGS(gpio_keys, DEFINE_BTNS_CTX)};
72 
73 static struct btn_rsc_ctx btn_rsc_ctx = {
74 	.btn = btn_rsc_data,
75 	.count = ARRAY_SIZE(btn_rsc_data),
76 };
77 
78 static otCoapResource btn_rsc = {
79 	.mUriPath = BTN_URI,
80 	.mHandler = btn_handler,
81 	.mContext = &btn_rsc_ctx,
82 	.mNext = NULL,
83 };
84 
button_init_rsc(otCoapResource * rsc)85 static int button_init_rsc(otCoapResource *rsc)
86 {
87 	int ret = 0;
88 
89 	struct btn_rsc_ctx *btn_ctx = rsc->mContext;
90 	const struct gpio_dt_spec *gpio;
91 
92 	LOG_INF("Initializing the buttons");
93 	for (int i = 0; i < btn_ctx->count; i++) {
94 		gpio = &btn_ctx->btn[i].gpio;
95 		ret = button_init(gpio);
96 		if (ret) {
97 			break;
98 		}
99 	}
100 
101 	return ret;
102 }
103 
coap_btn_reg_rsc(void)104 void coap_btn_reg_rsc(void)
105 {
106 	otInstance *ot = openthread_get_default_instance();
107 
108 	button_init_rsc(&btn_rsc);
109 	LOG_INF("Registering button rsc");
110 	otCoapAddResource(ot, &btn_rsc);
111 }
112 #endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
113 
button_init(const struct gpio_dt_spec * gpio)114 int button_init(const struct gpio_dt_spec *gpio)
115 {
116 	int ret;
117 
118 	if (!gpio_is_ready_dt(gpio)) {
119 		LOG_ERR("Error: button device %s is not ready\n", gpio->port->name);
120 		return -ENODEV;
121 	}
122 
123 	ret = gpio_pin_configure_dt(gpio, GPIO_INPUT);
124 	if (ret != 0) {
125 		LOG_ERR("Error %d: failed to configure %s pin %d\n", ret, gpio->port->name,
126 			gpio->pin);
127 		return ret;
128 	}
129 
130 	ret = gpio_pin_interrupt_configure_dt(gpio, GPIO_INT_EDGE_TO_ACTIVE);
131 	if (ret != 0) {
132 		LOG_ERR("Error %d: failed to configure interrupt on %s pin %d\n", ret,
133 			gpio->port->name, gpio->pin);
134 		return ret;
135 	}
136 
137 	return 0;
138 }
139 
coap_btn_get_state_cb(void * ctx,otMessage * msg,const otMessageInfo * msg_info,otError error)140 static void coap_btn_get_state_cb(void *ctx, otMessage *msg, const otMessageInfo *msg_info,
141 				  otError error)
142 {
143 	uint8_t buf[COAP_MAX_BUF_SIZE];
144 	int len = COAP_MAX_BUF_SIZE;
145 	struct json_btn_get *btn = (struct json_btn_get *)ctx;
146 	int ret;
147 
148 	ret = coap_get_data(msg, buf, &len);
149 	if (ret) {
150 		btn->count = 0;
151 		goto exit;
152 	}
153 
154 	json_obj_parse(buf, len, json_btn_get_descr, ARRAY_SIZE(json_btn_get_descr), btn);
155 exit:
156 	k_sem_give(&btn_get_sem);
157 }
158 
coap_btn_get_state(const char * addr,int btn_id,int * state)159 int coap_btn_get_state(const char *addr, int btn_id, int *state)
160 {
161 	struct json_btn_get btn;
162 	int ret;
163 
164 	ret = coap_get_req_send(addr, BTN_URI, NULL, 0, coap_btn_get_state_cb, &btn);
165 	if (ret) {
166 		return ret;
167 	}
168 
169 	ret = k_sem_take(&btn_get_sem, K_FOREVER);
170 	if (ret) {
171 		return ret;
172 	}
173 
174 	if (btn_id >= btn.count) {
175 		return -ENODEV;
176 	}
177 
178 	*state = btn.btns[btn_id].state;
179 	return ret;
180 }
181