Skip to content

Conversation

@pdgendt
Copy link
Contributor

@pdgendt pdgendt commented Jul 4, 2022

This PR adds support to operate as an OpenThread Host-Processor connected to an RCP radio controller over SPI. Both FTD and MTD modes are supported.

Some remarks up for improvement:

  • Added a default SPI peripheral device init priority, should this be moved to a device specific one?
  • The dummy IEEE802.15.4 interface feels iffy, not sure if there is an alternative though.
  • Currently only SPI support, UART/USB should be fairly straightforward to be added in the future.
  • C++ source files are injected into the OpenThread library, this is the easiest approach to make sure all OpenThread configs are applied to the header-only included files.
  • Ported POSIX SPI implementation from OpenThread's repository. The source file license header has to be kept, not sure if this breaks compliance.

Tested on a custom board using NXP's i.MX RT1064 connected to a Nordic nRF52840.

pdgendt added 3 commits July 4, 2022 13:53
Add a Kconfig option for SPI peripheral devices' default init priority.

Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
This commit adds support to use OpenThread on a host-processor device,
connected to an RCP device over SPI.

Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
Add a Kconfig option for a host-controller to enable RCP
restoration.

Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
Copy link
Contributor Author

@pdgendt pdgendt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a self-review for some comments/fixes.

required: false
description: Interrupt pin

Optional interript pin asserted by the RCP
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Optional interript pin asserted by the RCP
Optional interrupt pin asserted by the RCP

#ifdef CONFIG_OPENTHREAD_HOSTPROCESSOR_SPI
#include "spi_interface.hpp"

