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