277 lines
5.8 KiB
C
277 lines
5.8 KiB
C
/*
|
|
* Copyright (c) 2018 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/drivers/gpio.h>
|
|
|
|
#include <openthread/platform/diag.h>
|
|
|
|
#include "platform-zephyr.h"
|
|
|
|
/**
|
|
* Diagnostics mode variables.
|
|
*
|
|
*/
|
|
static bool sDiagMode;
|
|
static void *sDiagCallbackContext;
|
|
static otPlatDiagOutputCallback sDiagOutputCallback;
|
|
|
|
static void diag_output(const char *aFormat, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, aFormat);
|
|
|
|
if (sDiagOutputCallback != NULL) {
|
|
sDiagOutputCallback(aFormat, args, sDiagCallbackContext);
|
|
}
|
|
|
|
va_end(args);
|
|
}
|
|
|
|
void otPlatDiagSetOutputCallback(otInstance *aInstance,
|
|
otPlatDiagOutputCallback aCallback,
|
|
void *aContext)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
sDiagOutputCallback = aCallback;
|
|
sDiagCallbackContext = aContext;
|
|
}
|
|
|
|
otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[])
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
ARG_UNUSED(aArgsLength);
|
|
|
|
/* Add more platform specific diagnostics features here. */
|
|
diag_output("diag feature '%s' is not supported\r\n", aArgs[0]);
|
|
|
|
return OT_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
void otPlatDiagModeSet(bool aMode)
|
|
{
|
|
sDiagMode = aMode;
|
|
|
|
if (!sDiagMode) {
|
|
otPlatRadioSleep(NULL);
|
|
}
|
|
}
|
|
|
|
bool otPlatDiagModeGet(void)
|
|
{
|
|
return sDiagMode;
|
|
}
|
|
|
|
void otPlatDiagChannelSet(uint8_t aChannel)
|
|
{
|
|
ARG_UNUSED(aChannel);
|
|
}
|
|
|
|
void otPlatDiagTxPowerSet(int8_t aTxPower)
|
|
{
|
|
ARG_UNUSED(aTxPower);
|
|
}
|
|
|
|
void otPlatDiagRadioReceived(otInstance *aInstance,
|
|
otRadioFrame *aFrame,
|
|
otError aError)
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
ARG_UNUSED(aFrame);
|
|
ARG_UNUSED(aError);
|
|
}
|
|
|
|
otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable)
|
|
{
|
|
if (!otPlatDiagModeGet()) {
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
|
|
return platformRadioTransmitCarrier(aInstance, aEnable);
|
|
}
|
|
|
|
void otPlatDiagAlarmCallback(otInstance *aInstance)
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
}
|
|
|
|
/*
|
|
* To enable gpio diag commands, in Devicetree create `openthread` node in `/options/` path
|
|
* with `compatible = "openthread,config"` property and `diag-gpios` property,
|
|
* which should contain array of GPIO pin's configuration properties containing controller phandles,
|
|
* pin numbers and pin flags. e.g:
|
|
*
|
|
* options {
|
|
* openthread {
|
|
* compatible = "openthread,config";
|
|
* diag-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>,
|
|
* <&gpio1 0 GPIO_ACTIVE_LOW>;
|
|
* };
|
|
* };
|
|
*
|
|
* To enable reading current gpio pin mode, define
|
|
* `CONFIG_GPIO_GET_DIRECTION` in prj.conf.
|
|
*
|
|
* Note: `<gpio>` in `diag gpio` commands is an index of diag-gpios array. For example shown above,
|
|
* `ot diag gpio mode 0` will return current mode of pin nmb 0 controlled by `gpio0` controller.
|
|
*/
|
|
#if DT_HAS_COMPAT_STATUS_OKAY(openthread_config) && \
|
|
DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config), diag_gpios)
|
|
|
|
static const struct gpio_dt_spec gpio_spec[] = {
|
|
DT_FOREACH_PROP_ELEM_SEP(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config),
|
|
diag_gpios, GPIO_DT_SPEC_GET_BY_IDX, (,))};
|
|
|
|
static otError gpio_get_spec(uint32_t gpio_idx, const struct gpio_dt_spec **spec)
|
|
{
|
|
if (gpio_idx >= ARRAY_SIZE(gpio_spec)) {
|
|
return OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
*spec = &gpio_spec[gpio_idx];
|
|
|
|
if (!otPlatDiagModeGet()) {
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
|
|
if (!gpio_is_ready_dt(*spec)) {
|
|
return OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
const struct gpio_driver_config *const cfg =
|
|
(const struct gpio_driver_config *)((*spec)->port->config);
|
|
|
|
if ((cfg->port_pin_mask & (gpio_port_pins_t)BIT((*spec)->pin)) == 0U) {
|
|
return OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue)
|
|
{
|
|
const struct gpio_dt_spec *spec;
|
|
otError error;
|
|
|
|
error = gpio_get_spec(aGpio, &spec);
|
|
|
|
if (error != OT_ERROR_NONE) {
|
|
return error;
|
|
}
|
|
|
|
#if defined(CONFIG_GPIO_GET_DIRECTION)
|
|
if (gpio_pin_is_output_dt(spec) != 1) {
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
#endif
|
|
|
|
if (gpio_pin_set_dt(spec, (int)aValue) != 0) {
|
|
return OT_ERROR_FAILED;
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue)
|
|
{
|
|
const struct gpio_dt_spec *spec;
|
|
otError error;
|
|
int rv;
|
|
|
|
error = gpio_get_spec(aGpio, &spec);
|
|
|
|
if (error != OT_ERROR_NONE) {
|
|
return error;
|
|
}
|
|
|
|
if (aValue == NULL) {
|
|
return OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(CONFIG_GPIO_GET_DIRECTION)
|
|
if (gpio_pin_is_input_dt(spec) != 1) {
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
#endif
|
|
|
|
rv = gpio_pin_get_dt(spec);
|
|
if (rv < 0) {
|
|
return OT_ERROR_FAILED;
|
|
}
|
|
*aValue = (bool)rv;
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode)
|
|
{
|
|
const struct gpio_dt_spec *spec;
|
|
otError error;
|
|
int rv = 0;
|
|
|
|
error = gpio_get_spec(aGpio, &spec);
|
|
|
|
if (error != OT_ERROR_NONE) {
|
|
return error;
|
|
}
|
|
|
|
switch (aMode) {
|
|
case OT_GPIO_MODE_INPUT:
|
|
rv = gpio_pin_configure_dt(spec, GPIO_INPUT);
|
|
break;
|
|
|
|
case OT_GPIO_MODE_OUTPUT:
|
|
rv = gpio_pin_configure_dt(spec, GPIO_OUTPUT);
|
|
break;
|
|
|
|
default:
|
|
return OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
if (rv != 0) {
|
|
return OT_ERROR_FAILED;
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
#if defined(CONFIG_GPIO_GET_DIRECTION)
|
|
otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode)
|
|
{
|
|
const struct gpio_dt_spec *spec;
|
|
otError error;
|
|
gpio_port_pins_t pins_in, pins_out;
|
|
|
|
error = gpio_get_spec(aGpio, &spec);
|
|
|
|
if (error != OT_ERROR_NONE) {
|
|
return error;
|
|
}
|
|
if (aMode == NULL) {
|
|
return OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
if (gpio_port_get_direction(spec->port, BIT(spec->pin), &pins_in, &pins_out) < 0) {
|
|
return OT_ERROR_FAILED;
|
|
}
|
|
|
|
if (((gpio_port_pins_t)BIT(spec->pin) & pins_in) != 0U) {
|
|
*aMode = OT_GPIO_MODE_INPUT;
|
|
} else if (((gpio_port_pins_t)BIT(spec->pin) & pins_out) != 0U) {
|
|
*aMode = OT_GPIO_MODE_OUTPUT;
|
|
} else {
|
|
return OT_ERROR_FAILED;
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
#endif /* CONFIG_GPIO_GET_DIRECTION */
|
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(openthread_config) && \
|
|
* DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config), diag_gpios)
|
|
*/
|