aboutsummaryrefslogtreecommitdiff
path: root/pw_stream_shmem_mcuxpresso/stream.cc
diff options
context:
space:
mode:
Diffstat (limited to 'pw_stream_shmem_mcuxpresso/stream.cc')
-rw-r--r--pw_stream_shmem_mcuxpresso/stream.cc109
1 files changed, 109 insertions, 0 deletions
diff --git a/pw_stream_shmem_mcuxpresso/stream.cc b/pw_stream_shmem_mcuxpresso/stream.cc
new file mode 100644
index 000000000..cd2badd33
--- /dev/null
+++ b/pw_stream_shmem_mcuxpresso/stream.cc
@@ -0,0 +1,109 @@
+// Copyright 2023 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.
+
+#include "pw_stream_shmem_mcuxpresso/stream.h"
+
+#include <atomic>
+#include <cstdint>
+
+namespace pw::stream {
+namespace {
+
+constexpr uint32_t kMuRegDataSize = 0;
+constexpr uint32_t kMuRegDataCopied = 1;
+
+} // namespace
+
+ShmemMcuxpressoStream::~ShmemMcuxpressoStream() { Disable(); }
+
+void ShmemMcuxpressoStream::Enable() {
+ MU_Init(base_);
+ MU_EnableInterrupts(base_,
+ kMU_Tx0EmptyInterruptEnable | kMU_Rx0FullInterruptEnable |
+ kMU_Rx1FullInterruptEnable);
+}
+
+void ShmemMcuxpressoStream::Disable() {
+ MU_DisableInterrupts(base_,
+ kMU_Tx0EmptyInterruptEnable |
+ kMU_Rx0FullInterruptEnable |
+ kMU_Rx1FullInterruptEnable);
+ MU_Deinit(base_);
+}
+
+StatusWithSize ShmemMcuxpressoStream::DoRead(ByteSpan data) {
+ read_semaphore_.acquire();
+
+ const uint32_t msg_len = MU_ReceiveMsgNonBlocking(base_, kMuRegDataSize);
+ StatusWithSize result(msg_len);
+
+ if (msg_len > shared_read_buffer_.size()) {
+ result = StatusWithSize::Internal();
+ } else if (msg_len > data.size()) {
+ result = StatusWithSize::InvalidArgument();
+ } else {
+ std::copy(shared_read_buffer_.begin(),
+ shared_read_buffer_.begin() + msg_len,
+ data.begin());
+ // Ensure all data is read before MU message is written.
+ std::atomic_thread_fence(std::memory_order_release);
+ }
+
+ // Ack we're done with our copy. Use blocking send as the other side will
+ // process the message directly in ISR.
+ MU_SendMsg(base_, kMuRegDataCopied, msg_len);
+
+ // Turn back on Rx0 interrupt, which will unblock next read.
+ MU_EnableInterrupts(base_, kMU_Rx0FullInterruptEnable);
+
+ return result;
+}
+
+Status ShmemMcuxpressoStream::DoWrite(ConstByteSpan data) {
+ if (data.size() > shared_write_buffer_.size()) {
+ return Status::InvalidArgument();
+ }
+ write_semaphore_.acquire();
+
+ std::copy(data.begin(), data.end(), shared_write_buffer_.begin());
+
+ // Ensure MU message is written after shared buffer is populated.
+ std::atomic_thread_fence(std::memory_order_release);
+
+ MU_SendMsgNonBlocking(base_, kMuRegDataSize, data.size());
+
+ write_done_semaphore_.acquire();
+
+ MU_EnableInterrupts(base_, kMU_Tx0EmptyInterruptEnable);
+
+ return OkStatus();
+}
+
+void ShmemMcuxpressoStream::HandleInterrupt() {
+ const uint32_t flags = MU_GetStatusFlags(base_);
+ if (flags & kMU_Tx0EmptyFlag) {
+ write_semaphore_.release();
+ MU_DisableInterrupts(base_, kMU_Tx0EmptyInterruptEnable);
+ }
+ if (flags & kMU_Rx0FullFlag) {
+ read_semaphore_.release();
+ MU_DisableInterrupts(base_, kMU_Rx0FullInterruptEnable);
+ }
+ if (flags & kMU_Rx1FullFlag) {
+ write_done_semaphore_.release();
+ MU_ReceiveMsgNonBlocking(base_, kMuRegDataCopied);
+ }
+}
+
+} // namespace pw::stream