1 // Copyright 2024 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 #pragma once
15
16 #include "pw_assert/assert.h"
17 #include "pw_channel/channel.h"
18
19 namespace pw::channel {
20
21 // Channel functions that must be implemented inline after AnyChannel.
22
23 template <DataType kDataType, Property... kProperties>
is_read_open()24 constexpr bool Channel<kDataType, kProperties...>::is_read_open() const {
25 return readable() && static_cast<const AnyChannel&>(*this).is_read_open();
26 }
27
28 template <DataType kDataType, Property... kProperties>
is_write_open()29 constexpr bool Channel<kDataType, kProperties...>::is_write_open() const {
30 return writable() && static_cast<const AnyChannel&>(*this).is_write_open();
31 }
32
33 template <DataType kDataType, Property... kProperties>
34 async2::Poll<Result<multibuf::MultiBuf>>
PendRead(async2::Context & cx)35 Channel<kDataType, kProperties...>::PendRead(async2::Context& cx) {
36 static_assert(readable(), "PendRead may only be called on readable channels");
37 return static_cast<AnyChannel&>(*this).PendRead(cx);
38 }
39
40 template <DataType kDataType, Property... kProperties>
PendReadyToWrite(pw::async2::Context & cx)41 async2::Poll<Status> Channel<kDataType, kProperties...>::PendReadyToWrite(
42 pw::async2::Context& cx) {
43 static_assert(writable(),
44 "PendReadyToWrite may only be called on writable channels");
45 return static_cast<AnyChannel&>(*this).PendReadyToWrite(cx);
46 }
47 template <DataType kDataType, Property... kProperties>
48 async2::Poll<std::optional<multibuf::MultiBuf>>
PendAllocateWriteBuffer(async2::Context & cx,size_t min_bytes)49 Channel<kDataType, kProperties...>::PendAllocateWriteBuffer(async2::Context& cx,
50 size_t min_bytes) {
51 static_assert(
52 writable(),
53 "PendAllocateWriteBuffer may only be called on writable channels");
54 return static_cast<AnyChannel&>(*this).PendAllocateWriteBuffer(cx, min_bytes);
55 }
56 template <DataType kDataType, Property... kProperties>
StageWrite(multibuf::MultiBuf && data)57 Status Channel<kDataType, kProperties...>::StageWrite(
58 multibuf::MultiBuf&& data) {
59 static_assert(writable(),
60 "StageWrite may only be called on writable channels");
61 return static_cast<AnyChannel&>(*this).StageWrite(std::move(data));
62 }
63 template <DataType kDataType, Property... kProperties>
PendWrite(async2::Context & cx)64 async2::Poll<Status> Channel<kDataType, kProperties...>::PendWrite(
65 async2::Context& cx) {
66 static_assert(writable(),
67 "PendWrite may only be called on writable channels");
68 return static_cast<AnyChannel&>(*this).PendWrite(cx);
69 }
70
71 template <DataType kDataType, Property... kProperties>
PendClose(async2::Context & cx)72 async2::Poll<pw::Status> Channel<kDataType, kProperties...>::PendClose(
73 async2::Context& cx) {
74 return static_cast<AnyChannel&>(*this).PendClose(cx);
75 }
76
77 namespace internal {
78
79 template <DataType kDataType, Property... kProperties>
80 class BaseChannelImpl : public AnyChannel {
81 public:
82 using Channel = Channel<kDataType, kProperties...>;
83
channel()84 Channel& channel() { return *this; }
channel()85 const Channel& channel() const { return *this; }
86
87 using Channel::as;
88 using Channel::IgnoreDatagramBoundaries;
89
90 // Use the Channel's version of the AnyChannel API, so unsupported methods are
91 // disabled.
92 using Channel::PendAllocateWriteBuffer;
93 using Channel::PendClose;
94 using Channel::PendRead;
95 using Channel::PendReadyToWrite;
96 using Channel::PendWrite;
97 using Channel::StageWrite;
98
99 private:
100 friend class ChannelImpl<kDataType, kProperties...>;
101
BaseChannelImpl()102 constexpr BaseChannelImpl()
103 : AnyChannel(kDataType, (static_cast<uint8_t>(kProperties) | ...)) {}
104 };
105
106 } // namespace internal
107
108 // Defines a channel specialization with the specified properties. ChannelImpl
109 // is specialized for each supported combination of byte/datagram and read/write
110 // attribute. Invalid combinations fall back to the default implementation and
111 // fail a static_assert.
112 //
113 // Channel is specialized to implement unsupported operations in a standard way.
114 // That way, extending a channel only requires implementing supported functions.
115 #define _PW_CHANNEL_IMPL(type, ...) \
116 template <> \
117 class ChannelImpl<DataType::__VA_ARGS__> \
118 : public internal::BaseChannelImpl<DataType::__VA_ARGS__> { \
119 protected: \
120 explicit constexpr ChannelImpl() = default; \
121 \
122 private: \
123 _PW_CHANNEL_##type \
124 }
125
126 // Macros that stub out read/write if unsupported.
127 #define _PW_CHANNEL_READ_WRITE
128
129 #define _PW_CHANNEL_WRITE_ONLY \
130 async2::Poll<Result<multibuf::MultiBuf>> DoPendRead(async2::Context&) \
131 final { \
132 return async2::Ready(Result<multibuf::MultiBuf>(Status::Unimplemented())); \
133 }
134
135 #define _PW_CHANNEL_READ_ONLY \
136 async2::Poll<Status> DoPendReadyToWrite(async2::Context&) final { \
137 return Status::Unimplemented(); \
138 } \
139 async2::Poll<std::optional<multibuf::MultiBuf>> DoPendAllocateWriteBuffer( \
140 async2::Context&, size_t) final { \
141 PW_ASSERT(false); /* shouldn't be called on non-writeable channels */ \
142 } \
143 Status DoStageWrite(multibuf::MultiBuf&&) final { \
144 return Status::Unimplemented(); \
145 } \
146 async2::Poll<Status> DoPendWrite(async2::Context&) final { \
147 return async2::Ready(Status::Unimplemented()); \
148 }
149
150 // Generate specializations for the supported channel types.
151 _PW_CHANNEL_IMPL(READ_WRITE, kByte, kReliable, kReadable, kWritable);
152 _PW_CHANNEL_IMPL(READ_ONLY, kByte, kReliable, kReadable);
153 _PW_CHANNEL_IMPL(WRITE_ONLY, kByte, kReliable, kWritable);
154
155 _PW_CHANNEL_IMPL(READ_WRITE, kByte, kReadable, kWritable);
156 _PW_CHANNEL_IMPL(READ_ONLY, kByte, kReadable);
157 _PW_CHANNEL_IMPL(WRITE_ONLY, kByte, kWritable);
158
159 _PW_CHANNEL_IMPL(READ_WRITE, kDatagram, kReliable, kReadable, kWritable);
160 _PW_CHANNEL_IMPL(READ_ONLY, kDatagram, kReliable, kReadable);
161 _PW_CHANNEL_IMPL(WRITE_ONLY, kDatagram, kReliable, kWritable);
162
163 _PW_CHANNEL_IMPL(READ_WRITE, kDatagram, kReadable, kWritable);
164 _PW_CHANNEL_IMPL(READ_ONLY, kDatagram, kReadable);
165 _PW_CHANNEL_IMPL(WRITE_ONLY, kDatagram, kWritable);
166
167 #undef _PW_CHANNEL_IMPL
168 #undef _PW_CHANNEL_READ_WRITE
169 #undef _PW_CHANNEL_READ_ONLY
170 #undef _PW_CHANNEL_WRITE_ONLY
171
172 } // namespace pw::channel
173