typedef ot::Spinel::RadioSpinel<SpiInterface, RadioSpinelContext> PlatformSpiRadioSpinel;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
typedef ot::Spinel::RadioSpinel<SpiInterface, RadioSpinelContext> PlatformSpiRadioSpinel;
typedef ot::Spinel::RadioSpinel<SpiInterface, RadioSpinelContext> PlatformRadioSpinel;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my own understanding and for the sake of argumenting the use of C++ code here (it's kind of a gray area when it comes to the Zephyr codebase), please correct me if I'm wrong:

  1. In order to implement OT host functionality, we need to use an ot::Spinel::RadioSpinel template class, which takes two template parameters: InterfaceType and ProcessContextType. The latter is not a concern, but InterfaceType is expected to be a class implementing certain methods (defined in spi_interface.hpp), therefore there's no other way around than using C++ for that.
  2. The ot::Spinel::RadioSpinel template class does not expose a C API, therefore the spinel-based otPlatRadio* driver also needs to be implemented in C++.

Is that more or less correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Yes, kind of, we could try to re-implement the entire Spinel protocol, however this would be almost impossible to maintain.
  2. Yes

{
OT_UNUSED_VARIABLE(aInstance);

SuccessOrDie(RadioSpinelInstance().GetIeeeEui64(aIeeeEui64));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if the SuccessOrDie is the best approach to "fail".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MIght not be the best idea, from what I've seen the newlib wrapper for this function will simply end up in a busy-loop.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some caveat here, the included radio_spinel_impl.hpp also uses these functions a lot.


if (!data->ready) {
data->ready = true;
gpio_add_callback(config->irq_gpio.port, &data->irq_gpio_callback);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
gpio_add_callback(config->irq_gpio.port, &data->irq_gpio_callback);
if (config->irq_gpio.port) {
gpio_add_callback(config->irq_gpio.port, &data->irq_gpio_callback);
}

Comment on lines +1 to +27
/*
* Copyright (c) 2019, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original header from the posix ported SPI implementation.

Comment on lines +42 to +44
struct RadioSpinelContext {
otInstance *mInstance;
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simply wraps an instance pointer, as the Process method only accepts const arguments.

Copy link
Contributor

@rlubos rlubos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial high-level feedback after digesting (and hopefully understanding) this codebase.

}

#ifdef CONFIG_OPENTHREAD_HOSTPROCESSOR
uint64_t otPlatTimeGet(void)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it'd make sense to create time.c platform implementation and implement API defined in OT's time.h in there, there are only 2 functions in there, but currently, they're scattered around different files.

#ifdef CONFIG_OPENTHREAD_HOSTPROCESSOR_SPI
#include "spi_interface.hpp"

typedef ot::Spinel::RadioSpinel<SpiInterface, RadioSpinelContext> PlatformSpiRadioSpinel;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my own understanding and for the sake of argumenting the use of C++ code here (it's kind of a gray area when it comes to the Zephyr codebase), please correct me if I'm wrong:

  1. In order to implement OT host functionality, we need to use an ot::Spinel::RadioSpinel template class, which takes two template parameters: InterfaceType and ProcessContextType. The latter is not a concern, but InterfaceType is expected to be a class implementing certain methods (defined in spi_interface.hpp), therefore there's no other way around than using C++ for that.
  2. The ot::Spinel::RadioSpinel template class does not expose a C API, therefore the spinel-based otPlatRadio* driver also needs to be implemented in C++.

Is that more or less correct?

{
OT_UNUSED_VARIABLE(aInstance);

SuccessOrDie(RadioSpinelInstance().GetIeeeEui64(aIeeeEui64));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MIght not be the best idea, from what I've seen the newlib wrapper for this function will simply end up in a busy-loop.

endif()

if(CONFIG_OPENTHREAD_HOSTPROCESSOR)
# Inject our platform spinel implementation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on why is it needed to inject those sources into OT libs, why can't it end up in the openthread_platform library with other platform files?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OpenThread build system has private compile definitions for each library. This was the easiest way to make sure exactly the same definitions are set.
Because of the C++ template for the RadioSpinel instance, which is header-only.

Comment on lines +140 to +148
.get_capabilities = ot_spinel_get_capabilities,
.cca = ot_spinel_cca,
.set_channel = ot_spinel_set_channel,
.filter = ot_spinel_filter,
.set_txpower = ot_spinel_set_txpower,
.tx = ot_spinel_tx,
.start = ot_spinel_start,
.stop = ot_spinel_stop,
.configure = ot_spinel_configure,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are those dummy-implementations even needed, couldn't they be just NULL pointers? Noone should actaully call them as the radio layer for OT gets a custom implementation for this configuration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure how to add this file, but if the API isn't called from other subsystems, I can leave them as NULL. Isn't some shell command able to call these?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK they're not.

* Copyright (c) 2019, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a problem, AFAIK no license other than Apache-2.0 is allowed in Zephyr tree.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I figured that much. If this blocks the PR, I could move this into the zephyrproject/openthread repo.
But then the code is split across repositories.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's also an option, although still not perfect, as we're aiming to keep the fork as close to the original repo as possible. Pehrpas you could ask the OT community if they're if we reuse parts of the code under Zephyr license? It seems that there's not that much overlap between the original file and the version you've posted here.

Copy link
Contributor Author

@pdgendt pdgendt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rlubos Thanks for the review, I'm going to clean it up and I'll also try to add the HDCL (UART) so it's easier to test with an USB dongle.

See my responses for more information.

Comment on lines +140 to +148
.get_capabilities = ot_spinel_get_capabilities,
.cca = ot_spinel_cca,
.set_channel = ot_spinel_set_channel,
.filter = ot_spinel_filter,
.set_txpower = ot_spinel_set_txpower,
.tx = ot_spinel_tx,
.start = ot_spinel_start,
.stop = ot_spinel_stop,
.configure = ot_spinel_configure,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure how to add this file, but if the API isn't called from other subsystems, I can leave them as NULL. Isn't some shell command able to call these?

endif()

if(CONFIG_OPENTHREAD_HOSTPROCESSOR)
# Inject our platform spinel implementation
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OpenThread build system has private compile definitions for each library. This was the easiest way to make sure exactly the same definitions are set.
Because of the C++ template for the RadioSpinel instance, which is header-only.

#ifdef CONFIG_OPENTHREAD_HOSTPROCESSOR_SPI
#include "spi_interface.hpp"

typedef ot::Spinel::RadioSpinel<SpiInterface, RadioSpinelContext> PlatformSpiRadioSpinel;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Yes, kind of, we could try to re-implement the entire Spinel protocol, however this would be almost impossible to maintain.
  2. Yes

{
OT_UNUSED_VARIABLE(aInstance);

SuccessOrDie(RadioSpinelInstance().GetIeeeEui64(aIeeeEui64));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some caveat here, the included radio_spinel_impl.hpp also uses these functions a lot.

* Copyright (c) 2019, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I figured that much. If this blocks the PR, I could move this into the zephyrproject/openthread repo.
But then the code is split across repositories.

@github-actions
Copy link

github-actions bot commented Sep 7, 2022

This pull request has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this pull request will automatically be closed in 14 days. Note, that you can always re-open a closed pull request at any time.

@github-actions github-actions bot added the Stale label Sep 7, 2022
@github-actions github-actions bot closed this Sep 21, 2022
@pdgendt
Copy link
Contributor Author

pdgendt commented Sep 21, 2022

I'm going to pick this up again later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants