1 // 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
17 #include "pw_assert/check.h"
18 #include "pw_bytes/byte_builder.h"
19
20 namespace pw {
21 namespace i2c {
22 namespace {
23
24 // Puts the register address data into the buffer based on the size of the
25 // register address.
PutRegisterAddressInByteBuilder(ByteBuilder & byte_builder,const uint32_t register_address,const endian order,RegisterAddressSize register_address_size)26 void PutRegisterAddressInByteBuilder(
27 ByteBuilder& byte_builder,
28 const uint32_t register_address,
29 const 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
PutRegisterData16InByteBuilder(ByteBuilder & byte_builder,ConstByteSpan register_data,const endian order)51 void PutRegisterData16InByteBuilder(ByteBuilder& byte_builder,
52 ConstByteSpan register_data,
53 const 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
PutRegisterData32InByteBuilder(ByteBuilder & byte_builder,ConstByteSpan register_data,const endian order)64 Status PutRegisterData32InByteBuilder(ByteBuilder& byte_builder,
65 ConstByteSpan register_data,
66 const 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
WriteRegisters(const uint32_t register_address,ConstByteSpan register_data,const size_t register_data_size,ByteSpan buffer,chrono::SystemClock::duration timeout)87 Status RegisterDevice::WriteRegisters(const uint32_t register_address,
88 ConstByteSpan register_data,
89 const size_t register_data_size,
90 ByteSpan buffer,
91 chrono::SystemClock::duration timeout) {
92 // Make sure the buffer is big enough to handle the address and data.
93 if (buffer.size() <
94 register_data.size() + static_cast<uint32_t>(register_address_size_)) {
95 return pw::Status::OutOfRange();
96 }
97
98 ByteBuilder builder = ByteBuilder(buffer);
99 PutRegisterAddressInByteBuilder(builder,
100 register_address,
101 register_address_order_,
102 register_address_size_);
103
104 switch (register_data_size) {
105 case 1:
106 builder.append(register_data.data(), register_data.size());
107 break;
108
109 case 2:
110 PutRegisterData16InByteBuilder(builder, register_data, data_order_);
111 break;
112
113 case 4:
114 PutRegisterData32InByteBuilder(builder, register_data, data_order_)
115 .IgnoreError(); // TODO: b/242598609 - Handle Status properly
116 break;
117
118 default:
119 PW_CRASH("Invalid data size being put in byte buffer");
120 }
121
122 if (!builder.ok()) {
123 return pw::Status::Internal();
124 }
125
126 ConstByteSpan write_buffer(builder.data(), builder.size());
127 return WriteFor(write_buffer, timeout);
128 }
129
ReadRegisters(uint32_t register_address,ByteSpan return_data,chrono::SystemClock::duration timeout)130 Status RegisterDevice::ReadRegisters(uint32_t register_address,
131 ByteSpan return_data,
132 chrono::SystemClock::duration timeout) {
133 ByteBuffer<sizeof(register_address)> byte_buffer;
134
135 PutRegisterAddressInByteBuilder(byte_buffer,
136 register_address,
137 register_address_order_,
138 register_address_size_);
139
140 if (!byte_buffer.ok()) {
141 return pw::Status::Internal();
142 }
143
144 return WriteReadFor(byte_buffer.data(),
145 byte_buffer.size(),
146 return_data.data(),
147 return_data.size(),
148 timeout);
149 }
150
151 } // namespace i2c
152 } // namespace pw
153