1 /*
2  * Copyright (c) 2023, Vitrolife A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Datasheet:
7  * https://www.gassensing.co.uk/wp-content/uploads/2023/05/ExplorIR-M-Data-Sheet-Rev-4.13_3.pdf
8  *
9  */
10 
11 #define DT_DRV_COMPAT gss_explorir_m
12 
13 #include <stdio.h>
14 
15 #include <zephyr/kernel.h>
16 #include <zephyr/device.h>
17 #include <zephyr/drivers/sensor.h>
18 #include <zephyr/drivers/sensor/explorir_m.h>
19 #include <zephyr/drivers/uart.h>
20 #include <zephyr/logging/log.h>
21 
22 LOG_MODULE_REGISTER(explorir_m_sensor, CONFIG_SENSOR_LOG_LEVEL);
23 
24 #define EXPLORIR_M_BEGIN_CHAR ' '
25 
26 #define EXPLORIR_M_SET_FILTER_CHAR     'A'
27 #define EXPLORIR_M_GET_FILTER_CHAR     'a'
28 #define EXPLORIR_M_MODE_CHAR           'K'
29 #define EXPLORIR_M_CO2_FILTERED_CHAR   'Z'
30 #define EXPLORIR_M_SCALING_CHAR        '.'
31 #define EXPLORIR_M_NOT_RECOGNISED_CHAR '?'
32 
33 #define EXPLORIR_M_SEPARATOR_CHAR ' '
34 #define EXPLORIR_M_PRE_END_CHAR   '\r'
35 #define EXPLORIR_M_END_CHAR       '\n'
36 
37 #define EXPLORIR_M_TYPE_INDEX  1
38 #define EXPLORIR_M_VALUE_INDEX 3
39 
40 #define EXPLORIR_M_BUFFER_LENGTH 16
41 
42 #define EXPLORIR_M_MAX_RESPONSE_DELAY 200 /* Add margin to the specified 100 in datasheet */
43 #define EXPLORIR_M_CO2_VALID_DELAY    1200
44 
45 struct explorir_m_data {
46 	struct k_mutex uart_mutex;
47 	struct k_sem uart_rx_sem;
48 	uint16_t filtered;
49 	uint16_t scaling;
50 	uint8_t read_index;
51 	uint8_t read_buffer[EXPLORIR_M_BUFFER_LENGTH];
52 };
53 
54 struct explorir_m_cfg {
55 	const struct device *uart_dev;
56 	uart_irq_callback_user_data_t cb;
57 };
58 
59 enum explorir_m_uart_set_usage {
60 	EXPLORIR_M_SET_NONE,
61 	EXPLORIR_M_SET_VAL_ONE,
62 	EXPLORIR_M_SET_VAL_ONE_TWO,
63 };
64 
65 enum EXPLORIR_M_MODE {
66 	EXPLORIR_M_MODE_COMMAND,
67 	EXPLORIR_M_MODE_STREAM,
68 	EXPLORIR_M_MODE_POLL,
69 };
70 
explorir_m_uart_flush(const struct device * uart_dev)71 static void explorir_m_uart_flush(const struct device *uart_dev)
72 {
73 	uint8_t tmp;
74 
75 	while (uart_fifo_read(uart_dev, &tmp, 1) > 0) {
76 		continue;
77 	}
78 }
79 
explorir_m_uart_flush_until_end(const struct device * uart_dev)80 static void explorir_m_uart_flush_until_end(const struct device *uart_dev)
81 {
82 	uint8_t tmp;
83 	uint32_t uptime;
84 
85 	uptime = k_uptime_get_32();
86 	while (k_uptime_get_32() - uptime < EXPLORIR_M_MAX_RESPONSE_DELAY) {
87 		if (uart_poll_in(uart_dev, &tmp) == 0 && tmp == EXPLORIR_M_END_CHAR) {
88 			break;
89 		}
90 	}
91 }
92 
explorir_m_buffer_reset(struct explorir_m_data * data)93 static void explorir_m_buffer_reset(struct explorir_m_data *data)
94 {
95 	memset(data->read_buffer, 0, data->read_index);
96 	data->read_index = 0;
97 }
98 
explorir_m_buffer_verify(const struct explorir_m_data * data,char type)99 static int explorir_m_buffer_verify(const struct explorir_m_data *data, char type)
100 {
101 	char buffer_type = data->read_buffer[EXPLORIR_M_TYPE_INDEX];
102 
103 	if (data->read_buffer[0] == EXPLORIR_M_NOT_RECOGNISED_CHAR) {
104 		LOG_WRN("Sensor did not recognise the command");
105 		return -EIO;
106 	}
107 
108 	if (buffer_type != type) {
109 		LOG_WRN("Expected type %c but got %c", type, buffer_type);
110 		return -EIO;
111 	}
112 
113 	if (data->read_buffer[0] != EXPLORIR_M_BEGIN_CHAR ||
114 	    data->read_buffer[2] != EXPLORIR_M_SEPARATOR_CHAR ||
115 	    data->read_buffer[data->read_index - 2] != EXPLORIR_M_PRE_END_CHAR) {
116 		LOG_HEXDUMP_WRN(data->read_buffer, data->read_index, "Invalid buffer");
117 		return -EIO;
118 	}
119 
120 	return 0;
121 }
122 
explorir_m_buffer_process(struct explorir_m_data * data,char type,struct sensor_value * val)123 static int explorir_m_buffer_process(struct explorir_m_data *data, char type,
124 				     struct sensor_value *val)
125 {
126 	if (explorir_m_buffer_verify(data, type) != 0) {
127 		return -EIO;
128 	}
129 
130 	switch (type) {
131 	case EXPLORIR_M_SET_FILTER_CHAR:
132 	case EXPLORIR_M_MODE_CHAR:
133 		break;
134 
135 	case EXPLORIR_M_CO2_FILTERED_CHAR:
136 		data->scaling = strtol(&data->read_buffer[EXPLORIR_M_VALUE_INDEX], NULL, 10);
137 		break;
138 
139 	case EXPLORIR_M_SCALING_CHAR:
140 		data->filtered = strtol(&data->read_buffer[EXPLORIR_M_VALUE_INDEX], NULL, 10);
141 		break;
142 
143 	case EXPLORIR_M_GET_FILTER_CHAR:
144 		val->val1 = strtol(&data->read_buffer[EXPLORIR_M_VALUE_INDEX], NULL, 10);
145 		break;
146 
147 	default:
148 		LOG_ERR("Unknown type %c/0x%02x", type, type);
149 		return -EIO;
150 	}
151 
152 	return 0;
153 }
154 
explorir_m_uart_isr(const struct device * uart_dev,void * user_data)155 static void explorir_m_uart_isr(const struct device *uart_dev, void *user_data)
156 {
157 	const struct device *dev = user_data;
158 	struct explorir_m_data *data = dev->data;
159 	int rc, read_len;
160 
161 	if (!device_is_ready(uart_dev)) {
162 		LOG_DBG("UART device is not ready");
163 		return;
164 	}
165 
166 	if (!uart_irq_update(uart_dev)) {
167 		LOG_DBG("Unable to process interrupts");
168 		return;
169 	}
170 
171 	if (!uart_irq_rx_ready(uart_dev)) {
172 		LOG_DBG("No RX data");
173 		return;
174 	}
175 
176 	read_len = EXPLORIR_M_BUFFER_LENGTH - data->read_index;
177 	rc = uart_fifo_read(uart_dev, &data->read_buffer[data->read_index], read_len);
178 
179 	if (rc < 0 || rc == read_len) {
180 		LOG_ERR("UART read failed: %d", rc < 0 ? rc : -ERANGE);
181 		explorir_m_uart_flush(uart_dev);
182 		LOG_HEXDUMP_WRN(data->read_buffer, data->read_index, "Discarding");
183 		explorir_m_buffer_reset(data);
184 	} else {
185 		data->read_index += rc;
186 
187 		if (data->read_buffer[data->read_index - 1] != EXPLORIR_M_END_CHAR) {
188 			return;
189 		}
190 	}
191 
192 	k_sem_give(&data->uart_rx_sem);
193 }
194 
explorir_m_uart_terminate(const struct device * uart_dev)195 static void explorir_m_uart_terminate(const struct device *uart_dev)
196 {
197 	uart_poll_out(uart_dev, EXPLORIR_M_PRE_END_CHAR);
198 	uart_poll_out(uart_dev, EXPLORIR_M_END_CHAR);
199 }
200 
explorir_m_await_receive(struct explorir_m_data * data)201 static int explorir_m_await_receive(struct explorir_m_data *data)
202 {
203 	int rc = k_sem_take(&data->uart_rx_sem, K_MSEC(EXPLORIR_M_MAX_RESPONSE_DELAY));
204 
205 	/* Reset semaphore if sensor did not respond within maximum specified response time */
206 	if (rc == -EAGAIN) {
207 		k_sem_reset(&data->uart_rx_sem);
208 	}
209 
210 	return rc;
211 }
212 
explorir_m_uart_transceive(const struct device * dev,char type,struct sensor_value * val,enum explorir_m_uart_set_usage set)213 static int explorir_m_uart_transceive(const struct device *dev, char type, struct sensor_value *val,
214 				      enum explorir_m_uart_set_usage set)
215 {
216 	const struct explorir_m_cfg *cfg = dev->config;
217 	struct explorir_m_data *data = dev->data;
218 	char buf[EXPLORIR_M_BUFFER_LENGTH];
219 	int rc, len;
220 
221 	if (val == NULL && set != EXPLORIR_M_SET_NONE) {
222 		LOG_ERR("val is NULL but set is not NONE");
223 		return -EINVAL;
224 	}
225 
226 	k_mutex_lock(&data->uart_mutex, K_FOREVER);
227 
228 	explorir_m_buffer_reset(data);
229 
230 	uart_poll_out(cfg->uart_dev, type);
231 
232 	if (set == EXPLORIR_M_SET_VAL_ONE) {
233 		len = snprintf(buf, EXPLORIR_M_BUFFER_LENGTH, "%c%u", EXPLORIR_M_SEPARATOR_CHAR,
234 			       val->val1);
235 	} else if (set == EXPLORIR_M_SET_VAL_ONE_TWO) {
236 		len = snprintf(buf, EXPLORIR_M_BUFFER_LENGTH, "%c%u%c%u", EXPLORIR_M_SEPARATOR_CHAR,
237 			       val->val1, EXPLORIR_M_SEPARATOR_CHAR, val->val2);
238 	} else {
239 		len = 0;
240 	}
241 
242 	if (len == EXPLORIR_M_BUFFER_LENGTH) {
243 		LOG_WRN("Set value truncated");
244 	}
245 	for (int i = 0; i != len; i++) {
246 		uart_poll_out(cfg->uart_dev, buf[i]);
247 	}
248 
249 	explorir_m_uart_terminate(cfg->uart_dev);
250 
251 	rc = explorir_m_await_receive(data);
252 	if (rc != 0) {
253 		LOG_WRN("%c did not receive a response: %d", type, rc);
254 	}
255 
256 	if (rc == 0) {
257 		rc = explorir_m_buffer_process(data, type, val);
258 	}
259 
260 	k_mutex_unlock(&data->uart_mutex);
261 
262 	return rc;
263 }
264 
explorir_m_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)265 static int explorir_m_attr_get(const struct device *dev, enum sensor_channel chan,
266 			       enum sensor_attribute attr, struct sensor_value *val)
267 {
268 	if (chan != SENSOR_CHAN_CO2) {
269 		return -ENOTSUP;
270 	}
271 
272 	switch (attr) {
273 	case SENSOR_ATTR_EXPLORIR_M_FILTER:
274 		return explorir_m_uart_transceive(dev, EXPLORIR_M_GET_FILTER_CHAR, val,
275 						  EXPLORIR_M_SET_NONE);
276 	default:
277 		return -ENOTSUP;
278 	}
279 }
280 
explorir_m_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)281 static int explorir_m_attr_set(const struct device *dev, enum sensor_channel chan,
282 			       enum sensor_attribute attr, const struct sensor_value *val)
283 {
284 	if (chan != SENSOR_CHAN_CO2) {
285 		return -ENOTSUP;
286 	}
287 
288 	switch (attr) {
289 	case SENSOR_ATTR_EXPLORIR_M_FILTER:
290 		if (val->val1 < 0 || val->val1 > 255) {
291 			return -ERANGE;
292 		}
293 		return explorir_m_uart_transceive(dev, EXPLORIR_M_SET_FILTER_CHAR,
294 						  (struct sensor_value *)val,
295 						  EXPLORIR_M_SET_VAL_ONE);
296 	default:
297 		return -ENOTSUP;
298 	}
299 }
300 
explorir_m_sample_fetch(const struct device * dev,enum sensor_channel chan)301 static int explorir_m_sample_fetch(const struct device *dev, enum sensor_channel chan)
302 {
303 	if (chan != SENSOR_CHAN_CO2 && chan != SENSOR_CHAN_ALL) {
304 		return -ENOTSUP;
305 	}
306 
307 	return explorir_m_uart_transceive(dev, EXPLORIR_M_CO2_FILTERED_CHAR, NULL,
308 					  EXPLORIR_M_SET_NONE);
309 }
310 
explorir_m_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)311 static int explorir_m_channel_get(const struct device *dev, enum sensor_channel chan,
312 				  struct sensor_value *val)
313 {
314 	struct explorir_m_data *data = dev->data;
315 
316 	if (chan != SENSOR_CHAN_CO2) {
317 		return -ENOTSUP;
318 	}
319 
320 	if (k_uptime_get() < EXPLORIR_M_CO2_VALID_DELAY) {
321 		return -EAGAIN;
322 	}
323 
324 	val->val1 = data->filtered * data->scaling;
325 	val->val2 = 0;
326 
327 	return 0;
328 }
329 
330 static DEVICE_API(sensor, explorir_m_api_funcs) = {
331 	.attr_set = explorir_m_attr_set,
332 	.attr_get = explorir_m_attr_get,
333 	.sample_fetch = explorir_m_sample_fetch,
334 	.channel_get = explorir_m_channel_get,
335 };
336 
explorir_m_init(const struct device * dev)337 static int explorir_m_init(const struct device *dev)
338 {
339 	const struct explorir_m_cfg *cfg = dev->config;
340 	struct explorir_m_data *data = dev->data;
341 	struct sensor_value val;
342 	int rc;
343 
344 	LOG_DBG("Initializing %s", dev->name);
345 
346 	if (!device_is_ready(cfg->uart_dev)) {
347 		return -ENODEV;
348 	}
349 
350 	k_mutex_init(&data->uart_mutex);
351 	k_sem_init(&data->uart_rx_sem, 0, 1);
352 
353 	uart_irq_rx_disable(cfg->uart_dev);
354 	uart_irq_tx_disable(cfg->uart_dev);
355 
356 	rc = uart_irq_callback_user_data_set(cfg->uart_dev, cfg->cb, (void *)dev);
357 	if (rc != 0) {
358 		LOG_ERR("UART IRQ setup failed: %d", rc);
359 		return rc;
360 	}
361 
362 	/* Terminate garbled tx due to GPIO setup or crash during unfinished send */
363 	explorir_m_uart_terminate(cfg->uart_dev);
364 	explorir_m_uart_flush_until_end(cfg->uart_dev);
365 
366 	uart_irq_rx_enable(cfg->uart_dev);
367 
368 	val.val1 = EXPLORIR_M_MODE_POLL;
369 	explorir_m_uart_transceive(dev, EXPLORIR_M_MODE_CHAR, &val, EXPLORIR_M_SET_VAL_ONE);
370 	explorir_m_uart_transceive(dev, EXPLORIR_M_SCALING_CHAR, NULL, EXPLORIR_M_SET_NONE);
371 
372 	return rc;
373 }
374 
375 #define EXPLORIR_M_INIT(n)                                                                         \
376                                                                                                    \
377 	static struct explorir_m_data explorir_m_data_##n;                                         \
378                                                                                                    \
379 	static const struct explorir_m_cfg explorir_m_cfg_##n = {                                  \
380 		.uart_dev = DEVICE_DT_GET(DT_INST_BUS(n)),                                         \
381 		.cb = explorir_m_uart_isr,                                                         \
382 	};                                                                                         \
383                                                                                                    \
384 	SENSOR_DEVICE_DT_INST_DEFINE(n, explorir_m_init, NULL, &explorir_m_data_##n,               \
385 				     &explorir_m_cfg_##n, POST_KERNEL,                             \
386 				     CONFIG_SENSOR_INIT_PRIORITY, &explorir_m_api_funcs);
387 
388 DT_INST_FOREACH_STATUS_OKAY(EXPLORIR_M_INIT)
389