// 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(register_address)); break; case RegisterAddressSize::k2Bytes: byte_builder.PutUint16(static_cast(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( 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( 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(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 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