blob: cae58ce75bca2ced9c831ac78a74c6fbdba28f21 [file] [log] [blame]
Kevin Zeng58b43d32021-04-16 00:36:16 -07001// Copyright 2021 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
15#include "pw_i2c/register_device.h"
16
Wyatt Hepler4298fd02021-03-19 15:08:10 -070017#include "pw_assert/check.h"
Kevin Zeng58b43d32021-04-16 00:36:16 -070018#include "pw_bytes/byte_builder.h"
19
20namespace pw {
21namespace i2c {
22namespace {
23
24// Puts the register address data into the buffer based on the size of the
25// register address.
26void PutRegisterAddressInByteBuilder(
27 ByteBuilder& byte_builder,
28 const uint32_t register_address,
29 const std::endian order,
30 RegisterAddressSize register_address_size) {
31 // TODO(b/185952662): Simplify the call site by extending the byte builder
32 // and endian API.
33 switch (register_address_size) {
34 case RegisterAddressSize::k1Byte:
35 byte_builder.PutUint8(static_cast<uint8_t>(register_address));
36 break;
37
38 case RegisterAddressSize::k2Bytes:
39 byte_builder.PutUint16(static_cast<uint16_t>(register_address), order);
40 break;
41
42 case RegisterAddressSize::k4Bytes:
43 byte_builder.PutUint32(register_address, order);
44 break;
45
46 default:
47 PW_CRASH("Invalid address size being put in byte buffer");
48 }
49}
50
51void PutRegisterData16InByteBuilder(ByteBuilder& byte_builder,
52 ConstByteSpan register_data,
53 const std::endian order) {
54 uint32_t data_pointer_index = 0;
55
56 while (data_pointer_index < register_data.size()) {
57 const uint16_t data = *reinterpret_cast<const uint16_t*>(
58 register_data.data() + data_pointer_index);
59 byte_builder.PutUint16(data, order);
60 data_pointer_index += sizeof(data);
61 }
62}
63
64Status PutRegisterData32InByteBuilder(ByteBuilder& byte_builder,
65 ConstByteSpan register_data,
66 const std::endian order) {
67 uint32_t data_pointer_index = 0;
68
69 while (data_pointer_index < register_data.size()) {
70 const uint32_t data = *reinterpret_cast<const uint32_t*>(
71 register_data.data() + data_pointer_index);
72 byte_builder.PutUint32(data, order);
73 data_pointer_index += sizeof(data);
74 }
75
76 if (data_pointer_index == register_data.size()) {
77 return pw::OkStatus();
78 } else {
79 // The write data that was given doesn't align with the expected register
80 // data size.
81 return Status::InvalidArgument();
82 }
83}
84
85} // namespace
86
87Status RegisterDevice::WriteRegisters(
88 const uint32_t register_address,
89 ConstByteSpan register_data,
90 const size_t register_data_size,
91 ByteSpan buffer,
92 chrono::SystemClock::duration for_at_least) {
93 // Make sure the buffer is big enough to handle the address and data.
94 if (buffer.size() <
95 register_data.size() + static_cast<uint32_t>(register_address_size_)) {
96 return pw::Status::OutOfRange();
97 }
98
99 ByteBuilder builder = ByteBuilder(buffer);
100 PutRegisterAddressInByteBuilder(
101 builder, register_address, order_, register_address_size_);
102
103 switch (register_data_size) {
104 case 1:
105 builder.append(register_data.data(), register_data.size());
106 break;
107
108 case 2:
109 PutRegisterData16InByteBuilder(builder, register_data, order_);
110 break;
111
112 case 4:
113 PutRegisterData32InByteBuilder(builder, register_data, order_);
114 break;
115
116 default:
117 PW_CRASH("Invalid data size being put in byte buffer");
118 }
119
120 if (!builder.ok()) {
121 return pw::Status::Internal();
122 }
123
124 ConstByteSpan write_buffer(builder.data(), builder.size());
125 return WriteFor(write_buffer, for_at_least);
126}
127
128Status RegisterDevice::ReadRegisters(
129 uint32_t register_address,
130 ByteSpan return_data,
131 chrono::SystemClock::duration for_at_least) {
132 ByteBuffer<sizeof(register_address)> byte_buffer;
133
134 PutRegisterAddressInByteBuilder(
135 byte_buffer, register_address, order_, register_address_size_);
136
137 if (!byte_buffer.ok()) {
138 return pw::Status::Internal();
139 }
140
141 return WriteReadFor(byte_buffer.data(),
142 byte_buffer.size(),
143 return_data.data(),
144 return_data.size(),
145 for_at_least);
146}
147
148} // namespace i2c
149} // namespace pw