1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_stream_shmem_mcuxpresso/stream.h"
16
17 #include <atomic>
18 #include <cstdint>
19
20 namespace pw::stream {
21 namespace {
22
23 constexpr uint32_t kMuRegDataSize = 0;
24 constexpr uint32_t kMuRegDataCopied = 1;
25
26 } // namespace
27
~ShmemMcuxpressoStream()28 ShmemMcuxpressoStream::~ShmemMcuxpressoStream() { Disable(); }
29
Enable()30 void ShmemMcuxpressoStream::Enable() {
31 MU_Init(base_);
32 MU_EnableInterrupts(base_,
33 kMU_Tx0EmptyInterruptEnable | kMU_Rx0FullInterruptEnable |
34 kMU_Rx1FullInterruptEnable);
35 }
36
Disable()37 void ShmemMcuxpressoStream::Disable() {
38 MU_DisableInterrupts(base_,
39 kMU_Tx0EmptyInterruptEnable |
40 kMU_Rx0FullInterruptEnable |
41 kMU_Rx1FullInterruptEnable);
42 MU_Deinit(base_);
43 }
44
DoRead(ByteSpan data)45 StatusWithSize ShmemMcuxpressoStream::DoRead(ByteSpan data) {
46 read_semaphore_.acquire();
47
48 const uint32_t msg_len = MU_ReceiveMsgNonBlocking(base_, kMuRegDataSize);
49 StatusWithSize result(msg_len);
50
51 if (msg_len > shared_read_buffer_.size()) {
52 result = StatusWithSize::Internal();
53 } else if (msg_len > data.size()) {
54 result = StatusWithSize::InvalidArgument();
55 } else {
56 std::copy(shared_read_buffer_.begin(),
57 shared_read_buffer_.begin() + msg_len,
58 data.begin());
59 // Ensure all data is read before MU message is written.
60 std::atomic_thread_fence(std::memory_order_release);
61 }
62
63 // Ack we're done with our copy. Use blocking send as the other side will
64 // process the message directly in ISR.
65 MU_SendMsg(base_, kMuRegDataCopied, msg_len);
66
67 // Turn back on Rx0 interrupt, which will unblock next read.
68 MU_EnableInterrupts(base_, kMU_Rx0FullInterruptEnable);
69
70 return result;
71 }
72
DoWrite(ConstByteSpan data)73 Status ShmemMcuxpressoStream::DoWrite(ConstByteSpan data) {
74 if (data.size() > shared_write_buffer_.size()) {
75 return Status::InvalidArgument();
76 }
77 write_semaphore_.acquire();
78
79 std::copy(data.begin(), data.end(), shared_write_buffer_.begin());
80
81 // Ensure MU message is written after shared buffer is populated.
82 std::atomic_thread_fence(std::memory_order_release);
83
84 MU_SendMsgNonBlocking(base_, kMuRegDataSize, data.size());
85
86 write_done_semaphore_.acquire();
87
88 MU_EnableInterrupts(base_, kMU_Tx0EmptyInterruptEnable);
89
90 return OkStatus();
91 }
92
HandleInterrupt()93 void ShmemMcuxpressoStream::HandleInterrupt() {
94 const uint32_t flags = MU_GetInterruptsPending(base_);
95 if (flags & kMU_Tx0EmptyFlag) {
96 write_semaphore_.release();
97 MU_DisableInterrupts(base_, kMU_Tx0EmptyInterruptEnable);
98 }
99 if (flags & kMU_Rx0FullFlag) {
100 read_semaphore_.release();
101 MU_DisableInterrupts(base_, kMU_Rx0FullInterruptEnable);
102 }
103 if (flags & kMU_Rx1FullFlag) {
104 write_done_semaphore_.release();
105 MU_ReceiveMsgNonBlocking(base_, kMuRegDataCopied);
106 }
107 }
108
109 } // namespace pw::stream
110