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