Switch to using references where possible

This commit is contained in:
Allen Hill
2026-07-17 14:31:54 -07:00
parent 5208e083f3
commit 56936aed10
6 changed files with 96 additions and 96 deletions
+9 -9
View File
@@ -324,7 +324,7 @@ auto Bus::read(uint16_t address, Frame::Print print)
return in; return in;
} }
auto Bus::send(const Frame *out, Frame::Print print) -> Send { auto Bus::send(const Frame &out, Frame::Print print) -> Send {
struct errtype { struct errtype {
// Error enum is ordered such that a lower numeric value corresponds to // Error enum is ordered such that a lower numeric value corresponds to
// more success // more success
@@ -348,35 +348,35 @@ auto Bus::send(const Frame *out, Frame::Print print) -> Send {
goto handle_err; goto handle_err;
} }
handle.send<1>(static_cast<uint8_t>(out->is_unicast), no_parity); handle.send<1>(static_cast<uint8_t>(out.is_unicast), no_parity);
handle.send<12>(out->controller_addr, with_parity); handle.send<12>(out.controller_addr, with_parity);
if (auto serr = if (auto serr =
handle.send<12>(out->peripheral_addr, with_ack, out->is_unicast); handle.send<12>(out.peripheral_addr, with_ack, out.is_unicast);
serr == NAK) { serr == NAK) {
err.errno = NAK_ADDRESS; err.errno = NAK_ADDRESS;
goto handle_err; goto handle_err;
} }
if (auto serr = handle.send<4>(out->control, with_ack, out->is_unicast); if (auto serr = handle.send<4>(out.control, with_ack, out.is_unicast);
serr == NAK) { serr == NAK) {
err.errno = NAK_CONTROL; err.errno = NAK_CONTROL;
goto handle_err; goto handle_err;
} }
if (auto serr = handle.send<8>(out->length, with_ack, out->is_unicast); if (auto serr = handle.send<8>(out.length, with_ack, out.is_unicast);
serr == NAK) { serr == NAK) {
err.errno = NAK_MESSAGE_LENGTH; err.errno = NAK_MESSAGE_LENGTH;
goto handle_err; goto handle_err;
} }
for (uint8_t i = 0; i < out->length; i++) { for (uint8_t i = 0; i < out.length; i++) {
// Based on the µPD6708 datasheet, ACK bit for broadcast doesn't seem // Based on the µPD6708 datasheet, ACK bit for broadcast doesn't seem
// necessary (i.e. This deviates from the previous broadcast specific // necessary (i.e. This deviates from the previous broadcast specific
// function that sent an extra `1` bit after each byte/parity) // function that sent an extra `1` bit after each byte/parity)
// Explanation for why audio-group broadcast state report isn't working? // Explanation for why audio-group broadcast state report isn't working?
if (auto serr = handle.send<8>(out->data[i], with_ack, out->is_unicast); if (auto serr = handle.send<8>(out.data[i], with_ack, out.is_unicast);
serr == NAK) { serr == NAK) {
err.errno = NAK_DATA; err.errno = NAK_DATA;
err.val = i; err.val = i;
@@ -413,7 +413,7 @@ auto Bus::send(const Frame *out, Frame::Print print) -> Send {
} }
if (print.print) if (print.print)
out->print(print); out.print(print);
return err.errno; return err.errno;
} }
+1 -1
View File
@@ -81,7 +81,7 @@ public:
expected<std::unique_ptr<Frame>, Error::Read> read(uint16_t address, expected<std::unique_ptr<Frame>, Error::Read> read(uint16_t address,
Frame::Print print); Frame::Print print);
Error::Send send(const Frame *out, Frame::Print print); Error::Send send(const Frame &out, Frame::Print print);
private: private:
class Handle; class Handle;
+61 -61
View File
@@ -57,11 +57,11 @@ void CDChanger::init() {
cdtimer_init(this, &incrementTime_callback, &isPlaying_callback); cdtimer_init(this, &incrementTime_callback, &isPlaying_callback);
} }
void CDChanger::handle(const Frame *in, Frame *out) { void CDChanger::handle(const Frame &in, Frame &out) {
if (in->length < 4) if (in.length < 4)
return; // [Currently known] valid CDChanger frames have at least 4 bytes return; // [Currently known] valid CDChanger frames have at least 4 bytes
const uint8_t *data = &in->data[1]; const uint8_t *data = &in.data[1];
const auto from = static_cast<Device>(*data++); const auto from = static_cast<Device>(*data++);
/* const auto to = */ data++; /* const auto to = */ data++;
const auto action = static_cast<Action>(*data++); const auto action = static_cast<Action>(*data++);
@@ -75,25 +75,25 @@ void CDChanger::handle(const Frame *in, Frame *out) {
// Unicast to CD changer: bytes are (0x00, from, to, action, [extra...]). // Unicast to CD changer: bytes are (0x00, from, to, action, [extra...]).
switch (action) { switch (action) {
case Enable_Function_Req: case Enable_Function_Req:
out->is_unicast = true; out.is_unicast = true;
out->length = sizeof(function_change_resp); out.length = sizeof(function_change_resp);
memcpy(out->data, function_change_resp, sizeof(function_change_resp)); memcpy(out.data, function_change_resp, sizeof(function_change_resp));
out->data[3] = to_underlying(Enable_Function_Resp); out.data[3] = to_underlying(Enable_Function_Resp);
state = 0; state = 0;
flags2 = 0x80; flags2 = 0x80;
out->reaction = r_StatusReport; out.reaction = r_StatusReport;
break; break;
case Disable_Function_Req: case Disable_Function_Req:
// No change/response needed if we're already not playing // No change/response needed if we're already not playing
if (isPlaying()) { if (isPlaying()) {
stopPlaying(); stopPlaying();
out->length = sizeof(function_change_resp); out.length = sizeof(function_change_resp);
memcpy(out->data, function_change_resp, sizeof(function_change_resp)); memcpy(out.data, function_change_resp, sizeof(function_change_resp));
out->data[3] = to_underlying(Disable_Function_Resp); out.data[3] = to_underlying(Disable_Function_Resp);
state = 0; state = 0;
flags2 = 0x80; flags2 = 0x80;
out->is_unicast = true; out.is_unicast = true;
out->reaction = r_StatusReport; out.reaction = r_StatusReport;
} }
break; break;
case Eject: { case Eject: {
@@ -107,20 +107,20 @@ void CDChanger::handle(const Frame *in, Frame *out) {
if (static_cast<bool>(state & SEEKING)) { // FF/RW button released if (static_cast<bool>(state & SEEKING)) { // FF/RW button released
state &= ~SEEKING; state &= ~SEEKING;
} else { } else {
out->is_unicast = true; out.is_unicast = true;
{ {
const uint8_t msg[] = {0x00, to_underlying(Device::CD_CHANGER), const uint8_t msg[] = {0x00, to_underlying(Device::CD_CHANGER),
to_underlying(Device::CMD_SW), to_underlying(Device::CMD_SW),
to_underlying(Insertion), 0x01}; to_underlying(Insertion), 0x01};
out->length = sizeof(msg); out.length = sizeof(msg);
memcpy(out->data, msg, sizeof(msg)); memcpy(out.data, msg, sizeof(msg));
} }
out->reaction = r_SendOnly; out.reaction = r_SendOnly;
} }
break; break;
} }
case Initial_Report_Req: { case Initial_Report_Req: {
out->is_unicast = true; out.is_unicast = true;
// No knowledge/understanding of field meaning/interpretation // No knowledge/understanding of field meaning/interpretation
const uint8_t cdinitreport_resp[] = {0x00, const uint8_t cdinitreport_resp[] = {0x00,
to_underlying(Device::CD_CHANGER), to_underlying(Device::CD_CHANGER),
@@ -131,29 +131,29 @@ void CDChanger::handle(const Frame *in, Frame *out) {
0x10, 0x10,
0x01, 0x01,
0x01}; 0x01};
out->length = sizeof(cdinitreport_resp); out.length = sizeof(cdinitreport_resp);
memcpy(out->data, cdinitreport_resp, sizeof(cdinitreport_resp)); memcpy(out.data, cdinitreport_resp, sizeof(cdinitreport_resp));
out->reaction = r_SendOnly; out.reaction = r_SendOnly;
break; break;
} }
case Playback_Req: case Playback_Req:
out->data[0] = 0x00; out.data[0] = 0x00;
out->data[1] = to_underlying(Device::CD_CHANGER); out.data[1] = to_underlying(Device::CD_CHANGER);
out->data[2] = to_underlying(from); out.data[2] = to_underlying(from);
out->data[3] = to_underlying(Playback_Resp); out.data[3] = to_underlying(Playback_Resp);
out->length = WIRE_SIZE + 4; out.length = WIRE_SIZE + 4;
serialize(&out->data[4]); serialize(&out.data[4]);
out->is_unicast = true; out.is_unicast = true;
out->reaction = r_SendOnly; out.reaction = r_SendOnly;
break; break;
case Loading_Req: case Loading_Req:
out->data[0] = 0x00; out.data[0] = 0x00;
out->length = sizeof(cdloading_resp) + 1; out.length = sizeof(cdloading_resp) + 1;
memcpy(&out->data[1], cdloading_resp, sizeof(cdloading_resp)); memcpy(&out.data[1], cdloading_resp, sizeof(cdloading_resp));
out->data[2] = to_underlying(from); out.data[2] = to_underlying(from);
out->data[3] = to_underlying(Loading_Resp); out.data[3] = to_underlying(Loading_Resp);
out->is_unicast = true; out.is_unicast = true;
out->reaction = r_SendOnly; out.reaction = r_SendOnly;
break; break;
case Track_Seek_Up: case Track_Seek_Up:
state = SEEKING_TRACK; state = SEEKING_TRACK;
@@ -166,7 +166,7 @@ void CDChanger::handle(const Frame *in, Frame *out) {
flags2 = 0xc0; flags2 = 0xc0;
generateStatus(out, true, Device::CMD_SW); generateStatus(out, true, Device::CMD_SW);
media_action(MediaAction::Track_Next); media_action(MediaAction::Track_Next);
out->reaction = r_TrackChange; out.reaction = r_TrackChange;
break; break;
case Track_Seek_Down: case Track_Seek_Down:
state = SEEKING_TRACK; state = SEEKING_TRACK;
@@ -183,7 +183,7 @@ void CDChanger::handle(const Frame *in, Frame *out) {
secs = 0x7f; secs = 0x7f;
flags2 = 0xc0; flags2 = 0xc0;
generateStatus(out, true, Device::CMD_SW); generateStatus(out, true, Device::CMD_SW);
out->reaction = r_TrackChange; out.reaction = r_TrackChange;
break; break;
case Track_Fast_Forward: { case Track_Fast_Forward: {
state |= SEEKING; state |= SEEKING;
@@ -196,7 +196,7 @@ void CDChanger::handle(const Frame *in, Frame *out) {
media_action(MediaAction::Skip_Forward); media_action(MediaAction::Skip_Forward);
cdtimer_reset(); // Skipped to a whole/round sec; ensure next tick cdtimer_reset(); // Skipped to a whole/round sec; ensure next tick
// is ~1 sec from now // is ~1 sec from now
out->reaction = r_SendOnly; out.reaction = r_SendOnly;
break; break;
} }
case Track_Rewind: { case Track_Rewind: {
@@ -216,48 +216,48 @@ void CDChanger::handle(const Frame *in, Frame *out) {
media_action(MediaAction::Skip_Backward); media_action(MediaAction::Skip_Backward);
cdtimer_reset(); // Skipped to a whole/round sec; ensure next tick cdtimer_reset(); // Skipped to a whole/round sec; ensure next tick
// is ~1 sec from now // is ~1 sec from now
out->reaction = r_SendOnly; out.reaction = r_SendOnly;
break; break;
} }
case CD_Enable_Random: case CD_Enable_Random:
flags |= RANDOM; flags |= RANDOM;
generateStatus(out, true, Device::CMD_SW); generateStatus(out, true, Device::CMD_SW);
out->reaction = r_StatusReport; out.reaction = r_StatusReport;
break; break;
case CD_Disable_Random: case CD_Disable_Random:
flags &= ~RANDOM; flags &= ~RANDOM;
generateStatus(out, true, Device::CMD_SW); generateStatus(out, true, Device::CMD_SW);
out->reaction = r_StatusReport; out.reaction = r_StatusReport;
break; break;
case CD_Enable_Repeat: case CD_Enable_Repeat:
flags |= REPEAT; flags |= REPEAT;
generateStatus(out, true, Device::CMD_SW); generateStatus(out, true, Device::CMD_SW);
out->reaction = r_StatusReport; out.reaction = r_StatusReport;
break; break;
case CD_Disable_Repeat: case CD_Disable_Repeat:
flags &= ~REPEAT; flags &= ~REPEAT;
generateStatus(out, true, Device::CMD_SW); generateStatus(out, true, Device::CMD_SW);
out->reaction = r_StatusReport; out.reaction = r_StatusReport;
break; break;
case CD_Enable_Disk_Random: case CD_Enable_Disk_Random:
flags |= DISK_RANDOM; flags |= DISK_RANDOM;
generateStatus(out, true, Device::CMD_SW); generateStatus(out, true, Device::CMD_SW);
out->reaction = r_StatusReport; out.reaction = r_StatusReport;
break; break;
case CD_Disable_Disk_Random: case CD_Disable_Disk_Random:
flags &= ~DISK_RANDOM; flags &= ~DISK_RANDOM;
generateStatus(out, true, Device::CMD_SW); generateStatus(out, true, Device::CMD_SW);
out->reaction = r_StatusReport; out.reaction = r_StatusReport;
break; break;
case CD_Enable_Disk_Repeat: case CD_Enable_Disk_Repeat:
flags |= DISK_REPEAT; flags |= DISK_REPEAT;
generateStatus(out, true, Device::CMD_SW); generateStatus(out, true, Device::CMD_SW);
out->reaction = r_StatusReport; out.reaction = r_StatusReport;
break; break;
case CD_Disable_Disk_Repeat: case CD_Disable_Disk_Repeat:
flags &= ~DISK_REPEAT; flags &= ~DISK_REPEAT;
generateStatus(out, true, Device::CMD_SW); generateStatus(out, true, Device::CMD_SW);
out->reaction = r_StatusReport; out.reaction = r_StatusReport;
break; break;
default: break; default: break;
} }
@@ -308,12 +308,12 @@ std::unique_ptr<Frame> CDChanger::react(
[[fallthrough]]; [[fallthrough]];
case r_NormalizeState: case r_NormalizeState:
normalizeState(); normalizeState();
generateStatus(out, false, Device::STATUS); generateStatus(*out, false, Device::STATUS);
out->reaction = r_SendOnly; out->reaction = r_SendOnly;
break; break;
case r_StartPlaying: case r_StartPlaying:
normalizeState(); normalizeState();
generateStatus(out, false, Device::STATUS); generateStatus(*out, false, Device::STATUS);
out->reaction = r_BeganPlaying; out->reaction = r_BeganPlaying;
break; break;
case r_BeganPlaying: case r_BeganPlaying:
@@ -321,7 +321,7 @@ std::unique_ptr<Frame> CDChanger::react(
out->reaction = r_Nothing; out->reaction = r_Nothing;
break; break;
case r_StatusReport: case r_StatusReport:
generateStatus(out, false, Device::STATUS); generateStatus(*out, false, Device::STATUS);
out->reaction = r_SendOnly; out->reaction = r_SendOnly;
break; break;
case r_StateReport: [[fallthrough]]; case r_StateReport: [[fallthrough]];
@@ -337,7 +337,7 @@ std::unique_ptr<Frame> CDChanger::react(
return {}; return {};
} }
void CDChanger::enable(Frame *out) { void CDChanger::enable(Frame &out) {
if (!isPlaying()) { if (!isPlaying()) {
if (mins > TWODIGIT_MAX) if (mins > TWODIGIT_MAX)
mins = 0; mins = 0;
@@ -346,15 +346,15 @@ void CDChanger::enable(Frame *out) {
state = SEEKING | SEEKING_TRACK; state = SEEKING | SEEKING_TRACK;
flags2 = 0xc0; flags2 = 0xc0;
generateStatus(out, false, Device::STATUS); generateStatus(out, false, Device::STATUS);
out->reaction = r_StartPlaying; out.reaction = r_StartPlaying;
} }
} }
bool CDChanger::pending() { return cdtimer_pending(); } bool CDChanger::pending() { return cdtimer_pending(); }
void CDChanger::emit(Frame *out) { void CDChanger::emit(Frame &out) {
generateStatus(out, false, Device::STATUS); generateStatus(out, false, Device::STATUS);
out->reaction = r_StateReport; out.reaction = r_StateReport;
cdtimer_clear(); cdtimer_clear();
} }
@@ -411,15 +411,15 @@ void CDChanger::incrementTime() {
} }
// Used for changed status messages // Used for changed status messages
void CDChanger::generateStatus(Frame *status, bool is_unicast, void CDChanger::generateStatus(Frame &status, bool is_unicast,
Device to) const { Device to) const {
status->is_unicast = is_unicast; status.is_unicast = is_unicast;
if (!is_unicast) if (!is_unicast)
status->peripheral_addr = 0x1FF; status.peripheral_addr = 0x1FF;
status->control = 0xF; status.control = 0xF;
status->length = WIRE_SIZE + ((is_unicast) ? 4 : 3); status.length = WIRE_SIZE + ((is_unicast) ? 4 : 3);
uint8_t *data = status->data; uint8_t *data = status.data;
if (is_unicast) if (is_unicast)
*data++ = 0x00; *data++ = 0x00;
*data++ = to_underlying(Device::CD_CHANGER); *data++ = to_underlying(Device::CD_CHANGER);
+5 -5
View File
@@ -61,13 +61,13 @@ public:
static constexpr Device id = Device::CD_CHANGER; static constexpr Device id = Device::CD_CHANGER;
void init(); void init();
void handle(const Frame *in, Frame *out); void handle(const Frame &in, Frame &out);
std::unique_ptr<Frame> std::unique_ptr<Frame>
react(expected<std::unique_ptr<Frame>, detail::SendError> exp); react(expected<std::unique_ptr<Frame>, detail::SendError> exp);
void enable(Frame *out); void enable(Frame &out);
void disable(Frame *out); void disable(Frame &out);
static bool pending(); static bool pending();
void emit(Frame *out); void emit(Frame &out);
void incrementTime(); void incrementTime();
bool isPlaying() const; bool isPlaying() const;
#ifndef NDEBUG #ifndef NDEBUG
@@ -81,7 +81,7 @@ private:
void stopPlaying(); void stopPlaying();
void serialize(uint8_t *dst) const; void serialize(uint8_t *dst) const;
void setTime(uint8_t mins, uint8_t secs); void setTime(uint8_t mins, uint8_t secs);
void generateStatus(Frame *status, bool is_unicast, Device to) const; void generateStatus(Frame &status, bool is_unicast, Device to) const;
void normalizeState(); void normalizeState();
bool playing = false; bool playing = false;
+1 -1
View File
@@ -50,7 +50,7 @@ enum class Device : uint8_t {
template <class T> template <class T>
concept DeviceInterface = concept DeviceInterface =
requires { std::integral_constant<Device, T::id>{}; } && requires { std::integral_constant<Device, T::id>{}; } &&
requires(T dev, const Frame *in, Frame *out, requires(T dev, const Frame &in, Frame &out,
expected<std::unique_ptr<Frame>, detail::SendError> exp) { expected<std::unique_ptr<Frame>, detail::SendError> exp) {
dev.init(); dev.init();
dev.handle(in, out); dev.handle(in, out);
+19 -19
View File
@@ -63,9 +63,9 @@ public:
expected<std::unique_ptr<Frame>, detail::SendError> expected<std::unique_ptr<Frame>, detail::SendError>
send(std::unique_ptr<Frame> out, Frame::Print print = Frame::Print{}) { send(std::unique_ptr<Frame> out, Frame::Print print = Frame::Print{}) {
// To "forge" a controller_addr, instantiate a new/different Peripheral // To "forge" a controller_addr, instantiate a new/different Peripheral
stamp<Sender>(out.get()); stamp<Sender>(*out);
out->control = 0xF; out->control = 0xF;
auto err = bus.send(out.get(), print); auto err = bus.send(*out, print);
if (err != Error::Send{0}) if (err != Error::Send{0})
return unexpected{ return unexpected{
detail::SendError{out->owning_device, out->reaction, err}}; detail::SendError{out->owning_device, out->reaction, err}};
@@ -76,11 +76,11 @@ public:
#define PACK3(a, b, c) (((uint32_t)(a) << 16) | ((uint32_t)(b) << 8) | (c)) #define PACK3(a, b, c) (((uint32_t)(a) << 16) | ((uint32_t)(b) << 8) | (c))
// expected needed to distinguish don't vs can't respond // expected needed to distinguish don't vs can't respond
expected<std::unique_ptr<Frame>, Error::Read> route(const Frame *in) { expected<std::unique_ptr<Frame>, Error::Read> route(const Frame &in) {
using enum Device; using enum Device;
using enum Action; using enum Action;
if (is_muted() || in->length < 3) if (is_muted() || in.length < 3)
return {}; return {};
std::unique_ptr<Frame> out(new (std::nothrow) Frame); std::unique_ptr<Frame> out(new (std::nothrow) Frame);
@@ -94,17 +94,17 @@ public:
static const uint8_t lancheck_resp[] = {0x00, to_underlying(COMM_CTRL), static const uint8_t lancheck_resp[] = {0x00, to_underlying(COMM_CTRL),
to_underlying(LAN), 0xFF, 0xFF}; to_underlying(LAN), 0xFF, 0xFF};
stamp<Recipient>(out.get()); stamp<Recipient>(*out);
const uint8_t *data = in->data; const uint8_t *data = in.data;
const uint8_t b0 = *data++; const uint8_t b0 = *data++;
const uint8_t b1 = *data++; const uint8_t b1 = *data++;
const uint8_t b2 = *data++; const uint8_t b2 = *data++;
uint8_t b3 = 0; uint8_t b3 = 0;
if (in->length > 3) // the shortest known/valid messages are 3 bytes long if (in.length > 3) // the shortest known/valid messages are 3 bytes long
b3 = *data++; b3 = *data++;
if (!in->is_unicast) { if (!in.is_unicast) {
const auto from = b0; const auto from = b0;
const auto to = b1; const auto to = b1;
const auto action = b2; const auto action = b2;
@@ -140,7 +140,7 @@ public:
to_underlying(Advertise_Function)): { to_underlying(Advertise_Function)): {
auto enable_d = [](auto &d, auto &out) { d.enable(out); }; auto enable_d = [](auto &d, auto &out) { d.enable(out); };
((Devs::id == static_cast<Device>(b3) ((Devs::id == static_cast<Device>(b3)
? originate(std::get<Devs>(devices_), out.get(), enable_d) ? originate(std::get<Devs>(devices_), *out, enable_d)
: void()), : void()),
...); ...);
break; break;
@@ -160,8 +160,8 @@ public:
to_underlying(List_Functions_Req)): to_underlying(List_Functions_Req)):
case PACK3(COMMUNICATION_V2, COMM_CTRL, case PACK3(COMMUNICATION_V2, COMM_CTRL,
to_underlying(List_Functions_Req)): { to_underlying(List_Functions_Req)): {
controller_ = in->controller_addr; controller_ = in.controller_addr;
stamp<Recipient>(out.get()); // re-stamp now that controller_ is known stamp<Recipient>(*out); // re-stamp now that controller_ is known
out->is_unicast = true; out->is_unicast = true;
const uint8_t list_functions_resp[] = { const uint8_t list_functions_resp[] = {
0x00, to_underlying(COMM_CTRL), from, 0x00, to_underlying(COMM_CTRL), from,
@@ -174,10 +174,10 @@ public:
// case Restart_Lan: not handled // case Restart_Lan: not handled
default: break; default: break;
} }
} else if (in->peripheral_addr == address_ && b0 == 0x00) { } else if (in.peripheral_addr == address_ && b0 == 0x00) {
auto handle_d = [&](auto &d, auto &out) { d.handle(in, out); }; auto handle_d = [&](auto &d, auto &out) { d.handle(in, out); };
((Devs::id == static_cast<Device>(b2) ((Devs::id == static_cast<Device>(b2)
? originate(std::get<Devs>(devices_), out.get(), handle_d) ? originate(std::get<Devs>(devices_), *out, handle_d)
: void()), : void()),
...); ...);
} }
@@ -215,7 +215,7 @@ public:
return {}; return {};
} }
originate(dev, out.get(), [](auto &d, auto &out) { d.emit(out); }); originate(dev, *out, [](auto &d, auto &out) { d.emit(out); });
return out; return out;
}; };
@@ -249,15 +249,15 @@ public:
} }
private: private:
template <Party P> void stamp(Frame *out) const { template <Party P> void stamp(Frame &out) const {
if constexpr (P == Sender) if constexpr (P == Sender)
out->controller_addr = address_; out.controller_addr = address_;
else else
out->peripheral_addr = controller_; out.peripheral_addr = controller_;
} }
void originate(DeviceInterface auto &dev, Frame *out, auto &&fill) { void originate(DeviceInterface auto &dev, Frame &out, auto &&fill) {
out->owning_device = std::remove_reference_t<decltype(dev)>::id; out.owning_device = std::remove_reference_t<decltype(dev)>::id;
stamp<Recipient>(out); // default set FIRST; fill() may override stamp<Recipient>(out); // default set FIRST; fill() may override
fill(dev, out); fill(dev, out);
} }