1 /*
2 * Copyright (c) 2024, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "platform-posix.h"
30
31 #include "posix/platform/spinel_manager.hpp"
32
33 #include "common/code_utils.hpp"
34 #include "common/new.hpp"
35 #include "lib/spinel/spinel_driver.hpp"
36 #include "posix/platform/hdlc_interface.hpp"
37 #include "posix/platform/radio_url.hpp"
38 #include "posix/platform/spi_interface.hpp"
39 #include "posix/platform/spinel_driver_getter.hpp"
40 #include "posix/platform/vendor_interface.hpp"
41
42 static ot::Posix::SpinelManager sSpinelManager;
43
44 namespace ot {
45 namespace Posix {
46
47 // Implements `GetSpinelDriver` in spinel_driver_getter.hpp for external access to SpinelDriver
GetSpinelDriver(void)48 Spinel::SpinelDriver &GetSpinelDriver(void) { return sSpinelManager.GetSpinelDriver(); }
49
GetSpinelManager(void)50 SpinelManager &SpinelManager::GetSpinelManager(void) { return sSpinelManager; }
51
SpinelManager(void)52 SpinelManager::SpinelManager(void)
53 : mUrl(nullptr)
54 , mSpinelDriver()
55 , mSpinelInterface(nullptr)
56 {
57 }
58
~SpinelManager(void)59 SpinelManager::~SpinelManager(void) { Deinit(); }
60
Init(const char * aUrl)61 CoprocessorType SpinelManager::Init(const char *aUrl)
62 {
63 bool swReset;
64 spinel_iid_t iidList[Spinel::kSpinelHeaderMaxNumIid];
65 CoprocessorType mode;
66
67 mUrl.Init(aUrl);
68 VerifyOrDie(mUrl.GetPath() != nullptr, OT_EXIT_INVALID_ARGUMENTS);
69
70 GetIidListFromUrl(iidList);
71
72 #if OPENTHREAD_POSIX_VIRTUAL_TIME
73 VirtualTimeInit();
74 #endif
75
76 mSpinelInterface = CreateSpinelInterface(mUrl.GetProtocol());
77 VerifyOrDie(mSpinelInterface != nullptr, OT_EXIT_FAILURE);
78
79 swReset = !mUrl.HasParam("no-reset");
80
81 mode = mSpinelDriver.Init(*mSpinelInterface, swReset, iidList, OT_ARRAY_LENGTH(iidList));
82
83 otLogDebgPlat("instance init:%p - iid = %d", (void *)&mSpinelDriver, iidList[0]);
84
85 return mode;
86 }
87
Deinit(void)88 void SpinelManager::Deinit(void)
89 {
90 if (mSpinelInterface != nullptr)
91 {
92 mSpinelInterface->Deinit();
93 mSpinelInterface = nullptr;
94 }
95
96 mSpinelDriver.Deinit();
97 }
98
CreateSpinelInterface(const char * aInterfaceName)99 Spinel::SpinelInterface *SpinelManager::CreateSpinelInterface(const char *aInterfaceName)
100 {
101 Spinel::SpinelInterface *interface;
102
103 if (aInterfaceName == nullptr)
104 {
105 DieNow(OT_ERROR_FAILED);
106 }
107 #if OPENTHREAD_POSIX_CONFIG_SPINEL_HDLC_INTERFACE_ENABLE
108 else if (HdlcInterface::IsInterfaceNameMatch(aInterfaceName))
109 {
110 interface = new (&mSpinelInterfaceRaw) HdlcInterface(mUrl);
111 }
112 #endif
113 #if OPENTHREAD_POSIX_CONFIG_SPINEL_SPI_INTERFACE_ENABLE
114 else if (Posix::SpiInterface::IsInterfaceNameMatch(aInterfaceName))
115 {
116 interface = new (&mSpinelInterfaceRaw) SpiInterface(mUrl);
117 }
118 #endif
119 #if OPENTHREAD_POSIX_CONFIG_SPINEL_VENDOR_INTERFACE_ENABLE
120 else if (VendorInterface::IsInterfaceNameMatch(aInterfaceName))
121 {
122 interface = new (&mSpinelInterfaceRaw) VendorInterface(mUrl);
123 }
124 #endif
125 else
126 {
127 otLogCritPlat("The Spinel interface name \"%s\" is not supported!", aInterfaceName);
128 DieNow(OT_ERROR_FAILED);
129 }
130
131 return interface;
132 }
133
GetIidListFromUrl(spinel_iid_t (& aIidList)[Spinel::kSpinelHeaderMaxNumIid])134 void SpinelManager::GetIidListFromUrl(spinel_iid_t (&aIidList)[Spinel::kSpinelHeaderMaxNumIid])
135 {
136 const char *iidString;
137 const char *iidListString;
138
139 memset(aIidList, SPINEL_HEADER_INVALID_IID, sizeof(aIidList));
140
141 iidString = mUrl.GetValue("iid");
142 iidListString = mUrl.GetValue("iid-list");
143
144 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
145 // First entry to the aIidList must be the IID of the host application.
146 VerifyOrDie(iidString != nullptr, OT_EXIT_INVALID_ARGUMENTS);
147 aIidList[0] = static_cast<spinel_iid_t>(atoi(iidString));
148
149 if (iidListString != nullptr)
150 {
151 // Convert string to an array of integers.
152 // Integer i is for traverse the iidListString.
153 // Integer j is for aIidList array offset location.
154 // First entry of aIidList is for host application iid hence j start from 1.
155 for (uint8_t i = 0, j = 1; iidListString[i] != '\0' && j < Spinel::kSpinelHeaderMaxNumIid; i++)
156 {
157 if (iidListString[i] == ',')
158 {
159 j++;
160 continue;
161 }
162
163 if (iidListString[i] < '0' || iidListString[i] > '9')
164 {
165 DieNow(OT_EXIT_INVALID_ARGUMENTS);
166 }
167 else
168 {
169 aIidList[j] = iidListString[i] - '0';
170 VerifyOrDie(aIidList[j] < Spinel::kSpinelHeaderMaxNumIid, OT_EXIT_INVALID_ARGUMENTS);
171 }
172 }
173 }
174 #else // !OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
175 VerifyOrDie(iidString == nullptr, OT_EXIT_INVALID_ARGUMENTS);
176 VerifyOrDie(iidListString == nullptr, OT_EXIT_INVALID_ARGUMENTS);
177 aIidList[0] = 0;
178 #endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
179 }
180
181 #if OPENTHREAD_POSIX_VIRTUAL_TIME
VirtualTimeInit(void)182 void SpinelManager::VirtualTimeInit(void)
183 {
184 // The last argument must be the node id
185 const char *nodeId = nullptr;
186
187 for (const char *arg = nullptr; (arg = mUrl.GetValue("forkpty-arg", arg)) != nullptr; nodeId = arg)
188 {
189 }
190
191 virtualTimeInit(static_cast<uint16_t>(atoi(nodeId)));
192 }
193 #endif
194
195 } // namespace Posix
196 } // namespace ot
197
platformSpinelManagerInit(const char * aUrl)198 CoprocessorType platformSpinelManagerInit(const char *aUrl) { return sSpinelManager.Init(aUrl); }
199
platformSpinelManagerDeinit(void)200 void platformSpinelManagerDeinit(void) { return sSpinelManager.Deinit(); }
201
202 #if OPENTHREAD_POSIX_VIRTUAL_TIME
virtualTimeSpinelProcess(otInstance * aInstance,const struct VirtualTimeEvent * aEvent)203 void virtualTimeSpinelProcess(otInstance *aInstance, const struct VirtualTimeEvent *aEvent)
204 {
205 OT_UNUSED_VARIABLE(aInstance);
206 ot::Posix::GetSpinelDriver().Process(aEvent);
207 }
208 #else
platformSpinelManagerProcess(otInstance * aInstance,const otSysMainloopContext * aContext)209 void platformSpinelManagerProcess(otInstance *aInstance, const otSysMainloopContext *aContext)
210 {
211 OT_UNUSED_VARIABLE(aInstance);
212
213 ot::Posix::GetSpinelDriver().Process(aContext);
214 }
215 #endif // OPENTHREAD_POSIX_VIRTUAL_TIME
216
platformSpinelManagerUpdateFdSet(otSysMainloopContext * aContext)217 void platformSpinelManagerUpdateFdSet(otSysMainloopContext *aContext)
218 {
219 sSpinelManager.GetSpinelInterface().UpdateFdSet(aContext);
220
221 if (ot::Posix::GetSpinelDriver().HasPendingFrame())
222 {
223 aContext->mTimeout.tv_sec = 0;
224 aContext->mTimeout.tv_usec = 0;
225 }
226 }
227