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 	sChannel = aChannel;
108 	platformRadioChannelSet(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 (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 (!gpio_is_ready_dt(*spec)) {
179 		return OT_ERROR_INVALID_ARGS;
180 	}
181 
182 	const struct gpio_driver_config *const cfg =
183 		(const struct gpio_driver_config *)((*spec)->port->config);
184 
185 	if ((cfg->port_pin_mask & (gpio_port_pins_t)BIT((*spec)->pin)) == 0U) {
186 		return OT_ERROR_INVALID_ARGS;
187 	}
188 
189 	return OT_ERROR_NONE;
190 }
191 
otPlatDiagGpioSet(uint32_t aGpio,bool aValue)192 otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue)
193 {
194 	const struct gpio_dt_spec *spec;
195 	otError error;
196 
197 	error = gpio_get_spec(aGpio, &spec);
198 
199 	if (error != OT_ERROR_NONE) {
200 		return error;
201 	}
202 
203 #if defined(CONFIG_GPIO_GET_DIRECTION)
204 	if (gpio_pin_is_output_dt(spec) != 1) {
205 		return OT_ERROR_INVALID_STATE;
206 	}
207 #endif
208 
209 	if (gpio_pin_set_dt(spec, (int)aValue) != 0) {
210 		return OT_ERROR_FAILED;
211 	}
212 
213 	return OT_ERROR_NONE;
214 }
215 
otPlatDiagGpioGet(uint32_t aGpio,bool * aValue)216 otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue)
217 {
218 	const struct gpio_dt_spec *spec;
219 	otError error;
220 	int rv;
221 
222 	error = gpio_get_spec(aGpio, &spec);
223 
224 	if (error != OT_ERROR_NONE) {
225 		return error;
226 	}
227 
228 	if (aValue == NULL) {
229 		return OT_ERROR_INVALID_ARGS;
230 	}
231 
232 #if defined(CONFIG_GPIO_GET_DIRECTION)
233 	if (gpio_pin_is_input_dt(spec) != 1) {
234 		return OT_ERROR_INVALID_STATE;
235 	}
236 #endif
237 
238 	rv = gpio_pin_get_dt(spec);
239 	if (rv < 0) {
240 		return OT_ERROR_FAILED;
241 	}
242 	*aValue = (bool)rv;
243 
244 	return OT_ERROR_NONE;
245 }
246 
otPlatDiagGpioSetMode(uint32_t aGpio,otGpioMode aMode)247 otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode)
248 {
249 	const struct gpio_dt_spec *spec;
250 	otError error;
251 	int rv = 0;
252 
253 	error = gpio_get_spec(aGpio, &spec);
254 
255 	if (error != OT_ERROR_NONE) {
256 		return error;
257 	}
258 
259 	switch (aMode) {
260 	case OT_GPIO_MODE_INPUT:
261 		rv = gpio_pin_configure_dt(spec, GPIO_INPUT);
262 		break;
263 
264 	case OT_GPIO_MODE_OUTPUT:
265 		rv = gpio_pin_configure_dt(spec, GPIO_OUTPUT);
266 		break;
267 
268 	default:
269 		return OT_ERROR_INVALID_ARGS;
270 	}
271 
272 	if (rv != 0) {
273 		return OT_ERROR_FAILED;
274 	}
275 
276 	return OT_ERROR_NONE;
277 }
278 
279 #if defined(CONFIG_GPIO_GET_DIRECTION)
otPlatDiagGpioGetMode(uint32_t aGpio,otGpioMode * aMode)280 otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode)
281 {
282 	const struct gpio_dt_spec *spec;
283 	otError error;
284 	gpio_port_pins_t pins_in, pins_out;
285 
286 	error = gpio_get_spec(aGpio, &spec);
287 
288 	if (error != OT_ERROR_NONE) {
289 		return error;
290 	}
291 	if (aMode == NULL) {
292 		return OT_ERROR_INVALID_ARGS;
293 	}
294 
295 	if (gpio_port_get_direction(spec->port, BIT(spec->pin), &pins_in, &pins_out) < 0) {
296 		return OT_ERROR_FAILED;
297 	}
298 
299 	if (((gpio_port_pins_t)BIT(spec->pin) & pins_in) != 0U) {
300 		*aMode = OT_GPIO_MODE_INPUT;
301 	} else if (((gpio_port_pins_t)BIT(spec->pin) & pins_out) != 0U) {
302 		*aMode = OT_GPIO_MODE_OUTPUT;
303 	} else {
304 		return OT_ERROR_FAILED;
305 	}
306 
307 	return OT_ERROR_NONE;
308 }
309 #endif /* CONFIG_GPIO_GET_DIRECTION */
310 #endif /* DT_HAS_COMPAT_STATUS_OKAY(openthread_config) && \
311 	* DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config), diag_gpios)
312 	*/
313 
314 #if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS)
315 
startModCarrier(otInstance * aInstance,uint8_t aArgsLength,char * aArgs[])316 static otError startModCarrier(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[])
317 {
318 	bool enable = true;
319 	uint8_t data[OT_RADIO_FRAME_MAX_SIZE + 1];
320 
321 	if (aArgsLength <= 0) {
322 		return OT_ERROR_INVALID_ARGS;
323 	}
324 
325 	if (sTransmitMode != DIAG_TRANSMIT_MODE_IDLE &&
326 	    sTransmitMode != DIAG_TRANSMIT_MODE_MODCARRIER) {
327 		return OT_ERROR_INVALID_STATE;
328 	}
329 
330 	if (strcmp(aArgs[0], "stop") == 0) {
331 		enable = false;
332 		sTransmitMode = DIAG_TRANSMIT_MODE_IDLE;
333 	} else {
334 		if (hex2bin(aArgs[0], strlen(aArgs[0]), data, ARRAY_SIZE(data)) == 0) {
335 			return OT_ERROR_INVALID_ARGS;
336 		}
337 		sTransmitMode = DIAG_TRANSMIT_MODE_MODCARRIER;
338 	}
339 
340 	return platformRadioTransmitModulatedCarrier(aInstance, enable, data);
341 }
342 
343 #endif
344 
otPlatDiagAlarmCallback(otInstance * aInstance)345 void otPlatDiagAlarmCallback(otInstance *aInstance)
346 {
347 	uint32_t now;
348 	otRadioFrame *txPacket;
349 	const uint16_t diag_packet_len = 30;
350 
351 	if (sTransmitMode == DIAG_TRANSMIT_MODE_PACKETS) {
352 		if ((sTxCount > 0) || (sTxCount == -1)) {
353 			txPacket = otPlatRadioGetTransmitBuffer(aInstance);
354 
355 			txPacket->mInfo.mTxInfo.mTxDelayBaseTime = 0;
356 			txPacket->mInfo.mTxInfo.mTxDelay = 0;
357 			txPacket->mInfo.mTxInfo.mMaxCsmaBackoffs = 0;
358 			txPacket->mInfo.mTxInfo.mMaxFrameRetries = 0;
359 			txPacket->mInfo.mTxInfo.mRxChannelAfterTxDone = sChannel;
360 			txPacket->mInfo.mTxInfo.mTxPower = OT_RADIO_POWER_INVALID;
361 			txPacket->mInfo.mTxInfo.mIsHeaderUpdated = false;
362 			txPacket->mInfo.mTxInfo.mIsARetx = false;
363 			txPacket->mInfo.mTxInfo.mCsmaCaEnabled = false;
364 			txPacket->mInfo.mTxInfo.mCslPresent = false;
365 			txPacket->mInfo.mTxInfo.mIsSecurityProcessed = false;
366 
367 			txPacket->mLength = diag_packet_len;
368 
369 			for (uint8_t i = 0; i < diag_packet_len; i++) {
370 				txPacket->mPsdu[i] = i;
371 			}
372 
373 			otPlatRadioTransmit(aInstance, txPacket);
374 
375 			if (sTxCount != -1) {
376 				sTxCount--;
377 			}
378 
379 			now = otPlatAlarmMilliGetNow();
380 			otPlatAlarmMilliStartAt(aInstance, now, sTxPeriod);
381 		} else {
382 			sTransmitMode = DIAG_TRANSMIT_MODE_IDLE;
383 			otPlatAlarmMilliStop(aInstance);
384 			otPlatLog(OT_LOG_LEVEL_DEBG, OT_LOG_REGION_PLATFORM, "Transmit done");
385 		}
386 	}
387 }
388 
processTransmit(otInstance * aInstance,uint8_t aArgsLength,char * aArgs[])389 static otError processTransmit(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[])
390 {
391 	otError error = OT_ERROR_NONE;
392 	long value;
393 	uint32_t now;
394 
395 	if (aArgsLength == 0) {
396 		diag_output("transmit will send %" PRId32 " diagnostic messages with %" PRIu32
397 			    " ms interval\r\n",
398 			    sTxRequestedCount, sTxPeriod);
399 
400 	} else if (strcmp(aArgs[0], "stop") == 0) {
401 		if (sTransmitMode == DIAG_TRANSMIT_MODE_IDLE) {
402 			return OT_ERROR_INVALID_STATE;
403 		}
404 
405 		otPlatAlarmMilliStop(aInstance);
406 		diag_output("diagnostic message transmission is stopped\r\n");
407 		sTransmitMode = DIAG_TRANSMIT_MODE_IDLE;
408 		otPlatRadioReceive(aInstance, sChannel);
409 
410 	} else if (strcmp(aArgs[0], "start") == 0) {
411 		if (sTransmitMode != DIAG_TRANSMIT_MODE_IDLE) {
412 			return OT_ERROR_INVALID_STATE;
413 		}
414 
415 		otPlatAlarmMilliStop(aInstance);
416 		sTransmitMode = DIAG_TRANSMIT_MODE_PACKETS;
417 		sTxCount = sTxRequestedCount;
418 		now = otPlatAlarmMilliGetNow();
419 		otPlatAlarmMilliStartAt(aInstance, now, sTxPeriod);
420 		diag_output("sending %" PRId32 " diagnostic messages with %" PRIu32
421 			    " ms interval\r\n",
422 			    sTxRequestedCount, sTxPeriod);
423 	} else if (strcmp(aArgs[0], "interval") == 0) {
424 
425 		if (aArgsLength != 2) {
426 			return OT_ERROR_INVALID_ARGS;
427 		}
428 
429 		error = parse_long(aArgs[1], &value);
430 		if (error != OT_ERROR_NONE) {
431 			return error;
432 		}
433 
434 		if (value <= 0) {
435 			return OT_ERROR_INVALID_ARGS;
436 		}
437 		sTxPeriod = (uint32_t)(value);
438 		diag_output("set diagnostic messages interval to %" PRIu32
439 			    " ms\r\n", sTxPeriod);
440 
441 	} else if (strcmp(aArgs[0], "count") == 0) {
442 
443 		if (aArgsLength != 2) {
444 			return OT_ERROR_INVALID_ARGS;
445 		}
446 
447 		error = parse_long(aArgs[1], &value);
448 		if (error != OT_ERROR_NONE) {
449 			return error;
450 		}
451 
452 		if ((value <= 0) && (value != -1)) {
453 			return OT_ERROR_INVALID_ARGS;
454 		}
455 
456 		sTxRequestedCount = (uint32_t)(value);
457 		diag_output("set diagnostic messages count to %" PRId32 "\r\n",
458 			    sTxRequestedCount);
459 	} else {
460 		return OT_ERROR_INVALID_ARGS;
461 	}
462 
463 	return error;
464 }
465