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