1 /*
2  * Copyright (c) 2018 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/drivers/gpio.h>
9 
10 #include <openthread/error.h>
11 #include <openthread/platform/alarm-milli.h>
12 #include <openthread/platform/diag.h>
13 #include <openthread/platform/radio.h>
14 
15 #include "platform-zephyr.h"
16 #include "zephyr/sys/util.h"
17 
18 enum {
19 	DIAG_TRANSMIT_MODE_IDLE,
20 	DIAG_TRANSMIT_MODE_PACKETS,
21 	DIAG_TRANSMIT_MODE_CARRIER,
22 	DIAG_TRANSMIT_MODE_MODCARRIER
23 
24 } diag_trasmit_mode;
25 
26 /**
27  * Diagnostics mode variables.
28  *
29  */
30 
31 static bool sDiagMode;
32 static void *sDiagCallbackContext;
33 static otPlatDiagOutputCallback sDiagOutputCallback;
34 static uint8_t sTransmitMode = DIAG_TRANSMIT_MODE_IDLE;
35 static uint8_t sChannel = 20;
36 static uint32_t sTxPeriod = 1;
37 static int32_t sTxCount;
38 static int32_t sTxRequestedCount = 1;
39 
40 static otError startModCarrier(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]);
41 static otError processTransmit(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]);
42 
parse_long(char * aArgs,long * aValue)43 static otError parse_long(char *aArgs, long *aValue)
44 {
45 	char *endptr;
46 	*aValue = strtol(aArgs, &endptr, 0);
47 	return (*endptr == '\0') ? OT_ERROR_NONE : OT_ERROR_PARSE;
48 }
49 
diag_output(const char * aFormat,...)50 static void diag_output(const char *aFormat, ...)
51 {
52 	va_list args;
53 
54 	va_start(args, aFormat);
55 
56 	if (sDiagOutputCallback != NULL) {
57 		sDiagOutputCallback(aFormat, args, sDiagCallbackContext);
58 	}
59 
60 	va_end(args);
61 }
62 
otPlatDiagSetOutputCallback(otInstance * aInstance,otPlatDiagOutputCallback aCallback,void * aContext)63 void otPlatDiagSetOutputCallback(otInstance *aInstance,
64 				 otPlatDiagOutputCallback aCallback,
65 				 void *aContext)
66 {
67 	OT_UNUSED_VARIABLE(aInstance);
68 
69 	sDiagOutputCallback  = aCallback;
70 	sDiagCallbackContext = aContext;
71 }
72 
otPlatDiagProcess(otInstance * aInstance,uint8_t aArgsLength,char * aArgs[])73 otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[])
74 {
75 #if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS)
76 	if (strcmp(aArgs[0], "modcarrier") == 0) {
77 		return startModCarrier(aInstance, aArgsLength - 1, aArgs + 1);
78 	}
79 #endif
80 
81 	if (strcmp(aArgs[0], "transmit") == 0) {
82 		return processTransmit(aInstance, aArgsLength - 1, aArgs + 1);
83 	}
84 
85 	/* Add more platform specific diagnostics features here. */
86 	diag_output("diag feature '%s' is not supported\r\n", aArgs[0]);
87 
88 	return OT_ERROR_NOT_IMPLEMENTED;
89 }
90 
otPlatDiagModeSet(bool aMode)91 void otPlatDiagModeSet(bool aMode)
92 {
93 	sDiagMode = aMode;
94 
95 	if (!sDiagMode) {
96 		otPlatRadioSleep(NULL);
97 	}
98 }
99 
otPlatDiagModeGet(void)100 bool otPlatDiagModeGet(void)
101 {
102 	return sDiagMode;
103 }
104 
otPlatDiagChannelSet(uint8_t aChannel)105 void otPlatDiagChannelSet(uint8_t aChannel)
106 {
107 	ARG_UNUSED(aChannel);
108 	sChannel = aChannel;
109 }
110 
otPlatDiagTxPowerSet(int8_t aTxPower)111 void otPlatDiagTxPowerSet(int8_t aTxPower)
112 {
113 	ARG_UNUSED(aTxPower);
114 }
115 
otPlatDiagRadioReceived(otInstance * aInstance,otRadioFrame * aFrame,otError aError)116 void otPlatDiagRadioReceived(otInstance *aInstance,
117 			     otRadioFrame *aFrame,
118 			     otError aError)
119 {
120 	ARG_UNUSED(aInstance);
121 	ARG_UNUSED(aFrame);
122 	ARG_UNUSED(aError);
123 }
124 
125 #if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS)
otPlatDiagRadioTransmitCarrier(otInstance * aInstance,bool aEnable)126 otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable)
127 {
128 	if (!otPlatDiagModeGet() || (sTransmitMode != DIAG_TRANSMIT_MODE_IDLE &&
129 				     sTransmitMode != DIAG_TRANSMIT_MODE_CARRIER)) {
130 		return OT_ERROR_INVALID_STATE;
131 	}
132 
133 	if (aEnable) {
134 		sTransmitMode = DIAG_TRANSMIT_MODE_CARRIER;
135 	} else {
136 		sTransmitMode = DIAG_TRANSMIT_MODE_IDLE;
137 	}
138 
139 	return platformRadioTransmitCarrier(aInstance, aEnable);
140 }
141 #endif /* CONFIG_IEEE802154_CARRIER_FUNCTIONS */
142 
143 /*
144  * To enable gpio diag commands, in Devicetree create `openthread` node in `/options/` path
145  * with `compatible = "openthread,config"` property and `diag-gpios` property,
146  * which should contain array of GPIO pin's configuration properties containing controller phandles,
147  * pin numbers and pin flags. e.g:
148  *
149  * options {
150  *	openthread {
151  *		compatible = "openthread,config";
152  *		diag-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>,
153  *			     <&gpio1 0 GPIO_ACTIVE_LOW>;
154  *	};
155  * };
156  *
157  * To enable reading current gpio pin mode, define
158  * `CONFIG_GPIO_GET_DIRECTION` in prj.conf.
159  *
160  * Note: `<gpio>` in `diag gpio` commands is an index of diag-gpios array. For example shown above,
161  * `ot diag gpio mode 0` will return current mode of pin nmb 0 controlled by `gpio0` controller.
162  */
163 #if DT_HAS_COMPAT_STATUS_OKAY(openthread_config) && \
164 	DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config), diag_gpios)
165 
166 static const struct gpio_dt_spec gpio_spec[] = {
167 	DT_FOREACH_PROP_ELEM_SEP(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config),
168 				 diag_gpios, GPIO_DT_SPEC_GET_BY_IDX, (,))};
169 
gpio_get_spec(uint32_t gpio_idx,const struct gpio_dt_spec ** spec)170 static otError gpio_get_spec(uint32_t gpio_idx, const struct gpio_dt_spec **spec)
171 {
172 	if (gpio_idx >= ARRAY_SIZE(gpio_spec)) {
173 		return OT_ERROR_INVALID_ARGS;
174 	}
175 
176 	*spec = &gpio_spec[gpio_idx];
177 
178 	if (!otPlatDiagModeGet()) {
179 		return OT_ERROR_INVALID_STATE;
180 	}
181 
182 	if (!gpio_is_ready_dt(*spec)) {
183 		return OT_ERROR_INVALID_ARGS;
184 	}
185 
186 	const struct gpio_driver_config *const cfg =
187 		(const struct gpio_driver_config *)((*spec)->port->config);
188 
189 	if ((cfg->port_pin_mask & (gpio_port_pins_t)BIT((*spec)->pin)) == 0U) {
190 		return OT_ERROR_INVALID_ARGS;
191 	}
192 
193 	return OT_ERROR_NONE;
194 }
195 
otPlatDiagGpioSet(uint32_t aGpio,bool aValue)196 otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue)
197 {
198 	const struct gpio_dt_spec *spec;
199 	otError error;
200 
201 	error = gpio_get_spec(aGpio, &spec);
202 
203 	if (error != OT_ERROR_NONE) {
204 		return error;
205 	}
206 
207 #if defined(CONFIG_GPIO_GET_DIRECTION)
208 	if (gpio_pin_is_output_dt(spec) != 1) {
209 		return OT_ERROR_INVALID_STATE;
210 	}
211 #endif
212 
213 	if (gpio_pin_set_dt(spec, (int)aValue) != 0) {
214 		return OT_ERROR_FAILED;
215 	}
216 
217 	return OT_ERROR_NONE;
218 }
219 
otPlatDiagGpioGet(uint32_t aGpio,bool * aValue)220 otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue)
221 {
222 	const struct gpio_dt_spec *spec;
223 	otError error;
224 	int rv;
225 
226 	error = gpio_get_spec(aGpio, &spec);
227 
228 	if (error != OT_ERROR_NONE) {
229 		return error;
230 	}
231 
232 	if (aValue == NULL) {
233 		return OT_ERROR_INVALID_ARGS;
234 	}
235 
236 #if defined(CONFIG_GPIO_GET_DIRECTION)
237 	if (gpio_pin_is_input_dt(spec) != 1) {
238 		return OT_ERROR_INVALID_STATE;
239 	}
240 #endif
241 
242 	rv = gpio_pin_get_dt(spec);
243 	if (rv < 0) {
244 		return OT_ERROR_FAILED;
245 	}
246 	*aValue = (bool)rv;
247 
248 	return OT_ERROR_NONE;
249 }
250 
otPlatDiagGpioSetMode(uint32_t aGpio,otGpioMode aMode)251 otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode)
252 {
253 	const struct gpio_dt_spec *spec;
254 	otError error;
255 	int rv = 0;
256 
257 	error = gpio_get_spec(aGpio, &spec);
258 
259 	if (error != OT_ERROR_NONE) {
260 		return error;
261 	}
262 
263 	switch (aMode) {
264 	case OT_GPIO_MODE_INPUT:
265 		rv = gpio_pin_configure_dt(spec, GPIO_INPUT);
266 		break;
267 
268 	case OT_GPIO_MODE_OUTPUT:
269 		rv = gpio_pin_configure_dt(spec, GPIO_OUTPUT);
270 		break;
271 
272 	default:
273 		return OT_ERROR_INVALID_ARGS;
274 	}
275 
276 	if (rv != 0) {
277 		return OT_ERROR_FAILED;
278 	}
279 
280 	return OT_ERROR_NONE;
281 }
282 
283 #if defined(CONFIG_GPIO_GET_DIRECTION)
otPlatDiagGpioGetMode(uint32_t aGpio,otGpioMode * aMode)284 otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode)
285 {
286 	const struct gpio_dt_spec *spec;
287 	otError error;
288 	gpio_port_pins_t pins_in, pins_out;
289 
290 	error = gpio_get_spec(aGpio, &spec);
291 
292 	if (error != OT_ERROR_NONE) {
293 		return error;
294 	}
295 	if (aMode == NULL) {
296 		return OT_ERROR_INVALID_ARGS;
297 	}
298 
299 	if (gpio_port_get_direction(spec->port, BIT(spec->pin), &pins_in, &pins_out) < 0) {
300 		return OT_ERROR_FAILED;
301 	}
302 
303 	if (((gpio_port_pins_t)BIT(spec->pin) & pins_in) != 0U) {
304 		*aMode = OT_GPIO_MODE_INPUT;
305 	} else if (((gpio_port_pins_t)BIT(spec->pin) & pins_out) != 0U) {
306 		*aMode = OT_GPIO_MODE_OUTPUT;
307 	} else {
308 		return OT_ERROR_FAILED;
309 	}
310 
311 	return OT_ERROR_NONE;
312 }
313 #endif /* CONFIG_GPIO_GET_DIRECTION */
314 #endif /* DT_HAS_COMPAT_STATUS_OKAY(openthread_config) && \
315 	* DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config), diag_gpios)
316 	*/
317 
318 #if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS)
319 
startModCarrier(otInstance * aInstance,uint8_t aArgsLength,char * aArgs[])320 static otError startModCarrier(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[])
321 {
322 	bool enable = true;
323 	uint8_t data[OT_RADIO_FRAME_MAX_SIZE + 1];
324 
325 	if (aArgsLength <= 0) {
326 		return OT_ERROR_INVALID_ARGS;
327 	}
328 
329 	if (!otPlatDiagModeGet() || (sTransmitMode != DIAG_TRANSMIT_MODE_IDLE &&
330 				     sTransmitMode != DIAG_TRANSMIT_MODE_MODCARRIER)) {
331 		return OT_ERROR_INVALID_STATE;
332 	}
333 
334 	if (strcmp(aArgs[0], "stop") == 0) {
335 		enable = false;
336 		sTransmitMode = DIAG_TRANSMIT_MODE_IDLE;
337 	} else {
338 		if (hex2bin(aArgs[0], strlen(aArgs[0]), data, ARRAY_SIZE(data)) == 0) {
339 			return OT_ERROR_INVALID_ARGS;
340 		}
341 		sTransmitMode = DIAG_TRANSMIT_MODE_MODCARRIER;
342 	}
343 
344 	return platformRadioTransmitModulatedCarrier(aInstance, enable, data);
345 }
346 
347 #endif
348 
otPlatDiagAlarmCallback(otInstance * aInstance)349 void otPlatDiagAlarmCallback(otInstance *aInstance)
350 {
351 	uint32_t now;
352 	otRadioFrame *txPacket;
353 	const uint16_t diag_packet_len = 30;
354 
355 	if (sTransmitMode == DIAG_TRANSMIT_MODE_PACKETS) {
356 		if ((sTxCount > 0) || (sTxCount == -1)) {
357 			txPacket = otPlatRadioGetTransmitBuffer(aInstance);
358 
359 			txPacket->mInfo.mTxInfo.mTxDelayBaseTime = 0;
360 			txPacket->mInfo.mTxInfo.mTxDelay = 0;
361 			txPacket->mInfo.mTxInfo.mMaxCsmaBackoffs = 0;
362 			txPacket->mInfo.mTxInfo.mMaxFrameRetries = 0;
363 			txPacket->mInfo.mTxInfo.mRxChannelAfterTxDone = sChannel;
364 			txPacket->mInfo.mTxInfo.mTxPower = OT_RADIO_POWER_INVALID;
365 			txPacket->mInfo.mTxInfo.mIsHeaderUpdated = false;
366 			txPacket->mInfo.mTxInfo.mIsARetx = false;
367 			txPacket->mInfo.mTxInfo.mCsmaCaEnabled = false;
368 			txPacket->mInfo.mTxInfo.mCslPresent = false;
369 			txPacket->mInfo.mTxInfo.mIsSecurityProcessed = false;
370 
371 			txPacket->mLength = diag_packet_len;
372 
373 			for (uint8_t i = 0; i < diag_packet_len; i++) {
374 				txPacket->mPsdu[i] = i;
375 			}
376 
377 			otPlatRadioTransmit(aInstance, txPacket);
378 
379 			if (sTxCount != -1) {
380 				sTxCount--;
381 			}
382 
383 			now = otPlatAlarmMilliGetNow();
384 			otPlatAlarmMilliStartAt(aInstance, now, sTxPeriod);
385 		} else {
386 			sTransmitMode = DIAG_TRANSMIT_MODE_IDLE;
387 			otPlatAlarmMilliStop(aInstance);
388 			otPlatLog(OT_LOG_LEVEL_DEBG, OT_LOG_REGION_PLATFORM, "Transmit done");
389 		}
390 	}
391 }
392 
processTransmit(otInstance * aInstance,uint8_t aArgsLength,char * aArgs[])393 static otError processTransmit(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[])
394 {
395 	otError error = OT_ERROR_NONE;
396 	long value;
397 	uint32_t now;
398 
399 	if (!otPlatDiagModeGet()) {
400 		return OT_ERROR_INVALID_STATE;
401 	}
402 
403 	if (aArgsLength == 0) {
404 		diag_output("transmit will send %" PRId32 " diagnostic messages with %" PRIu32
405 			    " ms interval\r\n",
406 			    sTxRequestedCount, sTxPeriod);
407 
408 	} else if (strcmp(aArgs[0], "stop") == 0) {
409 		if (sTransmitMode == DIAG_TRANSMIT_MODE_IDLE) {
410 			return OT_ERROR_INVALID_STATE;
411 		}
412 
413 		otPlatAlarmMilliStop(aInstance);
414 		diag_output("diagnostic message transmission is stopped\r\n");
415 		sTransmitMode = DIAG_TRANSMIT_MODE_IDLE;
416 		otPlatRadioReceive(aInstance, sChannel);
417 
418 	} else if (strcmp(aArgs[0], "start") == 0) {
419 		if (sTransmitMode != DIAG_TRANSMIT_MODE_IDLE) {
420 			return OT_ERROR_INVALID_STATE;
421 		}
422 
423 		otPlatAlarmMilliStop(aInstance);
424 		sTransmitMode = DIAG_TRANSMIT_MODE_PACKETS;
425 		sTxCount = sTxRequestedCount;
426 		now = otPlatAlarmMilliGetNow();
427 		otPlatAlarmMilliStartAt(aInstance, now, sTxPeriod);
428 		diag_output("sending %" PRId32 " diagnostic messages with %" PRIu32
429 			    " ms interval\r\n",
430 			    sTxRequestedCount, sTxPeriod);
431 	} else if (strcmp(aArgs[0], "interval") == 0) {
432 
433 		if (aArgsLength != 2) {
434 			return OT_ERROR_INVALID_ARGS;
435 		}
436 
437 		error = parse_long(aArgs[1], &value);
438 		if (error != OT_ERROR_NONE) {
439 			return error;
440 		}
441 
442 		if (value <= 0) {
443 			return OT_ERROR_INVALID_ARGS;
444 		}
445 		sTxPeriod = (uint32_t)(value);
446 		diag_output("set diagnostic messages interval to %" PRIu32
447 			    " ms\r\n", sTxPeriod);
448 
449 	} else if (strcmp(aArgs[0], "count") == 0) {
450 
451 		if (aArgsLength != 2) {
452 			return OT_ERROR_INVALID_ARGS;
453 		}
454 
455 		error = parse_long(aArgs[1], &value);
456 		if (error != OT_ERROR_NONE) {
457 			return error;
458 		}
459 
460 		if ((value <= 0) && (value != -1)) {
461 			return OT_ERROR_INVALID_ARGS;
462 		}
463 
464 		sTxRequestedCount = (uint32_t)(value);
465 		diag_output("set diagnostic messages count to %" PRId32 "\r\n",
466 			    sTxRequestedCount);
467 	} else {
468 		return OT_ERROR_INVALID_ARGS;
469 	}
470 
471 	return error;
472 }
473