xref: /aosp_15_r20/external/pigweed/pw_i2c_linux/public/pw_i2c_linux/initiator.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2023 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 "pw_bytes/span.h"
17 #include "pw_chrono/system_clock.h"
18 #include "pw_i2c/address.h"
19 #include "pw_i2c/initiator.h"
20 #include "pw_result/result.h"
21 #include "pw_status/status.h"
22 #include "pw_sync/lock_annotations.h"
23 #include "pw_sync/timed_mutex.h"
24 
25 namespace pw::i2c {
26 
27 /// Initiator interface implementation using the Linux userspace i2c-dev driver.
28 ///
29 /// Takes exclusive control of an I2C bus device (ex. "/dev/i2c-0"). The user is
30 /// responsible to open the device node prior to creating the initiator. The
31 /// file descriptor is closed when the initiator object is destroyed.
32 ///
33 /// The bus device must support the full I2C functionality. Users of the class
34 /// are encouraged to use the `OpenI2cBus` helper to ensure the bus is valid.
35 ///
36 /// Access to the bus is guarded by an internal mutex, so this initiator can be
37 /// safely used from multiple threads.
38 ///
39 class LinuxInitiator final : public Initiator {
40  public:
41   /// Open an I2C bus and validate that full I2C functionality is supported.
42   ///
43   /// @param[in] bus_path Path to the I2C bus device node.
44   ///
45   /// @retval OK              The device node was opened successfully.
46   /// @retval InvalidArgument Failed to open the device node or to validate I2C
47   ///                         functionality.
48   ///
49   /// @return The open file descriptor on success.
50   ///
51   static Result<int> OpenI2cBus(const char* bus_path);
52 
53   /// Construct an instantiator using an open file descriptor.
54   /// The file descriptor is closed during destruction.
55   ///
56   /// @param[in] fd Valid file descriptor for an I2C device node.
57   ///
58   LinuxInitiator(int fd);
59 
60   LinuxInitiator(const LinuxInitiator&) = delete;
61   LinuxInitiator& operator=(const LinuxInitiator&) = delete;
62 
63   ~LinuxInitiator() override;
64 
65  private:
66   /// Implement pw::i2c::Initiator with the following additional requriements:
67   ///  - Asserts that `device_address` is a 7-bit address.
68   ///  - At least one of `tx_buffer` or `rx_buffer` must be not empty.
69   ///    Otherwise, returns InvalidArgument.
70   ///
71   /// @note
72   /// The timeout is used both for getting an exclusive lock on the initiator
73   /// and for getting exclusive use of a multi-controller bus. If the timeout
74   /// is zero or negative, the transaction will only execute if there is no
75   /// contention at either level.
76   ///
77   Status DoWriteReadFor(Address device_address,
78                         ConstByteSpan tx_buffer,
79                         ByteSpan rx_buffer,
80                         chrono::SystemClock::duration timeout) override
81       PW_LOCKS_EXCLUDED(mutex_);
82 
83   Status DoWriteReadForLocked(uint8_t address,
84                               ConstByteSpan tx_buffer,
85                               ByteSpan rx_buffer,
86                               chrono::SystemClock::duration timeout)
87       PW_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
88 
89   /// The file descriptor for the i2c-dev device representing this bus.
90   const int fd_;
91 
92   /// This mutex is used to synchronize access across multiple retries.
93   sync::TimedMutex mutex_;
94 };
95 
96 }  // namespace pw::i2c
97