blob: 9c3b15e2950582d942ae702e1b39a87eab84a561 [file] [log] [blame]
// Copyright 2021 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_i2c/register_device.h"
#include "pw_assert/check.h"
#include "pw_bytes/byte_builder.h"
namespace pw {
namespace i2c {
namespace {
// Puts the register address data into the buffer based on the size of the
// register address.
void PutRegisterAddressInByteBuilder(
ByteBuilder& byte_builder,
const uint32_t register_address,
const endian order,
RegisterAddressSize register_address_size) {
// TODO(b/185952662): Simplify the call site by extending the byte builder
// and endian API.
switch (register_address_size) {
case RegisterAddressSize::k1Byte:
byte_builder.PutUint8(static_cast<uint8_t>(register_address));
break;
case RegisterAddressSize::k2Bytes:
byte_builder.PutUint16(static_cast<uint16_t>(register_address), order);
break;
case RegisterAddressSize::k4Bytes:
byte_builder.PutUint32(register_address, order);
break;
default:
PW_CRASH("Invalid address size being put in byte buffer");
}
}
void PutRegisterData16InByteBuilder(ByteBuilder& byte_builder,
ConstByteSpan register_data,
const endian order) {
uint32_t data_pointer_index = 0;
while (data_pointer_index < register_data.size()) {
const uint16_t data = *reinterpret_cast<const uint16_t*>(
register_data.data() + data_pointer_index);
byte_builder.PutUint16(data, order);
data_pointer_index += sizeof(data);
}
}
Status PutRegisterData32InByteBuilder(ByteBuilder& byte_builder,
ConstByteSpan register_data,
const endian order) {
uint32_t data_pointer_index = 0;
while (data_pointer_index < register_data.size()) {
const uint32_t data = *reinterpret_cast<const uint32_t*>(
register_data.data() + data_pointer_index);
byte_builder.PutUint32(data, order);
data_pointer_index += sizeof(data);
}
if (data_pointer_index == register_data.size()) {
return pw::OkStatus();
} else {
// The write data that was given doesn't align with the expected register
// data size.
return Status::InvalidArgument();
}
}
} // namespace
Status RegisterDevice::WriteRegisters(const uint32_t register_address,
ConstByteSpan register_data,
const size_t register_data_size,
ByteSpan buffer,
chrono::SystemClock::duration timeout) {
// Make sure the buffer is big enough to handle the address and data.
if (buffer.size() <
register_data.size() + static_cast<uint32_t>(register_address_size_)) {
return pw::Status::OutOfRange();
}
ByteBuilder builder = ByteBuilder(buffer);
PutRegisterAddressInByteBuilder(builder,
register_address,
register_address_order_,
register_address_size_);
switch (register_data_size) {
case 1:
builder.append(register_data.data(), register_data.size());
break;
case 2:
PutRegisterData16InByteBuilder(builder, register_data, data_order_);
break;
case 4:
PutRegisterData32InByteBuilder(builder, register_data, data_order_)
.IgnoreError(); // TODO(b/242598609): Handle Status properly
break;
default:
PW_CRASH("Invalid data size being put in byte buffer");
}
if (!builder.ok()) {
return pw::Status::Internal();
}
ConstByteSpan write_buffer(builder.data(), builder.size());
return WriteFor(write_buffer, timeout);
}
Status RegisterDevice::ReadRegisters(uint32_t register_address,
ByteSpan return_data,
chrono::SystemClock::duration timeout) {
ByteBuffer<sizeof(register_address)> byte_buffer;
PutRegisterAddressInByteBuilder(byte_buffer,
register_address,
register_address_order_,
register_address_size_);
if (!byte_buffer.ok()) {
return pw::Status::Internal();
}
return WriteReadFor(byte_buffer.data(),
byte_buffer.size(),
return_data.data(),
return_data.size(),
timeout);
}
} // namespace i2c
} // namespace pw