| // Copyright 2024 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #define PW_LOG_MODULE_NAME "pw_system" |
| |
| #include "pw_system/system.h" |
| |
| #include <utility> |
| |
| #include "pw_allocator/best_fit.h" |
| #include "pw_allocator/synchronized_allocator.h" |
| #include "pw_assert/check.h" |
| #include "pw_async2/allocate_task.h" |
| #include "pw_async2/pend_func_task.h" |
| #include "pw_log/log.h" |
| #include "pw_rpc/echo_service_pwpb.h" |
| #include "pw_sync/interrupt_spin_lock.h" |
| #include "pw_system/config.h" |
| #include "pw_system/device_service.h" |
| #include "pw_system/file_service.h" |
| #include "pw_system/internal/async_packet_io.h" |
| #include "pw_system/log.h" |
| #include "pw_system/thread_snapshot_service.h" |
| #include "pw_system/transfer_service.h" |
| #include "pw_system/work_queue.h" |
| #include "pw_system_private/threads.h" |
| #include "pw_thread/detached_thread.h" |
| |
| #if PW_SYSTEM_ENABLE_CRASH_HANDLER |
| #include "pw_system/crash_handler.h" |
| #include "pw_system/crash_snapshot.h" |
| #endif // PW_SYSTEM_ENABLE_CRASH_HANDLER |
| |
| namespace pw { |
| |
| void SystemStart(channel::ByteReaderWriter& io_channel) { |
| System().Init(io_channel); |
| |
| system::StartScheduler(); |
| } |
| |
| namespace system { |
| namespace { |
| |
| using pw::allocator::BestFitAllocator; |
| using pw::allocator::SynchronizedAllocator; |
| |
| // TODO: b/349654108 - Standardize component declaration and initialization. |
| alignas(internal::PacketIO) std::byte packet_io_[sizeof(internal::PacketIO)]; |
| |
| internal::PacketIO& InitializePacketIoGlobal( |
| channel::ByteReaderWriter& io_channel) { |
| static std::byte buffer[256]; |
| internal::PacketIO* packet_io = new (packet_io_) internal::PacketIO( |
| io_channel, buffer, System().allocator(), System().rpc_server()); |
| return *packet_io; |
| } |
| |
| // Functions for quickly creating a task from a function. This functions could |
| // be moved to `pw::system::AsyncCore`. |
| template <typename Func> |
| [[nodiscard]] bool PostTaskFunction(Func&& func) { |
| async2::Task* task = async2::AllocateTask<async2::PendFuncTask<Func>>( |
| System().allocator(), std::forward<Func>(func)); |
| if (task == nullptr) { |
| return false; |
| } |
| System().dispatcher().Post(*task); |
| return true; |
| } |
| |
| template <typename Func> |
| void PostTaskFunctionOrCrash(Func&& func) { |
| PW_CHECK(PostTaskFunction(std::forward<Func>(func))); |
| } |
| |
| } // namespace |
| |
| async2::Dispatcher& AsyncCore::dispatcher() { |
| static async2::Dispatcher dispatcher; |
| return dispatcher; |
| } |
| |
| Allocator& AsyncCore::allocator() { |
| alignas(uintptr_t) static std::byte buffer[8192]; |
| static BestFitAllocator<> block_allocator(buffer); |
| static SynchronizedAllocator<::pw::sync::InterruptSpinLock> sync_allocator( |
| block_allocator); |
| return sync_allocator; |
| } |
| |
| bool AsyncCore::RunOnce(Function<void()>&& function) { |
| return GetWorkQueue().PushWork(std::move(function)).ok(); |
| } |
| |
| void AsyncCore::Init(channel::ByteReaderWriter& io_channel) { |
| #if PW_SYSTEM_ENABLE_CRASH_HANDLER |
| RegisterCrashHandler(); |
| #endif // PW_SYSTEM_ENABLE_CRASH_HANDLER |
| |
| PW_LOG_INFO("Initializing pw_system"); |
| |
| #if PW_SYSTEM_ENABLE_CRASH_HANDLER |
| if (HasCrashSnapshot()) { |
| PW_LOG_ERROR("=========================="); |
| PW_LOG_ERROR("======CRASH DETECTED======"); |
| PW_LOG_ERROR("=========================="); |
| PW_LOG_ERROR("Crash snapshots available."); |
| PW_LOG_ERROR( |
| "Run `device.get_crash_snapshots()` to download and clear the " |
| "snapshots."); |
| } else { |
| PW_LOG_DEBUG("No crash snapshot"); |
| } |
| #endif // PW_SYSTEM_ENABLE_CRASH_HANDLER |
| |
| PostTaskFunctionOrCrash(InitTask); |
| |
| // Initialize the packet_io subsystem |
| internal::PacketIO& packet_io = InitializePacketIoGlobal(io_channel); |
| packet_io.Start(System().dispatcher(), RpcThreadOptions()); |
| |
| thread::DetachedThread(DispatcherThreadOptions(), |
| [] { System().dispatcher().RunToCompletion(); }); |
| |
| thread::DetachedThread(WorkQueueThreadOptions(), GetWorkQueue()); |
| } |
| |
| async2::Poll<> AsyncCore::InitTask(async2::Context&) { |
| PW_LOG_INFO("Initializing pw_system"); |
| |
| const Status status = GetLogThread().OpenUnrequestedLogStream( |
| kLoggingRpcChannelId, System().rpc_server(), GetLogService()); |
| if (!status.ok()) { |
| PW_LOG_ERROR("Error opening unrequested log streams %d", |
| static_cast<int>(status.code())); |
| } |
| |
| System().rpc_server().RegisterService(GetLogService()); |
| thread::DetachedThread(system::LogThreadOptions(), GetLogThread()); |
| |
| static rpc::EchoService echo_service; |
| System().rpc_server().RegisterService(echo_service); |
| |
| RegisterDeviceService(System().rpc_server()); |
| |
| if (PW_SYSTEM_ENABLE_THREAD_SNAPSHOT_SERVICE != 0) { |
| RegisterThreadSnapshotService(System().rpc_server()); |
| } |
| |
| if (PW_SYSTEM_ENABLE_TRANSFER_SERVICE != 0) { |
| RegisterTransferService(System().rpc_server()); |
| RegisterFileService(System().rpc_server()); |
| thread::DetachedThread(system::TransferThreadOptions(), |
| GetTransferThread()); |
| InitTransferService(); |
| } |
| |
| PW_LOG_INFO("pw_system initialization complete"); |
| return async2::Ready(); |
| } |
| |
| } // namespace system |
| } // namespace pw |