| // 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 |