| // 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 std::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 std::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 std::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 for_at_least) { |
| // 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, 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, order_); |
| break; |
| |
| case 4: |
| PutRegisterData32InByteBuilder(builder, register_data, order_); |
| 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, for_at_least); |
| } |
| |
| Status RegisterDevice::ReadRegisters( |
| uint32_t register_address, |
| ByteSpan return_data, |
| chrono::SystemClock::duration for_at_least) { |
| ByteBuffer<sizeof(register_address)> byte_buffer; |
| |
| PutRegisterAddressInByteBuilder( |
| byte_buffer, 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(), |
| for_at_least); |
| } |
| |
| } // namespace i2c |
| } // namespace pw |