1 // Copyright 2020 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 #pragma once
15 
16 #include <cstdint>
17 
18 #include "pw_bytes/span.h"
19 #include "pw_chrono/system_clock.h"
20 #include "pw_i2c/address.h"
21 #include "pw_status/status.h"
22 
23 namespace pw::i2c {
24 
25 // Base driver interface for I2C initiating I2C transactions in a thread safe
26 // manner. Other documentation sources may call this style of interface an I2C
27 // "master", "central" or "controller".
28 //
29 // The Initiator is not required to support 10bit addressing. If only 7bit
30 // addressing is supported, the Initiator will assert when given an address
31 // which is out of 7bit address range.
32 //
33 // The implementer of this pure virtual interface is responsible for ensuring
34 // thread safety and enabling functionality such as initialization,
35 // configuration, enabling/disabling, unsticking SDA, and detecting device
36 // address registration collisions.
37 class Initiator {
38  public:
39   virtual ~Initiator() = default;
40 
41   // Write bytes and then read bytes as either one atomic or two independent I2C
42   // transaction. If the I2C bus is a multi-initiator bus then the implementer
43   // MUST ensure it is a single atomic I2C transaction.
44   // The signal on the bus should appear as follows:
45   // 1) Write Only:
46   //   START + I2C Address + WRITE(0) + TX_BUFFER_BYTES + STOP
47   // 2) Read Only:
48   //   START + I2C Address + READ(1) + RX_BUFFER_BYTES + STOP
49   // 3A) Write + Read (atomic):
50   //   START + I2C Address + WRITE(0) + TX_BUFFER_BYTES +
51   //   START + I2C Address + READ(1) + RX_BUFFER_BYTES + STOP
52   // 3B) Write + Read (separate):
53   //   START + I2C Address + WRITE(0) + TX_BUFFER_BYTES + STOP
54   //   START + I2C Address + READ(1) + RX_BUFFER_BYTES + STOP
55   //
56   // The timeout defines the minimum duration one may block waiting for both
57   // exclusive bus access and the completion of the I2C transaction.
58   //
59   // Preconditions:
60   // The Address must be supported by the Initiator, i.e. do not use a 10
61   //     address if the Initiator only supports 7 bit. This will assert.
62   //
63   // Returns:
64   // Ok - Success.
65   // InvalidArgument - device_address is larger than the 10 bit address space.
66   // DeadlineExceeded - Was unable to acquire exclusive Initiator access
67   //   and complete the I2C transaction in time.
68   // Unavailable - NACK condition occurred, meaning the addressed device did
69   //   not respond or was unable to process the request.
70   // FailedPrecondition - The interface is not currently initialized and/or
71   //    enabled.
WriteReadFor(Address device_address,ConstByteSpan tx_buffer,ByteSpan rx_buffer,chrono::SystemClock::duration for_at_least)72   Status WriteReadFor(Address device_address,
73                       ConstByteSpan tx_buffer,
74                       ByteSpan rx_buffer,
75                       chrono::SystemClock::duration for_at_least) {
76     return DoWriteReadFor(device_address, tx_buffer, rx_buffer, for_at_least);
77   }
WriteReadFor(Address device_address,const void * tx_buffer,size_t tx_size_bytes,void * rx_buffer,size_t rx_size_bytes,chrono::SystemClock::duration for_at_least)78   Status WriteReadFor(Address device_address,
79                       const void* tx_buffer,
80                       size_t tx_size_bytes,
81                       void* rx_buffer,
82                       size_t rx_size_bytes,
83                       chrono::SystemClock::duration for_at_least) {
84     return WriteReadFor(
85         device_address,
86         std::span(static_cast<const std::byte*>(tx_buffer), tx_size_bytes),
87         std::span(static_cast<std::byte*>(rx_buffer), rx_size_bytes),
88         for_at_least);
89   }
90 
91   // Write bytes. The signal on the bus should appear as follows:
92   //   START + I2C Address + WRITE(0) + TX_BUFFER_BYTES + STOP
93   //
94   // The timeout defines the minimum duration one may block waiting for both
95   // exclusive bus access and the completion of the I2C transaction.
96   //
97   // Preconditions:
98   // The Address must be supported by the Initiator, i.e. do not use a 10
99   //     address if the Initiator only supports 7 bit. This will assert.
100   //
101   // Returns:
102   // Ok - Success.
103   // InvalidArgument - device_address is larger than the 10 bit address space.
104   // DeadlineExceeded - Was unable to acquire exclusive Initiator access
105   //   and complete the I2C transaction in time.
106   // Unavailable - NACK condition occurred, meaning the addressed device did
107   //   not respond or was unable to process the request.
108   // FailedPrecondition - The interface is not currently initialized and/or
109   //    enabled.
WriteFor(Address device_address,ConstByteSpan tx_buffer,chrono::SystemClock::duration for_at_least)110   Status WriteFor(Address device_address,
111                   ConstByteSpan tx_buffer,
112                   chrono::SystemClock::duration for_at_least) {
113     return WriteReadFor(device_address, tx_buffer, ByteSpan(), for_at_least);
114   }
WriteFor(Address device_address,const void * tx_buffer,size_t tx_size_bytes,chrono::SystemClock::duration for_at_least)115   Status WriteFor(Address device_address,
116                   const void* tx_buffer,
117                   size_t tx_size_bytes,
118                   chrono::SystemClock::duration for_at_least) {
119     return WriteFor(
120         device_address,
121         std::span(static_cast<const std::byte*>(tx_buffer), tx_size_bytes),
122         for_at_least);
123   }
124 
125   // Read bytes. The signal on the bus should appear as follows:
126   //   START + I2C Address + READ(1) + RX_BUFFER_BYTES + STOP
127   //
128   // The timeout defines the minimum duration one may block waiting for both
129   // exclusive bus access and the completion of the I2C transaction.
130   //
131   // Preconditions:
132   // The Address must be supported by the Initiator, i.e. do not use a 10
133   //     address if the Initiator only supports 7 bit. This will assert.
134   //
135   // Returns:
136   // Ok - Success.
137   // InvalidArgument - device_address is larger than the 10 bit address space.
138   // DeadlineExceeded - Was unable to acquire exclusive Initiator access
139   //   and complete the I2C transaction in time.
140   // Unavailable - NACK condition occurred, meaning the addressed device did
141   //   not respond or was unable to process the request.
142   // FailedPrecondition - The interface is not currently initialized and/or
143   //    enabled.
ReadFor(Address device_address,ByteSpan rx_buffer,chrono::SystemClock::duration for_at_least)144   Status ReadFor(Address device_address,
145                  ByteSpan rx_buffer,
146                  chrono::SystemClock::duration for_at_least) {
147     return WriteReadFor(
148         device_address, ConstByteSpan(), rx_buffer, for_at_least);
149   }
ReadFor(Address device_address,void * rx_buffer,size_t rx_size_bytes,chrono::SystemClock::duration for_at_least)150   Status ReadFor(Address device_address,
151                  void* rx_buffer,
152                  size_t rx_size_bytes,
153                  chrono::SystemClock::duration for_at_least) {
154     return ReadFor(device_address,
155                    std::span(static_cast<std::byte*>(rx_buffer), rx_size_bytes),
156                    for_at_least);
157   }
158 
159   // Probes the device for an I2C ACK after only writing the address.
160   // This is done by attempting to read a single byte from the specified device.
161   //
162   // The timeout defines the minimum duration one may block waiting for both
163   // exclusive bus access and the completion of the I2C transaction.
164   //
165   // Preconditions:
166   // The Address must be supported by the Initiator, i.e. do not use a 10
167   //     address if the Initiator only supports 7 bit. This will assert.
168   //
169   // Returns:
170   // Ok - Success.
171   // InvalidArgument - device_address is larger than the 10 bit address space.
172   // DeadlineExceeded - Was unable to acquire exclusive Initiator access
173   //   and complete the I2C transaction in time.
174   // Unavailable - NACK condition occurred, meaning the addressed device did
175   //   not respond or was unable to process the request.
176   // FailedPrecondition - The interface is not currently initialized and/or
177   //    enabled.
ProbeDeviceFor(Address device_address,chrono::SystemClock::duration for_at_least)178   Status ProbeDeviceFor(Address device_address,
179                         chrono::SystemClock::duration for_at_least) {
180     std::byte ignored_buffer[1] = {};  // Read a dummy byte to probe.
181     return WriteReadFor(
182         device_address, ConstByteSpan(), ignored_buffer, for_at_least);
183   }
184 
185  private:
186   virtual Status DoWriteReadFor(Address device_address,
187                                 ConstByteSpan tx_buffer,
188                                 ByteSpan rx_buffer,
189                                 chrono::SystemClock::duration for_at_least) = 0;
190 };
191 
192 }  // namespace pw::i2c
193