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